Merge branch 'master' of github.com:Budibase/budibase into develop

This commit is contained in:
Martin McKeaveney 2022-06-01 09:47:13 +01:00
commit 9be1bddf85
24 changed files with 162 additions and 42 deletions

View File

@ -11,7 +11,7 @@ sources:
- https://github.com/Budibase/budibase - https://github.com/Budibase/budibase
- https://budibase.com - https://budibase.com
type: application type: application
version: 0.2.9 version: 0.2.10
appVersion: 1.0.48 appVersion: 1.0.48
dependencies: dependencies:
- name: couchdb - name: couchdb

View File

@ -78,6 +78,10 @@ spec:
value: {{ .Values.services.objectStore.url }} value: {{ .Values.services.objectStore.url }}
- name: PORT - name: PORT
value: {{ .Values.services.apps.port | quote }} value: {{ .Values.services.apps.port | quote }}
{{ if .Values.services.worker.publicApiRateLimitPerSecond }}
- name: API_REQ_LIMIT_PER_SEC
value: {{ .Values.globals.apps.publicApiRateLimitPerSecond | quote }}
{{ end }}
- name: MULTI_TENANCY - name: MULTI_TENANCY
value: {{ .Values.globals.multiTenancy | quote }} value: {{ .Values.globals.multiTenancy | quote }}
- name: LOG_LEVEL - name: LOG_LEVEL
@ -119,6 +123,12 @@ spec:
image: budibase/apps:{{ .Values.globals.appVersion }} image: budibase/apps:{{ .Values.globals.appVersion }}
imagePullPolicy: Always imagePullPolicy: Always
livenessProbe:
httpGet:
path: /health
port: {{ .Values.services.apps.port }}
initialDelaySeconds: 5
periodSeconds: 5
name: bbapps name: bbapps
ports: ports:
- containerPort: {{ .Values.services.apps.port }} - containerPort: {{ .Values.services.apps.port }}

View File

@ -119,6 +119,12 @@ spec:
value: {{ .Values.globals.google.secret | quote }} value: {{ .Values.globals.google.secret | quote }}
image: budibase/worker:{{ .Values.globals.appVersion }} image: budibase/worker:{{ .Values.globals.appVersion }}
imagePullPolicy: Always imagePullPolicy: Always
livenessProbe:
httpGet:
path: /health
port: {{ .Values.services.worker.port }}
initialDelaySeconds: 5
periodSeconds: 5
name: bbworker name: bbworker
ports: ports:
- containerPort: {{ .Values.services.worker.port }} - containerPort: {{ .Values.services.worker.port }}

View File

