2020-11-09 16:24:29 +01:00
|
|
|
require("svelte/register")
|
|
|
|
|
2020-05-07 11:53:34 +02:00
|
|
|
const send = require("koa-send")
|
2020-11-12 11:25:25 +01:00
|
|
|
const { resolve, join } = require("../../../utilities/centralPath")
|
2020-09-16 13:18:47 +02:00
|
|
|
const uuid = require("uuid")
|
2022-12-15 12:35:22 +01:00
|
|
|
import { ObjectStoreBuckets } from "../../../constants"
|
2021-01-20 14:32:15 +01:00
|
|
|
const { processString } = require("@budibase/string-templates")
|
2021-04-01 17:19:31 +02:00
|
|
|
const {
|
|
|
|
loadHandlebarsFile,
|
|
|
|
NODE_MODULES_PATH,
|
2021-04-01 17:36:27 +02:00
|
|
|
TOP_LEVEL_PATH,
|
2021-04-01 17:19:31 +02:00
|
|
|
} = require("../../../utilities/fileSystem")
|
2020-11-12 11:25:25 +01:00
|
|
|
const env = require("../../../environment")
|
2022-08-11 14:50:05 +02:00
|
|
|
const { DocumentType } = require("../../../db/utils")
|
2022-11-21 19:33:34 +01:00
|
|
|
const { context, objectStore, utils } = require("@budibase/backend-core")
|
2022-01-13 18:18:24 +01:00
|
|
|
const AWS = require("aws-sdk")
|
2022-06-07 00:30:36 +02:00
|
|
|
const fs = require("fs")
|
|
|
|
|
2022-08-09 14:31:12 +02:00
|
|
|
async function prepareUpload({ s3Key, bucket, metadata, file }: any) {
|
2022-11-25 16:01:46 +01:00
|
|
|
const response = await objectStore.upload({
|
2021-05-11 18:53:54 +02:00
|
|
|
bucket,
|
|
|
|
metadata,
|
|
|
|
filename: s3Key,
|
|
|
|
path: file.path,
|
|
|
|
type: file.type,
|
|
|
|
})
|
|
|
|
|
|
|
|
// don't store a URL, work this out on the way out as the URL could change
|
|
|
|
return {
|
|
|
|
size: file.size,
|
|
|
|
name: file.name,
|
2022-12-15 12:35:22 +01:00
|
|
|
url: objectStore.getAppFileUrl(s3Key),
|
2021-05-11 18:53:54 +02:00
|
|
|
extension: [...file.name.split(".")].pop(),
|
|
|
|
key: response.Key,
|
|
|
|
}
|
|
|
|
}
|
2020-09-17 17:36:39 +02:00
|
|
|
|
2022-08-09 14:31:12 +02:00
|
|
|
export const toggleBetaUiFeature = async function (ctx: any) {
|
2022-06-03 13:50:38 +02:00
|
|
|
const cookieName = `beta:${ctx.params.feature}`
|
2022-06-01 14:03:59 +02:00
|
|
|
|
2022-06-03 13:50:38 +02:00
|
|
|
if (ctx.cookies.get(cookieName)) {
|
2022-11-21 19:33:34 +01:00
|
|
|
utils.clearCookie(ctx, cookieName)
|
2022-06-03 13:50:38 +02:00
|
|
|
ctx.body = {
|
|
|
|
message: `${ctx.params.feature} disabled`,
|
|
|
|
}
|
|
|
|
return
|
2022-06-01 14:03:59 +02:00
|
|
|
}
|
|
|
|
|
2022-06-07 00:30:36 +02:00
|
|
|
let builderPath = resolve(TOP_LEVEL_PATH, "new_design_ui")
|
|
|
|
|
|
|
|
// // download it from S3
|
|
|
|
if (!fs.existsSync(builderPath)) {
|
|
|
|
fs.mkdirSync(builderPath)
|
|
|
|
}
|
2022-11-21 19:33:34 +01:00
|
|
|
await objectStore.downloadTarballDirect(
|
2022-06-07 00:30:36 +02:00
|
|
|
"https://cdn.budi.live/beta:design_ui/new_ui.tar.gz",
|
|
|
|
builderPath
|
|
|
|
)
|
2022-11-21 19:33:34 +01:00
|
|
|
utils.setCookie(ctx, {}, cookieName)
|
2022-06-03 13:50:38 +02:00
|
|
|
|
|
|
|
ctx.body = {
|
|
|
|
message: `${ctx.params.feature} enabled`,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-09 14:31:12 +02:00
|
|
|
export const serveBuilder = async function (ctx: any) {
|
2022-07-11 12:16:31 +02:00
|
|
|
const builderPath = resolve(TOP_LEVEL_PATH, "builder")
|
|
|
|
await send(ctx, ctx.file, { root: builderPath })
|
2020-04-14 16:14:57 +02:00
|
|
|
}
|
|
|
|
|
2022-08-09 14:31:12 +02:00
|
|
|
export const uploadFile = async function (ctx: any) {
|
2021-03-22 18:19:45 +01:00
|
|
|
let files =
|
2020-09-23 17:15:09 +02:00
|
|
|
ctx.request.files.file.length > 1
|
|
|
|
? Array.from(ctx.request.files.file)
|
|
|
|
: [ctx.request.files.file]
|
|
|
|
|
2022-08-09 14:31:12 +02:00
|
|
|
const uploads = files.map(async (file: any) => {
|
2020-09-23 18:02:06 +02:00
|
|
|
const fileExtension = [...file.name.split(".")].pop()
|
2021-03-22 18:19:45 +01:00
|
|
|
// filenames converted to UUIDs so they are unique
|
2020-09-23 17:15:09 +02:00
|
|
|
const processedFileName = `${uuid.v4()}.${fileExtension}`
|
2020-09-15 17:22:13 +02:00
|
|
|
|
2021-03-19 20:07:47 +01:00
|
|
|
return prepareUpload({
|
|
|
|
file,
|
2022-12-15 12:35:22 +01:00
|
|
|
s3Key: `${context.getProdAppId()}/attachments/${processedFileName}`,
|
2021-05-07 14:55:30 +02:00
|
|
|
bucket: ObjectStoreBuckets.APPS,
|
2020-09-23 18:29:32 +02:00
|
|
|
})
|
2021-03-19 20:07:47 +01:00
|
|
|
})
|
2020-09-23 18:29:32 +02:00
|
|
|
|
2021-03-19 20:07:47 +01:00
|
|
|
ctx.body = await Promise.all(uploads)
|
2020-09-23 18:02:06 +02:00
|
|
|
}
|
|
|
|
|
2022-08-12 12:29:57 +02:00
|
|
|
export const deleteObjects = async function (ctx: any) {
|
2022-11-25 16:01:46 +01:00
|
|
|
ctx.body = await objectStore.deleteFiles(
|
|
|
|
ObjectStoreBuckets.APPS,
|
|
|
|
ctx.request.body.keys
|
|
|
|
)
|
2022-08-12 12:29:57 +02:00
|
|
|
}
|
|
|
|
|
2022-08-09 14:31:12 +02:00
|
|
|
export const serveApp = async function (ctx: any) {
|
2022-11-21 19:33:34 +01:00
|
|
|
const db = context.getAppDB({ skip_setup: true })
|
2022-08-11 14:50:05 +02:00
|
|
|
const appInfo = await db.get(DocumentType.APP_METADATA)
|
2022-11-21 19:33:34 +01:00
|
|
|
let appId = context.getAppId()
|
2020-11-09 16:24:29 +01:00
|
|
|
|
2022-04-08 15:07:11 +02:00
|
|
|
if (!env.isJest()) {
|
|
|
|
const App = require("./templates/BudibaseApp.svelte").default
|
2022-12-15 12:35:22 +01:00
|
|
|
const plugins = objectStore.enrichPluginURLs(appInfo.usedPlugins)
|
2022-04-08 15:07:11 +02:00
|
|
|
const { head, html, css } = App.render({
|
2022-10-24 18:53:18 +02:00
|
|
|
metaImage:
|
|
|
|
"https://res.cloudinary.com/daog6scxm/image/upload/v1666109324/meta-images/budibase-meta-image_uukc1m.png",
|
2022-04-08 15:07:11 +02:00
|
|
|
title: appInfo.name,
|
|
|
|
production: env.isProd(),
|
|
|
|
appId,
|
2022-12-15 12:35:22 +01:00
|
|
|
clientLibPath: objectStore.clientLibraryUrl(appId, appInfo.version),
|
2022-10-04 17:28:01 +02:00
|
|
|
usedPlugins: plugins,
|
2022-04-08 15:07:11 +02:00
|
|
|
})
|
2020-11-09 16:24:29 +01:00
|
|
|
|
2022-04-08 15:07:11 +02:00
|
|
|
const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
|
|
|
|
ctx.body = await processString(appHbs, {
|
|
|
|
head,
|
|
|
|
body: html,
|
|
|
|
style: css.code,
|
|
|
|
appId,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
// just return the app info for jest to assert on
|
|
|
|
ctx.body = appInfo
|
|
|
|
}
|
2020-11-09 16:24:29 +01:00
|
|
|
}
|
|
|
|
|
2022-08-20 14:47:57 +02:00
|
|
|
export const serveBuilderPreview = async function (ctx: any) {
|
2022-11-21 19:33:34 +01:00
|
|
|
const db = context.getAppDB({ skip_setup: true })
|
2022-08-20 14:47:57 +02:00
|
|
|
const appInfo = await db.get(DocumentType.APP_METADATA)
|
|
|
|
|
|
|
|
if (!env.isJest()) {
|
2022-11-21 19:33:34 +01:00
|
|
|
let appId = context.getAppId()
|
2022-08-20 14:47:57 +02:00
|
|
|
const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`)
|
|
|
|
ctx.body = await processString(previewHbs, {
|
2022-12-15 12:35:22 +01:00
|
|
|
clientLibPath: objectStore.clientLibraryUrl(appId, appInfo.version),
|
2022-08-20 14:47:57 +02:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
// just return the app info for jest to assert on
|
|
|
|
ctx.body = { ...appInfo, builderPreview: true }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-09 14:31:12 +02:00
|
|
|
export const serveClientLibrary = async function (ctx: any) {
|
2021-04-01 13:48:38 +02:00
|
|
|
return send(ctx, "budibase-client.js", {
|
2021-04-01 17:36:27 +02:00
|
|
|
root: join(NODE_MODULES_PATH, "@budibase", "client", "dist"),
|
2021-04-01 13:48:38 +02:00
|
|
|
})
|
|
|
|
}
|
2022-01-13 18:18:24 +01:00
|
|
|
|
2022-08-09 14:31:12 +02:00
|
|
|
export const getSignedUploadURL = async function (ctx: any) {
|
2022-11-21 19:33:34 +01:00
|
|
|
const database = context.getAppDB()
|
2022-01-14 09:25:41 +01:00
|
|
|
|
|
|
|
// Ensure datasource is valid
|
|
|
|
let datasource
|
|
|
|
try {
|
|
|
|
const { datasourceId } = ctx.params
|
|
|
|
datasource = await database.get(datasourceId)
|
|
|
|
if (!datasource) {
|
|
|
|
ctx.throw(400, "The specified datasource could not be found")
|
|
|
|
}
|
|
|
|
} catch (error) {
|
2022-01-13 18:18:24 +01:00
|
|
|
ctx.throw(400, "The specified datasource could not be found")
|
|
|
|
}
|
|
|
|
|
2022-01-24 16:18:42 +01:00
|
|
|
// Ensure we aren't using a custom endpoint
|
|
|
|
if (datasource?.config?.endpoint) {
|
|
|
|
ctx.throw(400, "S3 datasources with custom endpoints are not supported")
|
|
|
|
}
|
|
|
|
|
2022-01-13 18:18:24 +01:00
|
|
|
// Determine type of datasource and generate signed URL
|
|
|
|
let signedUrl
|
2022-01-14 11:40:38 +01:00
|
|
|
let publicUrl
|
2022-06-15 17:29:11 +02:00
|
|
|
const awsRegion = datasource?.config?.region || "eu-west-1"
|
2022-01-13 18:18:24 +01:00
|
|
|
if (datasource.source === "S3") {
|
|
|
|
const { bucket, key } = ctx.request.body || {}
|
|
|
|
if (!bucket || !key) {
|
2022-01-14 09:25:41 +01:00
|
|
|
ctx.throw(400, "bucket and key values are required")
|
2022-01-13 18:18:24 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const s3 = new AWS.S3({
|
2022-06-15 17:27:07 +02:00
|
|
|
region: awsRegion,
|
2022-01-13 18:18:24 +01:00
|
|
|
accessKeyId: datasource?.config?.accessKeyId,
|
|
|
|
secretAccessKey: datasource?.config?.secretAccessKey,
|
|
|
|
apiVersion: "2006-03-01",
|
|
|
|
signatureVersion: "v4",
|
|
|
|
})
|
|
|
|
const params = { Bucket: bucket, Key: key }
|
|
|
|
signedUrl = s3.getSignedUrl("putObject", params)
|
2022-06-15 17:27:07 +02:00
|
|
|
publicUrl = `https://${bucket}.s3.${awsRegion}.amazonaws.com/${key}`
|
2022-01-13 18:18:24 +01:00
|
|
|
} catch (error) {
|
|
|
|
ctx.throw(400, error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 11:40:38 +01:00
|
|
|
ctx.body = { signedUrl, publicUrl }
|
2022-01-13 18:18:24 +01:00
|
|
|
}
|