Browse Source

Merge branch 'develop' of github.com:Budibase/budibase into new-design-ui

master
Andrew Kingston 4 years ago
parent
commit
949ab2dac6
  1. 2
      lerna.json
  2. 3
      packages/backend-core/cache.js
  3. 5
      packages/backend-core/package.json
  4. 2
      packages/backend-core/src/auth.js
  5. 49
      packages/backend-core/src/cache/generic.js
  6. 12
      packages/backend-core/src/context/index.js
  7. 12
      packages/backend-core/src/db/index.js
  8. 8
      packages/backend-core/src/db/pouch.js
  9. 2
      packages/backend-core/src/middleware/passport/local.js
  10. 10
      packages/backend-core/src/redis/authRedis.js
  11. 1
      packages/backend-core/src/redis/utils.js
  12. 41
      packages/backend-core/src/utils.js
  13. 169
      packages/backend-core/yarn.lock
  14. 4
      packages/bbui/package.json
  15. 10
      packages/builder/package.json
  16. 2
      packages/builder/src/builderStore/store/automation/index.js
  17. 107
      packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
  18. 72
      packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte
  19. 111
      packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte
  20. 133
      packages/builder/src/components/automation/AutomationBuilder/FlowChart/ResultsModal.svelte
  21. 1
      packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte
  22. 146
      packages/builder/src/components/automation/AutomationBuilder/TestPanel.svelte
  23. 35
      packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte
  24. 6
      packages/builder/src/pages/builder/auth/login.svelte
  25. 2
      packages/cli/package.json
  26. 8
      packages/client/package.json
  27. 4
      packages/frontend-core/package.json
  28. 4
      packages/server/Dockerfile
  29. 4
      packages/server/__mocks__/pg.ts
  30. 5
      packages/server/docker_run.sh
  31. 11
      packages/server/package.json
  32. 9
      packages/server/pm2.config.js
  33. 97
      packages/server/scripts/load/users.js
  34. 11
      packages/server/src/api/controllers/application.ts
  35. 27
      packages/server/src/api/controllers/public/users.ts
  36. 7
      packages/server/src/environment.js
  37. 49
      packages/server/src/integrations/postgres.ts
  38. 21
      packages/server/src/utilities/users.js
  39. 18
      packages/server/yarn.lock
  40. 2
      packages/string-templates/package.json
  41. 4
      packages/worker/Dockerfile
  42. 5
      packages/worker/docker_run.sh
  43. 9
      packages/worker/package.json
  44. 9
      packages/worker/pm2.config.js
  45. 112
      packages/worker/src/api/controllers/global/configs.js
  46. 18
      packages/worker/src/api/controllers/global/users.ts
  47. 14
      packages/worker/src/environment.js
  48. 1
      packages/worker/src/utilities/redis.js
  49. 18
      packages/worker/yarn.lock

2
lerna.json

@ -1,5 +1,5 @@
{ {
"version": "1.0.167-alpha.8", "version": "1.0.176-alpha.0",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

3
packages/backend-core/cache.js

@ -1,4 +1,7 @@
const generic = require("./src/cache/generic")
module.exports = { module.exports = {
user: require("./src/cache/user"), user: require("./src/cache/user"),
app: require("./src/cache/appMetadata"), app: require("./src/cache/appMetadata"),
...generic,
} }

5
packages/backend-core/package.json

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "1.0.167-alpha.8", "version": "1.0.176-alpha.0",
"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",
@ -42,8 +42,7 @@
"devDependencies": { "devDependencies": {
"ioredis-mock": "^5.5.5", "ioredis-mock": "^5.5.5",
"jest": "^26.6.3", "jest": "^26.6.3",
"pouchdb-adapter-memory": "^7.2.2", "pouchdb-adapter-memory": "^7.2.2"
"pouchdb-all-dbs": "^1.0.2"
}, },
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"
} }

2
packages/backend-core/src/auth.js

@ -29,7 +29,7 @@ passport.deserializeUser(async (user, done) => {
const user = await db.get(user._id) const user = await db.get(user._id)
return done(null, user) return done(null, user)
} catch (err) { } catch (err) {
console.error("User not found", err) console.error(`User not found`, err)
return done(null, false, { message: "User not found" }) return done(null, false, { message: "User not found" })
} }
}) })

49
packages/backend-core/src/cache/generic.js

@ -0,0 +1,49 @@
const redis = require("../redis/authRedis")
const env = require("../environment")
const { getTenantId } = require("../context")
exports.CacheKeys = {
CHECKLIST: "checklist",
}
exports.TTL = {
ONE_MINUTE: 600,
ONE_HOUR: 3600,
ONE_DAY: 86400,
}
function generateTenantKey(key) {
const tenantId = getTenantId()
return `${key}:${tenantId}`
}
exports.withCache = async (key, ttl, fetchFn) => {
key = generateTenantKey(key)
const client = await redis.getCacheClient()
const cachedValue = await client.get(key)
if (cachedValue) {
return cachedValue
}
try {
const fetchedValue = await fetchFn()
if (!env.isTest()) {
await client.store(key, fetchedValue, ttl)
}
return fetchedValue
} catch (err) {
console.error("Error fetching before cache - ", err)
throw err
}
}
exports.bustCache = async key => {
const client = await redis.getCacheClient()
try {
await client.delete(generateTenantKey(key))
} catch (err) {
console.error("Error busting cache - ", err)
throw err
}
}

12
packages/backend-core/src/context/index.js

