Some typescript conversions, moving a few imports around.
This commit is contained in:
parent
082b0851db
commit
de82eca502
26 changed files with 336 additions and 362 deletions
|
@ -390,7 +390,7 @@ export const uploadDirectory = async (
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.downloadTarballDirect = async (
|
export const downloadTarballDirect = async (
|
||||||
url: string,
|
url: string,
|
||||||
path: string,
|
path: string,
|
||||||
headers = {}
|
headers = {}
|
||||||
|
|
|
@ -17,13 +17,9 @@ const { clientLibraryPath } = require("../../../utilities")
|
||||||
const { upload, deleteFiles } = require("../../../utilities/fileSystem")
|
const { upload, deleteFiles } = require("../../../utilities/fileSystem")
|
||||||
const { attachmentsRelativeURL } = require("../../../utilities")
|
const { attachmentsRelativeURL } = require("../../../utilities")
|
||||||
const { DocumentType } = require("../../../db/utils")
|
const { DocumentType } = require("../../../db/utils")
|
||||||
const { getAppDB, getAppId } = require("@budibase/backend-core/context")
|
const { context, objectStore, utils } = require("@budibase/backend-core")
|
||||||
const { setCookie, clearCookie } = require("@budibase/backend-core/utils")
|
|
||||||
const AWS = require("aws-sdk")
|
const AWS = require("aws-sdk")
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const {
|
|
||||||
downloadTarballDirect,
|
|
||||||
} = require("../../../utilities/fileSystem/utilities")
|
|
||||||
|
|
||||||
async function prepareUpload({ s3Key, bucket, metadata, file }: any) {
|
async function prepareUpload({ s3Key, bucket, metadata, file }: any) {
|
||||||
const response = await upload({
|
const response = await upload({
|
||||||
|
@ -48,7 +44,7 @@ export const toggleBetaUiFeature = async function (ctx: any) {
|
||||||
const cookieName = `beta:${ctx.params.feature}`
|
const cookieName = `beta:${ctx.params.feature}`
|
||||||
|
|
||||||
if (ctx.cookies.get(cookieName)) {
|
if (ctx.cookies.get(cookieName)) {
|
||||||
clearCookie(ctx, cookieName)
|
utils.clearCookie(ctx, cookieName)
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: `${ctx.params.feature} disabled`,
|
message: `${ctx.params.feature} disabled`,
|
||||||
}
|
}
|
||||||
|
@ -61,11 +57,11 @@ export const toggleBetaUiFeature = async function (ctx: any) {
|
||||||
if (!fs.existsSync(builderPath)) {
|
if (!fs.existsSync(builderPath)) {
|
||||||
fs.mkdirSync(builderPath)
|
fs.mkdirSync(builderPath)
|
||||||
}
|
}
|
||||||
await downloadTarballDirect(
|
await objectStore.downloadTarballDirect(
|
||||||
"https://cdn.budi.live/beta:design_ui/new_ui.tar.gz",
|
"https://cdn.budi.live/beta:design_ui/new_ui.tar.gz",
|
||||||
builderPath
|
builderPath
|
||||||
)
|
)
|
||||||
setCookie(ctx, {}, cookieName)
|
utils.setCookie(ctx, {}, cookieName)
|
||||||
|
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: `${ctx.params.feature} enabled`,
|
message: `${ctx.params.feature} enabled`,
|
||||||
|
@ -103,9 +99,9 @@ export const deleteObjects = async function (ctx: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const serveApp = async function (ctx: any) {
|
export const serveApp = async function (ctx: any) {
|
||||||
const db = getAppDB({ skip_setup: true })
|
const db = context.getAppDB({ skip_setup: true })
|
||||||
const appInfo = await db.get(DocumentType.APP_METADATA)
|
const appInfo = await db.get(DocumentType.APP_METADATA)
|
||||||
let appId = getAppId()
|
let appId = context.getAppId()
|
||||||
|
|
||||||
if (!env.isJest()) {
|
if (!env.isJest()) {
|
||||||
const App = require("./templates/BudibaseApp.svelte").default
|
const App = require("./templates/BudibaseApp.svelte").default
|
||||||
|
@ -134,11 +130,11 @@ export const serveApp = async function (ctx: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const serveBuilderPreview = async function (ctx: any) {
|
export const serveBuilderPreview = async function (ctx: any) {
|
||||||
const db = getAppDB({ skip_setup: true })
|
const db = context.getAppDB({ skip_setup: true })
|
||||||
const appInfo = await db.get(DocumentType.APP_METADATA)
|
const appInfo = await db.get(DocumentType.APP_METADATA)
|
||||||
|
|
||||||
if (!env.isJest()) {
|
if (!env.isJest()) {
|
||||||
let appId = getAppId()
|
let appId = context.getAppId()
|
||||||
const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`)
|
const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`)
|
||||||
ctx.body = await processString(previewHbs, {
|
ctx.body = await processString(previewHbs, {
|
||||||
clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
|
clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
|
||||||
|
@ -156,7 +152,7 @@ export const serveClientLibrary = async function (ctx: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSignedUploadURL = async function (ctx: any) {
|
export const getSignedUploadURL = async function (ctx: any) {
|
||||||
const database = getAppDB()
|
const database = context.getAppDB()
|
||||||
|
|
||||||
// Ensure datasource is valid
|
// Ensure datasource is valid
|
||||||
let datasource
|
let datasource
|
||||||
|
|
|
@ -4,7 +4,7 @@ import authorized from "../../middleware/authorized"
|
||||||
import { BUILDER } from "@budibase/backend-core/permissions"
|
import { BUILDER } from "@budibase/backend-core/permissions"
|
||||||
import { applicationValidator } from "./utils/validators"
|
import { applicationValidator } from "./utils/validators"
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.post("/api/applications/:appId/sync", authorized(BUILDER), controller.sync)
|
.post("/api/applications/:appId/sync", authorized(BUILDER), controller.sync)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Router from "@koa/router"
|
import Router from "@koa/router"
|
||||||
import * as controller from "../controllers/auth"
|
import * as controller from "../controllers/auth"
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router.get("/api/self", controller.fetchSelf)
|
router.get("/api/self", controller.fetchSelf)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as controller from "../controllers/backup"
|
||||||
import authorized from "../../middleware/authorized"
|
import authorized from "../../middleware/authorized"
|
||||||
import { BUILDER } from "@budibase/backend-core/permissions"
|
import { BUILDER } from "@budibase/backend-core/permissions"
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router.get("/api/backups/export", authorized(BUILDER), controller.exportAppDump)
|
router.get("/api/backups/export", authorized(BUILDER), controller.exportAppDump)
|
||||||
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const controller = require("../controllers/deploy")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const { BUILDER } = require("@budibase/backend-core/permissions")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
router
|
|
||||||
.get("/api/deployments", authorized(BUILDER), controller.fetchDeployments)
|
|
||||||
.get(
|
|
||||||
"/api/deploy/:deploymentId",
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.deploymentProgress
|
|
||||||
)
|
|
||||||
.post("/api/deploy", authorized(BUILDER), controller.deployApp)
|
|
||||||
|
|
||||||
module.exports = router
|
|
21
packages/server/src/api/routes/deploy.ts
Normal file
21
packages/server/src/api/routes/deploy.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import * as controller from "../controllers/deploy"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.get(
|
||||||
|
"/api/deployments",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.fetchDeployments
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
"/api/deploy/:deploymentId",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.deploymentProgress
|
||||||
|
)
|
||||||
|
.post("/api/deploy", authorized(permissions.BUILDER), controller.deployApp)
|
||||||
|
|
||||||
|
export default router
|
|
@ -3,7 +3,7 @@ import * as controller from "../controllers/plugin"
|
||||||
import authorized from "../../middleware/authorized"
|
import authorized from "../../middleware/authorized"
|
||||||
import { BUILDER } from "@budibase/backend-core/permissions"
|
import { BUILDER } from "@budibase/backend-core/permissions"
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.post("/api/plugin/upload", authorized(BUILDER), controller.upload)
|
.post("/api/plugin/upload", authorized(BUILDER), controller.upload)
|
||||||
|
|
|
@ -8,7 +8,7 @@ const {
|
||||||
} = require("@budibase/backend-core/permissions")
|
} = require("@budibase/backend-core/permissions")
|
||||||
const { internalSearchValidator } = require("./utils/validators")
|
const { internalSearchValidator } = require("./utils/validators")
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
import * as env from "../../environment"
|
import * as env from "../../environment"
|
||||||
import { paramResource } from "../../middleware/resourceId"
|
import { paramResource } from "../../middleware/resourceId"
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
router.param("file", async (file: any, ctx: any, next: any) => {
|
router.param("file", async (file: any, ctx: any, next: any) => {
|
||||||
|
|
|
@ -1,20 +1,16 @@
|
||||||
const { joiValidator } = require("@budibase/backend-core/auth")
|
import { auth, permissions } from "@budibase/backend-core"
|
||||||
const { DataSourceOperation } = require("../../../constants")
|
import { DataSourceOperation } from "../../../constants"
|
||||||
const {
|
import { WebhookActionType } from "@budibase/types"
|
||||||
BuiltinPermissionID,
|
import Joi from "joi"
|
||||||
PermissionLevel,
|
|
||||||
} = require("@budibase/backend-core/permissions")
|
|
||||||
const { WebhookActionType } = require("@budibase/types")
|
|
||||||
const Joi = require("joi")
|
|
||||||
|
|
||||||
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
|
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
|
||||||
const OPTIONAL_NUMBER = Joi.number().optional().allow(null)
|
const OPTIONAL_NUMBER = Joi.number().optional().allow(null)
|
||||||
const OPTIONAL_BOOLEAN = Joi.boolean().optional().allow(null)
|
const OPTIONAL_BOOLEAN = Joi.boolean().optional().allow(null)
|
||||||
const APP_NAME_REGEX = /^[\w\s]+$/
|
const APP_NAME_REGEX = /^[\w\s]+$/
|
||||||
|
|
||||||
exports.tableValidator = () => {
|
export function tableValidator() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return auth.joiValidator.body(Joi.object({
|
||||||
_id: OPTIONAL_STRING,
|
_id: OPTIONAL_STRING,
|
||||||
_rev: OPTIONAL_STRING,
|
_rev: OPTIONAL_STRING,
|
||||||
type: OPTIONAL_STRING.valid("table", "internal", "external"),
|
type: OPTIONAL_STRING.valid("table", "internal", "external"),
|
||||||
|
@ -26,16 +22,16 @@ exports.tableValidator = () => {
|
||||||
}).unknown(true))
|
}).unknown(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.nameValidator = () => {
|
export function nameValidator() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return auth.joiValidator.body(Joi.object({
|
||||||
name: OPTIONAL_STRING,
|
name: OPTIONAL_STRING,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.datasourceValidator = () => {
|
export function datasourceValidator() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return auth.joiValidator.body(Joi.object({
|
||||||
_id: Joi.string(),
|
_id: Joi.string(),
|
||||||
_rev: Joi.string(),
|
_rev: Joi.string(),
|
||||||
type: OPTIONAL_STRING.allow("datasource_plus"),
|
type: OPTIONAL_STRING.allow("datasource_plus"),
|
||||||
|
@ -64,9 +60,9 @@ function filterObject() {
|
||||||
}).unknown(true)
|
}).unknown(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.internalSearchValidator = () => {
|
export function internalSearchValidator() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return auth.joiValidator.body(Joi.object({
|
||||||
tableId: OPTIONAL_STRING,
|
tableId: OPTIONAL_STRING,
|
||||||
query: filterObject(),
|
query: filterObject(),
|
||||||
limit: OPTIONAL_NUMBER,
|
limit: OPTIONAL_NUMBER,
|
||||||
|
@ -78,8 +74,8 @@ exports.internalSearchValidator = () => {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.externalSearchValidator = () => {
|
export function externalSearchValidator() {
|
||||||
return joiValidator.body(
|
return auth.joiValidator.body(
|
||||||
Joi.object({
|
Joi.object({
|
||||||
query: filterObject(),
|
query: filterObject(),
|
||||||
paginate: Joi.boolean().optional(),
|
paginate: Joi.boolean().optional(),
|
||||||
|
@ -96,9 +92,9 @@ exports.externalSearchValidator = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.datasourceQueryValidator = () => {
|
export function datasourceQueryValidator() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return auth.joiValidator.body(Joi.object({
|
||||||
endpoint: Joi.object({
|
endpoint: Joi.object({
|
||||||
datasourceId: Joi.string().required(),
|
datasourceId: Joi.string().required(),
|
||||||
operation: Joi.string().required().valid(...Object.values(DataSourceOperation)),
|
operation: Joi.string().required().valid(...Object.values(DataSourceOperation)),
|
||||||
|
@ -117,9 +113,9 @@ exports.datasourceQueryValidator = () => {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.webhookValidator = () => {
|
export function webhookValidator() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return auth.joiValidator.body(Joi.object({
|
||||||
live: Joi.bool(),
|
live: Joi.bool(),
|
||||||
_id: OPTIONAL_STRING,
|
_id: OPTIONAL_STRING,
|
||||||
_rev: OPTIONAL_STRING,
|
_rev: OPTIONAL_STRING,
|
||||||
|
@ -132,15 +128,15 @@ exports.webhookValidator = () => {
|
||||||
}).unknown(true))
|
}).unknown(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.roleValidator = () => {
|
export function roleValidator() {
|
||||||
const permLevelArray = Object.values(PermissionLevel)
|
const permLevelArray = Object.values(permissions.PermissionLevel)
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return auth.joiValidator.body(Joi.object({
|
||||||
_id: OPTIONAL_STRING,
|
_id: OPTIONAL_STRING,
|
||||||
_rev: OPTIONAL_STRING,
|
_rev: OPTIONAL_STRING,
|
||||||
name: Joi.string().required(),
|
name: Joi.string().required(),
|
||||||
// this is the base permission ID (for now a built in)
|
// this is the base permission ID (for now a built in)
|
||||||
permissionId: Joi.string().valid(...Object.values(BuiltinPermissionID)).required(),
|
permissionId: Joi.string().valid(...Object.values(permissions.BuiltinPermissionID)).required(),
|
||||||
permissions: Joi.object()
|
permissions: Joi.object()
|
||||||
.pattern(/.*/, [Joi.string().valid(...permLevelArray)])
|
.pattern(/.*/, [Joi.string().valid(...permLevelArray)])
|
||||||
.optional(),
|
.optional(),
|
||||||
|
@ -148,19 +144,19 @@ exports.roleValidator = () => {
|
||||||
}).unknown(true))
|
}).unknown(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.permissionValidator = () => {
|
export function permissionValidator() {
|
||||||
const permLevelArray = Object.values(PermissionLevel)
|
const permLevelArray = Object.values(permissions.PermissionLevel)
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.params(Joi.object({
|
return auth.joiValidator.params(Joi.object({
|
||||||
level: Joi.string().valid(...permLevelArray).required(),
|
level: Joi.string().valid(...permLevelArray).required(),
|
||||||
resourceId: Joi.string(),
|
resourceId: Joi.string(),
|
||||||
roleId: Joi.string(),
|
roleId: Joi.string(),
|
||||||
}).unknown(true))
|
}).unknown(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.screenValidator = () => {
|
export function screenValidator() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return auth.joiValidator.body(Joi.object({
|
||||||
name: Joi.string().required(),
|
name: Joi.string().required(),
|
||||||
showNavigation: OPTIONAL_BOOLEAN,
|
showNavigation: OPTIONAL_BOOLEAN,
|
||||||
width: OPTIONAL_STRING,
|
width: OPTIONAL_STRING,
|
||||||
|
@ -181,7 +177,7 @@ exports.screenValidator = () => {
|
||||||
}).unknown(true))
|
}).unknown(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateStepSchema(allowStepTypes) {
|
function generateStepSchema(allowStepTypes: string[]) {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
stepId: Joi.string().required(),
|
stepId: Joi.string().required(),
|
||||||
|
@ -196,9 +192,9 @@ function generateStepSchema(allowStepTypes) {
|
||||||
}).unknown(true)
|
}).unknown(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.automationValidator = (existing = false) => {
|
export function automationValidator(existing = false) {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return auth.joiValidator.body(Joi.object({
|
||||||
_id: existing ? Joi.string().required() : OPTIONAL_STRING,
|
_id: existing ? Joi.string().required() : OPTIONAL_STRING,
|
||||||
_rev: existing ? Joi.string().required() : OPTIONAL_STRING,
|
_rev: existing ? Joi.string().required() : OPTIONAL_STRING,
|
||||||
name: Joi.string().required(),
|
name: Joi.string().required(),
|
||||||
|
@ -210,9 +206,9 @@ exports.automationValidator = (existing = false) => {
|
||||||
}).unknown(true))
|
}).unknown(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.applicationValidator = (opts = { isCreate: true }) => {
|
export function applicationValidator(opts = { isCreate: true }) {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const base = {
|
const base: any = {
|
||||||
_id: OPTIONAL_STRING,
|
_id: OPTIONAL_STRING,
|
||||||
_rev: OPTIONAL_STRING,
|
_rev: OPTIONAL_STRING,
|
||||||
url: OPTIONAL_STRING,
|
url: OPTIONAL_STRING,
|
||||||
|
@ -230,7 +226,7 @@ exports.applicationValidator = (opts = { isCreate: true }) => {
|
||||||
base.name = appNameValidator.optional()
|
base.name = appNameValidator.optional()
|
||||||
}
|
}
|
||||||
|
|
||||||
return joiValidator.body(
|
return auth.joiValidator.body(
|
||||||
Joi.object({
|
Joi.object({
|
||||||
_id: OPTIONAL_STRING,
|
_id: OPTIONAL_STRING,
|
||||||
_rev: OPTIONAL_STRING,
|
_rev: OPTIONAL_STRING,
|
|
@ -5,7 +5,7 @@ import { permissions } from "@budibase/backend-core"
|
||||||
import { webhookValidator } from "./utils/validators"
|
import { webhookValidator } from "./utils/validators"
|
||||||
|
|
||||||
const BUILDER = permissions.BUILDER
|
const BUILDER = permissions.BUILDER
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.get("/api/webhooks", authorized(BUILDER), controller.fetch)
|
.get("/api/webhooks", authorized(BUILDER), controller.fetch)
|
||||||
|
|
|
@ -1,23 +1,13 @@
|
||||||
import {
|
import { roles, permissions, auth, context } from "@budibase/backend-core"
|
||||||
getUserRoleHierarchy,
|
import { Role } from "@budibase/types"
|
||||||
getRequiredResourceRole,
|
import builderMiddleware from "./builder"
|
||||||
BUILTIN_ROLE_IDS,
|
import { isWebhookEndpoint } from "./utils"
|
||||||
} from "@budibase/backend-core/roles"
|
|
||||||
const {
|
|
||||||
PermissionType,
|
|
||||||
PermissionLevel,
|
|
||||||
doesHaveBasePermission,
|
|
||||||
} = require("@budibase/backend-core/permissions")
|
|
||||||
const builderMiddleware = require("./builder")
|
|
||||||
const { isWebhookEndpoint } = require("./utils")
|
|
||||||
const { buildCsrfMiddleware } = require("@budibase/backend-core/auth")
|
|
||||||
const { getAppId } = require("@budibase/backend-core/context")
|
|
||||||
|
|
||||||
function hasResource(ctx: any) {
|
function hasResource(ctx: any) {
|
||||||
return ctx.resourceId != null
|
return ctx.resourceId != null
|
||||||
}
|
}
|
||||||
|
|
||||||
const csrf = buildCsrfMiddleware()
|
const csrf = auth.buildCsrfMiddleware()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply authorization to the requested resource:
|
* Apply authorization to the requested resource:
|
||||||
|
@ -33,7 +23,7 @@ const checkAuthorized = async (
|
||||||
) => {
|
) => {
|
||||||
// check if this is a builder api and the user is not a builder
|
// check if this is a builder api and the user is not a builder
|
||||||
const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
|
const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
|
||||||
const isBuilderApi = permType === PermissionType.BUILDER
|
const isBuilderApi = permType === permissions.PermissionType.BUILDER
|
||||||
if (isBuilderApi && !isBuilder) {
|
if (isBuilderApi && !isBuilder) {
|
||||||
return ctx.throw(403, "Not Authorized")
|
return ctx.throw(403, "Not Authorized")
|
||||||
}
|
}
|
||||||
|
@ -51,10 +41,10 @@ const checkAuthorizedResource = async (
|
||||||
permLevel: any
|
permLevel: any
|
||||||
) => {
|
) => {
|
||||||
// get the user's roles
|
// get the user's roles
|
||||||
const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC
|
const roleId = ctx.roleId || roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||||
const userRoles = (await getUserRoleHierarchy(roleId, {
|
const userRoles = (await roles.getUserRoleHierarchy(roleId, {
|
||||||
idOnly: false,
|
idOnly: false,
|
||||||
})) as { _id: string }[]
|
})) as Role[]
|
||||||
const permError = "User does not have permission"
|
const permError = "User does not have permission"
|
||||||
// check if the user has the required role
|
// check if the user has the required role
|
||||||
if (resourceRoles.length > 0) {
|
if (resourceRoles.length > 0) {
|
||||||
|
@ -66,7 +56,9 @@ const checkAuthorizedResource = async (
|
||||||
ctx.throw(403, permError)
|
ctx.throw(403, permError)
|
||||||
}
|
}
|
||||||
// fallback to the base permissions when no resource roles are found
|
// fallback to the base permissions when no resource roles are found
|
||||||
} else if (!doesHaveBasePermission(permType, permLevel, userRoles)) {
|
} else if (
|
||||||
|
!permissions.doesHaveBasePermission(permType, permLevel, userRoles)
|
||||||
|
) {
|
||||||
ctx.throw(403, permError)
|
ctx.throw(403, permError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,21 +83,22 @@ export = (permType: any, permLevel: any = null, opts = { schema: false }) =>
|
||||||
let resourceRoles: any = []
|
let resourceRoles: any = []
|
||||||
let otherLevelRoles: any = []
|
let otherLevelRoles: any = []
|
||||||
const otherLevel =
|
const otherLevel =
|
||||||
permLevel === PermissionLevel.READ
|
permLevel === permissions.PermissionLevel.READ
|
||||||
? PermissionLevel.WRITE
|
? permissions.PermissionLevel.WRITE
|
||||||
: PermissionLevel.READ
|
: permissions.PermissionLevel.READ
|
||||||
const appId = getAppId()
|
const appId = context.getAppId()
|
||||||
if (appId && hasResource(ctx)) {
|
if (appId && hasResource(ctx)) {
|
||||||
resourceRoles = await getRequiredResourceRole(permLevel, ctx)
|
resourceRoles = await roles.getRequiredResourceRole(permLevel, ctx)
|
||||||
if (opts && opts.schema) {
|
if (opts && opts.schema) {
|
||||||
otherLevelRoles = await getRequiredResourceRole(otherLevel, ctx)
|
otherLevelRoles = await roles.getRequiredResourceRole(otherLevel, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the resource is public, proceed
|
// if the resource is public, proceed
|
||||||
if (
|
if (
|
||||||
resourceRoles.includes(BUILTIN_ROLE_IDS.PUBLIC) ||
|
resourceRoles.includes(roles.BUILTIN_ROLE_IDS.PUBLIC) ||
|
||||||
(otherLevelRoles && otherLevelRoles.includes(BUILTIN_ROLE_IDS.PUBLIC))
|
(otherLevelRoles &&
|
||||||
|
otherLevelRoles.includes(roles.BUILTIN_ROLE_IDS.PUBLIC))
|
||||||
) {
|
) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,7 @@ jest.mock("../../environment", () => ({
|
||||||
)
|
)
|
||||||
const authorizedMiddleware = require("../authorized")
|
const authorizedMiddleware = require("../authorized")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const { PermissionType, PermissionLevel } = require("@budibase/backend-core/permissions")
|
const { permissions } = require("@budibase/backend-core")
|
||||||
const { doInAppContext } = require("@budibase/backend-core/context")
|
|
||||||
|
|
||||||
const APP_ID = ""
|
const APP_ID = ""
|
||||||
|
|
||||||
|
@ -113,7 +112,7 @@ describe("Authorization middleware", () => {
|
||||||
|
|
||||||
it("throws if the user does not have builder permissions", async () => {
|
it("throws if the user does not have builder permissions", async () => {
|
||||||
config.setEnvironment(false)
|
config.setEnvironment(false)
|
||||||
config.setMiddlewareRequiredPermission(PermissionType.BUILDER)
|
config.setMiddlewareRequiredPermission(permissions.PermissionType.BUILDER)
|
||||||
config.setUser({
|
config.setUser({
|
||||||
role: {
|
role: {
|
||||||
_id: ""
|
_id: ""
|
||||||
|
@ -125,13 +124,13 @@ describe("Authorization middleware", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("passes on to next() middleware if the user has resource permission", async () => {
|
it("passes on to next() middleware if the user has resource permission", async () => {
|
||||||
config.setResourceId(PermissionType.QUERY)
|
config.setResourceId(permissions.PermissionType.QUERY)
|
||||||
config.setUser({
|
config.setUser({
|
||||||
role: {
|
role: {
|
||||||
_id: ""
|
_id: ""
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
config.setMiddlewareRequiredPermission(PermissionType.QUERY)
|
config.setMiddlewareRequiredPermission(permissions.PermissionType.QUERY)
|
||||||
|
|
||||||
await config.executeMiddleware()
|
await config.executeMiddleware()
|
||||||
expect(config.next).toHaveBeenCalled()
|
expect(config.next).toHaveBeenCalled()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { db as dbCore } from "@budibase/backend-core"
|
import { db as dbCore, objectStore } from "@budibase/backend-core"
|
||||||
import { budibaseTempDir } from "../../../utilities/budibaseDir"
|
import { budibaseTempDir } from "../../../utilities/budibaseDir"
|
||||||
import { retrieveDirectory } from "../../../utilities/fileSystem/utilities"
|
|
||||||
import { streamFile, createTempFolder } from "../../../utilities/fileSystem"
|
import { streamFile, createTempFolder } from "../../../utilities/fileSystem"
|
||||||
import { ObjectStoreBuckets } from "../../../constants"
|
import { ObjectStoreBuckets } from "../../../constants"
|
||||||
import {
|
import {
|
||||||
|
@ -88,7 +87,10 @@ export async function exportApp(appId: string, config?: ExportOpts) {
|
||||||
// export bucket contents
|
// export bucket contents
|
||||||
let tmpPath
|
let tmpPath
|
||||||
if (!env.isTest()) {
|
if (!env.isTest()) {
|
||||||
tmpPath = await retrieveDirectory(ObjectStoreBuckets.APPS, appPath)
|
tmpPath = await objectStore.retrieveDirectory(
|
||||||
|
ObjectStoreBuckets.APPS,
|
||||||
|
appPath
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
tmpPath = createTempFolder(uuid())
|
tmpPath = createTempFolder(uuid())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import { db as dbCore } from "@budibase/backend-core"
|
import { db as dbCore, objectStore } from "@budibase/backend-core"
|
||||||
import { Database } from "@budibase/types"
|
import { Database } from "@budibase/types"
|
||||||
import { getAutomationParams, TABLE_ROW_PREFIX } from "../../../db/utils"
|
import { getAutomationParams, TABLE_ROW_PREFIX } from "../../../db/utils"
|
||||||
import { budibaseTempDir } from "../../../utilities/budibaseDir"
|
import { budibaseTempDir } from "../../../utilities/budibaseDir"
|
||||||
import { DB_EXPORT_FILE, GLOBAL_DB_EXPORT_FILE } from "./constants"
|
import { DB_EXPORT_FILE, GLOBAL_DB_EXPORT_FILE } from "./constants"
|
||||||
import {
|
|
||||||
upload,
|
|
||||||
uploadDirectory,
|
|
||||||
} from "../../../utilities/fileSystem/utilities"
|
|
||||||
import { downloadTemplate } from "../../../utilities/fileSystem"
|
import { downloadTemplate } from "../../../utilities/fileSystem"
|
||||||
import { FieldTypes, ObjectStoreBuckets } from "../../../constants"
|
import { FieldTypes, ObjectStoreBuckets } from "../../../constants"
|
||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
|
@ -174,11 +170,11 @@ export async function importApp(
|
||||||
filename = join(prodAppId, filename)
|
filename = join(prodAppId, filename)
|
||||||
if (fs.lstatSync(path).isDirectory()) {
|
if (fs.lstatSync(path).isDirectory()) {
|
||||||
promises.push(
|
promises.push(
|
||||||
uploadDirectory(ObjectStoreBuckets.APPS, path, filename)
|
objectStore.uploadDirectory(ObjectStoreBuckets.APPS, path, filename)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
promises.push(
|
promises.push(
|
||||||
upload({
|
objectStore.upload({
|
||||||
bucket: ObjectStoreBuckets.APPS,
|
bucket: ObjectStoreBuckets.APPS,
|
||||||
path,
|
path,
|
||||||
filename,
|
filename,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
require("../../db").init()
|
require("../../db").init()
|
||||||
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles")
|
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const {
|
const {
|
||||||
basicTable,
|
basicTable,
|
||||||
|
@ -13,18 +12,21 @@ const {
|
||||||
basicWebhook,
|
basicWebhook,
|
||||||
TENANT_ID,
|
TENANT_ID,
|
||||||
} = require("./structures")
|
} = require("./structures")
|
||||||
|
const {
|
||||||
|
constants,
|
||||||
|
tenancy,
|
||||||
|
sessions,
|
||||||
|
cache,
|
||||||
|
context,
|
||||||
|
db: dbCore,
|
||||||
|
encryption,
|
||||||
|
auth,
|
||||||
|
roles,
|
||||||
|
} = require("@budibase/backend-core")
|
||||||
const controllers = require("./controllers")
|
const controllers = require("./controllers")
|
||||||
const supertest = require("supertest")
|
const supertest = require("supertest")
|
||||||
const { cleanup } = require("../../utilities/fileSystem")
|
const { cleanup } = require("../../utilities/fileSystem")
|
||||||
const { Cookie, Header } = require("@budibase/backend-core/constants")
|
|
||||||
const { jwt } = require("@budibase/backend-core/auth")
|
|
||||||
const { doInTenant, doWithGlobalDB } = require("@budibase/backend-core/tenancy")
|
|
||||||
const { createASession } = require("@budibase/backend-core/sessions")
|
|
||||||
const { user: userCache } = require("@budibase/backend-core/cache")
|
|
||||||
const newid = require("../../db/newid")
|
const newid = require("../../db/newid")
|
||||||
const context = require("@budibase/backend-core/context")
|
|
||||||
const { generateDevInfoID, SEPARATOR } = require("@budibase/backend-core/db")
|
|
||||||
const { encrypt } = require("@budibase/backend-core/encryption")
|
|
||||||
const { DocumentType, generateUserMetadataID } = require("../../db/utils")
|
const { DocumentType, generateUserMetadataID } = require("../../db/utils")
|
||||||
const { startup } = require("../../startup")
|
const { startup } = require("../../startup")
|
||||||
|
|
||||||
|
@ -83,7 +85,7 @@ class TestConfiguration {
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
appId = this.appId
|
appId = this.appId
|
||||||
}
|
}
|
||||||
return doInTenant(TENANT_ID, () => {
|
return tenancy.doInTenant(TENANT_ID, () => {
|
||||||
// check if already in a context
|
// check if already in a context
|
||||||
if (context.getAppId() == null && appId !== null) {
|
if (context.getAppId() == null && appId !== null) {
|
||||||
return context.doInAppContext(appId, async () => {
|
return context.doInAppContext(appId, async () => {
|
||||||
|
@ -155,7 +157,7 @@ class TestConfiguration {
|
||||||
email = EMAIL,
|
email = EMAIL,
|
||||||
roles,
|
roles,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
return doWithGlobalDB(TENANT_ID, async db => {
|
return tenancy.doWithGlobalDB(TENANT_ID, async db => {
|
||||||
let existing
|
let existing
|
||||||
try {
|
try {
|
||||||
existing = await db.get(id)
|
existing = await db.get(id)
|
||||||
|
@ -170,7 +172,7 @@ class TestConfiguration {
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
}
|
}
|
||||||
await createASession(id, {
|
await sessions.createASession(id, {
|
||||||
sessionId: "sessionid",
|
sessionId: "sessionid",
|
||||||
tenantId: TENANT_ID,
|
tenantId: TENANT_ID,
|
||||||
csrfToken: CSRF_TOKEN,
|
csrfToken: CSRF_TOKEN,
|
||||||
|
@ -212,7 +214,7 @@ class TestConfiguration {
|
||||||
admin,
|
admin,
|
||||||
roles,
|
roles,
|
||||||
})
|
})
|
||||||
await userCache.invalidateUser(globalId)
|
await cache.user.invalidateUser(globalId)
|
||||||
return {
|
return {
|
||||||
...resp,
|
...resp,
|
||||||
globalId,
|
globalId,
|
||||||
|
@ -227,19 +229,19 @@ class TestConfiguration {
|
||||||
throw "Server has not been opened, cannot login."
|
throw "Server has not been opened, cannot login."
|
||||||
}
|
}
|
||||||
// make sure the user exists in the global DB
|
// make sure the user exists in the global DB
|
||||||
if (roleId !== BUILTIN_ROLE_IDS.PUBLIC) {
|
if (roleId !== roles.BUILTIN_ROLE_IDS.PUBLIC) {
|
||||||
await this.globalUser({
|
await this.globalUser({
|
||||||
id: userId,
|
id: userId,
|
||||||
builder,
|
builder,
|
||||||
roles: { [this.prodAppId]: roleId },
|
roles: { [this.prodAppId]: roleId },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
await createASession(userId, {
|
await sessions.createASession(userId, {
|
||||||
sessionId: "sessionid",
|
sessionId: "sessionid",
|
||||||
tenantId: TENANT_ID,
|
tenantId: TENANT_ID,
|
||||||
})
|
})
|
||||||
// have to fake this
|
// have to fake this
|
||||||
const auth = {
|
const authObj = {
|
||||||
userId,
|
userId,
|
||||||
sessionId: "sessionid",
|
sessionId: "sessionid",
|
||||||
tenantId: TENANT_ID,
|
tenantId: TENANT_ID,
|
||||||
|
@ -248,45 +250,45 @@ class TestConfiguration {
|
||||||
roleId: roleId,
|
roleId: roleId,
|
||||||
appId,
|
appId,
|
||||||
}
|
}
|
||||||
const authToken = jwt.sign(auth, env.JWT_SECRET)
|
const authToken = auth.jwt.sign(authObj, env.JWT_SECRET)
|
||||||
const appToken = jwt.sign(app, env.JWT_SECRET)
|
const appToken = auth.jwt.sign(app, env.JWT_SECRET)
|
||||||
|
|
||||||
// returning necessary request headers
|
// returning necessary request headers
|
||||||
await userCache.invalidateUser(userId)
|
await cache.user.invalidateUser(userId)
|
||||||
return {
|
return {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
Cookie: [
|
Cookie: [
|
||||||
`${Cookie.Auth}=${authToken}`,
|
`${constants.Cookie.Auth}=${authToken}`,
|
||||||
`${Cookie.CurrentApp}=${appToken}`,
|
`${constants.Cookie.CurrentApp}=${appToken}`,
|
||||||
],
|
],
|
||||||
[Header.APP_ID]: appId,
|
[constants.Header.APP_ID]: appId,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultHeaders(extras = {}) {
|
defaultHeaders(extras = {}) {
|
||||||
const auth = {
|
const authObj = {
|
||||||
userId: GLOBAL_USER_ID,
|
userId: GLOBAL_USER_ID,
|
||||||
sessionId: "sessionid",
|
sessionId: "sessionid",
|
||||||
tenantId: TENANT_ID,
|
tenantId: TENANT_ID,
|
||||||
}
|
}
|
||||||
const app = {
|
const app = {
|
||||||
roleId: BUILTIN_ROLE_IDS.ADMIN,
|
roleId: roles.BUILTIN_ROLE_IDS.ADMIN,
|
||||||
appId: this.appId,
|
appId: this.appId,
|
||||||
}
|
}
|
||||||
const authToken = jwt.sign(auth, env.JWT_SECRET)
|
const authToken = auth.jwt.sign(authObj, env.JWT_SECRET)
|
||||||
const appToken = jwt.sign(app, env.JWT_SECRET)
|
const appToken = auth.jwt.sign(app, env.JWT_SECRET)
|
||||||
const headers = {
|
const headers = {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
Cookie: [
|
Cookie: [
|
||||||
`${Cookie.Auth}=${authToken}`,
|
`${constants.Cookie.Auth}=${authToken}`,
|
||||||
`${Cookie.CurrentApp}=${appToken}`,
|
`${constants.Cookie.CurrentApp}=${appToken}`,
|
||||||
],
|
],
|
||||||
[Header.CSRF_TOKEN]: CSRF_TOKEN,
|
[constants.Header.CSRF_TOKEN]: CSRF_TOKEN,
|
||||||
...extras,
|
...extras,
|
||||||
}
|
}
|
||||||
if (this.appId) {
|
if (this.appId) {
|
||||||
headers[Header.APP_ID] = this.appId
|
headers[constants.Header.APP_ID] = this.appId
|
||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
@ -298,14 +300,14 @@ class TestConfiguration {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
}
|
}
|
||||||
if (appId) {
|
if (appId) {
|
||||||
headers[Header.APP_ID] = appId
|
headers[constants.Header.APP_ID] = appId
|
||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
async roleHeaders({
|
async roleHeaders({
|
||||||
email = EMAIL,
|
email = EMAIL,
|
||||||
roleId = BUILTIN_ROLE_IDS.ADMIN,
|
roleId = roles.BUILTIN_ROLE_IDS.ADMIN,
|
||||||
builder = false,
|
builder = false,
|
||||||
prodApp = true,
|
prodApp = true,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
@ -315,15 +317,17 @@ class TestConfiguration {
|
||||||
// API
|
// API
|
||||||
|
|
||||||
async generateApiKey(userId = GLOBAL_USER_ID) {
|
async generateApiKey(userId = GLOBAL_USER_ID) {
|
||||||
return doWithGlobalDB(TENANT_ID, async db => {
|
return tenancy.doWithGlobalDB(TENANT_ID, async db => {
|
||||||
const id = generateDevInfoID(userId)
|
const id = dbCore.generateDevInfoID(userId)
|
||||||
let devInfo
|
let devInfo
|
||||||
try {
|
try {
|
||||||
devInfo = await db.get(id)
|
devInfo = await db.get(id)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
devInfo = { _id: id, userId }
|
devInfo = { _id: id, userId }
|
||||||
}
|
}
|
||||||
devInfo.apiKey = encrypt(`${TENANT_ID}${SEPARATOR}${newid()}`)
|
devInfo.apiKey = encryption.encrypt(
|
||||||
|
`${TENANT_ID}${dbCore.SEPARATOR}${newid()}`
|
||||||
|
)
|
||||||
await db.put(devInfo)
|
await db.put(devInfo)
|
||||||
return devInfo.apiKey
|
return devInfo.apiKey
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles")
|
const { roles, permissions } = require("@budibase/backend-core")
|
||||||
const { BuiltinPermissionID } = require("@budibase/backend-core/permissions")
|
|
||||||
const { createHomeScreen } = require("../../constants/screens")
|
const { createHomeScreen } = require("../../constants/screens")
|
||||||
const { EMPTY_LAYOUT } = require("../../constants/layouts")
|
const { EMPTY_LAYOUT } = require("../../constants/layouts")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
|
@ -134,8 +133,8 @@ exports.basicLinkedRow = (tableId, linkedRowId, linkField = "link") => {
|
||||||
exports.basicRole = () => {
|
exports.basicRole = () => {
|
||||||
return {
|
return {
|
||||||
name: "NewRole",
|
name: "NewRole",
|
||||||
inherits: BUILTIN_ROLE_IDS.BASIC,
|
inherits: roles.BUILTIN_ROLE_IDS.BASIC,
|
||||||
permissionId: BuiltinPermissionID.READ_ONLY,
|
permissionId: permissions.BuiltinPermissionID.READ_ONLY,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const { join } = require("path")
|
const { join } = require("path")
|
||||||
const { ObjectStoreBuckets } = require("../../constants")
|
const { ObjectStoreBuckets } = require("../../constants")
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const { upload, retrieveToTmp, streamUpload } = require("./utilities")
|
const { objectStore } = require("@budibase/backend-core")
|
||||||
const { resolve } = require("../centralPath")
|
const { resolve } = require("../centralPath")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const TOP_LEVEL_PATH = join(__dirname, "..", "..", "..")
|
const TOP_LEVEL_PATH = join(__dirname, "..", "..", "..")
|
||||||
|
@ -38,13 +38,13 @@ exports.backupClientLibrary = async appId => {
|
||||||
let tmpManifestPath
|
let tmpManifestPath
|
||||||
try {
|
try {
|
||||||
// Try to load the manifest from the new file location
|
// Try to load the manifest from the new file location
|
||||||
tmpManifestPath = await retrieveToTmp(
|
tmpManifestPath = await objectStore.retrieveToTmp(
|
||||||
ObjectStoreBuckets.APPS,
|
ObjectStoreBuckets.APPS,
|
||||||
join(appId, "manifest.json")
|
join(appId, "manifest.json")
|
||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Fallback to loading it from the old location for old apps
|
// Fallback to loading it from the old location for old apps
|
||||||
tmpManifestPath = await retrieveToTmp(
|
tmpManifestPath = await objectStore.retrieveToTmp(
|
||||||
ObjectStoreBuckets.APPS,
|
ObjectStoreBuckets.APPS,
|
||||||
join(
|
join(
|
||||||
appId,
|
appId,
|
||||||
|
@ -58,19 +58,19 @@ exports.backupClientLibrary = async appId => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy existing client lib to tmp
|
// Copy existing client lib to tmp
|
||||||
const tmpClientPath = await retrieveToTmp(
|
const tmpClientPath = await objectStore.retrieveToTmp(
|
||||||
ObjectStoreBuckets.APPS,
|
ObjectStoreBuckets.APPS,
|
||||||
join(appId, "budibase-client.js")
|
join(appId, "budibase-client.js")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Upload manifest and client library as backups
|
// Upload manifest and client library as backups
|
||||||
const manifestUpload = upload({
|
const manifestUpload = objectStore.upload({
|
||||||
bucket: ObjectStoreBuckets.APPS,
|
bucket: ObjectStoreBuckets.APPS,
|
||||||
filename: join(appId, "manifest.json.bak"),
|
filename: join(appId, "manifest.json.bak"),
|
||||||
path: tmpManifestPath,
|
path: tmpManifestPath,
|
||||||
type: "application/json",
|
type: "application/json",
|
||||||
})
|
})
|
||||||
const clientUpload = upload({
|
const clientUpload = objectStore.upload({
|
||||||
bucket: ObjectStoreBuckets.APPS,
|
bucket: ObjectStoreBuckets.APPS,
|
||||||
filename: join(appId, "budibase-client.js.bak"),
|
filename: join(appId, "budibase-client.js.bak"),
|
||||||
path: tmpClientPath,
|
path: tmpClientPath,
|
||||||
|
@ -99,7 +99,7 @@ exports.updateClientLibrary = async appId => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload latest manifest and client library
|
// Upload latest manifest and client library
|
||||||
const manifestUpload = streamUpload(
|
const manifestUpload = objectStore.streamUpload(
|
||||||
ObjectStoreBuckets.APPS,
|
ObjectStoreBuckets.APPS,
|
||||||
join(appId, "manifest.json"),
|
join(appId, "manifest.json"),
|
||||||
fs.createReadStream(manifest),
|
fs.createReadStream(manifest),
|
||||||
|
@ -107,7 +107,7 @@ exports.updateClientLibrary = async appId => {
|
||||||
ContentType: "application/json",
|
ContentType: "application/json",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const clientUpload = streamUpload(
|
const clientUpload = objectStore.streamUpload(
|
||||||
ObjectStoreBuckets.APPS,
|
ObjectStoreBuckets.APPS,
|
||||||
join(appId, "budibase-client.js"),
|
join(appId, "budibase-client.js"),
|
||||||
fs.createReadStream(client),
|
fs.createReadStream(client),
|
||||||
|
@ -126,25 +126,25 @@ exports.updateClientLibrary = async appId => {
|
||||||
*/
|
*/
|
||||||
exports.revertClientLibrary = async appId => {
|
exports.revertClientLibrary = async appId => {
|
||||||
// Copy backups manifest to tmp directory
|
// Copy backups manifest to tmp directory
|
||||||
const tmpManifestPath = await retrieveToTmp(
|
const tmpManifestPath = await objectStore.retrieveToTmp(
|
||||||
ObjectStoreBuckets.APPS,
|
ObjectStoreBuckets.APPS,
|
||||||
join(appId, "manifest.json.bak")
|
join(appId, "manifest.json.bak")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Copy backup client lib to tmp
|
// Copy backup client lib to tmp
|
||||||
const tmpClientPath = await retrieveToTmp(
|
const tmpClientPath = await objectStore.retrieveToTmp(
|
||||||
ObjectStoreBuckets.APPS,
|
ObjectStoreBuckets.APPS,
|
||||||
join(appId, "budibase-client.js.bak")
|
join(appId, "budibase-client.js.bak")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Upload backups as new versions
|
// Upload backups as new versions
|
||||||
const manifestUpload = upload({
|
const manifestUpload = objectStore.upload({
|
||||||
bucket: ObjectStoreBuckets.APPS,
|
bucket: ObjectStoreBuckets.APPS,
|
||||||
filename: join(appId, "manifest.json"),
|
filename: join(appId, "manifest.json"),
|
||||||
path: tmpManifestPath,
|
path: tmpManifestPath,
|
||||||
type: "application/json",
|
type: "application/json",
|
||||||
})
|
})
|
||||||
const clientUpload = upload({
|
const clientUpload = objectStore.upload({
|
||||||
bucket: ObjectStoreBuckets.APPS,
|
bucket: ObjectStoreBuckets.APPS,
|
||||||
filename: join(appId, "budibase-client.js"),
|
filename: join(appId, "budibase-client.js"),
|
||||||
path: tmpClientPath,
|
path: tmpClientPath,
|
||||||
|
|
|
@ -2,7 +2,13 @@ const { budibaseTempDir } = require("../budibaseDir")
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const { join } = require("path")
|
const { join } = require("path")
|
||||||
const uuid = require("uuid/v4")
|
const uuid = require("uuid/v4")
|
||||||
|
const { context, objectStore } = require("@budibase/backend-core")
|
||||||
const { ObjectStoreBuckets } = require("../../constants")
|
const { ObjectStoreBuckets } = require("../../constants")
|
||||||
|
const { updateClientLibrary } = require("./clientLibrary")
|
||||||
|
const { checkSlashesInUrl } = require("../")
|
||||||
|
const env = require("../../environment")
|
||||||
|
const tar = require("tar")
|
||||||
|
const fetch = require("node-fetch")
|
||||||
const {
|
const {
|
||||||
upload,
|
upload,
|
||||||
retrieve,
|
retrieve,
|
||||||
|
@ -11,13 +17,7 @@ const {
|
||||||
downloadTarball,
|
downloadTarball,
|
||||||
downloadTarballDirect,
|
downloadTarballDirect,
|
||||||
deleteFiles,
|
deleteFiles,
|
||||||
} = require("./utilities")
|
} = objectStore
|
||||||
const { updateClientLibrary } = require("./clientLibrary")
|
|
||||||
const { checkSlashesInUrl } = require("../")
|
|
||||||
const env = require("../../environment")
|
|
||||||
const { getAppId } = require("@budibase/backend-core/context")
|
|
||||||
const tar = require("tar")
|
|
||||||
const fetch = require("node-fetch")
|
|
||||||
|
|
||||||
const TOP_LEVEL_PATH = join(__dirname, "..", "..", "..")
|
const TOP_LEVEL_PATH = join(__dirname, "..", "..", "..")
|
||||||
const NODE_MODULES_PATH = join(TOP_LEVEL_PATH, "node_modules")
|
const NODE_MODULES_PATH = join(TOP_LEVEL_PATH, "node_modules")
|
||||||
|
@ -165,7 +165,7 @@ exports.downloadTemplate = async (type, name) => {
|
||||||
* Retrieves component libraries from object store (or tmp symlink if in local)
|
* Retrieves component libraries from object store (or tmp symlink if in local)
|
||||||
*/
|
*/
|
||||||
exports.getComponentLibraryManifest = async library => {
|
exports.getComponentLibraryManifest = async library => {
|
||||||
const appId = getAppId()
|
const appId = context.getAppId()
|
||||||
const filename = "manifest.json"
|
const filename = "manifest.json"
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
// when testing in cypress and so on we need to get the package
|
// when testing in cypress and so on we need to get the package
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
const {
|
|
||||||
ObjectStore,
|
|
||||||
makeSureBucketExists,
|
|
||||||
upload,
|
|
||||||
deleteFiles,
|
|
||||||
streamUpload,
|
|
||||||
retrieve,
|
|
||||||
retrieveToTmp,
|
|
||||||
retrieveDirectory,
|
|
||||||
deleteFolder,
|
|
||||||
uploadDirectory,
|
|
||||||
downloadTarball,
|
|
||||||
downloadTarballDirect,
|
|
||||||
} = require("@budibase/backend-core/objectStore")
|
|
||||||
|
|
||||||
/***********************************
|
|
||||||
* NOTE *
|
|
||||||
* This file purely exists so that *
|
|
||||||
* the object store functionality *
|
|
||||||
* can easily be mocked out of *
|
|
||||||
* the server without mocking the *
|
|
||||||
* entire core library. *
|
|
||||||
***********************************/
|
|
||||||
|
|
||||||
exports.ObjectStore = ObjectStore
|
|
||||||
exports.makeSureBucketExists = makeSureBucketExists
|
|
||||||
exports.upload = upload
|
|
||||||
exports.streamUpload = streamUpload
|
|
||||||
exports.retrieve = retrieve
|
|
||||||
exports.retrieveToTmp = retrieveToTmp
|
|
||||||
exports.retrieveDirectory = retrieveDirectory
|
|
||||||
exports.deleteFolder = deleteFolder
|
|
||||||
exports.uploadDirectory = uploadDirectory
|
|
||||||
exports.downloadTarball = downloadTarball
|
|
||||||
exports.downloadTarballDirect = downloadTarballDirect
|
|
||||||
exports.deleteFiles = deleteFiles
|
|
|
@ -1,129 +1,40 @@
|
||||||
const linkRows = require("../../db/linkedRows")
|
import linkRows from "../../db/linkedRows"
|
||||||
|
import { FieldTypes, AutoFieldSubTypes } from "../../constants"
|
||||||
|
import { attachmentsRelativeURL } from "../index"
|
||||||
|
import { processFormulas, fixAutoColumnSubType } from "./utils"
|
||||||
|
import { ObjectStoreBuckets } from "../../constants"
|
||||||
|
import { context, db as dbCore, objectStore } from "@budibase/backend-core"
|
||||||
|
import { InternalTables } from "../../db/utils"
|
||||||
|
import { TYPE_TRANSFORM_MAP } from "./map"
|
||||||
|
import { Row, User, Table } from "@budibase/types"
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
const { FieldTypes, AutoFieldSubTypes } = require("../../constants")
|
|
||||||
const { attachmentsRelativeURL } = require("../index")
|
type AutoColumnProcessingOpts = {
|
||||||
const { processFormulas, fixAutoColumnSubType } = require("./utils")
|
reprocessing?: boolean
|
||||||
const { deleteFiles } = require("../../utilities/fileSystem/utilities")
|
noAutoRelationships?: boolean
|
||||||
const { ObjectStoreBuckets } = require("../../constants")
|
}
|
||||||
const {
|
|
||||||
isProdAppID,
|
|
||||||
getProdAppID,
|
|
||||||
dbExists,
|
|
||||||
} = require("@budibase/backend-core/db")
|
|
||||||
const { getAppId } = require("@budibase/backend-core/context")
|
|
||||||
const { InternalTables } = require("../../db/utils")
|
|
||||||
|
|
||||||
const BASE_AUTO_ID = 1
|
const BASE_AUTO_ID = 1
|
||||||
|
|
||||||
/**
|
|
||||||
* A map of how we convert various properties in rows to each other based on the row type.
|
|
||||||
*/
|
|
||||||
const TYPE_TRANSFORM_MAP = {
|
|
||||||
[FieldTypes.LINK]: {
|
|
||||||
"": [],
|
|
||||||
[null]: [],
|
|
||||||
[undefined]: undefined,
|
|
||||||
parse: link => {
|
|
||||||
if (Array.isArray(link) && typeof link[0] === "object") {
|
|
||||||
return link.map(el => (el && el._id ? el._id : el))
|
|
||||||
}
|
|
||||||
if (typeof link === "string") {
|
|
||||||
return [link]
|
|
||||||
}
|
|
||||||
return link
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[FieldTypes.OPTIONS]: {
|
|
||||||
"": null,
|
|
||||||
[null]: null,
|
|
||||||
[undefined]: undefined,
|
|
||||||
},
|
|
||||||
[FieldTypes.ARRAY]: {
|
|
||||||
"": [],
|
|
||||||
[null]: [],
|
|
||||||
[undefined]: undefined,
|
|
||||||
},
|
|
||||||
[FieldTypes.STRING]: {
|
|
||||||
"": "",
|
|
||||||
[null]: "",
|
|
||||||
[undefined]: undefined,
|
|
||||||
},
|
|
||||||
[FieldTypes.BARCODEQR]: {
|
|
||||||
"": "",
|
|
||||||
[null]: "",
|
|
||||||
[undefined]: undefined,
|
|
||||||
},
|
|
||||||
[FieldTypes.FORMULA]: {
|
|
||||||
"": "",
|
|
||||||
[null]: "",
|
|
||||||
[undefined]: undefined,
|
|
||||||
},
|
|
||||||
[FieldTypes.LONGFORM]: {
|
|
||||||
"": "",
|
|
||||||
[null]: "",
|
|
||||||
[undefined]: undefined,
|
|
||||||
},
|
|
||||||
[FieldTypes.NUMBER]: {
|
|
||||||
"": null,
|
|
||||||
[null]: null,
|
|
||||||
[undefined]: undefined,
|
|
||||||
parse: n => parseFloat(n),
|
|
||||||
},
|
|
||||||
[FieldTypes.DATETIME]: {
|
|
||||||
"": null,
|
|
||||||
[undefined]: undefined,
|
|
||||||
[null]: null,
|
|
||||||
parse: date => {
|
|
||||||
if (date instanceof Date) {
|
|
||||||
return date.toISOString()
|
|
||||||
}
|
|
||||||
return date
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[FieldTypes.ATTACHMENT]: {
|
|
||||||
"": [],
|
|
||||||
[null]: [],
|
|
||||||
[undefined]: undefined,
|
|
||||||
},
|
|
||||||
[FieldTypes.BOOLEAN]: {
|
|
||||||
"": null,
|
|
||||||
[null]: null,
|
|
||||||
[undefined]: undefined,
|
|
||||||
true: true,
|
|
||||||
false: false,
|
|
||||||
},
|
|
||||||
[FieldTypes.AUTO]: {
|
|
||||||
parse: () => undefined,
|
|
||||||
},
|
|
||||||
[FieldTypes.JSON]: {
|
|
||||||
parse: input => {
|
|
||||||
try {
|
|
||||||
if (input === "") {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
return JSON.parse(input)
|
|
||||||
} catch (err) {
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given the old state of the row and the new one after an update, this will
|
* Given the old state of the row and the new one after an update, this will
|
||||||
* find the keys that have been removed in the updated row.
|
* find the keys that have been removed in the updated row.
|
||||||
*/
|
*/
|
||||||
function getRemovedAttachmentKeys(oldRow, row, attachmentKey) {
|
function getRemovedAttachmentKeys(
|
||||||
|
oldRow: Row,
|
||||||
|
row: Row,
|
||||||
|
attachmentKey: string
|
||||||
|
) {
|
||||||
if (!oldRow[attachmentKey]) {
|
if (!oldRow[attachmentKey]) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
const oldKeys = oldRow[attachmentKey].map(attachment => attachment.key)
|
const oldKeys = oldRow[attachmentKey].map((attachment: any) => attachment.key)
|
||||||
// no attachments in new row, all removed
|
// no attachments in new row, all removed
|
||||||
if (!row[attachmentKey]) {
|
if (!row[attachmentKey]) {
|
||||||
return oldKeys
|
return oldKeys
|
||||||
}
|
}
|
||||||
const newKeys = row[attachmentKey].map(attachment => attachment.key)
|
const newKeys = row[attachmentKey].map((attachment: any) => attachment.key)
|
||||||
return oldKeys.filter(key => newKeys.indexOf(key) === -1)
|
return oldKeys.filter((key: any) => newKeys.indexOf(key) === -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -136,11 +47,11 @@ function getRemovedAttachmentKeys(oldRow, row, attachmentKey) {
|
||||||
* @returns {{row: Object, table: Object}} The updated row and table, the table may need to be updated
|
* @returns {{row: Object, table: Object}} The updated row and table, the table may need to be updated
|
||||||
* for automatic ID purposes.
|
* for automatic ID purposes.
|
||||||
*/
|
*/
|
||||||
function processAutoColumn(
|
export function processAutoColumn(
|
||||||
user,
|
user: User,
|
||||||
table,
|
table: Table,
|
||||||
row,
|
row: Row,
|
||||||
opts = { reprocessing: false, noAutoRelationships: false }
|
opts: AutoColumnProcessingOpts
|
||||||
) {
|
) {
|
||||||
let noUser = !user || !user.userId
|
let noUser = !user || !user.userId
|
||||||
let isUserTable = table._id === InternalTables.USER_METADATA
|
let isUserTable = table._id === InternalTables.USER_METADATA
|
||||||
|
@ -186,9 +97,6 @@ function processAutoColumn(
|
||||||
}
|
}
|
||||||
return { table, row }
|
return { table, row }
|
||||||
}
|
}
|
||||||
exports.processAutoColumn = processAutoColumn
|
|
||||||
exports.fixAutoColumnSubType = fixAutoColumnSubType
|
|
||||||
exports.processFormulas = processFormulas
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This will coerce a value to the correct types based on the type transform map
|
* This will coerce a value to the correct types based on the type transform map
|
||||||
|
@ -196,15 +104,17 @@ exports.processFormulas = processFormulas
|
||||||
* @param {object} type The type fo coerce to
|
* @param {object} type The type fo coerce to
|
||||||
* @returns {object} The coerced value
|
* @returns {object} The coerced value
|
||||||
*/
|
*/
|
||||||
exports.coerce = (row, type) => {
|
export function coerce(row: any, type: any) {
|
||||||
// no coercion specified for type, skip it
|
// no coercion specified for type, skip it
|
||||||
if (!TYPE_TRANSFORM_MAP[type]) {
|
if (!TYPE_TRANSFORM_MAP[type]) {
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line no-prototype-builtins
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(row)) {
|
if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(row)) {
|
||||||
|
// @ts-ignore
|
||||||
return TYPE_TRANSFORM_MAP[type][row]
|
return TYPE_TRANSFORM_MAP[type][row]
|
||||||
} else if (TYPE_TRANSFORM_MAP[type].parse) {
|
} else if (TYPE_TRANSFORM_MAP[type].parse) {
|
||||||
|
// @ts-ignore
|
||||||
return TYPE_TRANSFORM_MAP[type].parse(row)
|
return TYPE_TRANSFORM_MAP[type].parse(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,12 +130,12 @@ exports.coerce = (row, type) => {
|
||||||
* @param {object} opts some input processing options (like disabling auto-column relationships).
|
* @param {object} opts some input processing options (like disabling auto-column relationships).
|
||||||
* @returns {object} the row which has been prepared to be written to the DB.
|
* @returns {object} the row which has been prepared to be written to the DB.
|
||||||
*/
|
*/
|
||||||
exports.inputProcessing = (
|
export function inputProcessing(
|
||||||
user = {},
|
user: User,
|
||||||
table,
|
table: Table,
|
||||||
row,
|
row: Row,
|
||||||
opts = { noAutoRelationships: false }
|
opts: AutoColumnProcessingOpts
|
||||||
) => {
|
) {
|
||||||
let clonedRow = cloneDeep(row)
|
let clonedRow = cloneDeep(row)
|
||||||
// need to copy the table so it can be differenced on way out
|
// need to copy the table so it can be differenced on way out
|
||||||
const copiedTable = cloneDeep(table)
|
const copiedTable = cloneDeep(table)
|
||||||
|
@ -245,7 +155,7 @@ exports.inputProcessing = (
|
||||||
}
|
}
|
||||||
// otherwise coerce what is there to correct types
|
// otherwise coerce what is there to correct types
|
||||||
else {
|
else {
|
||||||
clonedRow[key] = exports.coerce(value, field.type)
|
clonedRow[key] = coerce(value, field.type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +177,11 @@ exports.inputProcessing = (
|
||||||
* @param {object} opts used to set some options for the output, such as disabling relationship squashing.
|
* @param {object} opts used to set some options for the output, such as disabling relationship squashing.
|
||||||
* @returns {object[]|object} the enriched rows will be returned.
|
* @returns {object[]|object} the enriched rows will be returned.
|
||||||
*/
|
*/
|
||||||
exports.outputProcessing = async (table, rows, opts = { squash: true }) => {
|
export async function outputProcessing(
|
||||||
|
table: Table,
|
||||||
|
rows: Row[],
|
||||||
|
opts = { squash: true }
|
||||||
|
) {
|
||||||
let wasArray = true
|
let wasArray = true
|
||||||
if (!(rows instanceof Array)) {
|
if (!(rows instanceof Array)) {
|
||||||
rows = [rows]
|
rows = [rows]
|
||||||
|
@ -286,7 +200,7 @@ exports.outputProcessing = async (table, rows, opts = { squash: true }) => {
|
||||||
if (row[property] == null || !Array.isArray(row[property])) {
|
if (row[property] == null || !Array.isArray(row[property])) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
row[property].forEach(attachment => {
|
row[property].forEach((attachment: any) => {
|
||||||
attachment.url = attachmentsRelativeURL(attachment.key)
|
attachment.url = attachmentsRelativeURL(attachment.key)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -308,20 +222,28 @@ exports.outputProcessing = async (table, rows, opts = { squash: true }) => {
|
||||||
* deleted attachment columns.
|
* deleted attachment columns.
|
||||||
* @return {Promise<void>} When all attachments have been removed this will return.
|
* @return {Promise<void>} When all attachments have been removed this will return.
|
||||||
*/
|
*/
|
||||||
exports.cleanupAttachments = async (table, { row, rows, oldRow, oldTable }) => {
|
export async function cleanupAttachments(
|
||||||
const appId = getAppId()
|
table: Table,
|
||||||
if (!isProdAppID(appId)) {
|
{
|
||||||
const prodAppId = getProdAppID(appId)
|
row,
|
||||||
|
rows,
|
||||||
|
oldRow,
|
||||||
|
oldTable,
|
||||||
|
}: { row?: Row; rows?: Row[]; oldRow?: Row; oldTable: Table }
|
||||||
|
): Promise<any> {
|
||||||
|
const appId = context.getAppId()
|
||||||
|
if (!dbCore.isProdAppID(appId)) {
|
||||||
|
const prodAppId = dbCore.getProdAppID(appId!)
|
||||||
// if prod exists, then don't allow deleting
|
// if prod exists, then don't allow deleting
|
||||||
const exists = await dbExists(prodAppId)
|
const exists = await dbCore.dbExists(prodAppId)
|
||||||
if (exists) {
|
if (exists) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let files = []
|
let files: string[] = []
|
||||||
function addFiles(row, key) {
|
function addFiles(row: Row, key: string) {
|
||||||
if (row[key]) {
|
if (row[key]) {
|
||||||
files = files.concat(row[key].map(attachment => attachment.key))
|
files = files.concat(row[key].map((attachment: any) => attachment.key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const schemaToUse = oldTable ? oldTable.schema : table.schema
|
const schemaToUse = oldTable ? oldTable.schema : table.schema
|
||||||
|
@ -330,7 +252,7 @@ exports.cleanupAttachments = async (table, { row, rows, oldRow, oldTable }) => {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// old table had this column, new table doesn't - delete it
|
// old table had this column, new table doesn't - delete it
|
||||||
if (oldTable && !table.schema[key]) {
|
if (rows && oldTable && !table.schema[key]) {
|
||||||
rows.forEach(row => addFiles(row, key))
|
rows.forEach(row => addFiles(row, key))
|
||||||
} else if (oldRow && row) {
|
} else if (oldRow && row) {
|
||||||
// if updating, need to manage the differences
|
// if updating, need to manage the differences
|
||||||
|
@ -342,6 +264,6 @@ exports.cleanupAttachments = async (table, { row, rows, oldRow, oldTable }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
return deleteFiles(ObjectStoreBuckets.APPS, files)
|
return objectStore.deleteFiles(ObjectStoreBuckets.APPS, files)
|
||||||
}
|
}
|
||||||
}
|
}
|
95
packages/server/src/utilities/rowProcessor/map.js
Normal file
95
packages/server/src/utilities/rowProcessor/map.js
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
const { FieldTypes } = require("../../constants")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of how we convert various properties in rows to each other based on the row type.
|
||||||
|
*/
|
||||||
|
exports.TYPE_TRANSFORM_MAP = {
|
||||||
|
[FieldTypes.LINK]: {
|
||||||
|
"": [],
|
||||||
|
[null]: [],
|
||||||
|
[undefined]: undefined,
|
||||||
|
parse: link => {
|
||||||
|
if (Array.isArray(link) && typeof link[0] === "object") {
|
||||||
|
return link.map(el => (el && el._id ? el._id : el))
|
||||||
|
}
|
||||||
|
if (typeof link === "string") {
|
||||||
|
return [link]
|
||||||
|
}
|
||||||
|
return link
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[FieldTypes.OPTIONS]: {
|
||||||
|
"": null,
|
||||||
|
[null]: null,
|
||||||
|
[undefined]: undefined,
|
||||||
|
},
|
||||||
|
[FieldTypes.ARRAY]: {
|
||||||
|
"": [],
|
||||||
|
[null]: [],
|
||||||
|
[undefined]: undefined,
|
||||||
|
},
|
||||||
|
[FieldTypes.STRING]: {
|
||||||
|
"": "",
|
||||||
|
[null]: "",
|
||||||
|
[undefined]: undefined,
|
||||||
|
},
|
||||||
|
[FieldTypes.BARCODEQR]: {
|
||||||
|
"": "",
|
||||||
|
[null]: "",
|
||||||
|
[undefined]: undefined,
|
||||||
|
},
|
||||||
|
[FieldTypes.FORMULA]: {
|
||||||
|
"": "",
|
||||||
|
[null]: "",
|
||||||
|
[undefined]: undefined,
|
||||||
|
},
|
||||||
|
[FieldTypes.LONGFORM]: {
|
||||||
|
"": "",
|
||||||
|
[null]: "",
|
||||||
|
[undefined]: undefined,
|
||||||
|
},
|
||||||
|
[FieldTypes.NUMBER]: {
|
||||||
|
"": null,
|
||||||
|
[null]: null,
|
||||||
|
[undefined]: undefined,
|
||||||
|
parse: n => parseFloat(n),
|
||||||
|
},
|
||||||
|
[FieldTypes.DATETIME]: {
|
||||||
|
"": null,
|
||||||
|
[undefined]: undefined,
|
||||||
|
[null]: null,
|
||||||
|
parse: date => {
|
||||||
|
if (date instanceof Date) {
|
||||||
|
return date.toISOString()
|
||||||
|
}
|
||||||
|
return date
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[FieldTypes.ATTACHMENT]: {
|
||||||
|
"": [],
|
||||||
|
[null]: [],
|
||||||
|
[undefined]: undefined,
|
||||||
|
},
|
||||||
|
[FieldTypes.BOOLEAN]: {
|
||||||
|
"": null,
|
||||||
|
[null]: null,
|
||||||
|
[undefined]: undefined,
|
||||||
|
true: true,
|
||||||
|
false: false,
|
||||||
|
},
|
||||||
|
[FieldTypes.AUTO]: {
|
||||||
|
parse: () => undefined,
|
||||||
|
},
|
||||||
|
[FieldTypes.JSON]: {
|
||||||
|
parse: input => {
|
||||||
|
try {
|
||||||
|
if (input === "") {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return JSON.parse(input)
|
||||||
|
} catch (err) {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,16 +1,17 @@
|
||||||
const {
|
import {
|
||||||
FieldTypes,
|
FieldTypes,
|
||||||
FormulaTypes,
|
FormulaTypes,
|
||||||
AutoFieldDefaultNames,
|
AutoFieldDefaultNames,
|
||||||
AutoFieldSubTypes,
|
AutoFieldSubTypes,
|
||||||
} = require("../../constants")
|
} from "../../constants"
|
||||||
const { processStringSync } = require("@budibase/string-templates")
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
|
import { FieldSchema, Table, Row } from "@budibase/types"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the subtype has been lost for any reason this works out what
|
* If the subtype has been lost for any reason this works out what
|
||||||
* subtype the auto column should be.
|
* subtype the auto column should be.
|
||||||
*/
|
*/
|
||||||
exports.fixAutoColumnSubType = column => {
|
export function fixAutoColumnSubType(column: FieldSchema) {
|
||||||
if (!column.autocolumn || !column.name || column.subtype) {
|
if (!column.autocolumn || !column.name || column.subtype) {
|
||||||
return column
|
return column
|
||||||
}
|
}
|
||||||
|
@ -32,11 +33,11 @@ exports.fixAutoColumnSubType = column => {
|
||||||
/**
|
/**
|
||||||
* Looks through the rows provided and finds formulas - which it then processes.
|
* Looks through the rows provided and finds formulas - which it then processes.
|
||||||
*/
|
*/
|
||||||
exports.processFormulas = (
|
export function processFormulas(
|
||||||
table,
|
table: Table,
|
||||||
rows,
|
rows: Row[],
|
||||||
{ dynamic, contextRows } = { dynamic: true }
|
{ dynamic, contextRows }: any = { dynamic: true }
|
||||||
) => {
|
) {
|
||||||
const single = !Array.isArray(rows)
|
const single = !Array.isArray(rows)
|
||||||
if (single) {
|
if (single) {
|
||||||
rows = [rows]
|
rows = [rows]
|
||||||
|
@ -70,7 +71,7 @@ exports.processFormulas = (
|
||||||
* Processes any date columns and ensures that those without the ignoreTimezones
|
* Processes any date columns and ensures that those without the ignoreTimezones
|
||||||
* flag set are parsed as UTC rather than local time.
|
* flag set are parsed as UTC rather than local time.
|
||||||
*/
|
*/
|
||||||
exports.processDates = (table, rows) => {
|
export function processDates(table: Table, rows: Row[]) {
|
||||||
let datesWithTZ = []
|
let datesWithTZ = []
|
||||||
for (let [column, schema] of Object.entries(table.schema)) {
|
for (let [column, schema] of Object.entries(table.schema)) {
|
||||||
if (schema.type !== FieldTypes.DATETIME) {
|
if (schema.type !== FieldTypes.DATETIME) {
|
|
@ -19,6 +19,8 @@ export interface FieldSchema {
|
||||||
formulaType?: string
|
formulaType?: string
|
||||||
main?: boolean
|
main?: boolean
|
||||||
ignoreTimezones?: boolean
|
ignoreTimezones?: boolean
|
||||||
|
timeOnly?: boolean
|
||||||
|
lastID?: number
|
||||||
meta?: {
|
meta?: {
|
||||||
toTable: string
|
toTable: string
|
||||||
toKey: string
|
toKey: string
|
||||||
|
|
|
@ -17,6 +17,7 @@ export interface User extends Document {
|
||||||
userGroups?: string[]
|
userGroups?: string[]
|
||||||
forceResetPassword?: boolean
|
forceResetPassword?: boolean
|
||||||
dayPassRecordedAt?: string
|
dayPassRecordedAt?: string
|
||||||
|
userId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserRoles {
|
export interface UserRoles {
|
||||||
|
|
Loading…
Reference in a new issue