Merge branch 'feature/draft-apps' of https://github.com/Budibase/budibase into feature/draft-apps

This commit is contained in:
Martin McKeaveney 2021-05-17 16:43:35 +01:00
commit ad64d58af4
5 changed files with 88 additions and 34 deletions

View File

@ -141,6 +141,18 @@ exports.getRoleParams = (roleId = null, otherProps = {}) => {
return getDocParams(DocumentTypes.ROLE, roleId, otherProps) return getDocParams(DocumentTypes.ROLE, roleId, otherProps)
} }
/**
* Convert a development app ID to a deployed app ID.
*/
exports.getDeployedAppID = appId => {
// if dev, convert it
if (appId.startsWith(exports.APP_DEV_PREFIX)) {
const id = appId.split(exports.APP_DEV_PREFIX)[1]
return `${exports.APP_PREFIX}${id}`
}
return appId
}
/** /**
* Lots of different points in the system need to find the full list of apps, this will * Lots of different points in the system need to find the full list of apps, this will
* enumerate the entire CouchDB cluster and get the list of databases (every app). * enumerate the entire CouchDB cluster and get the list of databases (every app).

View File

@ -2,6 +2,7 @@ const fetch = require("node-fetch")
const env = require("../environment") const env = require("../environment")
const { checkSlashesInUrl } = require("./index") const { checkSlashesInUrl } = require("./index")
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { getDeployedAppID } = require("@budibase/auth/db")
function getAppRole(appId, user) { function getAppRole(appId, user) {
if (!user.roles) { if (!user.roles) {
@ -95,6 +96,8 @@ exports.deleteGlobalUser = async (ctx, globalId) => {
} }
exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => { exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => {
// always use the deployed app
appId = getDeployedAppID(appId)
const endpoint = globalId const endpoint = globalId
? `/api/admin/users/${globalId}` ? `/api/admin/users/${globalId}`
: `/api/admin/users` : `/api/admin/users`
@ -119,9 +122,14 @@ exports.saveGlobalUser = async (ctx, appId, body) => {
const globalUser = body._id const globalUser = body._id
? await exports.getGlobalUsers(ctx, appId, body._id) ? await exports.getGlobalUsers(ctx, appId, body._id)
: {} : {}
const roles = globalUser.roles || {} const preRoles = globalUser.roles || {}
if (body.roleId) { if (body.roleId) {
roles[appId] = body.roleId preRoles[appId] = body.roleId
}
// make sure no dev app IDs in roles
const roles = {}
for (let [appId, roleId] of Object.entries(preRoles)) {
roles[getDeployedAppID(appId)] = roleId
} }
const endpoint = `/api/admin/users` const endpoint = `/api/admin/users`
const reqCfg = { const reqCfg = {

View File

@ -1,24 +1,37 @@
const { getAllRoles } = require("@budibase/auth/roles") const { getAllRoles } = require("@budibase/auth/roles")
const { getAllApps } = require("@budibase/auth/db") const { getAllApps, getDeployedAppID, DocumentTypes } = require("@budibase/auth/db")
const CouchDB = require("../../../db")
exports.fetch = async ctx => { exports.fetch = async ctx => {
// always use the dev apps as they'll be most up to date (true) // always use the dev apps as they'll be most up to date (true)
const apps = await getAllApps(true) const apps = await getAllApps(true)
const promises = [] const promises = []
for (let app of apps) { for (let app of apps) {
promises.push(getAllRoles(app._id)) // use dev app IDs
promises.push(getAllRoles(app.appId))
} }
const roles = await Promise.all(promises) const roles = await Promise.all(promises)
const response = {} const response = {}
for (let app of apps) { for (let app of apps) {
response[app._id] = roles.shift() const deployedAppId = getDeployedAppID(app.appId)
response[deployedAppId] = {
roles: roles.shift(),
name: app.name,
version: app.version,
url: app.url,
}
} }
ctx.body = response ctx.body = response
} }
exports.find = async ctx => { exports.find = async ctx => {
const appId = ctx.params.appId const appId = ctx.params.appId
const db = new CouchDB(appId)
const app = await db.get(DocumentTypes.APP_METADATA)
ctx.body = { ctx.body = {
roles: await getAllRoles(appId), roles: await getAllRoles(appId),
name: app.name,
version: app.version,
url: app.url,
} }
} }

View File

@ -30,45 +30,65 @@ const EmailTemplatePurpose = {
CUSTOM: "custom", CUSTOM: "custom",
} }
const InternalTemplateBindings = {
PLATFORM_URL: "platformUrl",
COMPANY: "company",
LOGO_URL: "logoUrl",
EMAIL: "email",
USER: "user",
REQUEST: "request",
DOCS_URL: "docsUrl",
LOGIN_URL: "loginUrl",
CURRENT_YEAR: "currentYear",
CURRENT_DATE: "currentDate",
BODY: "body",
STYLES: "styles",
RESET_URL: "resetUrl",
RESET_CODE: "resetCode",
INVITE_URL: "inviteUrl",
INVITE_CODE: "inviteUrl",
CONTENTS: "contents",
}
const TemplateBindings = { const TemplateBindings = {
PLATFORM_URL: { PLATFORM_URL: {
name: "platformUrl", name: InternalTemplateBindings.PLATFORM_URL,
description: "The URL used to access the budibase platform", description: "The URL used to access the budibase platform",
}, },
COMPANY: { COMPANY: {
name: "company", name: InternalTemplateBindings.COMPANY,
description: "The name of your organization", description: "The name of your organization",
}, },
LOGO_URL: { LOGO_URL: {
name: "logoUrl", name: InternalTemplateBindings.LOGO_URL,
description: "The URL of your organizations logo.", description: "The URL of your organizations logo.",
}, },
EMAIL: { EMAIL: {
name: "email", name: InternalTemplateBindings.EMAIL,
description: "The recipients email address.", description: "The recipients email address.",
}, },
USER: { USER: {
name: "user", name: InternalTemplateBindings.USER,
description: "The recipients user object.", description: "The recipients user object.",
}, },
REQUEST: { REQUEST: {
name: "request", name: InternalTemplateBindings.REQUEST,
description: "Additional request metadata.", description: "Additional request metadata.",
}, },
DOCS_URL: { DOCS_URL: {
name: "docsUrl", name: InternalTemplateBindings.DOCS_URL,
description: "Organization documentation URL.", description: "Organization documentation URL.",
}, },
LOGIN_URL: { LOGIN_URL: {
name: "loginUrl", name: InternalTemplateBindings.LOGIN_URL,
description: "The URL used to log into the organization budibase instance.", description: "The URL used to log into the organization budibase instance.",
}, },
CURRENT_YEAR: { CURRENT_YEAR: {
name: "currentYear", name: InternalTemplateBindings.CURRENT_YEAR,
description: "The current year.", description: "The current year.",
}, },
CURRENT_DATE: { CURRENT_DATE: {
name: "currentDate", name: InternalTemplateBindings.CURRENT_DATE,
description: "The current date.", description: "The current date.",
}, },
} }
@ -80,11 +100,11 @@ const TemplateMetadata = {
purpose: EmailTemplatePurpose.BASE, purpose: EmailTemplatePurpose.BASE,
bindings: [ bindings: [
{ {
name: "body", name: InternalTemplateBindings.BODY,
description: "The main body of another email template.", description: "The main body of another email template.",
}, },
{ {
name: "styles", name: InternalTemplateBindings.STYLES,
description: "The contents of the Styling email template.", description: "The contents of the Styling email template.",
}, },
], ],
@ -94,12 +114,12 @@ const TemplateMetadata = {
purpose: EmailTemplatePurpose.PASSWORD_RECOVERY, purpose: EmailTemplatePurpose.PASSWORD_RECOVERY,
bindings: [ bindings: [
{ {
name: "resetUrl", name: InternalTemplateBindings.RESET_URL,
description: description:
"The URL the recipient must click to reset their password.", "The URL the recipient must click to reset their password.",
}, },
{ {
name: "resetCode", name: InternalTemplateBindings.RESET_CODE,
description: description:
"The temporary password reset code used in the recipients password reset URL.", "The temporary password reset code used in the recipients password reset URL.",
}, },
@ -110,12 +130,12 @@ const TemplateMetadata = {
purpose: EmailTemplatePurpose.INVITATION, purpose: EmailTemplatePurpose.INVITATION,
bindings: [ bindings: [
{ {
name: "inviteUrl", name: InternalTemplateBindings.INVITE_URL,
description: description:
"The URL the recipient must click to accept the invitation and activate their account.", "The URL the recipient must click to accept the invitation and activate their account.",
}, },
{ {
name: "inviteCode", name: InternalTemplateBindings.INVITE_CODE,
description: description:
"The temporary invite code used in the recipients invitation URL.", "The temporary invite code used in the recipients invitation URL.",
}, },
@ -126,7 +146,7 @@ const TemplateMetadata = {
purpose: EmailTemplatePurpose.CUSTOM, purpose: EmailTemplatePurpose.CUSTOM,
bindings: [ bindings: [
{ {
name: "contents", name: InternalTemplateBindings.CONTENTS,
description: "Custom content body.", description: "Custom content body.",
}, },
], ],
@ -142,4 +162,5 @@ exports.TemplateTypes = TemplateTypes
exports.EmailTemplatePurpose = EmailTemplatePurpose exports.EmailTemplatePurpose = EmailTemplatePurpose
exports.TemplateMetadata = TemplateMetadata exports.TemplateMetadata = TemplateMetadata
exports.TemplateBindings = TemplateBindings exports.TemplateBindings = TemplateBindings
exports.InternalTemplateBindings = InternalTemplateBindings
exports.GLOBAL_OWNER = "global" exports.GLOBAL_OWNER = "global"

View File

@ -2,7 +2,7 @@ const CouchDB = require("../db")
const { getScopedConfig, StaticDatabases } = require("@budibase/auth").db const { getScopedConfig, StaticDatabases } = require("@budibase/auth").db
const { const {
Configs, Configs,
TemplateBindings, InternalTemplateBindings,
LOGO_URL, LOGO_URL,
EmailTemplatePurpose, EmailTemplatePurpose,
} = require("../constants") } = require("../constants")
@ -21,27 +21,27 @@ exports.getSettingsTemplateContext = async (purpose, code = null) => {
} }
const URL = settings.platformUrl const URL = settings.platformUrl
const context = { const context = {
[TemplateBindings.LOGO_URL]: [InternalTemplateBindings.LOGO_URL]:
checkSlashesInUrl(`${URL}/${settings.logoUrl}`) || LOGO_URL, checkSlashesInUrl(`${URL}/${settings.logoUrl}`) || LOGO_URL,
[TemplateBindings.PLATFORM_URL]: URL, [InternalTemplateBindings.PLATFORM_URL]: URL,
[TemplateBindings.COMPANY]: settings.company || BASE_COMPANY, [InternalTemplateBindings.COMPANY]: settings.company || BASE_COMPANY,
[TemplateBindings.DOCS_URL]: [InternalTemplateBindings.DOCS_URL]:
settings.docsUrl || "https://docs.budibase.com/", settings.docsUrl || "https://docs.budibase.com/",
[TemplateBindings.LOGIN_URL]: checkSlashesInUrl(`${URL}/login`), [InternalTemplateBindings.LOGIN_URL]: checkSlashesInUrl(`${URL}/login`),
[TemplateBindings.CURRENT_DATE]: new Date().toISOString(), [InternalTemplateBindings.CURRENT_DATE]: new Date().toISOString(),
[TemplateBindings.CURRENT_YEAR]: new Date().getFullYear(), [InternalTemplateBindings.CURRENT_YEAR]: new Date().getFullYear(),
} }
// attach purpose specific context // attach purpose specific context
switch (purpose) { switch (purpose) {
case EmailTemplatePurpose.PASSWORD_RECOVERY: case EmailTemplatePurpose.PASSWORD_RECOVERY:
context[TemplateBindings.RESET_CODE] = code context[InternalTemplateBindings.RESET_CODE] = code
context[TemplateBindings.RESET_URL] = checkSlashesInUrl( context[InternalTemplateBindings.RESET_URL] = checkSlashesInUrl(
`${URL}/reset?code=${code}` `${URL}/reset?code=${code}`
) )
break break
case EmailTemplatePurpose.INVITATION: case EmailTemplatePurpose.INVITATION:
context[TemplateBindings.INVITE_CODE] = code context[InternalTemplateBindings.INVITE_CODE] = code
context[TemplateBindings.INVITE_URL] = checkSlashesInUrl( context[InternalTemplateBindings.INVITE_URL] = checkSlashesInUrl(
`${URL}/invite?code=${code}` `${URL}/invite?code=${code}`
) )
break break