async templates working from S3
This commit is contained in:
parent
8d01cc8d8b
commit
b27f80071f
|
@ -2,19 +2,13 @@
|
||||||
import { Button, Heading } from "@budibase/bbui"
|
import { Button, Heading } from "@budibase/bbui"
|
||||||
import AppCard from "./AppCard.svelte"
|
import AppCard from "./AppCard.svelte"
|
||||||
import Spinner from "components/common/Spinner.svelte"
|
import Spinner from "components/common/Spinner.svelte"
|
||||||
|
import api from "builderStore/api"
|
||||||
|
|
||||||
export let onSelect
|
export let onSelect
|
||||||
|
|
||||||
let templates = []
|
async function fetchTemplates() {
|
||||||
|
const response = await api.get("/api/templates?type=app")
|
||||||
function fetchTemplates() {
|
return await response.json()
|
||||||
return Promise.resolve([
|
|
||||||
{
|
|
||||||
name: "Funky",
|
|
||||||
description: "Funky ass template",
|
|
||||||
minBuilderVersion: "",
|
|
||||||
},
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let templatesPromise = fetchTemplates()
|
let templatesPromise = fetchTemplates()
|
||||||
|
|
|
@ -1,36 +1,11 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
const { exportTemplateFromApp } = require("../src/utilities/templates")
|
||||||
|
|
||||||
// Script to export a budibase app into a package
|
// Script to export a chosen budibase app into a package
|
||||||
// Usage: foo
|
|
||||||
|
|
||||||
const fs = require("fs-extra")
|
const [name, instanceId, appId] = process.argv.slice(1)
|
||||||
const path = require("path")
|
|
||||||
const os = require("os")
|
|
||||||
const replicationStream = require("pouchdb-replication-stream")
|
|
||||||
|
|
||||||
const PouchDB = require("../src/db")
|
exportTemplateFromApp({
|
||||||
|
|
||||||
PouchDB.plugin(replicationStream.plugin)
|
|
||||||
PouchDB.adapter("writableStream", replicationStream.adapters.writableStream)
|
|
||||||
|
|
||||||
async function exportAppToTemplate({ instanceId, appId, templateName }) {
|
|
||||||
// Copy frontend files
|
|
||||||
console.log("Copying frontend definition...")
|
|
||||||
const appToExport = path.join(os.homedir(), ".budibase", appId, "pages")
|
|
||||||
const templateOutputPath = path.join(os.homedir(), ".budibase", templateName)
|
|
||||||
fs.copySync(appToExport, `${templateOutputPath}/pages`)
|
|
||||||
|
|
||||||
const writeStream = fs.createWriteStream(`${templateOutputPath}/dump.txt`)
|
|
||||||
|
|
||||||
// perform couch dump
|
|
||||||
const instanceDb = new PouchDB(instanceId)
|
|
||||||
|
|
||||||
console.log("Performing database dump..")
|
|
||||||
await instanceDb.dump(writeStream)
|
|
||||||
console.log("Export complete!")
|
|
||||||
}
|
|
||||||
|
|
||||||
exportAppToTemplate({
|
|
||||||
templateName: "Funky",
|
templateName: "Funky",
|
||||||
instanceId: "inst_b70abba_16feb394866140a1ac3f2e450e99f28a",
|
instanceId: "inst_b70abba_16feb394866140a1ac3f2e450e99f28a",
|
||||||
appId: "b70abba3874546bf99a339911b579937",
|
appId: "b70abba3874546bf99a339911b579937",
|
||||||
|
|
|
@ -82,11 +82,10 @@ exports.create = async function(ctx) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// TODO: pass template into here from create InstCtx
|
|
||||||
await instanceController.create(createInstCtx)
|
await instanceController.create(createInstCtx)
|
||||||
newApplication.instances.push(createInstCtx.body)
|
newApplication.instances.push(createInstCtx.body)
|
||||||
|
|
||||||
// TODO: if template is passed, create the app package from the template and seed the instance database
|
|
||||||
if (process.env.NODE_ENV !== "jest") {
|
if (process.env.NODE_ENV !== "jest") {
|
||||||
const newAppFolder = await createEmptyAppPackage(ctx, newApplication)
|
const newAppFolder = await createEmptyAppPackage(ctx, newApplication)
|
||||||
await downloadExtractComponentLibraries(newAppFolder)
|
await downloadExtractComponentLibraries(newAppFolder)
|
||||||
|
@ -160,10 +159,16 @@ const createEmptyAppPackage = async (ctx, app) => {
|
||||||
name: npmFriendlyAppName(app.name),
|
name: npmFriendlyAppName(app.name),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Copy the frontend page definition files from the template directory
|
// if this app is being created from a template,
|
||||||
// if this app is being created from a template.
|
// copy the frontend page definition files from
|
||||||
|
// the template directory.
|
||||||
if (app.template) {
|
if (app.template) {
|
||||||
const templatePageDefinitions = join(appsFolder, app.template.name, "pages")
|
const templatePageDefinitions = join(
|
||||||
|
appsFolder,
|
||||||
|
"templates",
|
||||||
|
app.template.key,
|
||||||
|
"pages"
|
||||||
|
)
|
||||||
await copy(templatePageDefinitions, join(appsFolder, app._id, "pages"))
|
await copy(templatePageDefinitions, join(appsFolder, app._id, "pages"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ const CouchDB = require("../../db")
|
||||||
const client = require("../../db/clientDb")
|
const client = require("../../db/clientDb")
|
||||||
const newid = require("../../db/newid")
|
const newid = require("../../db/newid")
|
||||||
const { budibaseAppsDir } = require("../../utilities/budibaseDir")
|
const { budibaseAppsDir } = require("../../utilities/budibaseDir")
|
||||||
|
const { downloadTemplate } = require("../../utilities/templates")
|
||||||
|
|
||||||
exports.create = async function(ctx) {
|
exports.create = async function(ctx) {
|
||||||
const instanceName = ctx.request.body.name
|
const instanceName = ctx.request.body.name
|
||||||
|
@ -54,13 +55,10 @@ exports.create = async function(ctx) {
|
||||||
budibaseApp.instances.push(instance)
|
budibaseApp.instances.push(instance)
|
||||||
await clientDb.put(budibaseApp)
|
await clientDb.put(budibaseApp)
|
||||||
|
|
||||||
// TODO: download the chosen template tar file from s3 and unpack it
|
|
||||||
// replicate the template data to the instance DB
|
// replicate the template data to the instance DB
|
||||||
// TODO: templates should be downloaded to .budibase/templates/something
|
|
||||||
if (template) {
|
if (template) {
|
||||||
const dbDumpReadStream = fs.createReadStream(
|
const templatePath = await downloadTemplate(...template.key.split("/"))
|
||||||
`${budibaseAppsDir()}/${template.name}/dump.txt`
|
const dbDumpReadStream = fs.createReadStream(`${templatePath}/db/dump.txt`)
|
||||||
)
|
|
||||||
const { ok } = await db.load(dbDumpReadStream)
|
const { ok } = await db.load(dbDumpReadStream)
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
ctx.throw(500, "Error loading database dump from template.")
|
ctx.throw(500, "Error loading database dump from template.")
|
||||||
|
|
|
@ -2,7 +2,7 @@ const send = require("koa-send")
|
||||||
const { resolve, join } = require("path")
|
const { resolve, join } = require("path")
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
const fetch = require("node-fetch")
|
const fetch = require("node-fetch")
|
||||||
const fs = require("fs")
|
const fs = require("fs-extra")
|
||||||
const uuid = require("uuid")
|
const uuid = require("uuid")
|
||||||
const AWS = require("aws-sdk")
|
const AWS = require("aws-sdk")
|
||||||
const { prepareUploadForS3 } = require("./deploy/aws")
|
const { prepareUploadForS3 } = require("./deploy/aws")
|
||||||
|
@ -138,8 +138,6 @@ exports.performLocalFileProcessing = async function(ctx) {
|
||||||
exports.serveApp = async function(ctx) {
|
exports.serveApp = async function(ctx) {
|
||||||
const mainOrAuth = ctx.isAuthenticated ? "main" : "unauthenticated"
|
const mainOrAuth = ctx.isAuthenticated ? "main" : "unauthenticated"
|
||||||
|
|
||||||
console.log(ctx.user)
|
|
||||||
|
|
||||||
// default to homedir
|
// default to homedir
|
||||||
const appPath = resolve(
|
const appPath = resolve(
|
||||||
budibaseAppsDir(),
|
budibaseAppsDir(),
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
const fetch = require("node-fetch")
|
||||||
|
const {
|
||||||
|
downloadTemplate,
|
||||||
|
exportTemplateFromApp,
|
||||||
|
} = require("../../utilities/templates")
|
||||||
|
|
||||||
|
const DEFAULT_TEMPLATES_BUCKET =
|
||||||
|
"prod-budi-templates.s3-eu-west-1.amazonaws.com"
|
||||||
|
|
||||||
|
exports.fetch = async function(ctx) {
|
||||||
|
const { type = "app" } = ctx.query
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`https://${DEFAULT_TEMPLATES_BUCKET}/manifest.json`
|
||||||
|
)
|
||||||
|
const json = await response.json()
|
||||||
|
ctx.body = Object.values(json.templates[type])
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.downloadTemplate = async function(ctx) {
|
||||||
|
const { type, name } = ctx.params
|
||||||
|
|
||||||
|
await downloadTemplate(type, name)
|
||||||
|
|
||||||
|
ctx.body = {
|
||||||
|
message: `template ${type}:${name} downloaded successfully.`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.exportTemplateFromApp = async function(ctx) {
|
||||||
|
const { appId, instanceId } = ctx.user.appId
|
||||||
|
const { templateName } = ctx.request.body
|
||||||
|
|
||||||
|
await exportTemplateFromApp({
|
||||||
|
appId,
|
||||||
|
instanceId,
|
||||||
|
templateName,
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.status = 200
|
||||||
|
ctx.body = {
|
||||||
|
message: `Created template: ${templateName}`,
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ const {
|
||||||
automationRoutes,
|
automationRoutes,
|
||||||
accesslevelRoutes,
|
accesslevelRoutes,
|
||||||
apiKeysRoutes,
|
apiKeysRoutes,
|
||||||
|
templatesRoutes,
|
||||||
} = require("./routes")
|
} = require("./routes")
|
||||||
|
|
||||||
const router = new Router()
|
const router = new Router()
|
||||||
|
@ -89,6 +90,9 @@ router.use(automationRoutes.allowedMethods())
|
||||||
|
|
||||||
router.use(deployRoutes.routes())
|
router.use(deployRoutes.routes())
|
||||||
router.use(deployRoutes.allowedMethods())
|
router.use(deployRoutes.allowedMethods())
|
||||||
|
|
||||||
|
router.use(templatesRoutes.routes())
|
||||||
|
router.use(templatesRoutes.allowedMethods())
|
||||||
// end auth routes
|
// end auth routes
|
||||||
|
|
||||||
router.use(pageRoutes.routes())
|
router.use(pageRoutes.routes())
|
||||||
|
|
|
@ -13,6 +13,7 @@ const automationRoutes = require("./automation")
|
||||||
const accesslevelRoutes = require("./accesslevel")
|
const accesslevelRoutes = require("./accesslevel")
|
||||||
const deployRoutes = require("./deploy")
|
const deployRoutes = require("./deploy")
|
||||||
const apiKeysRoutes = require("./apikeys")
|
const apiKeysRoutes = require("./apikeys")
|
||||||
|
const templatesRoutes = require("./templates")
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
deployRoutes,
|
deployRoutes,
|
||||||
|
@ -30,4 +31,5 @@ module.exports = {
|
||||||
automationRoutes,
|
automationRoutes,
|
||||||
accesslevelRoutes,
|
accesslevelRoutes,
|
||||||
apiKeysRoutes,
|
apiKeysRoutes,
|
||||||
|
templatesRoutes,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
const Router = require("@koa/router")
|
||||||
|
const controller = require("../controllers/templates")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
|
const router = Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.get("/api/templates", authorized(BUILDER), controller.fetch)
|
||||||
|
.get(
|
||||||
|
"/api/templates/:type/:name",
|
||||||
|
// authorized(BUILDER),
|
||||||
|
controller.downloadTemplate
|
||||||
|
)
|
||||||
|
.post("/api/templates", authorized(BUILDER), controller.exportTemplateFromApp)
|
||||||
|
|
||||||
|
module.exports = router
|
|
@ -0,0 +1,60 @@
|
||||||
|
const path = require("path")
|
||||||
|
const fs = require("fs-extra")
|
||||||
|
const os = require("os")
|
||||||
|
const fetch = require("node-fetch")
|
||||||
|
const stream = require("stream")
|
||||||
|
const tar = require("tar-fs")
|
||||||
|
const zlib = require("zlib")
|
||||||
|
const { promisify } = require("util")
|
||||||
|
const streamPipeline = promisify(stream.pipeline)
|
||||||
|
const { budibaseAppsDir } = require("./budibaseDir")
|
||||||
|
const CouchDB = require("../db")
|
||||||
|
|
||||||
|
const DEFAULT_TEMPLATES_BUCKET =
|
||||||
|
"prod-budi-templates.s3-eu-west-1.amazonaws.com"
|
||||||
|
|
||||||
|
exports.downloadTemplate = async function(type, name) {
|
||||||
|
const templateUrl = `https://${DEFAULT_TEMPLATES_BUCKET}/templates/${type}/${name}.tar.gz`
|
||||||
|
console.log(templateUrl, type, name)
|
||||||
|
const response = await fetch(templateUrl)
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Error downloading template ${type}:${name}: ${response.statusText}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stream the response, unzip and extract
|
||||||
|
await streamPipeline(
|
||||||
|
response.body,
|
||||||
|
zlib.Unzip(),
|
||||||
|
tar.extract(path.join(budibaseAppsDir(), "templates", type))
|
||||||
|
)
|
||||||
|
|
||||||
|
return path.join(budibaseAppsDir(), "templates", type, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.exportTemplateFromApp = async function({
|
||||||
|
appId,
|
||||||
|
templateName,
|
||||||
|
instanceId,
|
||||||
|
}) {
|
||||||
|
// Copy frontend files
|
||||||
|
console.log("Copying frontend definition...")
|
||||||
|
const appToExport = path.join(os.homedir(), ".budibase", appId, "pages")
|
||||||
|
const templatesDir = path.join(os.homedir(), ".budibase", "templates")
|
||||||
|
fs.ensureDirSync(templatesDir)
|
||||||
|
|
||||||
|
const templateOutputPath = path.join(templatesDir, templateName)
|
||||||
|
fs.copySync(appToExport, `${templateOutputPath}/pages`)
|
||||||
|
|
||||||
|
fs.ensureDirSync(path.join(templateOutputPath, "db"))
|
||||||
|
const writeStream = fs.createWriteStream(`${templateOutputPath}/db/dump.txt`)
|
||||||
|
|
||||||
|
// perform couch dump
|
||||||
|
const instanceDb = new CouchDB(instanceId)
|
||||||
|
|
||||||
|
console.log("Performing database dump..")
|
||||||
|
await instanceDb.dump(writeStream)
|
||||||
|
console.log("Export complete!")
|
||||||
|
}
|
Loading…
Reference in New Issue