@ -1,5 +1,5 @@
{ {
"version": "1.0.189", "version": "1.0.191",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -0,0 +1 @@
module.exports = require("./src/logging")

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "1.0.189", "version": "1.0.191",
"description": "Budibase backend core libraries used in server and worker", "description": "Budibase backend core libraries used in server and worker",
"main": "src/index.js", "main": "src/index.js",
"author": "Budibase", "author": "Budibase",

View File

@ -0,0 +1,16 @@
const NonErrors = ["AccountError"]
function isSuppressed(e) {
return e && e["suppressAlert"]
}
module.exports.logAlert = (message, e = null) => {
if (e && NonErrors.includes(e.name) && isSuppressed(e)) {
return
}
let errorJson = ""
if (e) {
errorJson = ": " + JSON.stringify(e, Object.getOwnPropertyNames(e))
}
console.error(`bb-alert: ${message} ${errorJson}`)
}

View File

@ -23,7 +23,7 @@ function connectionError(timeout, err) {
if (CLOSED) { if (CLOSED) {
return return
} }
CLIENT.end() CLIENT.disconnect()
CLOSED = true CLOSED = true
// always clear this on error // always clear this on error
clearTimeout(timeout) clearTimeout(timeout)

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "1.0.189", "version": "1.0.191",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",
@ -38,7 +38,7 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1", "@adobe/spectrum-css-workflow-icons": "^1.2.1",
"@budibase/string-templates": "^1.0.189", "@budibase/string-templates": "^1.0.191",
"@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2", "@spectrum-css/avatar": "^3.0.2",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "1.0.189", "version": "1.0.191",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -69,10 +69,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.189", "@budibase/bbui": "^1.0.191",
"@budibase/client": "^1.0.189", "@budibase/client": "^1.0.191",
"@budibase/frontend-core": "^1.0.189", "@budibase/frontend-core": "^1.0.191",
"@budibase/string-templates": "^1.0.189", "@budibase/string-templates": "^1.0.191",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "1.0.189", "version": "1.0.191",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "1.0.189", "version": "1.0.191",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -19,9 +19,9 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.189", "@budibase/bbui": "^1.0.191",
"@budibase/frontend-core": "^1.0.189", "@budibase/frontend-core": "^1.0.191",
"@budibase/string-templates": "^1.0.189", "@budibase/string-templates": "^1.0.191",
"@spectrum-css/button": "^3.0.3", "@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3", "@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3", "@spectrum-css/divider": "^1.0.3",

View File

@ -1,12 +1,12 @@
{ {
"name": "@budibase/frontend-core", "name": "@budibase/frontend-core",
"version": "1.0.189", "version": "1.0.191",
"description": "Budibase frontend core libraries used in builder and client", "description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.189", "@budibase/bbui": "^1.0.191",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"svelte": "^3.46.2" "svelte": "^3.46.2"
} }

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.189", "version": "1.0.191",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -70,10 +70,10 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@apidevtools/swagger-parser": "^10.0.3", "@apidevtools/swagger-parser": "^10.0.3",
"@budibase/backend-core": "^1.0.189", "@budibase/backend-core": "^1.0.191",
"@budibase/client": "^1.0.189", "@budibase/client": "^1.0.191",
"@budibase/pro": "1.0.189", "@budibase/pro": "1.0.191",
"@budibase/string-templates": "^1.0.189", "@budibase/string-templates": "^1.0.191",
"@bull-board/api": "^3.7.0", "@bull-board/api": "^3.7.0",
"@bull-board/koa": "^3.7.0", "@bull-board/koa": "^3.7.0",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",

View File

@ -12,6 +12,7 @@ const { mainRoutes, staticRoutes, publicRoutes } = require("./routes")
const pkg = require("../../package.json") const pkg = require("../../package.json")
const env = require("../environment") const env = require("../environment")
const { middleware: pro } = require("@budibase/pro") const { middleware: pro } = require("@budibase/pro")
const { shutdown } = require("./routes/public")
const router = new Router() const router = new Router()
@ -90,4 +91,5 @@ router.use(publicRoutes.allowedMethods())
router.use(staticRoutes.routes()) router.use(staticRoutes.routes())
router.use(staticRoutes.allowedMethods()) router.use(staticRoutes.allowedMethods())
module.exports = router module.exports.router = router
module.exports.shutdown = shutdown

View File

@ -29,6 +29,7 @@ function getApiLimitPerSecond(): number {
return parseInt(env.API_REQ_LIMIT_PER_SEC) return parseInt(env.API_REQ_LIMIT_PER_SEC)
} }
let rateLimitStore: any = null
if (!env.isTest()) { if (!env.isTest()) {
const REDIS_OPTS = getRedisOptions() const REDIS_OPTS = getRedisOptions()
let options let options
@ -47,8 +48,9 @@ if (!env.isTest()) {
database: 1, database: 1,
} }
} }
rateLimitStore = new Stores.Redis(options)
RateLimit.defaultOptions({ RateLimit.defaultOptions({
store: new Stores.Redis(options), store: rateLimitStore,
}) })
} }
// rate limiting, allows for 2 requests per second // rate limiting, allows for 2 requests per second
@ -128,3 +130,10 @@ applyRoutes(queryEndpoints, PermissionTypes.QUERY, "queryId")
applyRoutes(rowEndpoints, PermissionTypes.TABLE, "tableId", "rowId") applyRoutes(rowEndpoints, PermissionTypes.TABLE, "tableId", "rowId")
export default publicRouter export default publicRouter
export const shutdown = () => {
if (rateLimitStore) {
rateLimitStore.client.disconnect()
rateLimitStore = null
}
}

