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

This commit is contained in:
Andrew Kingston 2022-05-24 09:47:00 +01:00
commit bff49a33f0
49 changed files with 811 additions and 593 deletions

View File

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

View File

@ -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,
} }

View File

@ -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"
} }

View File

@ -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" })
} }
}) })

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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]
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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
},
} }

View File

@ -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

View File

@ -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.
*/ */

View File

@ -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"

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.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",

View File

@ -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",

View File

@ -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
}) })

View File

@ -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);

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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")
} }

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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": {

View File

@ -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",

View File

@ -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"
} }

View File

@ -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"]

View File

@ -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 Executable file
View File

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

View File

@ -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",

View File

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

View File

@ -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)
})

View File

@ -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")

View File

@ -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()
} }

View File

@ -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,

View File

@ -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,23 +191,16 @@ 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}'`
}
/** /**
* Fetches the tables from the postgres table and assigns them to the datasource. * Fetches the tables from the postgres table and assigns them to the datasource.
* @param {*} datasourceId - datasourceId to fetch * @param {*} datasourceId - datasourceId to fetch
@ -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)

View File

@ -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
}

View File

@ -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":

View File

@ -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",

View File

@ -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 Executable file
View File

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

View File

@ -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",

View File

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

View File

@ -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,
env.CHECKLIST_CACHE_TTL,
async () => {
let apps = []
if (!env.MULTI_TENANCY || tenantId) {
// Apps exist
apps = await getAllApps({ idsOnly: true, efficient: true })
}
let apps = [] // They have set up SMTP
if (!env.MULTI_TENANCY || tenantId) { const smtpConfig = await getScopedFullConfig(db, {
// Apps exist type: Configs.SMTP,
apps = await getAllApps({ idsOnly: true, efficient: true }) })
}
// They have set up SMTP // They have set up Google Auth
const smtpConfig = await getScopedFullConfig(db, { const googleConfig = await getScopedFullConfig(db, {
type: Configs.SMTP, type: Configs.GOOGLE,
}) })
// They have set up Google Auth // They have set up OIDC
const googleConfig = await getScopedFullConfig(db, { const oidcConfig = await getScopedFullConfig(db, {
type: Configs.GOOGLE, type: Configs.OIDC,
}) })
// They have set up OIDC // They have set up an global user
const oidcConfig = await getScopedFullConfig(db, { const users = await db.allDocs(
type: Configs.OIDC, getGlobalUserParams(null, {
}) include_docs: true,
// They have set up an global user limit: 1,
const users = await db.allDocs( })
getGlobalUserParams(null, { )
include_docs: true, 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)
} }

View File

@ -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)
} }

View File

@ -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

View File

@ -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) => {

View File

@ -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":