1
0
Fork 0
mirror of synced 2024-09-30 00:57:16 +13:00

Refactoring worker, converting all controllers/routes to Typescript and all imports of backend-core to new style.

This commit is contained in:
mike12345567 2022-11-23 18:25:20 +00:00
parent df80aa974a
commit bfaef13768
41 changed files with 453 additions and 433 deletions

View file

@ -4,7 +4,7 @@ const JwtStrategy = require("passport-jwt").Strategy
import { getGlobalDB } from "./tenancy" import { getGlobalDB } from "./tenancy"
const refresh = require("passport-oauth2-refresh") const refresh = require("passport-oauth2-refresh")
import { Config } from "./constants" import { Config } from "./constants"
import { getScopedConfig } from "./db/utils" import { getScopedConfig } from "./db"
import { import {
jwt, jwt,
local, local,

View file

@ -1,14 +1,15 @@
const { objectStore } = require("@budibase/backend-core")
const fs = require("fs")
const { join } = require("path")
const { TEMP_DIR, MINIO_DIR } = require("./utils")
const { progressBar } = require("../utils")
const { const {
ObjectStoreBuckets, ObjectStoreBuckets,
ObjectStore, ObjectStore,
retrieve, retrieve,
uploadDirectory, uploadDirectory,
makeSureBucketExists, makeSureBucketExists,
} = require("@budibase/backend-core/objectStore") } = objectStore
const fs = require("fs")
const { join } = require("path")
const { TEMP_DIR, MINIO_DIR } = require("./utils")
const { progressBar } = require("../utils")
const bucketList = Object.values(ObjectStoreBuckets) const bucketList = Object.values(ObjectStoreBuckets)

View file

@ -4,7 +4,7 @@ const { getSkeleton, fleshOutSkeleton } = require("./skeleton")
const questions = require("../questions") const questions = require("../questions")
const fs = require("fs") const fs = require("fs")
const { PLUGIN_TYPE_ARR } = require("@budibase/types") const { PLUGIN_TYPE_ARR } = require("@budibase/types")
const { validate } = require("@budibase/backend-core/plugins") const { plugins } = require("@budibase/backend-core")
const { runPkgCommand } = require("../exec") const { runPkgCommand } = require("../exec")
const { join } = require("path") const { join } = require("path")
const { success, error, info, moveDirectory } = require("../utils") const { success, error, info, moveDirectory } = require("../utils")
@ -107,7 +107,7 @@ async function verify() {
} }
name = pkgJson.name name = pkgJson.name
version = pkgJson.version version = pkgJson.version
validate(schemaJson) plugins.validate(schemaJson)
return { name, version } return { name, version }
} catch (err) { } catch (err) {
if (err && err.message && err.message.includes("not valid JSON")) { if (err && err.message && err.message.includes("not valid JSON")) {

View file

@ -62,7 +62,9 @@ export const isOIDCConfig = (config: Config): config is OIDCConfig =>
export enum ConfigType { export enum ConfigType {
SETTINGS = "settings", SETTINGS = "settings",
ACCOUNT = "account",
SMTP = "smtp", SMTP = "smtp",
GOOGLE = "google", GOOGLE = "google",
OIDC = "oidc", OIDC = "oidc",
OIDC_LOGOS = "logos_oidc",
} }

View file

@ -4,3 +4,4 @@ export * from "./userGroup"
export * from "./plugin" export * from "./plugin"
export * from "./quotas" export * from "./quotas"
export * from "./schedule" export * from "./schedule"
export * from "./templates"

View file

@ -0,0 +1,9 @@
import { Document } from "../document"
export interface Template extends Document {
ownerId?: string
name?: string
contents: string
purpose: string
type?: string
}

View file

@ -1,25 +1,25 @@
import core from "@budibase/backend-core" import { utils, constants, auth, db as dbCore } from "@budibase/backend-core"
import { import {
events, events,
users as usersCore, users as usersCore,
context, context,
tenancy, tenancy,
} from "@budibase/backend-core" } from "@budibase/backend-core"
import { Config, EmailTemplatePurpose } from "../../../constants" import { EmailTemplatePurpose } from "../../../constants"
import { sendEmail, isEmailConfigured } from "../../../utilities/email" import { sendEmail, isEmailConfigured } from "../../../utilities/email"
import { checkResetPasswordCode } from "../../../utilities/redis" import { checkResetPasswordCode } from "../../../utilities/redis"
import env from "../../../environment" import env from "../../../environment"
import sdk from "../../../sdk" import sdk from "../../../sdk"
import { User } from "@budibase/types" import { User, Config, ConfigType } from "@budibase/types"
const { setCookie, getCookie, clearCookie, hash, platformLogout } = core.utils const { setCookie, getCookie, clearCookie, hash, platformLogout } = utils
const { Cookie, Header } = core.constants const { Cookie, Header } = constants
const { passport, ssoCallbackUrl, google, oidc } = core.auth const { passport, ssoCallbackUrl, google, oidc } = auth
export const googleCallbackUrl = async (config: any) => { export async function googleCallbackUrl(config?: Config) {
return ssoCallbackUrl(tenancy.getGlobalDB(), config, "google") return ssoCallbackUrl(tenancy.getGlobalDB(), config, "google")
} }
export const oidcCallbackUrl = async (config: any) => { export async function oidcCallbackUrl(config?: Config) {
return ssoCallbackUrl(tenancy.getGlobalDB(), config, "oidc") return ssoCallbackUrl(tenancy.getGlobalDB(), config, "oidc")
} }
@ -135,7 +135,7 @@ export const logout = async (ctx: any) => {
export const datasourcePreAuth = async (ctx: any, next: any) => { export const datasourcePreAuth = async (ctx: any, next: any) => {
const provider = ctx.params.provider const provider = ctx.params.provider
const middleware = require(`@budibase/backend-core/middleware`) const { middleware } = require(`@budibase/backend-core`)
const handler = middleware.datasource[provider] const handler = middleware.datasource[provider]
setCookie( setCookie(
@ -154,7 +154,7 @@ export const datasourcePreAuth = async (ctx: any, next: any) => {
export const datasourceAuth = async (ctx: any, next: any) => { export const datasourceAuth = async (ctx: any, next: any) => {
const authStateCookie = getCookie(ctx, Cookie.DatasourceAuth) const authStateCookie = getCookie(ctx, Cookie.DatasourceAuth)
const provider = authStateCookie.provider const provider = authStateCookie.provider
const middleware = require(`@budibase/backend-core/middleware`) const { middleware } = require(`@budibase/backend-core`)
const handler = middleware.datasource[provider] const handler = middleware.datasource[provider]
return handler.postAuth(passport, ctx, next) return handler.postAuth(passport, ctx, next)
} }
@ -166,11 +166,11 @@ export const datasourceAuth = async (ctx: any, next: any) => {
export const googlePreAuth = async (ctx: any, next: any) => { export const googlePreAuth = async (ctx: any, next: any) => {
const db = tenancy.getGlobalDB() const db = tenancy.getGlobalDB()
const config = await core.db.getScopedConfig(db, { const config = await dbCore.getScopedConfig(db, {
type: Config.GOOGLE, type: ConfigType.GOOGLE,
workspace: ctx.query.workspace, workspace: ctx.query.workspace,
}) })
let callbackUrl = await exports.googleCallbackUrl(config) let callbackUrl = await googleCallbackUrl(config)
const strategy = await google.strategyFactory( const strategy = await google.strategyFactory(
config, config,
callbackUrl, callbackUrl,
@ -187,11 +187,11 @@ export const googlePreAuth = async (ctx: any, next: any) => {
export const googleAuth = async (ctx: any, next: any) => { export const googleAuth = async (ctx: any, next: any) => {
const db = tenancy.getGlobalDB() const db = tenancy.getGlobalDB()
const config = await core.db.getScopedConfig(db, { const config = await dbCore.getScopedConfig(db, {
type: Config.GOOGLE, type: ConfigType.GOOGLE,
workspace: ctx.query.workspace, workspace: ctx.query.workspace,
}) })
const callbackUrl = await exports.googleCallbackUrl(config) const callbackUrl = await googleCallbackUrl(config)
const strategy = await google.strategyFactory( const strategy = await google.strategyFactory(
config, config,
callbackUrl, callbackUrl,
@ -213,13 +213,13 @@ export const googleAuth = async (ctx: any, next: any) => {
export const oidcStrategyFactory = async (ctx: any, configId: any) => { export const oidcStrategyFactory = async (ctx: any, configId: any) => {
const db = tenancy.getGlobalDB() const db = tenancy.getGlobalDB()
const config = await core.db.getScopedConfig(db, { const config = await dbCore.getScopedConfig(db, {
type: Config.OIDC, type: ConfigType.OIDC,
group: ctx.query.group, group: ctx.query.group,
}) })
const chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0] const chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0]
let callbackUrl = await exports.oidcCallbackUrl(chosenConfig) let callbackUrl = await oidcCallbackUrl(chosenConfig)
//Remote Config //Remote Config
const enrichedConfig = await oidc.fetchStrategyConfig( const enrichedConfig = await oidc.fetchStrategyConfig(
@ -240,8 +240,8 @@ export const oidcPreAuth = async (ctx: any, next: any) => {
setCookie(ctx, configId, Cookie.OIDC_CONFIG) setCookie(ctx, configId, Cookie.OIDC_CONFIG)
const db = tenancy.getGlobalDB() const db = tenancy.getGlobalDB()
const config = await core.db.getScopedConfig(db, { const config = await dbCore.getScopedConfig(db, {
type: Config.OIDC, type: ConfigType.OIDC,
group: ctx.query.group, group: ctx.query.group,
}) })

View file

@ -1,28 +1,26 @@
const { import * as email from "../../../utilities/email"
generateConfigID, import env from "../../../environment"
getConfigParams, import { googleCallbackUrl, oidcCallbackUrl } from "./auth"
getScopedFullConfig, import {
getAllApps, events,
} = require("@budibase/backend-core/db")
const { Config } = require("../../../constants")
const email = require("../../../utilities/email")
const {
upload,
ObjectStoreBuckets,
} = require("@budibase/backend-core/objectStore")
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
const env = require("../../../environment")
const { googleCallbackUrl, oidcCallbackUrl } = require("./auth")
const {
withCache,
CacheKeys,
bustCache,
cache, cache,
} = require("@budibase/backend-core/cache") objectStore,
const { events } = require("@budibase/backend-core") tenancy,
const { checkAnyUserExists } = require("../../../utilities/users") db as dbCore,
} from "@budibase/backend-core"
import { checkAnyUserExists } from "../../../utilities/users"
import {
Database,
Config as ConfigDoc,
ConfigType,
SSOType,
GoogleConfig,
OIDCConfig,
SettingsConfig,
BBContext,
} from "@budibase/types"
const getEventFns = async (db, config) => { const getEventFns = async (db: Database, config: ConfigDoc) => {
const fns = [] const fns = []
const type = config.type const type = config.type
@ -31,41 +29,45 @@ const getEventFns = async (db, config) => {
existing = await db.get(config._id) existing = await db.get(config._id)
} }
const ssoType = type as SSOType
if (!existing) { if (!existing) {
switch (config.type) { switch (config.type) {
case Config.SMTP: { case ConfigType.SMTP: {
fns.push(events.email.SMTPCreated) fns.push(events.email.SMTPCreated)
break break
} }
case Config.GOOGLE: { case ConfigType.GOOGLE: {
fns.push(() => events.auth.SSOCreated(type)) const googleCfg = config as GoogleConfig
if (config.config.activated) { fns.push(() => events.auth.SSOCreated(ssoType))
fns.push(() => events.auth.SSOActivated(type)) if (googleCfg.config.activated) {
fns.push(() => events.auth.SSOActivated(ssoType))
} }
break break
} }
case Config.OIDC: { case ConfigType.OIDC: {
fns.push(() => events.auth.SSOCreated(type)) const oidcCfg = config as OIDCConfig
if (config.config.configs[0].activated) { fns.push(() => events.auth.SSOCreated(ssoType))
fns.push(() => events.auth.SSOActivated(type)) if (oidcCfg.config.configs[0].activated) {
fns.push(() => events.auth.SSOActivated(ssoType))
} }
break break
} }
case Config.SETTINGS: { case ConfigType.SETTINGS: {
// company // company
const company = config.config.company const settingsCfg = config as SettingsConfig
const company = settingsCfg.config.company
if (company && company !== "Budibase") { if (company && company !== "Budibase") {
fns.push(events.org.nameUpdated) fns.push(events.org.nameUpdated)
} }
// logo // logo
const logoUrl = config.config.logoUrl const logoUrl = settingsCfg.config.logoUrl
if (logoUrl) { if (logoUrl) {
fns.push(events.org.logoUpdated) fns.push(events.org.logoUpdated)
} }
// platform url // platform url
const platformUrl = config.config.platformUrl const platformUrl = settingsCfg.config.platformUrl
if ( if (
platformUrl && platformUrl &&
platformUrl !== "http://localhost:10000" && platformUrl !== "http://localhost:10000" &&
@ -78,52 +80,55 @@ const getEventFns = async (db, config) => {
} }
} else { } else {
switch (config.type) { switch (config.type) {
case Config.SMTP: { case ConfigType.SMTP: {
fns.push(events.email.SMTPUpdated) fns.push(events.email.SMTPUpdated)
break break
} }
case Config.GOOGLE: { case ConfigType.GOOGLE: {
fns.push(() => events.auth.SSOUpdated(type)) const googleCfg = config as GoogleConfig
if (!existing.config.activated && config.config.activated) { fns.push(() => events.auth.SSOUpdated(ssoType))
fns.push(() => events.auth.SSOActivated(type)) if (!existing.config.activated && googleCfg.config.activated) {
} else if (existing.config.activated && !config.config.activated) { fns.push(() => events.auth.SSOActivated(ssoType))
fns.push(() => events.auth.SSODeactivated(type)) } else if (existing.config.activated && !googleCfg.config.activated) {
fns.push(() => events.auth.SSODeactivated(ssoType))
} }
break break
} }
case Config.OIDC: { case ConfigType.OIDC: {
fns.push(() => events.auth.SSOUpdated(type)) const oidcCfg = config as OIDCConfig
fns.push(() => events.auth.SSOUpdated(ssoType))
if ( if (
!existing.config.configs[0].activated && !existing.config.configs[0].activated &&
config.config.configs[0].activated oidcCfg.config.configs[0].activated
) { ) {
fns.push(() => events.auth.SSOActivated(type)) fns.push(() => events.auth.SSOActivated(ssoType))
} else if ( } else if (
existing.config.configs[0].activated && existing.config.configs[0].activated &&
!config.config.configs[0].activated !oidcCfg.config.configs[0].activated
) { ) {
fns.push(() => events.auth.SSODeactivated(type)) fns.push(() => events.auth.SSODeactivated(ssoType))
} }
break break
} }
case Config.SETTINGS: { case ConfigType.SETTINGS: {
// company // company
const settingsCfg = config as SettingsConfig
const existingCompany = existing.config.company const existingCompany = existing.config.company
const company = config.config.company const company = settingsCfg.config.company
if (company && company !== "Budibase" && existingCompany !== company) { if (company && company !== "Budibase" && existingCompany !== company) {
fns.push(events.org.nameUpdated) fns.push(events.org.nameUpdated)
} }
// logo // logo
const existingLogoUrl = existing.config.logoUrl const existingLogoUrl = existing.config.logoUrl
const logoUrl = config.config.logoUrl const logoUrl = settingsCfg.config.logoUrl
if (logoUrl && existingLogoUrl !== logoUrl) { if (logoUrl && existingLogoUrl !== logoUrl) {
fns.push(events.org.logoUpdated) fns.push(events.org.logoUpdated)
} }
// platform url // platform url
const existingPlatformUrl = existing.config.platformUrl const existingPlatformUrl = existing.config.platformUrl
const platformUrl = config.config.platformUrl const platformUrl = settingsCfg.config.platformUrl
if ( if (
platformUrl && platformUrl &&
platformUrl !== "http://localhost:10000" && platformUrl !== "http://localhost:10000" &&
@ -140,13 +145,13 @@ const getEventFns = async (db, config) => {
return fns return fns
} }
exports.save = async function (ctx) { export async function save(ctx: BBContext) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
const { type, workspace, user, config } = ctx.request.body const { type, workspace, user, config } = ctx.request.body
let eventFns = await getEventFns(db, ctx.request.body) let eventFns = await getEventFns(db, ctx.request.body)
// Config does not exist yet // Config does not exist yet
if (!ctx.request.body._id) { if (!ctx.request.body._id) {
ctx.request.body._id = generateConfigID({ ctx.request.body._id = dbCore.generateConfigID({
type, type,
workspace, workspace,
user, user,
@ -155,18 +160,18 @@ exports.save = async function (ctx) {
try { try {
// verify the configuration // verify the configuration
switch (type) { switch (type) {
case Config.SMTP: case ConfigType.SMTP:
await email.verifyConfig(config) await email.verifyConfig(config)
break break
} }
} catch (err) { } catch (err: any) {
ctx.throw(400, err) ctx.throw(400, err)
} }
try { try {
const response = await db.put(ctx.request.body) const response = await db.put(ctx.request.body)
await bustCache(CacheKeys.CHECKLIST) await cache.bustCache(cache.CacheKeys.CHECKLIST)
await bustCache(CacheKeys.ANALYTICS_ENABLED) await cache.bustCache(cache.CacheKeys.ANALYTICS_ENABLED)
for (const fn of eventFns) { for (const fn of eventFns) {
await fn() await fn()
@ -177,15 +182,15 @@ exports.save = async function (ctx) {
_id: response.id, _id: response.id,
_rev: response.rev, _rev: response.rev,
} }
} catch (err) { } catch (err: any) {
ctx.throw(400, err) ctx.throw(400, err)
} }
} }
exports.fetch = async function (ctx) { export async function fetch(ctx: BBContext) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
const response = await db.allDocs( const response = await db.allDocs(
getConfigParams( dbCore.getConfigParams(
{ type: ctx.params.type }, { type: ctx.params.type },
{ {
include_docs: true, include_docs: true,
@ -199,23 +204,23 @@ exports.fetch = async function (ctx) {
* Gets the most granular config for a particular configuration type. * Gets the most granular config for a particular configuration type.
* The hierarchy is type -> workspace -> user. * The hierarchy is type -> workspace -> user.
*/ */
exports.find = async function (ctx) { export async function find(ctx: BBContext) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
const { userId, workspaceId } = ctx.query const { userId, workspaceId } = ctx.query
if (workspaceId && userId) { if (workspaceId && userId) {
const workspace = await db.get(workspaceId) const workspace = await db.get(workspaceId as string)
const userInWorkspace = workspace.users.some( const userInWorkspace = workspace.users.some(
workspaceUser => workspaceUser === userId (workspaceUser: any) => workspaceUser === userId
) )
if (!ctx.user.admin && !userInWorkspace) { if (!ctx.user!.admin && !userInWorkspace) {
ctx.throw(400, `User is not in specified workspace: ${workspace}.`) ctx.throw(400, `User is not in specified workspace: ${workspace}.`)
} }
} }
try { try {
// Find the config with the most granular scope based on context // Find the config with the most granular scope based on context
const scopedConfig = await getScopedFullConfig(db, { const scopedConfig = await dbCore.getScopedFullConfig(db, {
type: ctx.params.type, type: ctx.params.type,
user: userId, user: userId,
workspace: workspaceId, workspace: workspaceId,
@ -227,48 +232,48 @@ exports.find = async function (ctx) {
// don't throw an error, there simply is nothing to return // don't throw an error, there simply is nothing to return
ctx.body = {} ctx.body = {}
} }
} catch (err) { } catch (err: any) {
ctx.throw(err.status, err) ctx.throw(err?.status || 400, err)
} }
} }
exports.publicOidc = async function (ctx) { export async function publicOidc(ctx: BBContext) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
try { try {
// Find the config with the most granular scope based on context // Find the config with the most granular scope based on context
const oidcConfig = await getScopedFullConfig(db, { const oidcConfig = await dbCore.getScopedFullConfig(db, {
type: Config.OIDC, type: ConfigType.OIDC,
}) })
if (!oidcConfig) { if (!oidcConfig) {
ctx.body = {} ctx.body = {}
} else { } else {
ctx.body = oidcConfig.config.configs.map(config => ({ ctx.body = oidcConfig.config.configs.map((config: any) => ({
logo: config.logo, logo: config.logo,
name: config.name, name: config.name,
uuid: config.uuid, uuid: config.uuid,
})) }))
} }
} catch (err) { } catch (err: any) {
ctx.throw(err.status, err) ctx.throw(err.status, err)
} }
} }
exports.publicSettings = async function (ctx) { export async function publicSettings(ctx: BBContext) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
try { try {
// Find the config with the most granular scope based on context // Find the config with the most granular scope based on context
const publicConfig = await getScopedFullConfig(db, { const publicConfig = await dbCore.getScopedFullConfig(db, {
type: Config.SETTINGS, type: ConfigType.SETTINGS,
}) })
const googleConfig = await getScopedFullConfig(db, { const googleConfig = await dbCore.getScopedFullConfig(db, {
type: Config.GOOGLE, type: ConfigType.GOOGLE,
}) })
const oidcConfig = await getScopedFullConfig(db, { const oidcConfig = await dbCore.getScopedFullConfig(db, {
type: Config.OIDC, type: ConfigType.OIDC,
}) })
let config let config
@ -301,12 +306,12 @@ exports.publicSettings = async function (ctx) {
} }
ctx.body = config ctx.body = config
} catch (err) { } catch (err: any) {
ctx.throw(err.status, err) ctx.throw(err.status, err)
} }
} }
exports.upload = async function (ctx) { export async function upload(ctx: BBContext) {
if (ctx.request.files == null || ctx.request.files.file.length > 1) { if (ctx.request.files == null || ctx.request.files.file.length > 1) {
ctx.throw(400, "One file must be uploaded.") ctx.throw(400, "One file must be uploaded.")
} }
@ -315,19 +320,19 @@ exports.upload = async function (ctx) {
let bucket let bucket
if (env.SELF_HOSTED) { if (env.SELF_HOSTED) {
bucket = ObjectStoreBuckets.GLOBAL bucket = objectStore.ObjectStoreBuckets.GLOBAL
} else { } else {
bucket = ObjectStoreBuckets.GLOBAL_CLOUD bucket = objectStore.ObjectStoreBuckets.GLOBAL_CLOUD
} }
let key let key
if (env.MULTI_TENANCY) { if (env.MULTI_TENANCY) {
key = `${getTenantId()}/${type}/${name}` key = `${tenancy.getTenantId()}/${type}/${name}`
} else { } else {
key = `${type}/${name}` key = `${type}/${name}`
} }
await upload({ await objectStore.upload({
bucket, bucket,
filename: key, filename: key,
path: file.path, path: file.path,
@ -336,11 +341,11 @@ exports.upload = async function (ctx) {
// add to configuration structure // add to configuration structure
// TODO: right now this only does a global level // TODO: right now this only does a global level
const db = getGlobalDB() const db = tenancy.getGlobalDB()
let cfgStructure = await getScopedFullConfig(db, { type }) let cfgStructure = await dbCore.getScopedFullConfig(db, { type })
if (!cfgStructure) { if (!cfgStructure) {
cfgStructure = { cfgStructure = {
_id: generateConfigID({ type }), _id: dbCore.generateConfigID({ type }),
config: {}, config: {},
} }
} }
@ -361,49 +366,49 @@ exports.upload = async function (ctx) {
} }
} }
exports.destroy = async function (ctx) { export async function destroy(ctx: BBContext) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
const { id, rev } = ctx.params const { id, rev } = ctx.params
try { try {
await db.remove(id, rev) await db.remove(id, rev)
await cache.delete(CacheKeys.CHECKLIST) await cache.delete(cache.CacheKeys.CHECKLIST)
ctx.body = { message: "Config deleted successfully" } ctx.body = { message: "Config deleted successfully" }
} catch (err) { } catch (err: any) {
ctx.throw(err.status, err) ctx.throw(err.status, err)
} }
} }
exports.configChecklist = async function (ctx) { export async function configChecklist(ctx: BBContext) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
const tenantId = getTenantId() const tenantId = tenancy.getTenantId()
try { try {
ctx.body = await withCache( ctx.body = await cache.withCache(
CacheKeys.CHECKLIST, cache.CacheKeys.CHECKLIST,
env.CHECKLIST_CACHE_TTL, env.CHECKLIST_CACHE_TTL,
async () => { async () => {
let apps = [] let apps = []
if (!env.MULTI_TENANCY || tenantId) { if (!env.MULTI_TENANCY || tenantId) {
// Apps exist // Apps exist
apps = await getAllApps({ idsOnly: true, efficient: true }) apps = await dbCore.getAllApps({ idsOnly: true, efficient: true })
} }
// They have set up SMTP // They have set up SMTP
const smtpConfig = await getScopedFullConfig(db, { const smtpConfig = await dbCore.getScopedFullConfig(db, {
type: Config.SMTP, type: ConfigType.SMTP,
}) })
// They have set up Google Auth // They have set up Google Auth
const googleConfig = await getScopedFullConfig(db, { const googleConfig = await dbCore.getScopedFullConfig(db, {
type: Config.GOOGLE, type: ConfigType.GOOGLE,
}) })
// They have set up OIDC // They have set up OIDC
const oidcConfig = await getScopedFullConfig(db, { const oidcConfig = await dbCore.getScopedFullConfig(db, {
type: Config.OIDC, type: ConfigType.OIDC,
}) })
// They have set up an global user // They have set up a global user
const userExists = await checkAnyUserExists() const userExists = await checkAnyUserExists()
return { return {
apps: { apps: {
@ -429,7 +434,7 @@ exports.configChecklist = async function (ctx) {
} }
} }
) )
} catch (err) { } catch (err: any) {
ctx.throw(err.status, err) ctx.throw(err.status, err)
} }
} }

View file

@ -1,7 +1,8 @@
const { sendEmail } = require("../../../utilities/email") import { sendEmail as sendEmailFn } from "../../../utilities/email"
const { getGlobalDB } = require("@budibase/backend-core/tenancy") import { tenancy } from "@budibase/backend-core"
import { BBContext } from "@budibase/types"
exports.sendEmail = async ctx => { export async function sendEmail(ctx: BBContext) {
let { let {
workspaceId, workspaceId,
email, email,
@ -16,10 +17,10 @@ exports.sendEmail = async ctx => {
} = ctx.request.body } = ctx.request.body
let user let user
if (userId) { if (userId) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
user = await db.get(userId) user = await db.get(userId)
} }
const response = await sendEmail(email, purpose, { const response = await sendEmailFn(email, purpose, {
workspaceId, workspaceId,
user, user,
contents, contents,

View file

@ -1,68 +0,0 @@
const { getAllRoles } = require("@budibase/backend-core/roles")
const {
getAllApps,
getProdAppID,
getDevAppID,
DocumentType,
} = require("@budibase/backend-core/db")
const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
const { user: userCache } = require("@budibase/backend-core/cache")
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
const { allUsers } = require("../../../sdk/users")
exports.fetch = async ctx => {
const tenantId = ctx.user.tenantId
// always use the dev apps as they'll be most up to date (true)
const apps = await getAllApps({ tenantId, all: true })
const promises = []
for (let app of apps) {
// use dev app IDs
promises.push(getAllRoles(app.appId))
}
const roles = await Promise.all(promises)
const response = {}
for (let app of apps) {
const deployedAppId = getProdAppID(app.appId)
response[deployedAppId] = {
roles: roles.shift(),
name: app.name,
version: app.version,
url: app.url,
}
}
ctx.body = response
}
exports.find = async ctx => {
const appId = ctx.params.appId
await doInAppContext(getDevAppID(appId), async () => {
const db = getAppDB()
const app = await db.get(DocumentType.APP_METADATA)
ctx.body = {
roles: await getAllRoles(),
name: app.name,
version: app.version,
url: app.url,
}
})
}
exports.removeAppRole = async ctx => {
const { appId } = ctx.params
const db = getGlobalDB()
const users = await allUsers(ctx)
const bulk = []
const cacheInvalidations = []
for (let user of users) {
if (user.roles[appId]) {
cacheInvalidations.push(userCache.invalidateUser(user._id))
delete user.roles[appId]
bulk.push(user)
}
}
await db.bulkDocs(bulk)
await Promise.all(cacheInvalidations)
ctx.body = {
message: "App role removed from all users",
}
}

View file

@ -0,0 +1,66 @@
import {
db as dbCore,
roles,
context,
cache,
tenancy,
} from "@budibase/backend-core"
import { BBContext, App } from "@budibase/types"
import { allUsers } from "../../../sdk/users"
export async function fetch(ctx: BBContext) {
const tenantId = ctx.user!.tenantId
// always use the dev apps as they'll be most up to date (true)
const apps = (await dbCore.getAllApps({ tenantId, all: true })) as App[]
const promises = []
for (let app of apps) {
// use dev app IDs
promises.push(roles.getAllRoles(app.appId))
}
const roleList = await Promise.all(promises)
const response: any = {}
for (let app of apps) {
const deployedAppId = dbCore.getProdAppID(app.appId)
response[deployedAppId] = {
roles: roleList.shift(),
name: app.name,
version: app.version,
url: app.url,
}
}
ctx.body = response
}
export async function find(ctx: BBContext) {
const appId = ctx.params.appId
await context.doInAppContext(dbCore.getDevAppID(appId), async () => {
const db = context.getAppDB()
const app = await db.get(dbCore.DocumentType.APP_METADATA)
ctx.body = {
roles: await roles.getAllRoles(),
name: app.name,
version: app.version,
url: app.url,
}
})
}
export async function removeAppRole(ctx: BBContext) {
const { appId } = ctx.params
const db = tenancy.getGlobalDB()
const users = await allUsers()
const bulk = []
const cacheInvalidations = []
for (let user of users) {
if (user.roles[appId]) {
cacheInvalidations.push(cache.user.invalidateUser(user._id))
delete user.roles[appId]
bulk.push(user)
}
}
await db.bulkDocs(bulk)
await Promise.all(cacheInvalidations)
ctx.body = {
message: "App role removed from all users",
}
}

View file

@ -1,16 +1,13 @@
const { import { tenancy, db as dbCore } from "@budibase/backend-core"
getWorkspaceParams, import { BBContext } from "@budibase/types"
generateWorkspaceID,
} = require("@budibase/backend-core/db")
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
exports.save = async function (ctx) { export async function save(ctx: BBContext) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
const workspaceDoc = ctx.request.body const workspaceDoc = ctx.request.body
// workspace does not exist yet // workspace does not exist yet
if (!workspaceDoc._id) { if (!workspaceDoc._id) {
workspaceDoc._id = generateWorkspaceID() workspaceDoc._id = dbCore.generateWorkspaceID()
} }
try { try {
@ -19,38 +16,38 @@ exports.save = async function (ctx) {
_id: response.id, _id: response.id,
_rev: response.rev, _rev: response.rev,
} }
} catch (err) { } catch (err: any) {
ctx.throw(err.status, err) ctx.throw(err.status, err)
} }
} }
exports.fetch = async function (ctx) { export async function fetch(ctx: BBContext) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
const response = await db.allDocs( const response = await db.allDocs(
getWorkspaceParams(undefined, { dbCore.getWorkspaceParams(undefined, {
include_docs: true, include_docs: true,
}) })
) )
ctx.body = response.rows.map(row => row.doc) ctx.body = response.rows.map(row => row.doc)
} }
exports.find = async function (ctx) { export async function find(ctx: BBContext) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
try { try {
ctx.body = await db.get(ctx.params.id) ctx.body = await db.get(ctx.params.id)
} catch (err) { } catch (err: any) {
ctx.throw(err.status, err) ctx.throw(err.status, err)
} }
} }
exports.destroy = async function (ctx) { export async function destroy(ctx: BBContext) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
const { id, rev } = ctx.params const { id, rev } = ctx.params
try { try {
await db.remove(id, rev) await db.remove(id, rev)
ctx.body = { message: "Workspace deleted successfully" } ctx.body = { message: "Workspace deleted successfully" }
} catch (err) { } catch (err: any) {
ctx.throw(err.status, err) ctx.throw(err.status, err)
} }
} }

View file

@ -104,7 +104,7 @@ const NO_TENANCY_ENDPOINTS = [
// add them all to be safe // add them all to be safe
const NO_CSRF_ENDPOINTS = [...PUBLIC_ENDPOINTS] const NO_CSRF_ENDPOINTS = [...PUBLIC_ENDPOINTS]
const router = new Router() const router: Router = new Router()
router router
.use( .use(
compress({ compress({
@ -163,4 +163,4 @@ for (let route of routes) {
router.use(route.allowedMethods()) router.use(route.allowedMethods())
} }
module.exports = router export = router

View file

@ -1,13 +1,13 @@
const Router = require("@koa/router") import Router from "@koa/router"
const authController = require("../../controllers/global/auth") import * as authController from "../../controllers/global/auth"
const { joiValidator } = require("@budibase/backend-core/auth") import { auth } from "@budibase/backend-core"
const Joi = require("joi") import Joi from "joi"
const router = new Router() const router: Router = new Router()
function buildAuthValidation() { function buildAuthValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
username: Joi.string().required(), username: Joi.string().required(),
password: Joi.string().required(), password: Joi.string().required(),
}).required().unknown(false)) }).required().unknown(false))
@ -15,14 +15,14 @@ function buildAuthValidation() {
function buildResetValidation() { function buildResetValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
email: Joi.string().required(), email: Joi.string().required(),
}).required().unknown(false)) }).required().unknown(false))
} }
function buildResetUpdateValidation() { function buildResetUpdateValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
resetCode: Joi.string().required(), resetCode: Joi.string().required(),
password: Joi.string().required(), password: Joi.string().required(),
}).required().unknown(false)) }).required().unknown(false))
@ -85,4 +85,4 @@ router
.get("/api/global/auth/oidc/callback", authController.oidcAuth) .get("/api/global/auth/oidc/callback", authController.oidcAuth)
.get("/api/admin/auth/oidc/callback", authController.oidcAuth) .get("/api/admin/auth/oidc/callback", authController.oidcAuth)
module.exports = router export = router

View file

@ -1,11 +1,10 @@
const Router = require("@koa/router") import Router from "@koa/router"
const controller = require("../../controllers/global/configs") import * as controller from "../../controllers/global/configs"
const { joiValidator } = require("@budibase/backend-core/auth") import { auth } from "@budibase/backend-core"
const { adminOnly } = require("@budibase/backend-core/auth") import Joi from "joi"
const Joi = require("joi") import { ConfigType } from "@budibase/types"
const { Config } = require("@budibase/backend-core/constants")
const router = new Router() const router: Router = new Router()
function smtpValidation() { function smtpValidation() {
// prettier-ignore // prettier-ignore
@ -55,27 +54,27 @@ function oidcValidation() {
activated: Joi.boolean().required(), activated: Joi.boolean().required(),
scopes: Joi.array().optional() scopes: Joi.array().optional()
}) })
).required(true) ).required()
}).unknown(true) }).unknown(true)
} }
function buildConfigSaveValidation() { function buildConfigSaveValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
_id: Joi.string().optional(), _id: Joi.string().optional(),
_rev: Joi.string().optional(), _rev: Joi.string().optional(),
workspace: Joi.string().optional(), workspace: Joi.string().optional(),
type: Joi.string().valid(...Object.values(Config)).required(), type: Joi.string().valid(...Object.values(ConfigType)).required(),
createdAt: Joi.string().optional(), createdAt: Joi.string().optional(),
updatedAt: Joi.string().optional(), updatedAt: Joi.string().optional(),
config: Joi.alternatives() config: Joi.alternatives()
.conditional("type", { .conditional("type", {
switch: [ switch: [
{ is: Config.SMTP, then: smtpValidation() }, { is: ConfigType.SMTP, then: smtpValidation() },
{ is: Config.SETTINGS, then: settingValidation() }, { is: ConfigType.SETTINGS, then: settingValidation() },
{ is: Config.ACCOUNT, then: Joi.object().unknown(true) }, { is: ConfigType.ACCOUNT, then: Joi.object().unknown(true) },
{ is: Config.GOOGLE, then: googleValidation() }, { is: ConfigType.GOOGLE, then: googleValidation() },
{ is: Config.OIDC, then: oidcValidation() } { is: ConfigType.OIDC, then: oidcValidation() }
], ],
}), }),
}).required().unknown(true), }).required().unknown(true),
@ -84,27 +83,27 @@ function buildConfigSaveValidation() {
function buildUploadValidation() { function buildUploadValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.params(Joi.object({ return auth.joiValidator.params(Joi.object({
type: Joi.string().valid(...Object.values(Config)).required(), type: Joi.string().valid(...Object.values(ConfigType)).required(),
name: Joi.string().required(), name: Joi.string().required(),
}).required().unknown(true)) }).required().unknown(true))
} }
function buildConfigGetValidation() { function buildConfigGetValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.params(Joi.object({ return auth.joiValidator.params(Joi.object({
type: Joi.string().valid(...Object.values(Config)).required() type: Joi.string().valid(...Object.values(ConfigType)).required()
}).required().unknown(true)) }).required().unknown(true))
} }
router router
.post( .post(
"/api/global/configs", "/api/global/configs",
adminOnly, auth.adminOnly,
buildConfigSaveValidation(), buildConfigSaveValidation(),
controller.save controller.save
) )
.delete("/api/global/configs/:id/:rev", adminOnly, controller.destroy) .delete("/api/global/configs/:id/:rev", auth.adminOnly, controller.destroy)
.get("/api/global/configs", controller.fetch) .get("/api/global/configs", controller.fetch)
.get("/api/global/configs/checklist", controller.configChecklist) .get("/api/global/configs/checklist", controller.configChecklist)
.get( .get(
@ -117,9 +116,9 @@ router
.get("/api/global/configs/:type", buildConfigGetValidation(), controller.find) .get("/api/global/configs/:type", buildConfigGetValidation(), controller.find)
.post( .post(
"/api/global/configs/upload/:type/:name", "/api/global/configs/upload/:type/:name",
adminOnly, auth.adminOnly,
buildUploadValidation(), buildUploadValidation(),
controller.upload controller.upload
) )
module.exports = router export = router

View file

@ -1,15 +1,14 @@
const Router = require("@koa/router") import Router from "@koa/router"
const controller = require("../../controllers/global/email") import * as controller from "../../controllers/global/email"
const { EmailTemplatePurpose } = require("../../../constants") import { EmailTemplatePurpose } from "../../../constants"
const { joiValidator } = require("@budibase/backend-core/auth") import { auth } from "@budibase/backend-core"
const { adminOnly } = require("@budibase/backend-core/auth") import Joi from "joi"
const Joi = require("joi")
const router = new Router() const router: Router = new Router()
function buildEmailSendValidation() { function buildEmailSendValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
email: Joi.string().email({ email: Joi.string().email({
multiple: true, multiple: true,
}), }),
@ -30,8 +29,8 @@ function buildEmailSendValidation() {
router.post( router.post(
"/api/global/email/send", "/api/global/email/send",
buildEmailSendValidation(), buildEmailSendValidation(),
adminOnly, auth.adminOnly,
controller.sendEmail controller.sendEmail
) )
module.exports = router export = router

View file

@ -1,7 +1,7 @@
import Router from "@koa/router" import Router from "@koa/router"
import * as controller from "../../controllers/global/license" import * as controller from "../../controllers/global/license"
const router = new Router() const router: Router = new Router()
router router
.post("/api/global/license/activate", controller.activate) .post("/api/global/license/activate", controller.activate)

View file

@ -1,12 +0,0 @@
const Router = require("@koa/router")
const controller = require("../../controllers/global/roles")
const { builderOrAdmin } = require("@budibase/backend-core/auth")
const router = new Router()
router
.get("/api/global/roles", builderOrAdmin, controller.fetch)
.get("/api/global/roles/:appId", builderOrAdmin, controller.find)
.delete("/api/global/roles/:appId", builderOrAdmin, controller.removeAppRole)
module.exports = router

View file

@ -0,0 +1,16 @@
import Router from "@koa/router"
import * as controller from "../../controllers/global/roles"
import { auth } from "@budibase/backend-core"
const router: Router = new Router()
router
.get("/api/global/roles", auth.builderOrAdmin, controller.fetch)
.get("/api/global/roles/:appId", auth.builderOrAdmin, controller.find)
.delete(
"/api/global/roles/:appId",
auth.builderOrAdmin,
controller.removeAppRole
)
export = router

View file

@ -3,7 +3,7 @@ import * as controller from "../../controllers/global/self"
import { auth } from "@budibase/backend-core" import { auth } from "@budibase/backend-core"
import { users } from "../validation" import { users } from "../validation"
const router = new Router() const router: Router = new Router()
router router
.post("/api/global/self/api_key", auth.builderOnly, controller.generateAPIKey) .post("/api/global/self/api_key", auth.builderOnly, controller.generateAPIKey)
@ -15,4 +15,4 @@ router
controller.updateSelf controller.updateSelf
) )
export default router as any export = router

View file

@ -5,7 +5,7 @@ import { auth as authCore } from "@budibase/backend-core"
import Joi from "joi" import Joi from "joi"
const { adminOnly, joiValidator } = authCore const { adminOnly, joiValidator } = authCore
const router = new Router() const router: Router = new Router()
function buildTemplateSaveValidation() { function buildTemplateSaveValidation() {
// prettier-ignore // prettier-ignore
@ -34,4 +34,4 @@ router
.get("/api/global/template/:id", controller.find) .get("/api/global/template/:id", controller.find)
.delete("/api/global/template/:id/:rev", adminOnly, controller.destroy) .delete("/api/global/template/:id/:rev", adminOnly, controller.destroy)
export default router export = router

View file

@ -1,17 +1,15 @@
const Router = require("@koa/router") import Router from "@koa/router"
const controller = require("../../controllers/global/users") import * as controller from "../../controllers/global/users"
const { joiValidator } = require("@budibase/backend-core/auth") import { auth } from "@budibase/backend-core"
const { adminOnly } = require("@budibase/backend-core/auth") import Joi from "joi"
const Joi = require("joi") import cloudRestricted from "../../../middleware/cloudRestricted"
const cloudRestricted = require("../../../middleware/cloudRestricted") import { users } from "../validation"
const { users } = require("../validation") import * as selfController from "../../controllers/global/self"
const selfController = require("../../controllers/global/self")
const { builderOrAdmin } = require("@budibase/backend-core/auth")
const router = new Router() const router: Router = new Router()
function buildAdminInitValidation() { function buildAdminInitValidation() {
return joiValidator.body( return auth.joiValidator.body(
Joi.object({ Joi.object({
email: Joi.string().required(), email: Joi.string().required(),
password: Joi.string(), password: Joi.string(),
@ -24,7 +22,7 @@ function buildAdminInitValidation() {
function buildInviteValidation() { function buildInviteValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
email: Joi.string().required(), email: Joi.string().required(),
userInfo: Joi.object().optional(), userInfo: Joi.object().optional(),
}).required()) }).required())
@ -32,7 +30,7 @@ function buildInviteValidation() {
function buildInviteMultipleValidation() { function buildInviteMultipleValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.array().required().items( return auth.joiValidator.body(Joi.array().required().items(
Joi.object({ Joi.object({
email: Joi.string(), email: Joi.string(),
userInfo: Joi.object().optional(), userInfo: Joi.object().optional(),
@ -42,7 +40,7 @@ function buildInviteMultipleValidation() {
function buildInviteAcceptValidation() { function buildInviteAcceptValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
inviteCode: Joi.string().required(), inviteCode: Joi.string().required(),
password: Joi.string().required(), password: Joi.string().required(),
}).required().unknown(true)) }).required().unknown(true))
@ -51,31 +49,35 @@ function buildInviteAcceptValidation() {
router router
.post( .post(
"/api/global/users", "/api/global/users",
adminOnly, auth.adminOnly,
users.buildUserSaveValidation(), users.buildUserSaveValidation(),
controller.save controller.save
) )
.post( .post(
"/api/global/users/bulk", "/api/global/users/bulk",
adminOnly, auth.adminOnly,
users.buildUserBulkUserValidation(), users.buildUserBulkUserValidation(),
controller.bulkUpdate controller.bulkUpdate
) )
.get("/api/global/users", builderOrAdmin, controller.fetch) .get("/api/global/users", auth.builderOrAdmin, controller.fetch)
.post("/api/global/users/search", builderOrAdmin, controller.search) .post("/api/global/users/search", auth.builderOrAdmin, controller.search)
.delete("/api/global/users/:id", adminOnly, controller.destroy) .delete("/api/global/users/:id", auth.adminOnly, controller.destroy)
.get("/api/global/users/count/:appId", builderOrAdmin, controller.countByApp) .get(
"/api/global/users/count/:appId",
auth.builderOrAdmin,
controller.countByApp
)
.get("/api/global/roles/:appId") .get("/api/global/roles/:appId")
.post( .post(
"/api/global/users/invite", "/api/global/users/invite",
adminOnly, auth.adminOnly,
buildInviteValidation(), buildInviteValidation(),
controller.invite controller.invite
) )
.post( .post(
"/api/global/users/multi/invite", "/api/global/users/multi/invite",
adminOnly, auth.adminOnly,
buildInviteMultipleValidation(), buildInviteMultipleValidation(),
controller.inviteMultiple controller.inviteMultiple
) )
@ -94,7 +96,7 @@ router
) )
.get("/api/global/users/tenant/:id", controller.tenantUserLookup) .get("/api/global/users/tenant/:id", controller.tenantUserLookup)
// global endpoint but needs to come at end (blocks other endpoints otherwise) // global endpoint but needs to come at end (blocks other endpoints otherwise)
.get("/api/global/users/:id", builderOrAdmin, controller.find) .get("/api/global/users/:id", auth.builderOrAdmin, controller.find)
// DEPRECATED - use new versions with self API // DEPRECATED - use new versions with self API
.get("/api/global/users/self", selfController.getSelf) .get("/api/global/users/self", selfController.getSelf)
.post( .post(
@ -103,4 +105,4 @@ router
selfController.updateSelf selfController.updateSelf
) )
module.exports = router export = router

View file

@ -1,14 +1,13 @@
const Router = require("@koa/router") import Router from "@koa/router"
const controller = require("../../controllers/global/workspaces") import * as controller from "../../controllers/global/workspaces"
const { joiValidator } = require("@budibase/backend-core/auth") import { auth } from "@budibase/backend-core"
const { adminOnly } = require("@budibase/backend-core/auth") import Joi from "joi"
const Joi = require("joi")
const router = new Router() const router: Router = new Router()
function buildWorkspaceSaveValidation() { function buildWorkspaceSaveValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
_id: Joi.string().optional(), _id: Joi.string().optional(),
_rev: Joi.string().optional(), _rev: Joi.string().optional(),
name: Joi.string().required(), name: Joi.string().required(),
@ -27,12 +26,12 @@ function buildWorkspaceSaveValidation() {
router router
.post( .post(
"/api/global/workspaces", "/api/global/workspaces",
adminOnly, auth.adminOnly,
buildWorkspaceSaveValidation(), buildWorkspaceSaveValidation(),
controller.save controller.save
) )
.delete("/api/global/workspaces/:id", adminOnly, controller.destroy) .delete("/api/global/workspaces/:id", auth.adminOnly, controller.destroy)
.get("/api/global/workspaces", controller.fetch) .get("/api/global/workspaces", controller.fetch)
.get("/api/global/workspaces/:id", controller.find) .get("/api/global/workspaces/:id", controller.find)
module.exports = router export = router

View file

@ -1,3 +1,4 @@
import Router from "@koa/router"
import { api } from "@budibase/pro" import { api } from "@budibase/pro"
import userRoutes from "./global/users" import userRoutes from "./global/users"
import configRoutes from "./global/configs" import configRoutes from "./global/configs"
@ -16,7 +17,7 @@ import accountRoutes from "./system/accounts"
import restoreRoutes from "./system/restore" import restoreRoutes from "./system/restore"
let userGroupRoutes = api.groups let userGroupRoutes = api.groups
export const routes = [ export const routes: Router[] = [
configRoutes, configRoutes,
userRoutes, userRoutes,
workspaceRoutes, workspaceRoutes,

View file

@ -2,7 +2,7 @@ import Router from "@koa/router"
import * as controller from "../../controllers/system/accounts" import * as controller from "../../controllers/system/accounts"
import { middleware } from "@budibase/backend-core" import { middleware } from "@budibase/backend-core"
const router = new Router() const router: Router = new Router()
router router
.put( .put(

View file

@ -1,8 +1,8 @@
import Router from "@koa/router" import Router from "@koa/router"
import * as controller from "../../controllers/system/environment" import * as controller from "../../controllers/system/environment"
const router = new Router() const router: Router = new Router()
router.get("/api/system/environment", controller.fetch) router.get("/api/system/environment", controller.fetch)
export default router export = router

View file

@ -2,7 +2,7 @@ import Router from "@koa/router"
import * as migrationsController from "../../controllers/system/migrations" import * as migrationsController from "../../controllers/system/migrations"
import { auth } from "@budibase/backend-core" import { auth } from "@budibase/backend-core"
const router = new Router() const router: Router = new Router()
router router
.post( .post(

View file

@ -1,7 +1,7 @@
import * as controller from "../../controllers/system/restore" import * as controller from "../../controllers/system/restore"
import Router from "@koa/router" import Router from "@koa/router"
const router = new Router() const router: Router = new Router()
router.post("/api/system/restored", controller.systemRestored) router.post("/api/system/restored", controller.systemRestored)

View file

@ -1,8 +1,8 @@
import Router from "@koa/router" import Router from "@koa/router"
import * as controller from "../../controllers/system/status" import * as controller from "../../controllers/system/status"
const router = new Router() const router: Router = new Router()
router.get("/api/system/status", controller.fetch) router.get("/api/system/status", controller.fetch)
export default router export = router

View file

@ -2,7 +2,7 @@ import Router from "@koa/router"
import * as controller from "../../controllers/system/tenants" import * as controller from "../../controllers/system/tenants"
import { middleware } from "@budibase/backend-core" import { middleware } from "@budibase/backend-core"
const router = new Router() const router: Router = new Router()
router.delete( router.delete(
"/api/system/tenants/:tenantId", "/api/system/tenants/:tenantId",
@ -10,4 +10,4 @@ router.delete(
controller.delete controller.delete
) )
export default router export = router

View file

@ -1,4 +1,4 @@
const { joiValidator } = require("@budibase/backend-core/auth") import { auth } from "@budibase/backend-core"
import Joi from "joi" import Joi from "joi"
let schema: any = { let schema: any = {
@ -25,7 +25,7 @@ export const buildUserSaveValidation = (isSelf = false) => {
_rev: Joi.string(), _rev: Joi.string(),
} }
} }
return joiValidator.body(Joi.object(schema).required().unknown(true)) return auth.joiValidator.body(Joi.object(schema).required().unknown(true))
} }
export const buildUserBulkUserValidation = (isSelf = false) => { export const buildUserBulkUserValidation = (isSelf = false) => {
@ -46,5 +46,5 @@ export const buildUserBulkUserValidation = (isSelf = false) => {
}), }),
} }
return joiValidator.body(Joi.object(bulkSchema).required().unknown(true)) return auth.joiValidator.body(Joi.object(bulkSchema).required().unknown(true))
} }

View file

@ -1,15 +1,15 @@
const { readStaticFile } = require("../../utilities/fileSystem") import { readStaticFile } from "../../utilities/fileSystem"
const { import {
EmailTemplatePurpose, EmailTemplatePurpose,
TemplateType, TemplateType,
TemplatePurpose, TemplatePurpose,
GLOBAL_OWNER, GLOBAL_OWNER,
} = require("../index") } from "../index"
const { join } = require("path") import { join } from "path"
const { getTemplateParams } = require("@budibase/backend-core/db") import { db as dbCore, tenancy } from "@budibase/backend-core"
const { getGlobalDB } = require("@budibase/backend-core/tenancy") import { Template } from "@budibase/types"
exports.EmailTemplates = { export const EmailTemplates = {
[EmailTemplatePurpose.PASSWORD_RECOVERY]: readStaticFile( [EmailTemplatePurpose.PASSWORD_RECOVERY]: readStaticFile(
join(__dirname, "passwordRecovery.hbs") join(__dirname, "passwordRecovery.hbs")
), ),
@ -23,7 +23,7 @@ exports.EmailTemplates = {
[EmailTemplatePurpose.CUSTOM]: readStaticFile(join(__dirname, "custom.hbs")), [EmailTemplatePurpose.CUSTOM]: readStaticFile(join(__dirname, "custom.hbs")),
} }
exports.addBaseTemplates = (templates, type = null) => { export function addBaseTemplates(templates: Template[], type?: string) {
let purposeList let purposeList
switch (type) { switch (type) {
case TemplateType.EMAIL: case TemplateType.EMAIL:
@ -38,9 +38,9 @@ exports.addBaseTemplates = (templates, type = null) => {
if (templates.find(template => template.purpose === purpose)) { if (templates.find(template => template.purpose === purpose)) {
continue continue
} }
if (exports.EmailTemplates[purpose]) { if (EmailTemplates[purpose]) {
templates.push({ templates.push({
contents: exports.EmailTemplates[purpose], contents: EmailTemplates[purpose],
purpose, purpose,
type, type,
}) })
@ -49,10 +49,14 @@ exports.addBaseTemplates = (templates, type = null) => {
return templates return templates
} }
exports.getTemplates = async ({ ownerId, type, id } = {}) => { export async function getTemplates({
const db = getGlobalDB() ownerId,
type,
id,
}: { ownerId?: string; type?: string; id?: string } = {}) {
const db = tenancy.getGlobalDB()
const response = await db.allDocs( const response = await db.allDocs(
getTemplateParams(ownerId || GLOBAL_OWNER, id, { dbCore.getTemplateParams(ownerId || GLOBAL_OWNER, id, {
include_docs: true, include_docs: true,
}) })
) )
@ -64,10 +68,10 @@ exports.getTemplates = async ({ ownerId, type, id } = {}) => {
if (type) { if (type) {
templates = templates.filter(template => template.type === type) templates = templates.filter(template => template.type === type)
} }
return exports.addBaseTemplates(templates, type) return addBaseTemplates(templates, type)
} }
exports.getTemplateByPurpose = async (type, purpose) => { export async function getTemplateByPurpose(type: string, purpose: string) {
const templates = await exports.getTemplates({ type }) const templates = await getTemplates({ type })
return templates.find(template => template.purpose === purpose) return templates.find((template: Template) => template.purpose === purpose)
} }

View file

@ -1,5 +1,5 @@
// need to load environment first // need to load environment first
const env = require("./environment") import env from "./environment"
// enable APM if configured // enable APM if configured
if (process.env.ELASTIC_APM_ENABLED) { if (process.env.ELASTIC_APM_ENABLED) {
@ -14,19 +14,17 @@ import { Event } from "@sentry/types/dist/event"
import Application from "koa" import Application from "koa"
import { bootstrap } from "global-agent" import { bootstrap } from "global-agent"
import * as db from "./db" import * as db from "./db"
import { auth, logging, events, pinoSettings } from "@budibase/backend-core"
db.init() db.init()
const Koa = require("koa") import Koa from "koa"
const destroyable = require("server-destroy") import koaBody from "koa-body"
const koaBody = require("koa-body") import http from "http"
import * as api from "./api"
import * as redis from "./utilities/redis"
import Sentry from "@sentry/node"
const koaSession = require("koa-session") const koaSession = require("koa-session")
const { passport } = require("@budibase/backend-core/auth")
const { logAlert } = require("@budibase/backend-core/logging")
const logger = require("koa-pino-logger") const logger = require("koa-pino-logger")
const http = require("http") const destroyable = require("server-destroy")
const api = require("./api")
const redis = require("./utilities/redis")
const Sentry = require("@sentry/node")
import { events, pinoSettings } from "@budibase/backend-core"
// this will setup http and https proxies form env variables // this will setup http and https proxies form env variables
bootstrap() bootstrap()
@ -41,8 +39,8 @@ app.use(koaSession(app))
app.use(logger(pinoSettings())) app.use(logger(pinoSettings()))
// authentication // authentication
app.use(passport.initialize()) app.use(auth.passport.initialize())
app.use(passport.session()) app.use(auth.passport.session())
// api routes // api routes
app.use(api.routes()) app.use(api.routes())
@ -81,17 +79,18 @@ server.on("close", async () => {
const shutdown = () => { const shutdown = () => {
server.close() server.close()
// @ts-ignore
server.destroy() server.destroy()
} }
export = server.listen(parseInt(env.PORT || 4002), async () => { export = server.listen(parseInt(env.PORT || "4002"), async () => {
console.log(`Worker running on ${JSON.stringify(server.address())}`) console.log(`Worker running on ${JSON.stringify(server.address())}`)
await redis.init() await redis.init()
}) })
process.on("uncaughtException", err => { process.on("uncaughtException", err => {
errCode = -1 errCode = -1
logAlert("Uncaught exception.", err) logging.logAlert("Uncaught exception.", err)
shutdown() shutdown()
}) })

View file

@ -1,13 +1,14 @@
const env = require("../environment") import env from "../environment"
const { Header } = require("@budibase/backend-core/constants") import { constants } from "@budibase/backend-core"
import { BBContext } from "@budibase/types"
/** /**
* This is a restricted endpoint in the cloud. * This is a restricted endpoint in the cloud.
* Ensure that the correct API key has been supplied. * Ensure that the correct API key has been supplied.
*/ */
module.exports = async (ctx, next) => { export = async (ctx: BBContext, next: any) => {
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
const apiKey = ctx.request.headers[Header.API_KEY] const apiKey = ctx.request.headers[constants.Header.API_KEY]
if (apiKey !== env.INTERNAL_API_KEY) { if (apiKey !== env.INTERNAL_API_KEY) {
ctx.throw(403, "Unauthorized") ctx.throw(403, "Unauthorized")
} }

View file

@ -1,33 +0,0 @@
const fetch = require("node-fetch")
const { Header } = require("@budibase/backend-core/constants")
const { getTenantId, isTenantIdSet } = require("@budibase/backend-core/tenancy")
const { checkSlashesInUrl } = require("../utilities")
const env = require("../environment")
async function makeAppRequest(url, method, body) {
if (env.isTest()) {
return
}
const request = { headers: {} }
request.headers[Header.API_KEY] = env.INTERNAL_API_KEY
if (isTenantIdSet()) {
request.headers[Header.TENANT_ID] = getTenantId()
}
if (body) {
request.headers["Content-Type"] = "application/json"
request.body = JSON.stringify(body)
}
request.method = method
return fetch(checkSlashesInUrl(env.APPS_URL + url), request)
}
exports.syncUserInApps = async userId => {
const response = await makeAppRequest(
`/api/users/metadata/sync/${userId}`,
"POST",
{}
)
if (response && response.status !== 200) {
throw "Unable to sync user."
}
}

View file

@ -0,0 +1,32 @@
import fetch from "node-fetch"
import { constants, tenancy } from "@budibase/backend-core"
import { checkSlashesInUrl } from "../utilities"
import env from "../environment"
async function makeAppRequest(url: string, method: string, body: any) {
if (env.isTest()) {
return
}
const request: any = { headers: {} }
request.headers[constants.Header.API_KEY] = env.INTERNAL_API_KEY
if (tenancy.isTenantIdSet()) {
request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId()
}
if (body) {
request.headers["Content-Type"] = "application/json"
request.body = JSON.stringify(body)
}
request.method = method
return fetch(checkSlashesInUrl(env.APPS_URL + url), request)
}
export async function syncUserInApps(userId: string) {
const response = await makeAppRequest(
`/api/users/metadata/sync/${userId}`,
"POST",
{}
)
if (response && response.status !== 200) {
throw "Unable to sync user."
}
}

View file

@ -1,5 +0,0 @@
const { readFileSync } = require("fs")
exports.readStaticFile = path => {
return readFileSync(path, "utf-8")
}

View file

@ -0,0 +1,5 @@
import { readFileSync } from "fs"
export function readStaticFile(path: string) {
return readFileSync(path, "utf-8")
}

View file

@ -4,6 +4,6 @@
* @param {string} url The URL to test and remove any extra double slashes. * @param {string} url The URL to test and remove any extra double slashes.
* @return {string} The updated url. * @return {string} The updated url.
*/ */
exports.checkSlashesInUrl = url => { export function checkSlashesInUrl(url: string) {
return url.replace(/(https?:\/\/)|(\/)+/g, "$1$2") return url.replace(/(https?:\/\/)|(\/)+/g, "$1$2")
} }

View file

@ -1,17 +0,0 @@
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
const { getGlobalUserParams } = require("@budibase/backend-core/db")
exports.checkAnyUserExists = async () => {
try {
const db = getGlobalDB()
const users = await db.allDocs(
getGlobalUserParams(null, {
include_docs: true,
limit: 1,
})
)
return users && users.rows.length >= 1
} catch (err) {
throw new Error("Unable to retrieve user list")
}
}

View file

@ -0,0 +1,16 @@
import { tenancy, db as dbCore } from "@budibase/backend-core"
export async function checkAnyUserExists() {
try {
const db = tenancy.getGlobalDB()
const users = await db.allDocs(
dbCore.getGlobalUserParams(null, {
include_docs: true,
limit: 1,
})
)
return users && users.rows.length >= 1
} catch (err) {
throw new Error("Unable to retrieve user list")
}
}