@ -73,7 +73,7 @@ exports.isMultiTenant = () => {
} }
// used for automations, API endpoints should always be in context already // used for automations, API endpoints should always be in context already
exports.doInTenant = (tenantId, task) => { exports.doInTenant = (tenantId, task, { forceNew } = {}) => {
// the internal function is so that we can re-use an existing // the internal function is so that we can re-use an existing
// context - don't want to close DB on a parent context // context - don't want to close DB on a parent context
async function internal(opts = { existing: false }) { async function internal(opts = { existing: false }) {
@ -98,7 +98,11 @@ exports.doInTenant = (tenantId, task) => {
} }
} }
const using = cls.getFromContext(ContextKeys.IN_USE) const using = cls.getFromContext(ContextKeys.IN_USE)
if (using && cls.getFromContext(ContextKeys.TENANT_ID) === tenantId) { if (
!forceNew &&
using &&
cls.getFromContext(ContextKeys.TENANT_ID) === tenantId
) {
cls.setOnContext(ContextKeys.IN_USE, using + 1) cls.setOnContext(ContextKeys.IN_USE, using + 1)
return internal({ existing: true }) return internal({ existing: true })
} else { } else {
@ -135,7 +139,7 @@ const setAppTenantId = appId => {
exports.updateTenantId(appTenantId) exports.updateTenantId(appTenantId)
} }
exports.doInAppContext = (appId, task) => { exports.doInAppContext = (appId, task, { forceNew } = {}) => {
if (!appId) { if (!appId) {
throw new Error("appId is required") throw new Error("appId is required")
} }
@ -162,7 +166,7 @@ exports.doInAppContext = (appId, task) => {
} }
} }
const using = cls.getFromContext(ContextKeys.IN_USE) const using = cls.getFromContext(ContextKeys.IN_USE)
if (using && cls.getFromContext(ContextKeys.APP_ID) === appId) { if (!forceNew && using && cls.getFromContext(ContextKeys.APP_ID) === appId) {
cls.setOnContext(ContextKeys.IN_USE, using + 1) cls.setOnContext(ContextKeys.IN_USE, using + 1)
return internal({ existing: true }) return internal({ existing: true })
} else { } else {

12
packages/backend-core/src/db/index.js

@ -3,13 +3,13 @@ const env = require("../environment")
let PouchDB let PouchDB
let initialised = false let initialised = false
const dbList = new Set()
const put = const put =
dbPut => dbPut =>
async (doc, options = {}) => { async (doc, options = {}) => {
const response = await dbPut(doc, options)
// TODO: add created / updated // TODO: add created / updated
return response return await dbPut(doc, options)
} }
const checkInitialised = () => { const checkInitialised = () => {
@ -28,6 +28,9 @@ exports.init = opts => {
// in situations that using the function doWithDB does not work // in situations that using the function doWithDB does not work
exports.dangerousGetDB = (dbName, opts) => { exports.dangerousGetDB = (dbName, opts) => {
checkInitialised() checkInitialised()
if (env.isTest()) {
dbList.add(dbName)
}
const db = new PouchDB(dbName, opts) const db = new PouchDB(dbName, opts)
const dbPut = db.put const dbPut = db.put
db.put = put(dbPut) db.put = put(dbPut)
@ -63,6 +66,9 @@ exports.doWithDB = async (dbName, cb, opts) => {
} }
exports.allDbs = () => { exports.allDbs = () => {
if (!env.isTest()) {
throw new Error("Cannot be used outside test environment.")
}
checkInitialised() checkInitialised()
return PouchDB.allDbs() return [...dbList]
} }

8
packages/backend-core/src/db/pouch.js

@ -92,11 +92,5 @@ exports.getPouch = (opts = {}) => {
PouchDB.plugin(find) PouchDB.plugin(find)
} }
const Pouch = PouchDB.defaults(POUCH_DB_DEFAULTS) return PouchDB.defaults(POUCH_DB_DEFAULTS)
if (opts.allDbs) {
const allDbs = require("pouchdb-all-dbs")
allDbs(Pouch)
}
return Pouch
} }

2
packages/backend-core/src/middleware/passport/local.js

@ -30,7 +30,7 @@ exports.authenticate = async function (ctx, email, password, done) {
const dbUser = await getGlobalUserByEmail(email) const dbUser = await getGlobalUserByEmail(email)
if (dbUser == null) { if (dbUser == null) {
return authError(done, "User not found") return authError(done, `User not found: [${email}]`)
} }
// check that the user is currently inactive, if this is the case throw invalid // check that the user is currently inactive, if this is the case throw invalid

10
packages/backend-core/src/redis/authRedis.js

@ -1,18 +1,20 @@
const Client = require("./index") const Client = require("./index")
const utils = require("./utils") const utils = require("./utils")
let userClient, sessionClient, appClient let userClient, sessionClient, appClient, cacheClient
async function init() { async function init() {
userClient = await new Client(utils.Databases.USER_CACHE).init() userClient = await new Client(utils.Databases.USER_CACHE).init()
sessionClient = await new Client(utils.Databases.SESSIONS).init() sessionClient = await new Client(utils.Databases.SESSIONS).init()
appClient = await new Client(utils.Databases.APP_METADATA).init() appClient = await new Client(utils.Databases.APP_METADATA).init()
cacheClient = await new Client(utils.Databases.GENERIC_CACHE).init()
} }
process.on("exit", async () => { process.on("exit", async () => {
if (userClient) await userClient.finish() if (userClient) await userClient.finish()
if (sessionClient) await sessionClient.finish() if (sessionClient) await sessionClient.finish()
if (appClient) await appClient.finish() if (appClient) await appClient.finish()
if (cacheClient) await cacheClient.finish()
}) })
module.exports = { module.exports = {
@ -34,4 +36,10 @@ module.exports = {
} }
return appClient return appClient
}, },
getCacheClient: async () => {
if (!cacheClient) {
await init()
}
return cacheClient
},
} }

1
packages/backend-core/src/redis/utils.js

@ -18,6 +18,7 @@ exports.Databases = {
APP_METADATA: "appMetadata", APP_METADATA: "appMetadata",
QUERY_VARS: "queryVars", QUERY_VARS: "queryVars",
LICENSES: "license", LICENSES: "license",
GENERIC_CACHE: "data_cache",
} }
exports.SEPARATOR = SEPARATOR exports.SEPARATOR = SEPARATOR

41
packages/backend-core/src/utils.js

@ -197,11 +197,16 @@ exports.getBuildersCount = async () => {
return builders.length return builders.length
} }
exports.saveUser = async ( const DEFAULT_SAVE_USER = {
hashPassword: true,
requirePassword: true,
bulkCreate: false,
}
exports.internalSaveUser = async (
user, user,
tenantId, tenantId,
hashPassword = true, { hashPassword, requirePassword, bulkCreate } = DEFAULT_SAVE_USER
requirePassword = true
) => { ) => {
if (!tenantId) { if (!tenantId) {
throw "No tenancy specified." throw "No tenancy specified."
@ -213,7 +218,10 @@ exports.saveUser = async (
let { email, password, _id } = user let { email, password, _id } = user
// make sure another user isn't using the same email // make sure another user isn't using the same email
let dbUser let dbUser
if (email) { // user can't exist in bulk creation
if (bulkCreate) {
dbUser = null
} else if (email) {
// check budibase users inside the tenant // check budibase users inside the tenant
dbUser = await exports.getGlobalUserByEmail(email) dbUser = await exports.getGlobalUserByEmail(email)
if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) { if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) {
@ -267,11 +275,17 @@ exports.saveUser = async (
user.status = UserStatus.ACTIVE user.status = UserStatus.ACTIVE
} }
try { try {
const response = await db.put({ const putOpts = {
password: hashedPassword, password: hashedPassword,
...user, ...user,
}) }
await tryAddTenant(tenantId, _id, email) if (bulkCreate) {
return putOpts
}
const response = await db.put(putOpts)
if (env.MULTI_TENANCY) {
await tryAddTenant(tenantId, _id, email)
}
await userCache.invalidateUser(response.id) await userCache.invalidateUser(response.id)
return { return {
_id: response.id, _id: response.id,
@ -288,6 +302,19 @@ exports.saveUser = async (
}) })
} }
// maintained for api compat, don't want to change function signature
exports.saveUser = async (
user,
tenantId,
hashPassword = true,
requirePassword = true
) => {
return exports.internalSaveUser(user, tenantId, {
hashPassword,
requirePassword,
})
}
/** /**
* Logs a user out from budibase. Re-used across account portal and builder. * Logs a user out from budibase. Re-used across account portal and builder.
*/ */

169
packages/backend-core/yarn.lock

@ -661,11 +661,6 @@ acorn-walk@^7.1.1:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
acorn@^5.2.1:
version "5.7.4"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
acorn@^7.1.1: acorn@^7.1.1:
version "7.4.1" version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
@ -701,11 +696,6 @@ ajv@^6.12.3:
json-schema-traverse "^0.4.1" json-schema-traverse "^0.4.1"
uri-js "^4.2.2" uri-js "^4.2.2"
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
ansi-escapes@^4.2.1: ansi-escapes@^4.2.1:
version "4.3.2" version "4.3.2"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
@ -800,11 +790,6 @@ assign-symbols@^1.0.0:
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
ast-types@0.9.6:
version "0.9.6"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9"
integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=
async@~2.1.4: async@~2.1.4:
version "2.1.5" version "2.1.5"
resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc"
@ -938,11 +923,6 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base62@^1.1.0:
version "1.2.8"
resolved "https://registry.yarnpkg.com/base62/-/base62-1.2.8.tgz#1264cb0fb848d875792877479dbe8bae6bae3428"
integrity sha512-V6YHUbjLxN1ymqNLb1DPHoU1CpfdL7d2YTIp5W3U4hhoG4hhxNmsFDs66M9EXxBiSEke5Bt5dwdfMwwZF70iLA==
base64-js@^1.0.2, base64-js@^1.3.1: base64-js@^1.0.2, base64-js@^1.3.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@ -1259,26 +1239,6 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
dependencies: dependencies:
delayed-stream "~1.0.0" delayed-stream "~1.0.0"
commander@^2.5.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commoner@^0.10.1:
version "0.10.8"
resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.8.tgz#34fc3672cd24393e8bb47e70caa0293811f4f2c5"
integrity sha1-NPw2cs0kOT6LtH5wyqApOBH08sU=
dependencies:
commander "^2.5.0"
detective "^4.3.1"
glob "^5.0.15"
graceful-fs "^4.1.2"
iconv-lite "^0.4.5"
mkdirp "^0.5.0"
private "^0.1.6"
q "^1.1.2"
recast "^0.11.17"
component-emitter@^1.2.1: component-emitter@^1.2.1:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
@ -1445,11 +1405,6 @@ define-property@^2.0.2:
is-descriptor "^1.0.2" is-descriptor "^1.0.2"
isobject "^3.0.1" isobject "^3.0.1"
defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
delayed-stream@~1.0.0: delayed-stream@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
@ -1465,14 +1420,6 @@ detect-newline@^3.0.0:
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
detective@^4.3.1:
version "4.7.1"
resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e"
integrity sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==
dependencies:
acorn "^5.2.1"
defined "^1.0.0"
diff-sequences@^26.6.2: diff-sequences@^26.6.2:
version "26.6.2" version "26.6.2"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1"
@ -1577,15 +1524,6 @@ error-ex@^1.3.1:
dependencies: dependencies:
is-arrayish "^0.2.1" is-arrayish "^0.2.1"
es3ify@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/es3ify/-/es3ify-0.2.2.tgz#5dae3e650e5be3684b88066513d528d092629862"
integrity sha1-Xa4+ZQ5b42hLiAZlE9Uo0JJimGI=
dependencies:
esprima "^2.7.1"
jstransform "~11.0.0"
through "~2.3.4"
escalade@^3.1.1: escalade@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@ -1613,26 +1551,11 @@ escodegen@^2.0.0:
optionalDependencies: optionalDependencies:
source-map "~0.6.1" source-map "~0.6.1"
esprima-fb@^15001.1.0-dev-harmony-fb:
version "15001.1.0-dev-harmony-fb"
resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz#30a947303c6b8d5e955bee2b99b1d233206a6901"
integrity sha1-MKlHMDxrjV6VW+4rmbHSMyBqaQE=
esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=
esprima@^4.0.0, esprima@^4.0.1: esprima@^4.0.0, esprima@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
esprima@~3.1.0:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=
estraverse@^5.2.0: estraverse@^5.2.0:
version "5.3.0" version "5.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
@ -1950,17 +1873,6 @@ getpass@^0.1.1:
dependencies: dependencies:
assert-plus "^1.0.0" assert-plus "^1.0.0"
glob@^5.0.15:
version "5.0.15"
resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=
dependencies:
inflight "^1.0.4"
inherits "2"
minimatch "2 || 3"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
version "7.2.0" version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
@ -2004,7 +1916,7 @@ googleapis@^16.0.0:
google-auth-library "~0.10.0" google-auth-library "~0.10.0"
string-template "~1.0.0" string-template "~1.0.0"
graceful-fs@^4.1.2, graceful-fs@^4.2.4: graceful-fs@^4.2.4:
version "4.2.8" version "4.2.8"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
@ -2170,7 +2082,7 @@ human-signals@^1.1.1:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
iconv-lite@0.4.24, iconv-lite@^0.4.5: iconv-lite@0.4.24:
version "0.4.24" version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@ -2993,17 +2905,6 @@ jsprim@^1.2.2:
json-schema "0.2.3" json-schema "0.2.3"
verror "1.3.6" verror "1.3.6"
jstransform@~11.0.0:
version "11.0.3"
resolved "https://registry.yarnpkg.com/jstransform/-/jstransform-11.0.3.tgz#09a78993e0ae4d4ef4487f6155a91f6190cb4223"
integrity sha1-CaeJk+CuTU70SH9hVakfYZDLQiM=
dependencies:
base62 "^1.1.0"
commoner "^0.10.1"
esprima-fb "^15001.1.0-dev-harmony-fb"
object-assign "^2.0.0"
source-map "^0.4.2"
jwa@^1.1.4: jwa@^1.1.4:
version "1.1.5" version "1.1.5"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.5.tgz#a0552ce0220742cd52e153774a32905c30e756e5" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.5.tgz#a0552ce0220742cd52e153774a32905c30e756e5"
@ -3392,7 +3293,7 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
"minimatch@2 || 3", minimatch@^3.0.4: minimatch@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@ -3417,13 +3318,6 @@ mkdirp-classic@^0.5.2:
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
mkdirp@^0.5.0:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "^1.2.5"
ms@2.0.0: ms@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@ -3586,11 +3480,6 @@ oauth@0.9.x, oauth@^0.9.15:
resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1"
integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE= integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE=
object-assign@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa"
integrity sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=
object-copy@^0.1.0: object-copy@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
@ -3923,17 +3812,6 @@ pouchdb-adapter-utils@7.2.2:
pouchdb-merge "7.2.2" pouchdb-merge "7.2.2"
pouchdb-utils "7.2.2" pouchdb-utils "7.2.2"
pouchdb-all-dbs@^1.0.2:
version "1.1.1"
resolved "https://registry.yarnpkg.com/pouchdb-all-dbs/-/pouchdb-all-dbs-1.1.1.tgz#85f04a39cafda52497ec49abf1c93bb5e72813f6"
integrity sha512-UUnsdmcnRSQ8MAOYSJjfTwKkQNb/6fvOfd/f7dNNivWZ2YDYVuMfgw1WQdL634yEtcXTxAENZ/EyLRdzPCB41A==
dependencies:
argsarray "0.0.1"
es3ify "^0.2.2"
inherits "~2.0.1"
pouchdb-promise "6.4.3"
tiny-queue "^0.2.0"
pouchdb-binary-utils@7.2.2: pouchdb-binary-utils@7.2.2:
version "7.2.2" version "7.2.2"
resolved "https://registry.yarnpkg.com/pouchdb-binary-utils/-/pouchdb-binary-utils-7.2.2.tgz#0690b348052c543b1e67f032f47092ca82bcb10e" resolved "https://registry.yarnpkg.com/pouchdb-binary-utils/-/pouchdb-binary-utils-7.2.2.tgz#0690b348052c543b1e67f032f47092ca82bcb10e"
@ -4010,7 +3888,7 @@ pouchdb-merge@7.2.2:
resolved "https://registry.yarnpkg.com/pouchdb-merge/-/pouchdb-merge-7.2.2.tgz#940d85a2b532d6a93a6cab4b250f5648511bcc16" resolved "https://registry.yarnpkg.com/pouchdb-merge/-/pouchdb-merge-7.2.2.tgz#940d85a2b532d6a93a6cab4b250f5648511bcc16"
integrity sha512-6yzKJfjIchBaS7Tusuk8280WJdESzFfQ0sb4jeMUNnrqs4Cx3b0DIEOYTRRD9EJDM+je7D3AZZ4AT0tFw8gb4A== integrity sha512-6yzKJfjIchBaS7Tusuk8280WJdESzFfQ0sb4jeMUNnrqs4Cx3b0DIEOYTRRD9EJDM+je7D3AZZ4AT0tFw8gb4A==
pouchdb-promise@6.4.3, pouchdb-promise@^6.0.4: pouchdb-promise@^6.0.4:
version "6.4.3" version "6.4.3"
resolved "https://registry.yarnpkg.com/pouchdb-promise/-/pouchdb-promise-6.4.3.tgz#74516f4acf74957b54debd0fb2c0e5b5a68ca7b3" resolved "https://registry.yarnpkg.com/pouchdb-promise/-/pouchdb-promise-6.4.3.tgz#74516f4acf74957b54debd0fb2c0e5b5a68ca7b3"
integrity sha512-ruJaSFXwzsxRHQfwNHjQfsj58LBOY1RzGzde4PM5CWINZwFjCQAhZwfMrch2o/0oZT6d+Xtt0HTWhq35p3b0qw== integrity sha512-ruJaSFXwzsxRHQfwNHjQfsj58LBOY1RzGzde4PM5CWINZwFjCQAhZwfMrch2o/0oZT6d+Xtt0HTWhq35p3b0qw==
@ -4093,11 +3971,6 @@ pretty-format@^26.6.2:
ansi-styles "^4.0.0" ansi-styles "^4.0.0"
react-is "^17.0.1" react-is "^17.0.1"
private@^0.1.6, private@~0.1.5:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
process-nextick-args@~2.0.0: process-nextick-args@~2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@ -4144,11 +4017,6 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
q@^1.1.2:
version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
qs@~6.4.0: qs@~6.4.0:
version "6.4.0" version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
@ -4230,16 +4098,6 @@ readline-sync@^1.4.9:
resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b"
integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw== integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==
recast@^0.11.17:
version "0.11.23"
resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3"
integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=
dependencies:
ast-types "0.9.6"
esprima "~3.1.0"
private "~0.1.5"
source-map "~0.5.0"
redis-commands@1.7.0: redis-commands@1.7.0:
version "1.7.0" version "1.7.0"
resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89"
@ -4597,14 +4455,7 @@ source-map-url@^0.4.0:
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56"
integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==
source-map@^0.4.2: source-map@^0.5.0, source-map@^0.5.6:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
integrity sha1-66T12pwNyZneaAMti092FzZSA2s=
dependencies:
amdefine ">=0.0.4"
source-map@^0.5.0, source-map@^0.5.6, source-map@~0.5.0:
version "0.5.7" version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@ -4884,16 +4735,6 @@ through2@^2.0.0, through2@^2.0.2, through2@^2.0.3:
readable-stream "~2.3.6" readable-stream "~2.3.6"
xtend "~4.0.1" xtend "~4.0.1"
through@~2.3.4:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
tiny-queue@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046"
integrity sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY=
tmp@^0.0.33: tmp@^0.0.33:
version "0.0.33" version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"

4
packages/bbui/package.json

@ -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.167-alpha.8", "version": "1.0.176-alpha.0",
"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.167-alpha.8", "@budibase/string-templates": "^1.0.176-alpha.0",
"@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",

10
packages/builder/package.json

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "1.0.167-alpha.8", "version": "1.0.176-alpha.0",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -69,10 +69,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.167-alpha.8", "@budibase/bbui": "^1.0.176-alpha.0",
"@budibase/client": "^1.0.167-alpha.8", "@budibase/client": "^1.0.176-alpha.0",
"@budibase/frontend-core": "^1.0.167-alpha.8", "@budibase/frontend-core": "^1.0.176-alpha.0",
"@budibase/string-templates": "^1.0.167-alpha.8", "@budibase/string-templates": "^1.0.176-alpha.0",
"@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",

2
packages/builder/src/builderStore/store/automation/index.js

@ -105,9 +105,7 @@ const automationActions = store => ({
}, },
select: automation => { select: automation => {
store.update(state => { store.update(state => {
let testResults = state.selectedAutomation?.testResults
state.selectedAutomation = new Automation(cloneDeep(automation)) state.selectedAutomation = new Automation(cloneDeep(automation))
state.selectedAutomation.testResults = testResults
state.selectedBlock = null state.selectedBlock = null
return state return state
}) })

107
packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte

@ -14,7 +14,6 @@
} from "@budibase/bbui" } from "@budibase/bbui"
export let automation export let automation
let testDataModal let testDataModal
let blocks let blocks
let confirmDeleteDialog let confirmDeleteDialog
@ -41,66 +40,70 @@
</script> </script>
<div class="canvas"> <div class="canvas">
<div class="content"> <div style="float: left; padding-left: var(--spacing-xl);">
<div class="title"> <Heading size="S">{automation.name}</Heading>
<div class="subtitle"> </div>
<Heading size="S">{automation.name}</Heading> <div style="float: right; padding-right: var(--spacing-xl);" class="title">
<div style="display:flex; align-items: center;"> <div class="subtitle">
<div class="iconPadding"> <div style="display:flex; align-items: center;">
<div class="icon"> <div class="icon">
<Icon <Icon
on:click={confirmDeleteDialog.show} on:click={confirmDeleteDialog.show}
hoverable hoverable
size="M" size="M"
name="DeleteOutline" name="DeleteOutline"
/> />
</div> </div>
</div> <ActionButton
on:click={() => {
testDataModal.show()
}}
icon="MultipleCheck"
size="M">Run test</ActionButton
>
<div style="padding-left: var(--spacing-m);">
<ActionButton <ActionButton
disabled={!$automationStore.selectedAutomation?.testResults}
on:click={() => { on:click={() => {
testDataModal.show() $automationStore.selectedAutomation.automation.showTestPanel = true
}} }}
icon="MultipleCheck" size="M">Test Details</ActionButton
size="M">Run test</ActionButton
> >
</div> </div>
</div> </div>
</div> </div>
{#each blocks as block, idx (block.id)}
<div
class="block"
animate:flip={{ duration: 500 }}
in:fly|local={{ x: 500, duration: 1500 }}
>
{#if block.stepId !== "LOOP"}
<FlowItem {testDataModal} {block} />
{/if}
</div>
{/each}
</div> </div>
<ConfirmDialog
bind:this={confirmDeleteDialog}
okText="Delete Automation"
onOk={deleteAutomation}
title="Confirm Deletion"
>
Are you sure you wish to delete the automation
<i>{automation.name}?</i>
This action cannot be undone.
</ConfirmDialog>
<Modal bind:this={testDataModal} width="30%">
<TestDataModal />
</Modal>
</div> </div>
<div class="content">
{#each blocks as block, idx (block.id)}
<div
class="block"
animate:flip={{ duration: 500 }}
in:fly|local={{ x: 500, duration: 500 }}
out:fly|local={{ x: 500, duration: 500 }}
>
{#if block.stepId !== "LOOP"}
<FlowItem {testDataModal} {block} />
{/if}
</div>
{/each}
</div>
<ConfirmDialog
bind:this={confirmDeleteDialog}
okText="Delete Automation"
onOk={deleteAutomation}
title="Confirm Deletion"
>
Are you sure you wish to delete the automation
<i>{automation.name}?</i>
This action cannot be undone.
</ConfirmDialog>
<Modal bind:this={testDataModal} width="30%">
<TestDataModal />
</Modal>
<style> <style>
.canvas {
margin: 0 -40px calc(-1 * var(--spacing-l)) -40px;
overflow-y: auto;
text-align: center;
height: 100%;
}
/* Fix for firefox not respecting bottom padding in scrolling containers */ /* Fix for firefox not respecting bottom padding in scrolling containers */
.canvas > *:last-child { .canvas > *:last-child {
padding-bottom: 40px; padding-bottom: 40px;
@ -128,10 +131,6 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.iconPadding {
padding-top: var(--spacing-s);
}
.icon { .icon {
cursor: pointer; cursor: pointer;
padding-right: var(--spacing-m); padding-right: var(--spacing-m);

72
packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte

@ -1,30 +1,27 @@
<script> <script>
import FlowItemHeader from "./FlowItemHeader.svelte"
import { automationStore } from "builderStore" import { automationStore } from "builderStore"
import { import {
Icon, Icon,
Divider, Divider,
Layout, Layout,
Body,
Detail, Detail,
Modal, Modal,
Button, Button,
StatusLight,
Select, Select,
ActionButton, ActionButton,
notifications, notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte" import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte"
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte" import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
import ResultsModal from "./ResultsModal.svelte"
import ActionModal from "./ActionModal.svelte" import ActionModal from "./ActionModal.svelte"
import { externalActions } from "./ExternalActions"
export let block export let block
export let testDataModal export let testDataModal
let selected let selected
let webhookModal let webhookModal
let actionModal let actionModal
let resultsModal
let blockComplete let blockComplete
let showLooping = false let showLooping = false
@ -32,9 +29,6 @@
$: showBindingPicker = $: showBindingPicker =
block.stepId === "CREATE_ROW" || block.stepId === "UPDATE_ROW" block.stepId === "CREATE_ROW" || block.stepId === "UPDATE_ROW"
$: testResult = $automationStore.selectedAutomation.testResults?.steps.filter(
step => (block.id ? step.id === block.id : step.stepId === block.stepId)
)
$: isTrigger = block.type === "TRIGGER" $: isTrigger = block.type === "TRIGGER"
$: selected = $automationStore.selectedBlock?.id === block.id $: selected = $automationStore.selectedBlock?.id === block.id
@ -182,63 +176,7 @@
{/if} {/if}
{/if} {/if}
<div class="blockSection"> <FlowItemHeader bind:blockComplete {block} {testDataModal} />
<div
on:click={() => {
blockComplete = !blockComplete
}}
class="splitHeader"
>
<div class="center-items">
{#if externalActions[block.stepId]}
<img
alt={externalActions[block.stepId].name}
width="28px"
height="28px"
src={externalActions[block.stepId].icon}
/>
{:else}
<svg
width="28px"
height="28px"
class="spectrum-Icon"
style="color:grey;"
focusable="false"
>
<use xlink:href="#spectrum-icon-18-{block.icon}" />
</svg>
{/if}
<div class="iconAlign">
{#if isTrigger}
<Body size="XS">When this happens:</Body>
{:else}
<Body size="XS">Do this:</Body>
{/if}
<Detail size="S">{block?.name?.toUpperCase() || ""}</Detail>
</div>
</div>
<div class="blockTitle">
{#if testResult && testResult[0]}
<div style="float: right;" on:click={() => resultsModal.show()}>
<StatusLight
positive={isTrigger || testResult[0].outputs?.success}
negative={!testResult[0].outputs?.success}
><Body size="XS">View response</Body></StatusLight
>
</div>
{/if}
<div
style="margin-left: 10px;"
on:click={() => {
onSelect(block)
}}
>
<Icon name={blockComplete ? "ChevronDown" : "ChevronUp"} />
</div>
</div>
</div>
</div>
{#if !blockComplete} {#if !blockComplete}
<Divider noMargin /> <Divider noMargin />
<div class="blockSection"> <div class="blockSection">
@ -283,10 +221,6 @@
</div> </div>
{/if} {/if}
<Modal bind:this={resultsModal} width="30%">
<ResultsModal {isTrigger} {testResult} />
</Modal>
<Modal bind:this={actionModal} width="30%"> <Modal bind:this={actionModal} width="30%">
<ActionModal {blockIdx} bind:blockComplete /> <ActionModal {blockIdx} bind:blockComplete />
</Modal> </Modal>

111
packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte

@ -0,0 +1,111 @@
<script>
import { automationStore } from "builderStore"
import { Icon, Body, Detail, StatusLight } from "@budibase/bbui"
import { externalActions } from "./ExternalActions"
export let block
export let blockComplete
export let showTestStatus = false
export let showParameters = {}
$: testResult =
$automationStore.selectedAutomation?.testResults?.steps.filter(step =>
block.id ? step.id === block.id : step.stepId === block.stepId
)
$: isTrigger = block.type === "TRIGGER"
async function onSelect(block) {
await automationStore.update(state => {
state.selectedBlock = block
return state
})
}
</script>
<div class="blockSection">
<div
on:click={() => {
blockComplete = !blockComplete
showParameters[block.id] = blockComplete
}}
class="splitHeader"
>
<div class="center-items">
{#if externalActions[block.stepId]}
<img
alt={externalActions[block.stepId].name}
width="28px"
height="28px"
src={externalActions[block.stepId].icon}
/>
{:else}
<svg
width="28px"
height="28px"
class="spectrum-Icon"
style="color:grey;"
focusable="false"
>
<use xlink:href="#spectrum-icon-18-{block.icon}" />
</svg>
{/if}
<div class="iconAlign">
{#if isTrigger}
<Body size="XS">When this happens:</Body>
{:else}
<Body size="XS">Do this:</Body>
{/if}
<Detail size="S">{block?.name?.toUpperCase() || ""}</Detail>
</div>
</div>
<div class="blockTitle">
{#if showTestStatus && testResult && testResult[0]}
<div style="float: right;">
<StatusLight
positive={isTrigger || testResult[0].outputs?.success}
negative={!testResult[0].outputs?.success}
><Body size="XS"
>{testResult[0].outputs?.success || isTrigger
? "Success"
: "Error"}</Body
></StatusLight
>
</div>
{/if}
<div
style="margin-left: 10px; margin-bottom: var(--spacing-xs);"
on:click={() => {
onSelect(block)
}}
>
<Icon name={blockComplete ? "ChevronUp" : "ChevronDown"} />
</div>
</div>
</div>
</div>
<style>
.center-items {
display: flex;
align-items: center;
}
.splitHeader {
display: flex;
justify-content: space-between;
align-items: center;
}
.iconAlign {
padding: 0 0 0 var(--spacing-m);
display: inline-block;
}
.blockSection {
padding: var(--spacing-xl);
}
.blockTitle {
display: flex;
align-items: center;
}
</style>

133
packages/builder/src/components/automation/AutomationBuilder/FlowChart/ResultsModal.svelte

@ -1,133 +0,0 @@
<script>
import { ModalContent, Icon, Detail, TextArea, Label } from "@budibase/bbui"
export let testResult
export let isTrigger
let inputToggled
let outputToggled
</script>
<ModalContent
showCloseIcon={false}
showConfirmButton={false}
cancelText="Close"
>
<div slot="header" class="result-modal-header">
<span>Test Results</span>
<div>
{#if isTrigger || testResult[0].outputs.success}
<div class="iconSuccess">
<Icon size="S" name="CheckmarkCircle" />
</div>
{:else}
<div class="iconFailure">
<Icon size="S" name="CloseCircle" />
</div>
{/if}
</div>
</div>
<span>
{#if testResult[0].outputs.iterations}
<div style="display: flex;">
<Icon name="Reuse" />
<div style="margin-left: 10px;">
<Label>
This loop ran {testResult[0].outputs.iterations} times.</Label
>
</div>
</div>
{/if}
</span>
<div
on:click={() => {
inputToggled = !inputToggled
}}
class="toggle splitHeader"
>
<div>
<div style="display: flex; align-items: center;">
<span style="padding-left: var(--spacing-s);">
<Detail size="S">Input</Detail>
</span>
</div>
</div>
<div>
{#if inputToggled}
<Icon size="M" name="ChevronDown" />
{:else}
<Icon size="M" name="ChevronRight" />
{/if}
</div>
</div>
{#if inputToggled}
<div class="text-area-container">
<TextArea
disabled
value={JSON.stringify(testResult[0].inputs, null, 2)}
/>
</div>
{/if}
<div
on:click={() => {
outputToggled = !outputToggled
}}
class="toggle splitHeader"
>
<div>
<div style="display: flex; align-items: center;">
<span style="padding-left: var(--spacing-s);">
<Detail size="S">Output</Detail>
</span>
</div>
</div>
<div>
{#if outputToggled}
<Icon size="M" name="ChevronDown" />
{:else}
<Icon size="M" name="ChevronRight" />
{/if}
</div>
</div>
{#if outputToggled}
<div class="text-area-container">
<TextArea
disabled
value={JSON.stringify(testResult[0].outputs, null, 2)}
/>
</div>
{/if}
</ModalContent>
<style>
.result-modal-header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
}
.iconSuccess {
color: var(--spectrum-global-color-green-600);
}
.iconFailure {
color: var(--spectrum-global-color-red-600);
}
.splitHeader {
cursor: pointer;
display: flex;
justify-content: space-between;
}
.toggle {
display: flex;
align-items: center;
}
.text-area-container :global(textarea) {
height: 150px;
}
</style>

1
packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte

@ -51,6 +51,7 @@
$automationStore.selectedAutomation?.automation, $automationStore.selectedAutomation?.automation,
testData testData
) )
$automationStore.selectedAutomation.automation.showTestPanel = true
} catch (error) { } catch (error) {
notifications.error("Error testing notification") notifications.error("Error testing notification")
} }

146
packages/builder/src/components/automation/AutomationBuilder/TestPanel.svelte

@ -0,0 +1,146 @@
<script>
import { Icon, Divider, Tabs, Tab, TextArea, Label } from "@budibase/bbui"
import FlowItemHeader from "./FlowChart/FlowItemHeader.svelte"
import { automationStore } from "builderStore"
export let automation
let showParameters
let blocks
$: {
blocks = []
if (automation) {
if (automation.definition.trigger) {
blocks.push(automation.definition.trigger)
}
blocks = blocks
.concat(automation.definition.steps || [])
.filter(x => x.stepId !== "LOOP")
}
}
$: testResults =
$automationStore.selectedAutomation?.testResults?.steps.filter(
x => x.stepId !== "LOOP" || []
)
</script>
<div class="title">
<div class="title-text">
<Icon name="MultipleCheck" />
<div style="padding-left: var(--spacing-l)">Test Details</div>
</div>
<div style="padding-right: var(--spacing-xl)">
<Icon
on:click={async () => {
$automationStore.selectedAutomation.automation.showTestPanel = false
}}
hoverable
name="Close"
/>
</div>
</div>
<Divider />
<div class="container">
{#each blocks as block, idx}
<div class="block">
{#if block.stepId !== "LOOP"}
<FlowItemHeader showTestStatus={true} bind:showParameters {block} />
{#if showParameters && showParameters[block.id]}
<Divider noMargin />
{#if testResults?.[idx]?.outputs.iterations}
<div style="display: flex; padding: 10px 10px 0px 12px;">
<Icon name="Reuse" />
<div style="margin-left: 10px;">
<Label>
This loop ran {testResults?.[idx]?.outputs.iterations} times.</Label
>
</div>
</div>
{/if}
<div class="tabs">
<Tabs quiet noPadding selected="Input">
<Tab title="Input">
<div style="padding: 10px 10px 10px 10px;">
<TextArea
minHeight="80px"
disabled
value={JSON.stringify(testResults?.[idx]?.inputs, null, 2)}
/>
</div></Tab
>
<Tab title="Output">
<div style="padding: 10px 10px 10px 10px;">
<TextArea
minHeight="100px"
disabled
value={JSON.stringify(testResults?.[idx]?.outputs, null, 2)}
/>
</div>
</Tab>
</Tabs>
</div>
{/if}
{/if}
</div>
{#if blocks.length - 1 !== idx}
<div class="separator" />
{/if}
{/each}
</div>
<style>
.container {
padding: 0px 30px 0px 30px;
}
.title {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--spacing-xs);
padding-left: var(--spacing-xl);
justify-content: space-between;
}
.tabs {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
position: relative;
flex: 1 1 auto;
}
.title-text {
display: flex;
flex-direction: row;
align-items: center;
}
.title :global(h1) {
flex: 1 1 auto;
}
.block {
display: inline-block;
width: 400px;
font-size: 16px;
background-color: var(--background);
border: 1px solid var(--spectrum-global-color-gray-300);
border-radius: 4px 4px 4px 4px;
}
.separator {
width: 1px;
height: 40px;
border-left: 1px dashed var(--grey-4);
color: var(--grey-4);
/* center horizontally */
text-align: center;
margin-left: 50%;
}
</style>

35
packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte

@ -4,7 +4,12 @@
import AutomationPanel from "components/automation/AutomationPanel/AutomationPanel.svelte" import AutomationPanel from "components/automation/AutomationPanel/AutomationPanel.svelte"
import CreateAutomationModal from "components/automation/AutomationPanel/CreateAutomationModal.svelte" import CreateAutomationModal from "components/automation/AutomationPanel/CreateAutomationModal.svelte"
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte" import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
$: automation = $automationStore.automations[0] import TestPanel from "components/automation/AutomationBuilder/TestPanel.svelte"
$: automation =
$automationStore.selectedAutomation?.automation ||
$automationStore.automations[0]
let modal let modal
let webhookModal let webhookModal
</script> </script>
@ -39,6 +44,12 @@
</div> </div>
{/if} {/if}
</div> </div>
{#if automation?.showTestPanel}
<div class="setup">
<TestPanel {automation} />
</div>
{/if}
<Modal bind:this={modal}> <Modal bind:this={modal}>
<CreateAutomationModal {webhookModal} /> <CreateAutomationModal {webhookModal} />
</Modal> </Modal>
@ -52,7 +63,9 @@
flex: 1 1 auto; flex: 1 1 auto;
height: 0; height: 0;
display: grid; display: grid;
grid-template-columns: 260px minmax(510px, 1fr); grid-auto-flow: column dense;
grid-template-columns: 260px minmax(510px, 1fr) fit-content(500px);
overflow: hidden;
} }
.nav { .nav {
@ -64,17 +77,18 @@
border-right: var(--border-light); border-right: var(--border-light);
background-color: var(--background); background-color: var(--background);
padding-bottom: 60px; padding-bottom: 60px;
overflow: hidden;
} }
.content { .content {
position: relative; position: relative;
padding: var(--spacing-l) 40px; padding-top: var(--spacing-l);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
align-items: stretch; align-items: stretch;
gap: var(--spacing-l); gap: var(--spacing-l);
overflow: hidden; overflow: auto;
} }
.centered { .centered {
top: 0; top: 0;
@ -92,4 +106,17 @@
.main { .main {
width: 300px; width: 300px;
} }
.setup {
padding-top: var(--spectrum-global-dimension-size-200);
border-left: var(--border-light);
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-l);
background-color: var(--background);
grid-column: 3;
overflow: auto;
}
</style> </style>

6
packages/builder/src/pages/builder/auth/login.svelte

@ -28,7 +28,7 @@
async function login() { async function login() {
try { try {
await auth.login({ await auth.login({
username, username: username.trim(),
password, password,
}) })
if ($auth?.user?.forceResetPassword) { if ($auth?.user?.forceResetPassword) {
@ -80,7 +80,9 @@
/> />
</Layout> </Layout>
<Layout gap="XS" noPadding> <Layout gap="XS" noPadding>
<Button cta on:click={login}>Sign in to {company}</Button> <Button cta disabled={!username && !password} on:click={login}
>Sign in to {company}</Button
>
<ActionButton quiet on:click={() => $goto("./forgot")}> <ActionButton quiet on:click={() => $goto("./forgot")}>
Forgot password? Forgot password?
</ActionButton> </ActionButton>

2
packages/cli/package.json

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "1.0.167-alpha.8", "version": "1.0.176-alpha.0",
"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": {

8
packages/client/package.json

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "1.0.167-alpha.8", "version": "1.0.176-alpha.0",
"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.167-alpha.8", "@budibase/bbui": "^1.0.176-alpha.0",
"@budibase/frontend-core": "^1.0.167-alpha.8", "@budibase/frontend-core": "^1.0.176-alpha.0",
"@budibase/string-templates": "^1.0.167-alpha.8", "@budibase/string-templates": "^1.0.176-alpha.0",
"@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",

4
packages/frontend-core/package.json

@ -1,12 +1,12 @@
{ {
"name": "@budibase/frontend-core", "name": "@budibase/frontend-core",
"version": "1.0.167-alpha.8", "version": "1.0.176-alpha.0",
"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.167-alpha.8", "@budibase/bbui": "^1.0.176-alpha.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"svelte": "^3.46.2" "svelte": "^3.46.2"
} }

4
packages/server/Dockerfile

@ -16,6 +16,7 @@ ENV BUDIBASE_ENVIRONMENT=PRODUCTION
# copy files and install dependencies # copy files and install dependencies
COPY . ./ COPY . ./
RUN yarn RUN yarn
RUN yarn global add pm2
RUN yarn build RUN yarn build
# Install client for oracle datasource # Install client for oracle datasource
@ -28,4 +29,5 @@ EXPOSE 4001
# due to this causing yarn to stop installing dev dependencies # due to this causing yarn to stop installing dev dependencies
# which are actually needed to get this environment up and running # which are actually needed to get this environment up and running
ENV NODE_ENV=production ENV NODE_ENV=production
CMD ["yarn", "run:docker"] ENV CLUSTER_MODE=${CLUSTER_MODE}
CMD ["./docker_run.sh"]

4
packages/server/__mocks__/pg.ts

@ -14,7 +14,9 @@ module PgMock {
function Client() {} function Client() {}
Client.prototype.query = query Client.prototype.query = query
Client.prototype.end = jest.fn() Client.prototype.end = jest.fn(cb => {
if (cb) cb()
})
Client.prototype.connect = jest.fn() Client.prototype.connect = jest.fn()
Client.prototype.release = jest.fn() Client.prototype.release = jest.fn()

5
packages/server/docker_run.sh

@ -0,0 +1,5 @@
if [ -z $CLUSTER_MODE ]; then
yarn run:docker
else
yarn run:docker:cluster
fi

11
packages/server/package.json

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.167-alpha.8", "version": "1.0.176-alpha.0",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -18,6 +18,7 @@
"build:docker": "yarn run predocker && docker build . -t app-service --label version=$BUDIBASE_RELEASE_VERSION", "build:docker": "yarn run predocker && docker build . -t app-service --label version=$BUDIBASE_RELEASE_VERSION",
"build:docs": "node ./scripts/docs/generate.js open", "build:docs": "node ./scripts/docs/generate.js open",
"run:docker": "node dist/index.js", "run:docker": "node dist/index.js",
"run:docker:cluster": "pm2-runtime start pm2.config.js",
"dev:stack:up": "node scripts/dev/manage.js up", "dev:stack:up": "node scripts/dev/manage.js up",
"dev:stack:down": "node scripts/dev/manage.js down", "dev:stack:down": "node scripts/dev/manage.js down",
"dev:stack:nuke": "node scripts/dev/manage.js nuke", "dev:stack:nuke": "node scripts/dev/manage.js nuke",
@ -69,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.167-alpha.8", "@budibase/backend-core": "^1.0.176-alpha.0",
"@budibase/client": "^1.0.167-alpha.8", "@budibase/client": "^1.0.176-alpha.0",
"@budibase/pro": "1.0.167-alpha.8", "@budibase/pro": "1.0.176-alpha.0",
"@budibase/string-templates": "^1.0.167-alpha.8", "@budibase/string-templates": "^1.0.176-alpha.0",
"@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",

9
packages/server/pm2.config.js

@ -0,0 +1,9 @@
module.exports = {
apps: [
{
script: "dist/index.js",
instances: "max",
exec_mode: "cluster",
},
],
}

97
packages/server/scripts/load/users.js

@ -0,0 +1,97 @@
// get the JWT secret etc
require("../../src/environment")
require("@budibase/backend-core").init()
const {
getProdAppID,
generateGlobalUserID,
} = require("@budibase/backend-core/db")
const { doInTenant, getGlobalDB } = require("@budibase/backend-core/tenancy")
const { internalSaveUser } = require("@budibase/backend-core/utils")
const { publicApiUserFix } = require("../../src/utilities/users")
const { hash } = require("@budibase/backend-core/utils")
const USER_LOAD_NUMBER = 10000
const BATCH_SIZE = 200
const PASSWORD = "test"
const TENANT_ID = "default"
const APP_ID = process.argv[2]
const words = [
"test",
"testing",
"budi",
"mail",
"age",
"risk",
"load",
"uno",
"arm",
"leg",
"pen",
"glass",
"box",
"chicken",
"bottle",
]
if (!APP_ID) {
console.error("Must supply app ID as first CLI option!")
process.exit(-1)
}
const WORD_1 = words[Math.floor(Math.random() * words.length)]
const WORD_2 = words[Math.floor(Math.random() * words.length)]
let HASHED_PASSWORD
function generateUser(count) {
return {
_id: generateGlobalUserID(),
password: HASHED_PASSWORD,
email: `${WORD_1}${count}@${WORD_2}.com`,
roles: {
[getProdAppID(APP_ID)]: "BASIC",
},
status: "active",
forceResetPassword: false,
firstName: "John",
lastName: "Smith",
}
}
async function run() {
HASHED_PASSWORD = await hash(PASSWORD)
return doInTenant(TENANT_ID, async () => {
const db = getGlobalDB()
for (let i = 0; i < USER_LOAD_NUMBER; i += BATCH_SIZE) {
let userSavePromises = []
for (let j = 0; j < BATCH_SIZE; j++) {
// like the public API
const ctx = publicApiUserFix({
request: {
body: generateUser(i + j),
},
})
userSavePromises.push(
internalSaveUser(ctx.request.body, TENANT_ID, {
hashPassword: false,
requirePassword: true,
bulkCreate: true,
})
)
}
const users = await Promise.all(userSavePromises)
await db.bulkDocs(users)
console.log(`${i + BATCH_SIZE} users have been created.`)
}
})
}
run()
.then(() => {
console.log(`Generated ${USER_LOAD_NUMBER} users!`)
})
.catch(err => {
console.error("Failed for reason: ", err)
process.exit(-1)
})

11
packages/server/src/api/controllers/application.ts

@ -25,6 +25,7 @@ const {
import { BASE_LAYOUTS } from "../../constants/layouts" import { BASE_LAYOUTS } from "../../constants/layouts"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
const { processObject } = require("@budibase/string-templates") const { processObject } = require("@budibase/string-templates")
const { CacheKeys, bustCache } = require("@budibase/backend-core/cache")
const { const {
getAllApps, getAllApps,
isDevAppID, isDevAppID,
@ -326,6 +327,7 @@ const appPostCreate = async (ctx: any, appId: string) => {
export const create = async (ctx: any) => { export const create = async (ctx: any) => {
const newApplication = await quotas.addApp(() => performAppCreate(ctx)) const newApplication = await quotas.addApp(() => performAppCreate(ctx))
await appPostCreate(ctx, newApplication.appId) await appPostCreate(ctx, newApplication.appId)
await bustCache(CacheKeys.CHECKLIST)
ctx.body = newApplication ctx.body = newApplication
ctx.status = 200 ctx.status = 200
} }
@ -449,6 +451,15 @@ export const destroy = async (ctx: any) => {
} }
export const sync = async (ctx: any, next: any) => { export const sync = async (ctx: any, next: any) => {
if (env.DISABLE_AUTO_PROD_APP_SYNC) {
ctx.status = 200
ctx.body = {
message:
"App sync disabled. You can reenable with the DISABLE_AUTO_PROD_APP_SYNC environment variable.",
}
return next()
}
const appId = ctx.params.appId const appId = ctx.params.appId
if (!isDevAppID(appId)) { if (!isDevAppID(appId)) {
ctx.throw(400, "This action cannot be performed for production apps") ctx.throw(400, "This action cannot be performed for production apps")

27
packages/server/src/api/controllers/public/users.ts

@ -4,30 +4,9 @@ import {
readGlobalUser, readGlobalUser,
saveGlobalUser, saveGlobalUser,
} from "../../../utilities/workerRequests" } from "../../../utilities/workerRequests"
import { publicApiUserFix } from "../../../utilities/users"
import { search as stringSearch } from "./utils" import { search as stringSearch } from "./utils"
const { getProdAppID } = require("@budibase/backend-core/db")
function fixUser(ctx: any) {
if (!ctx.request.body) {
return ctx
}
if (!ctx.request.body._id && ctx.params.userId) {
ctx.request.body._id = ctx.params.userId
}
if (!ctx.request.body.roles) {
ctx.request.body.roles = {}
} else {
const newRoles: { [key: string]: string } = {}
for (let [appId, role] of Object.entries(ctx.request.body.roles)) {
// @ts-ignore
newRoles[getProdAppID(appId)] = role
}
ctx.request.body.roles = newRoles
}
return ctx
}
function getUser(ctx: any, userId?: string) { function getUser(ctx: any, userId?: string) {
if (userId) { if (userId) {
ctx.params = { userId } ctx.params = { userId }
@ -45,7 +24,7 @@ export async function search(ctx: any, next: any) {
} }
export async function create(ctx: any, next: any) { export async function create(ctx: any, next: any) {
const response = await saveGlobalUser(fixUser(ctx)) const response = await saveGlobalUser(publicApiUserFix(ctx))
ctx.body = await getUser(ctx, response._id) ctx.body = await getUser(ctx, response._id)
await next() await next()
} }
@ -61,7 +40,7 @@ export async function update(ctx: any, next: any) {
...ctx.request.body, ...ctx.request.body,
_rev: user._rev, _rev: user._rev,
} }
const response = await saveGlobalUser(fixUser(ctx)) const response = await saveGlobalUser(publicApiUserFix(ctx))
ctx.body = await getUser(ctx, response._id) ctx.body = await getUser(ctx, response._id)
await next() await next()
} }

7
packages/server/src/environment.js

@ -1,3 +1,5 @@
const { join } = require("path")
function isTest() { function isTest() {
return ( return (
process.env.NODE_ENV === "jest" || process.env.NODE_ENV === "jest" ||
@ -20,7 +22,9 @@ function isCypress() {
let LOADED = false let LOADED = false
if (!LOADED && isDev() && !isTest()) { if (!LOADED && isDev() && !isTest()) {
require("dotenv").config() require("dotenv").config({
path: join(__dirname, "..", ".env"),
})
LOADED = true LOADED = true
} }
@ -55,6 +59,7 @@ module.exports = {
BUDIBASE_ENVIRONMENT: process.env.BUDIBASE_ENVIRONMENT, BUDIBASE_ENVIRONMENT: process.env.BUDIBASE_ENVIRONMENT,
DISABLE_ACCOUNT_PORTAL: process.env.DISABLE_ACCOUNT_PORTAL, DISABLE_ACCOUNT_PORTAL: process.env.DISABLE_ACCOUNT_PORTAL,
TEMPLATE_REPOSITORY: process.env.TEMPLATE_REPOSITORY || "app", TEMPLATE_REPOSITORY: process.env.TEMPLATE_REPOSITORY || "app",
DISABLE_AUTO_PROD_APP_SYNC: process.env.DISABLE_AUTO_PROD_APP_SYNC,
// minor // minor
SALT_ROUNDS: process.env.SALT_ROUNDS, SALT_ROUNDS: process.env.SALT_ROUNDS,
LOGGER: process.env.LOGGER, LOGGER: process.env.LOGGER,

49
packages/server/src/integrations/postgres.ts

@ -136,7 +136,7 @@ module PostgresModule {
: undefined, : undefined,
} }
this.client = new Client(newConfig) this.client = new Client(newConfig)
this.setSchema() this.open = false
} }
getBindingIdentifier(): string { getBindingIdentifier(): string {
@ -147,7 +147,34 @@ module PostgresModule {
return parts.join(" || ") return parts.join(" || ")
} }
async openConnection() {
await this.client.connect()
if (!this.config.schema) {
this.config.schema = "public"
}
this.client.query(`SET search_path TO ${this.config.schema}`)
this.COLUMNS_SQL = `select * from information_schema.columns where table_schema = '${this.config.schema}'`
this.open = true
}
closeConnection() {
const pg = this
return new Promise<void>((resolve, reject) => {
this.client.end((err: any) => {
pg.open = false
if (err) {
reject(err)
} else {
resolve()
}
})
})
}
async internalQuery(query: SqlQuery, close: boolean = true) { async internalQuery(query: SqlQuery, close: boolean = true) {
if (!this.open) {
await this.openConnection()
}
const client = this.client const client = this.client
this.index = 1 this.index = 1
// need to handle a specific issue with json data types in postgres, // need to handle a specific issue with json data types in postgres,
@ -164,21 +191,14 @@ module PostgresModule {
try { try {
return await client.query(query.sql, query.bindings || []) return await client.query(query.sql, query.bindings || [])
} catch (err) { } catch (err) {
await this.client.end() await this.closeConnection()
// @ts-ignore // @ts-ignore
throw new Error(err) throw new Error(err)
} finally { } finally {
if (close) await this.client.end() if (close) {
} await this.closeConnection()
} }
async setSchema() {
await this.client.connect()
if (!this.config.schema) {
this.config.schema = "public"
} }
this.client.query(`SET search_path TO ${this.config.schema}`)
this.COLUMNS_SQL = `select * from information_schema.columns where table_schema = '${this.config.schema}'`
} }
/** /**
@ -188,6 +208,7 @@ module PostgresModule {
*/ */
async buildSchema(datasourceId: string, entities: Record<string, Table>) { async buildSchema(datasourceId: string, entities: Record<string, Table>) {
let tableKeys: { [key: string]: string[] } = {} let tableKeys: { [key: string]: string[] } = {}
await this.openConnection()
try { try {
const primaryKeysResponse = await this.client.query( const primaryKeysResponse = await this.client.query(
this.PRIMARY_KEYS_SQL this.PRIMARY_KEYS_SQL
@ -251,7 +272,7 @@ module PostgresModule {
// @ts-ignore // @ts-ignore
throw new Error(err) throw new Error(err)
} finally { } finally {
await this.client.end() await this.closeConnection()
} }
} }
@ -283,7 +304,7 @@ module PostgresModule {
for (let query of input) { for (let query of input) {
responses.push(await this.internalQuery(query, false)) responses.push(await this.internalQuery(query, false))
} }
await this.client.end() await this.closeConnection()
return responses return responses
} else { } else {
const response = await this.internalQuery(input) const response = await this.internalQuery(input)

21
packages/server/src/utilities/users.js

@ -1,6 +1,7 @@
const { InternalTables } = require("../db/utils") const { InternalTables } = require("../db/utils")
const { getGlobalUser } = require("../utilities/global") const { getGlobalUser } = require("../utilities/global")
const { getAppDB } = require("@budibase/backend-core/context") const { getAppDB } = require("@budibase/backend-core/context")
const { getProdAppID } = require("@budibase/backend-core/db")
exports.getFullUser = async (ctx, userId) => { exports.getFullUser = async (ctx, userId) => {
const global = await getGlobalUser(userId) const global = await getGlobalUser(userId)
@ -22,3 +23,23 @@ exports.getFullUser = async (ctx, userId) => {
_id: userId, _id: userId,
} }
} }
exports.publicApiUserFix = ctx => {
if (!ctx.request.body) {
return ctx
}
if (!ctx.request.body._id && ctx.params.userId) {
ctx.request.body._id = ctx.params.userId
}
if (!ctx.request.body.roles) {
ctx.request.body.roles = {}
} else {
const newRoles = {}
for (let [appId, role] of Object.entries(ctx.request.body.roles)) {
// @ts-ignore
newRoles[getProdAppID(appId)] = role
}
ctx.request.body.roles = newRoles
}
return ctx
}

18
packages/server/yarn.lock

@ -1014,10 +1014,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@1.0.167-alpha.8": "@budibase/backend-core@1.0.176-alpha.0":
version "1.0.167-alpha.8" version "1.0.176-alpha.0"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.167-alpha.8.tgz#e663928587cb3ec90d06b3c5766fce61c11e93a6" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.176-alpha.0.tgz#eca4d405f6a3feadc231ce851323005762b83b88"
integrity sha512-J4LtucuaJJd5lAs9mV6Rt9D5dCZhKNrrKajnFTXqOrjsyw0jsIfN2ku8ClV0C9J3NA3wAu2zjpfDaKSnNfg2OQ== integrity sha512-7GFAO499/rX/066jpChEH2CwOo+UI88MrZcceWqfbv3hBcV4nDUtVFUtzG63z74vQb9SHi0B15/qOYB5mzVQLw==
dependencies: dependencies:
"@techpass/passport-openidconnect" "^0.3.0" "@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0" aws-sdk "^2.901.0"
@ -1092,12 +1092,12 @@
svelte-flatpickr "^3.2.3" svelte-flatpickr "^3.2.3"
svelte-portal "^1.0.0" svelte-portal "^1.0.0"
"@budibase/pro@1.0.167-alpha.8": "@budibase/pro@1.0.176-alpha.0":
version "1.0.167-alpha.8" version "1.0.176-alpha.0"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.167-alpha.8.tgz#dfcecb85550bc07a89b8e0bd2dbff13543d99117" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.176-alpha.0.tgz#416522800ef47b6ccc9e8bfc0e8d722db54f5c68"
integrity sha512-MPEtYLnHfFchBzQs5nZSAavCANh32hd1VSXUB5KJSkEdtpQ9eoJ+hOGThrM9PJtTVX3Jq31nsg0/PmUkswBdAQ== integrity sha512-oe/HQsDpGZ3HO7t0TPqfKfh/FjZw5f9V22LxS45WXoOgL8b66U2WGwCaN7iF/zzxgPXlZ3h8TSHo9ai9d2UOgA==
dependencies: dependencies:
"@budibase/backend-core" "1.0.167-alpha.8" "@budibase/backend-core" "1.0.176-alpha.0"
node-fetch "^2.6.1" node-fetch "^2.6.1"
"@budibase/standard-components@^0.9.139": "@budibase/standard-components@^0.9.139":

2
packages/string-templates/package.json

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "1.0.167-alpha.8", "version": "1.0.176-alpha.0",
"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",

4
packages/worker/Dockerfile

@ -10,6 +10,7 @@ WORKDIR /app
# copy files and install dependencies # copy files and install dependencies
COPY . ./ COPY . ./
RUN yarn RUN yarn
RUN yarn global add pm2
EXPOSE 4001 EXPOSE 4001
@ -17,4 +18,5 @@ EXPOSE 4001
# due to this causing yarn to stop installing dev dependencies # due to this causing yarn to stop installing dev dependencies
# which are actually needed to get this environment up and running # which are actually needed to get this environment up and running
ENV NODE_ENV=production ENV NODE_ENV=production
CMD ["yarn", "run:docker"] ENV CLUSTER_MODE=${CLUSTER_MODE}
CMD ["./docker_run.sh"]

5
packages/worker/docker_run.sh

@ -0,0 +1,5 @@
if [[ -z $CLUSTER_MODE ]]; then
yarn run:docker
else
yarn run:docker:cluster
fi

9
packages/worker/package.json

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.167-alpha.8", "version": "1.0.176-alpha.0",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -15,6 +15,7 @@
"build": "rimraf dist/ && tsc", "build": "rimraf dist/ && tsc",
"postbuild": "copyfiles -u 1 src/**/*.hbs dist/", "postbuild": "copyfiles -u 1 src/**/*.hbs dist/",
"run:docker": "node dist/index.js", "run:docker": "node dist/index.js",
"run:docker:cluster": "pm2-runtime start pm2.config.js",
"build:docker": "docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION", "build:docker": "docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION",
"dev:stack:init": "node ./scripts/dev/manage.js init", "dev:stack:init": "node ./scripts/dev/manage.js init",
"dev:builder": "npm run dev:stack:init && nodemon", "dev:builder": "npm run dev:stack:init && nodemon",
@ -31,9 +32,9 @@
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "^1.0.167-alpha.8", "@budibase/backend-core": "^1.0.176-alpha.0",
"@budibase/pro": "1.0.167-alpha.8", "@budibase/pro": "1.0.176-alpha.0",
"@budibase/string-templates": "^1.0.167-alpha.8", "@budibase/string-templates": "^1.0.176-alpha.0",
"@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",

9
packages/worker/pm2.config.js

@ -0,0 +1,9 @@
module.exports = {
apps: [
{
script: "./dist/index.js",
instances: "max",
exec_mode: "cluster",
},
],
}

112
packages/worker/src/api/controllers/global/configs.js

@ -14,6 +14,11 @@ const {
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy") const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
const env = require("../../../environment") const env = require("../../../environment")
const { googleCallbackUrl, oidcCallbackUrl } = require("./auth") const { googleCallbackUrl, oidcCallbackUrl } = require("./auth")
const {
withCache,
CacheKeys,
bustCache,
} = require("@budibase/backend-core/cache")
const BB_TENANT_CDN = "https://tenants.cdn.budi.live" const BB_TENANT_CDN = "https://tenants.cdn.budi.live"
@ -43,6 +48,7 @@ exports.save = async function (ctx) {
try { try {
const response = await db.put(ctx.request.body) const response = await db.put(ctx.request.body)
await bustCache(CacheKeys.CHECKLIST)
ctx.body = { ctx.body = {
type, type,
_id: response.id, _id: response.id,
@ -249,58 +255,62 @@ exports.configChecklist = async function (ctx) {
const tenantId = getTenantId() const tenantId = getTenantId()
try { try {
// TODO: Watch get started video ctx.body = await withCache(
CacheKeys.CHECKLIST,
let apps = [] env.CHECKLIST_CACHE_TTL,
if (!env.MULTI_TENANCY || tenantId) { async () => {
// Apps exist let apps = []
apps = await getAllApps({ idsOnly: true, efficient: true }) if (!env.MULTI_TENANCY || tenantId) {
} // Apps exist
apps = await getAllApps({ idsOnly: true, efficient: true })
// They have set up SMTP }
const smtpConfig = await getScopedFullConfig(db, {
type: Configs.SMTP, // They have set up SMTP
}) const smtpConfig = await getScopedFullConfig(db, {
type: Configs.SMTP,
// They have set up Google Auth })
const googleConfig = await getScopedFullConfig(db, {
type: Configs.GOOGLE, // They have set up Google Auth
}) const googleConfig = await getScopedFullConfig(db, {
type: Configs.GOOGLE,
// They have set up OIDC })
const oidcConfig = await getScopedFullConfig(db, {
type: Configs.OIDC, // They have set up OIDC
}) const oidcConfig = await getScopedFullConfig(db, {
// They have set up an global user type: Configs.OIDC,
const users = await db.allDocs( })
getGlobalUserParams(null, {
include_docs: true, // They have set up an global user
}) const users = await db.allDocs(
getGlobalUserParams(null, {
include_docs: true,
limit: 1,
})
)
return {
apps: {
checked: apps.length > 0,
label: "Create your first app",
link: "/builder/portal/apps",
},
smtp: {
checked: !!smtpConfig,
label: "Set up email",
link: "/builder/portal/manage/email",
},
adminUser: {
checked: users && users.rows.length >= 1,
label: "Create your first user",
link: "/builder/portal/manage/users",
},
sso: {
checked: !!googleConfig || !!oidcConfig,
label: "Set up single sign-on",
link: "/builder/portal/manage/auth",
},
}
}
) )
const adminUser = users.rows.some(row => row.doc.admin)
ctx.body = {
apps: {
checked: apps.length > 0,
label: "Create your first app",
link: "/builder/portal/apps",
},
smtp: {
checked: !!smtpConfig,
label: "Set up email",
link: "/builder/portal/manage/email",
},
adminUser: {
checked: adminUser,
label: "Create your first user",
link: "/builder/portal/manage/users",
},
sso: {
checked: !!googleConfig || !!oidcConfig,
label: "Set up single sign-on",
link: "/builder/portal/manage/auth",
},
}
} catch (err) { } catch (err) {
ctx.throw(err.status, err) ctx.throw(err.status, err)
} }

18
packages/worker/src/api/controllers/global/users.ts

@ -3,9 +3,6 @@ const {
StaticDatabases, StaticDatabases,
} = require("@budibase/backend-core/db") } = require("@budibase/backend-core/db")
const { getGlobalUserByEmail } = require("@budibase/backend-core/utils") const { getGlobalUserByEmail } = require("@budibase/backend-core/utils")
import { EmailTemplatePurpose } from "../../../constants"
import { checkInviteCode } from "../../../utilities/redis"
import { sendEmail } from "../../../utilities/email"
const { user: userCache } = require("@budibase/backend-core/cache") const { user: userCache } = require("@budibase/backend-core/cache")
const { invalidateSessions } = require("@budibase/backend-core/sessions") const { invalidateSessions } = require("@budibase/backend-core/sessions")
const accounts = require("@budibase/backend-core/accounts") const accounts = require("@budibase/backend-core/accounts")
@ -17,11 +14,15 @@ const {
doesTenantExist, doesTenantExist,
} = require("@budibase/backend-core/tenancy") } = require("@budibase/backend-core/tenancy")
const { removeUserFromInfoDB } = require("@budibase/backend-core/deprovision") const { removeUserFromInfoDB } = require("@budibase/backend-core/deprovision")
const { errors } = require("@budibase/backend-core")
const { CacheKeys, bustCache } = require("@budibase/backend-core/cache")
import env from "../../../environment" import env from "../../../environment"
import { syncUserInApps } from "../../../utilities/appService" import { syncUserInApps } from "../../../utilities/appService"
import { quotas, users } from "@budibase/pro" import { quotas, users } from "@budibase/pro"
const { errors } = require("@budibase/backend-core")
import { allUsers, getUser } from "../../utilities" import { allUsers, getUser } from "../../utilities"
import { EmailTemplatePurpose } from "../../../constants"
import { checkInviteCode } from "../../../utilities/redis"
import { sendEmail } from "../../../utilities/email"
export const save = async (ctx: any) => { export const save = async (ctx: any) => {
try { try {
@ -93,7 +94,14 @@ export const adminUser = async (ctx: any) => {
tenantId, tenantId,
} }
try { try {
ctx.body = await users.save(user, tenantId, hashPassword, requirePassword) const finalUser = await users.save(
user,
tenantId,
hashPassword,
requirePassword
)
await bustCache(CacheKeys.CHECKLIST)
ctx.body = finalUser
} catch (err: any) { } catch (err: any) {
ctx.throw(err.status || 400, err) ctx.throw(err.status || 400, err)
} }

14
packages/worker/src/environment.js

@ -1,3 +1,5 @@
const { join } = require("path")
function isDev() { function isDev() {
return process.env.NODE_ENV !== "production" return process.env.NODE_ENV !== "production"
} }
@ -12,10 +14,18 @@ function isTest() {
let LOADED = false let LOADED = false
if (!LOADED && isDev() && !isTest()) { if (!LOADED && isDev() && !isTest()) {
require("dotenv").config() require("dotenv").config({
path: join(__dirname, "..", ".env"),
})
LOADED = true LOADED = true
} }
function parseIntSafe(number) {
if (number) {
return parseInt(number)
}
}
module.exports = { module.exports = {
// auth // auth
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
@ -49,6 +59,8 @@ module.exports = {
SMTP_HOST: process.env.SMTP_HOST, SMTP_HOST: process.env.SMTP_HOST,
SMTP_PORT: process.env.SMTP_PORT, SMTP_PORT: process.env.SMTP_PORT,
SMTP_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS, SMTP_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS,
// other
CHECKLIST_CACHE_TTL: parseIntSafe(process.env.CHECKLIST_CACHE_TTL) || 3600,
_set(key, value) { _set(key, value) {
process.env[key] = value process.env[key] = value
module.exports[key] = value module.exports[key] = value

1
packages/worker/src/utilities/redis.js

@ -61,6 +61,7 @@ exports.shutdown = async () => {
* Given a user ID this will store a code (that is returned) for an hour in redis. * Given a user ID this will store a code (that is returned) for an hour in redis.
* The user can then return this code for resetting their password (through their reset link). * The user can then return this code for resetting their password (through their reset link).
* @param {string} userId the ID of the user which is to be reset. * @param {string} userId the ID of the user which is to be reset.
* @param {object} info Info about the user/the reset process.
* @return {Promise<string>} returns the code that was stored to redis. * @return {Promise<string>} returns the code that was stored to redis.
*/ */
exports.getResetPasswordCode = async (userId, info) => { exports.getResetPasswordCode = async (userId, info) => {

18
packages/worker/yarn.lock

@ -293,10 +293,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@1.0.167-alpha.8": "@budibase/backend-core@1.0.176-alpha.0":
version "1.0.167-alpha.8" version "1.0.176-alpha.0"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.167-alpha.8.tgz#e663928587cb3ec90d06b3c5766fce61c11e93a6" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.176-alpha.0.tgz#eca4d405f6a3feadc231ce851323005762b83b88"
integrity sha512-J4LtucuaJJd5lAs9mV6Rt9D5dCZhKNrrKajnFTXqOrjsyw0jsIfN2ku8ClV0C9J3NA3wAu2zjpfDaKSnNfg2OQ== integrity sha512-7GFAO499/rX/066jpChEH2CwOo+UI88MrZcceWqfbv3hBcV4nDUtVFUtzG63z74vQb9SHi0B15/qOYB5mzVQLw==
dependencies: dependencies:
"@techpass/passport-openidconnect" "^0.3.0" "@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0" aws-sdk "^2.901.0"
@ -322,12 +322,12 @@
uuid "^8.3.2" uuid "^8.3.2"
zlib "^1.0.5" zlib "^1.0.5"
"@budibase/pro@1.0.167-alpha.8": "@budibase/pro@1.0.176-alpha.0":
version "1.0.167-alpha.8" version "1.0.176-alpha.0"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.167-alpha.8.tgz#dfcecb85550bc07a89b8e0bd2dbff13543d99117" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.176-alpha.0.tgz#416522800ef47b6ccc9e8bfc0e8d722db54f5c68"
integrity sha512-MPEtYLnHfFchBzQs5nZSAavCANh32hd1VSXUB5KJSkEdtpQ9eoJ+hOGThrM9PJtTVX3Jq31nsg0/PmUkswBdAQ== integrity sha512-oe/HQsDpGZ3HO7t0TPqfKfh/FjZw5f9V22LxS45WXoOgL8b66U2WGwCaN7iF/zzxgPXlZ3h8TSHo9ai9d2UOgA==
dependencies: dependencies:
"@budibase/backend-core" "1.0.167-alpha.8" "@budibase/backend-core" "1.0.176-alpha.0"
node-fetch "^2.6.1" node-fetch "^2.6.1"
"@cspotcode/source-map-consumer@0.8.0": "@cspotcode/source-map-consumer@0.8.0":

Loading…
Cancel
Save