Updating templates to be able to run locally with an environment variable LOCAL_TEMPLATES and making them work using the DB. Users are also no longer included in the db dump.

This commit is contained in:
mike12345567 2020-11-06 12:30:30 +00:00
parent d57368eb8b
commit e353c2a1c4
11 changed files with 88 additions and 246 deletions

View File

@ -1,23 +1,15 @@
import { writable, get } from "svelte/store" import { get, writable } from "svelte/store"
import { cloneDeep } from "lodash/fp" import {cloneDeep} from "lodash/fp"
import { import { createProps, getBuiltin, makePropsSafe } from "components/userInterface/pagesParsing/createProps"
createProps,
makePropsSafe,
getBuiltin,
} from "components/userInterface/pagesParsing/createProps"
import { getExactComponent } from "components/userInterface/pagesParsing/searchComponents" import { getExactComponent } from "components/userInterface/pagesParsing/searchComponents"
import { backendUiStore, allScreens } from "builderStore" import {allScreens, backendUiStore } from "builderStore"
import { generate_screen_css } from "../generate_css" import { generate_screen_css } from "../generate_css"
import { fetchComponentLibDefinitions } from "../loadComponentLibraries" import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
import api from "../api" import api from "../api"
import { DEFAULT_PAGES_OBJECT } from "../../constants" import { DEFAULT_PAGES_OBJECT } from "../../constants"
import getNewComponentName from "../getNewComponentName" import getNewComponentName from "../getNewComponentName"
import analytics from "analytics" import analytics from "analytics"
import { import { findChildComponentType, generateNewIdsForComponent, getComponentDefinition, getParent } from "../storeUtils"
getParent,
generateNewIdsForComponent,
getComponentDefinition, findChildComponentType,
} from "../storeUtils"
const INITIAL_FRONTEND_STATE = { const INITIAL_FRONTEND_STATE = {
apps: [], apps: [],
@ -332,8 +324,7 @@ export const getFrontendStore = () => {
...presetProps, ...presetProps,
_instanceId: instanceId, _instanceId: instanceId,
_instanceName: instanceName, _instanceName: instanceName,
}, }
state
) )
const currentComponent = const currentComponent =
@ -367,8 +358,7 @@ export const getFrontendStore = () => {
}, },
copy: (component, cut = false) => { copy: (component, cut = false) => {
store.update(state => { store.update(state => {
const copiedComponent = cloneDeep(component) state.componentToPaste = cloneDeep(component)
state.componentToPaste = copiedComponent
state.componentToPaste.isCut = cut state.componentToPaste.isCut = cut
if (cut) { if (cut) {
const parent = getParent( const parent = getParent(
@ -469,9 +459,7 @@ export const getFrontendStore = () => {
const IdList = allComponents.map(c => c._id) const IdList = allComponents.map(c => c._id)
// Construct ID Path: // Construct ID Path:
const path = IdList.join("/") return IdList.join("/")
return path
}, },
links: { links: {
save: async (url, title) => { save: async (url, title) => {

View File

@ -62,7 +62,7 @@
</div> </div>
</div> </div>
<!-- <TemplateList onSelect={selectTemplate} /> --> <TemplateList onSelect={selectTemplate} />
<AppList /> <AppList />
</div> </div>

View File

@ -1,63 +0,0 @@
import { buildCodeForScreens } from "../src/builderStore/buildCodeForScreens"
describe("buildCodeForScreen", () => {
it("should package _code into runnable function, for simple screen props", () => {
const screen = {
props: {
_id: "1234",
_code: "render('render argument');",
},
}
let renderArg
const render = arg => {
renderArg = arg
}
const uiFunctions = getFunctions(screen)
const targetfunction = uiFunctions[screen.props._id]
expect(targetfunction).toBeDefined()
targetfunction(render)
expect(renderArg).toBe("render argument")
})
it("should package _code into runnable function, for _children ", () => {
const screen = {
props: {
_id: "parent",
_code: "render('parent argument');",
_children: [
{
_id: "child1",
_code: "render('child 1 argument');",
},
{
_id: "child2",
_code: "render('child 2 argument');",
},
],
},
}
let renderArg
const render = arg => {
renderArg = arg
}
const uiFunctions = getFunctions(screen)
const targetfunction = uiFunctions["child2"]
expect(targetfunction).toBeDefined()
targetfunction(render)
expect(renderArg).toBe("child 2 argument")
})
})
const getFunctions = screen => {
const code = buildCodeForScreens([screen])
const func = new Function(`return ${code}`)()
return func
}

View File

@ -23,6 +23,12 @@ yargs
}, },
async args => { async args => {
console.log("Exporting app..") console.log("Exporting app..")
if (args.name == null || args.appId == null) {
console.error(
"Unable to export without a name and app ID being specified, check help for more info."
)
return
}
const exportPath = await exportTemplateFromApp({ const exportPath = await exportTemplateFromApp({
templateName: args.name, templateName: args.name,
appId: args.appId, appId: args.appId,

View File

@ -1,7 +1,7 @@
const CouchDB = require("../../db") const CouchDB = require("../../db")
const { compileStaticAssetsForPage } = require("../../utilities/builder") const { compileStaticAssetsForPage } = require("../../utilities/builder")
const env = require("../../environment") const env = require("../../environment")
const { copy, existsSync } = require("fs-extra") const { existsSync } = require("fs-extra")
const { budibaseAppsDir } = require("../../utilities/budibaseDir") const { budibaseAppsDir } = require("../../utilities/budibaseDir")
const setBuilderToken = require("../../utilities/builder/setBuilderToken") const setBuilderToken = require("../../utilities/builder/setBuilderToken")
const fs = require("fs-extra") const fs = require("fs-extra")
@ -160,21 +160,6 @@ const createEmptyAppPackage = async (ctx, app) => {
} }
fs.mkdirpSync(newAppFolder) fs.mkdirpSync(newAppFolder)
// if this app is being created from a template,
// copy the frontend page definition files from
// the template directory.
if (app.template) {
const templatePageDefinitions = join(
appsFolder,
"templates",
app.template.key,
"pages"
)
// TODO: write the template page JSON to couch
// iterate over the pages and write them to the db
await copy(templatePageDefinitions, join(appsFolder, app._id, "pages"))
}
const mainPage = cloneDeep(MAIN) const mainPage = cloneDeep(MAIN)
mainPage._id = generatePageID() mainPage._id = generatePageID()
mainPage.title = app.name mainPage.title = app.name

View File

@ -8,11 +8,7 @@ exports.save = async function(ctx) {
const appPackage = ctx.request.body const appPackage = ctx.request.body
const page = await db.get(ctx.params.pageId) const page = await db.get(ctx.params.pageId)
await compileStaticAssetsForPage( await compileStaticAssetsForPage(ctx.user.appId, page.name, ctx.request.body)
ctx.user.appId,
page.name,
ctx.request.body
)
// remove special doc props which couch will complain about // remove special doc props which couch will complain about
delete appPackage.page._css delete appPackage.page._css

View File

@ -2,25 +2,34 @@ const fetch = require("node-fetch")
const { const {
downloadTemplate, downloadTemplate,
exportTemplateFromApp, exportTemplateFromApp,
getLocalTemplates,
} = require("../../utilities/templates") } = require("../../utilities/templates")
const env = require("../../environment")
// development flag, can be used to test against templates exported locally
const DEFAULT_TEMPLATES_BUCKET = const DEFAULT_TEMPLATES_BUCKET =
"prod-budi-templates.s3-eu-west-1.amazonaws.com" "prod-budi-templates.s3-eu-west-1.amazonaws.com"
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {
const { type = "app" } = ctx.query const { type = "app" } = ctx.query
const response = await fetch( if (env.LOCAL_TEMPLATES) {
`https://${DEFAULT_TEMPLATES_BUCKET}/manifest.json` ctx.body = Object.values(getLocalTemplates()[type])
) } else {
const json = await response.json() const response = await fetch(
ctx.body = Object.values(json.templates[type]) `https://${DEFAULT_TEMPLATES_BUCKET}/manifest.json`
)
const json = await response.json()
ctx.body = Object.values(json.templates[type])
}
} }
exports.downloadTemplate = async function(ctx) { exports.downloadTemplate = async function(ctx) {
const { type, name } = ctx.params const { type, name } = ctx.params
await downloadTemplate(type, name) if (!env.LOCAL_TEMPLATES) {
await downloadTemplate(type, name)
}
ctx.body = { ctx.body = {
message: `template ${type}:${name} downloaded successfully.`, message: `template ${type}:${name} downloaded successfully.`,

View File

@ -37,15 +37,22 @@ exports.create = async function(ctx) {
accessLevelId, accessLevelId,
} }
const response = await db.post(user) try {
const response = await db.post(user)
ctx.status = 200 ctx.status = 200
ctx.message = "User created successfully." ctx.message = "User created successfully."
ctx.userId = response._id ctx.userId = response._id
ctx.body = { ctx.body = {
_rev: response.rev, _rev: response.rev,
username, username,
name, name,
}
} catch (err) {
if (err.status === 409) {
ctx.throw(400, "User exists already")
} else {
ctx.throw(err.status, err)
}
} }
} }

View File

@ -1,111 +0,0 @@
const Router = require("@koa/router")
const StatusCodes = require("../../utilities/statusCodes")
const joiValidator = require("../../middleware/joi-validator")
const Joi = require("joi")
const {
listScreens,
saveScreen,
compileStaticAssetsForPage,
deleteScreen,
} = require("../../utilities/builder")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels")
const router = Router()
function generateSaveValidation() {
// prettier-ignore
return joiValidator.body(Joi.object({
_css: Joi.string().allow(""),
name: Joi.string().required(),
route: Joi.string().required(),
props: Joi.object({
_id: Joi.string().required(),
_component: Joi.string().required(),
_children: Joi.array().required(),
_instanceName: Joi.string().required(),
_styles: Joi.object().required(),
type: Joi.string().optional(),
table: Joi.string().optional(),
}).required().unknown(true),
}).unknown(true))
}
function generatePatchValidation() {
return joiValidator.body(
Joi.object({
oldname: Joi.string().required(),
newname: Joi.string().required(),
}).unknown(true)
)
}
router.post(
"/_builder/api/:appId/pages/:pageName",
authorized(BUILDER),
async ctx => {
await compileStaticAssetsForPage(
ctx.params.appId,
ctx.params.pageName,
ctx.request.body
)
ctx.response.status = StatusCodes.OK
}
)
router.get(
"/_builder/api/:appId/pages/:pagename/screens",
authorized(BUILDER),
async ctx => {
ctx.body = await listScreens(ctx.params.appId, ctx.params.pagename)
ctx.response.status = StatusCodes.OK
}
)
router.post(
"/_builder/api/:appId/pages/:pagename/screen",
authorized(BUILDER),
generateSaveValidation(),
async ctx => {
ctx.body = await saveScreen(
ctx.config,
ctx.params.appId,
ctx.params.pagename,
ctx.request.body
)
ctx.response.status = StatusCodes.OK
}
)
router.patch(
"/_builder/api/:appname/pages/:pagename/screen",
authorized(BUILDER),
generatePatchValidation(),
async ctx => {
await renameScreen(
ctx.config,
ctx.params.appname,
ctx.params.pagename,
ctx.request.body.oldname,
ctx.request.body.newname
)
ctx.response.status = StatusCodes.OK
}
)
router.delete(
"/_builder/api/pages/:pagename/screens/:id",
authorized(BUILDER),
async ctx => {
await deleteScreen(
ctx.config,
ctx.user.appId,
ctx.params.pagename,
ctx.params.id
)
ctx.response.status = StatusCodes.OK
}
)
module.exports = router

View File

@ -34,6 +34,7 @@ module.exports = {
USERID_API_KEY: process.env.USERID_API_KEY, USERID_API_KEY: process.env.USERID_API_KEY,
ENABLE_ANALYTICS: process.env.ENABLE_ANALYTICS, ENABLE_ANALYTICS: process.env.ENABLE_ANALYTICS,
DEPLOYMENT_DB_URL: process.env.DEPLOYMENT_DB_URL, DEPLOYMENT_DB_URL: process.env.DEPLOYMENT_DB_URL,
LOCAL_TEMPLATES: process.env.LOCAL_TEMPLATES,
_set(key, value) { _set(key, value) {
process.env[key] = value process.env[key] = value
module.exports[key] = value module.exports[key] = value

View File

@ -8,12 +8,35 @@ const zlib = require("zlib")
const { promisify } = require("util") const { promisify } = require("util")
const streamPipeline = promisify(stream.pipeline) const streamPipeline = promisify(stream.pipeline)
const { budibaseAppsDir } = require("./budibaseDir") const { budibaseAppsDir } = require("./budibaseDir")
const env = require("../environment")
const CouchDB = require("../db") const CouchDB = require("../db")
const { DocumentTypes } = require("../db/utils")
const DEFAULT_TEMPLATES_BUCKET = const DEFAULT_TEMPLATES_BUCKET =
"prod-budi-templates.s3-eu-west-1.amazonaws.com" "prod-budi-templates.s3-eu-west-1.amazonaws.com"
exports.getLocalTemplates = function() {
const templatesDir = join(os.homedir(), ".budibase", "templates", "app")
const templateObj = { app: {} }
fs.ensureDirSync(templatesDir)
const templateNames = fs.readdirSync(templatesDir)
for (let name of templateNames) {
templateObj.app[name] = {
name,
category: "local",
description: "local template",
type: "app",
key: `app/${name}`,
}
}
return templateObj
}
exports.downloadTemplate = async function(type, name) { exports.downloadTemplate = async function(type, name) {
const dirName = join(budibaseAppsDir(), "templates", type, name)
if (env.LOCAL_TEMPLATES) {
return dirName
}
const templateUrl = `https://${DEFAULT_TEMPLATES_BUCKET}/templates/${type}/${name}.tar.gz` const templateUrl = `https://${DEFAULT_TEMPLATES_BUCKET}/templates/${type}/${name}.tar.gz`
const response = await fetch(templateUrl) const response = await fetch(templateUrl)
@ -30,26 +53,27 @@ exports.downloadTemplate = async function(type, name) {
tar.extract(join(budibaseAppsDir(), "templates", type)) tar.extract(join(budibaseAppsDir(), "templates", type))
) )
return join(budibaseAppsDir(), "templates", type, name) return dirName
} }
exports.exportTemplateFromApp = async function({ templateName, appId }) { exports.exportTemplateFromApp = async function({ templateName, appId }) {
// Copy frontend files // Copy frontend files
const appToExport = join(os.homedir(), ".budibase", appId, "pages") const templatesDir = join(
const templatesDir = join(os.homedir(), ".budibase", "templates") os.homedir(),
fs.ensureDirSync(templatesDir) ".budibase",
"templates",
const templateOutputPath = join(templatesDir, templateName) "app",
fs.copySync(appToExport, join(templateOutputPath, "pages")) templateName,
"db"
fs.ensureDirSync(join(templateOutputPath, "db"))
const writeStream = fs.createWriteStream(
join(templateOutputPath, "db", "dump.txt")
) )
fs.ensureDirSync(templatesDir)
const writeStream = fs.createWriteStream(join(templatesDir, "dump.txt"))
// perform couch dump // perform couch dump
const instanceDb = new CouchDB(appId) const instanceDb = new CouchDB(appId)
await instanceDb.dump(writeStream, {
await instanceDb.dump(writeStream) filter: doc => {
return templateOutputPath return !doc._id.startsWith(DocumentTypes.USER)
},
})
return templatesDir
} }