View File

@ -14,6 +14,8 @@ const automations = require("./automations/index")
const Sentry = require("@sentry/node") const Sentry = require("@sentry/node")
const fileSystem = require("./utilities/fileSystem") const fileSystem = require("./utilities/fileSystem")
const bullboard = require("./automations/bullboard") const bullboard = require("./automations/bullboard")
const { logAlert } = require("@budibase/backend-core/logging")
const { Thread } = require("./threads")
import redis from "./utilities/redis" import redis from "./utilities/redis"
import * as migrations from "./migrations" import * as migrations from "./migrations"
@ -49,7 +51,7 @@ app.context.eventEmitter = eventEmitter
app.context.auth = {} app.context.auth = {}
// api routes // api routes
app.use(api.routes()) app.use(api.router.routes())
if (env.isProd()) { if (env.isProd()) {
env._set("NODE_ENV", "production") env._set("NODE_ENV", "production")
@ -68,11 +70,24 @@ if (env.isProd()) {
const server = http.createServer(app.callback()) const server = http.createServer(app.callback())
destroyable(server) destroyable(server)
let shuttingDown = false,
errCode = 0
server.on("close", async () => { server.on("close", async () => {
if (env.NODE_ENV !== "jest") { // already in process
if (shuttingDown) {
return
}
shuttingDown = true
if (!env.isTest()) {
console.log("Server Closed") console.log("Server Closed")
} }
await automations.shutdown()
await redis.shutdown() await redis.shutdown()
await Thread.shutdown()
api.shutdown()
if (!env.isTest()) {
process.exit(errCode)
}
}) })
module.exports = server.listen(env.PORT || 0, async () => { module.exports = server.listen(env.PORT || 0, async () => {
@ -90,7 +105,13 @@ const shutdown = () => {
} }
process.on("uncaughtException", err => { process.on("uncaughtException", err => {
console.error(err) // @ts-ignore
// don't worry about this error, comes from zlib isn't important
if (err && err["code"] === "ERR_INVALID_CHAR") {
return
}
errCode = -1
logAlert("Uncaught exception.", err)
shutdown() shutdown()
}) })
@ -102,7 +123,7 @@ process.on("SIGTERM", () => {
// not recommended in a clustered environment // not recommended in a clustered environment
if (!env.HTTP_MIGRATIONS) { if (!env.HTTP_MIGRATIONS) {
migrations.migrate().catch(err => { migrations.migrate().catch(err => {
console.error("Error performing migrations. Exiting.\n", err) logAlert("Error performing migrations. Exiting.", err)
shutdown() shutdown()
}) })
} }

View File

@ -45,4 +45,12 @@ exports.init = () => {
return serverAdapter.registerPlugin() return serverAdapter.registerPlugin()
} }
exports.shutdown = async () => {
if (automationQueue) {
clearInterval(cleanupInternal)
await automationQueue.close()
automationQueue = null
}
}
exports.queue = automationQueue exports.queue = automationQueue

View File

@ -1,5 +1,5 @@
const { processEvent } = require("./utils") const { processEvent } = require("./utils")
const { queue } = require("./bullboard") const { queue, shutdown } = require("./bullboard")
/** /**
* This module is built purely to kick off the worker farm and manage the inputs/outputs * This module is built purely to kick off the worker farm and manage the inputs/outputs
@ -14,4 +14,9 @@ exports.init = function () {
exports.getQueues = () => { exports.getQueues = () => {
return [queue] return [queue]
} }
exports.shutdown = () => {
return shutdown()
}
exports.queue = queue exports.queue = queue

View File

@ -28,6 +28,8 @@ export class Thread {
workers: any workers: any
timeoutMs: any timeoutMs: any
static workerRefs: any[] = []
constructor(type: any, opts: any = { timeoutMs: null, count: 1 }) { constructor(type: any, opts: any = { timeoutMs: null, count: 1 }) {
this.type = type this.type = type
this.count = opts.count ? opts.count : 1 this.count = opts.count ? opts.count : 1
@ -46,6 +48,7 @@ export class Thread {
workerOpts.maxCallTime = opts.timeoutMs workerOpts.maxCallTime = opts.timeoutMs
} }
this.workers = workerFarm(workerOpts, typeToFile(type)) this.workers = workerFarm(workerOpts, typeToFile(type))
Thread.workerRefs.push(this.workers)
} }
} }
@ -73,4 +76,23 @@ export class Thread {
}) })
}) })
} }
static shutdown() {
return new Promise<void>(resolve => {
if (Thread.workerRefs.length === 0) {
resolve()
}
let count = 0
function complete() {
count++
if (count >= Thread.workerRefs.length) {
resolve()
}
}
for (let worker of Thread.workerRefs) {
workerFarm.end(worker, complete)
}
Thread.workerRefs = []
})
}
} }

View File

@ -75,6 +75,13 @@ class InMemoryQueue {
this._emitter.emit("message") this._emitter.emit("message")
} }
/**
* replicating the close function from bull, which waits for jobs to finish.
*/
async close() {
return []
}
/** /**
* This removes a cron which has been implemented, this is part of Bull API. * This removes a cron which has been implemented, this is part of Bull API.
* @param {string} cronJobId The cron which is to be removed. * @param {string} cronJobId The cron which is to be removed.

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "1.0.189", "version": "1.0.191",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.189", "version": "1.0.191",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -32,9 +32,9 @@
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "^1.0.189", "@budibase/backend-core": "^1.0.191",
"@budibase/pro": "1.0.189", "@budibase/pro": "1.0.191",
"@budibase/string-templates": "^1.0.189", "@budibase/string-templates": "^1.0.191",
"@koa/router": "^8.0.0", "@koa/router": "^8.0.0",
"@sentry/node": "6.17.7", "@sentry/node": "6.17.7",
"@techpass/passport-openidconnect": "^0.3.0", "@techpass/passport-openidconnect": "^0.3.0",

View File

@ -12,6 +12,7 @@ const destroyable = require("server-destroy")
const koaBody = require("koa-body") const koaBody = require("koa-body")
const koaSession = require("koa-session") const koaSession = require("koa-session")
const { passport } = require("@budibase/backend-core/auth") const { passport } = require("@budibase/backend-core/auth")
const { logAlert } = require("@budibase/backend-core/logging")
const logger = require("koa-pino-logger") const logger = require("koa-pino-logger")
const http = require("http") const http = require("http")
const api = require("./api") const api = require("./api")
@ -28,7 +29,6 @@ app.keys = ["secret", "key"]
// set up top level koa middleware // set up top level koa middleware
app.use(koaBody({ multipart: true })) app.use(koaBody({ multipart: true }))
app.use(koaSession(app)) app.use(koaSession(app))
app.use( app.use(
logger({ logger({
prettyPrint: { prettyPrint: {
@ -62,25 +62,38 @@ if (env.isProd()) {
const server = http.createServer(app.callback()) const server = http.createServer(app.callback())
destroyable(server) destroyable(server)
let shuttingDown = false,
errCode = 0
server.on("close", async () => { server.on("close", async () => {
if (env.isProd()) { if (shuttingDown) {
return
}
shuttingDown = true
if (!env.isTest()) {
console.log("Server Closed") console.log("Server Closed")
} }
await redis.shutdown() await redis.shutdown()
if (!env.isTest()) {
process.exit(errCode)
}
}) })
const shutdown = () => {
server.close()
server.destroy()
}
module.exports = server.listen(parseInt(env.PORT || 4002), async () => { module.exports = server.listen(parseInt(env.PORT || 4002), async () => {
console.log(`Worker running on ${JSON.stringify(server.address())}`) console.log(`Worker running on ${JSON.stringify(server.address())}`)
await redis.init() await redis.init()
}) })
process.on("uncaughtException", err => { process.on("uncaughtException", err => {
console.error(err) errCode = -1
server.close() logAlert("Uncaught exception.", err)
server.destroy() shutdown()
}) })
process.on("SIGTERM", () => { process.on("SIGTERM", () => {
server.close() shutdown()
server.destroy()
}) })