diff --git a/hosting/Dockerfile.all b/hosting/Dockerfile.all new file mode 100644 index 0000000000..bbe21df982 --- /dev/null +++ b/hosting/Dockerfile.all @@ -0,0 +1,10 @@ +FROM ubuntu:latest + + +FROM envoyproxy/envoy:v1.16-latest +COPY envoy.yaml /etc/envoy/envoy.yaml + +FROM apache/couchdb:3.0 as db + +FROM minio/minio + diff --git a/packages/builder/src/components/start/AppCard.svelte b/packages/builder/src/components/start/AppCard.svelte index 6a6941b67d..e032752bdd 100644 --- a/packages/builder/src/components/start/AppCard.svelte +++ b/packages/builder/src/components/start/AppCard.svelte @@ -2,7 +2,27 @@ import { TextButton } from "@budibase/bbui" import { Heading } from "@budibase/bbui" import { Spacer } from "@budibase/bbui" + import api from "builderStore/api" + import { notifier } from "builderStore/store/notifications" + import Spinner from "components/common/Spinner.svelte" + export let name, _id + + let appExportLoading = false + + async function exportApp() { + appExportLoading = true + try { + const response = await api.post(`/api/backups/export`, { appId: _id }) + const { url } = await response.json() + notifier.success("App Export Complete.") + window.location = url + } catch (err) { + notifier.danger("App Export Failed.") + } finally { + appExportLoading = false + } + }
@@ -10,9 +30,12 @@
diff --git a/packages/builder/src/components/start/Steps/Info.svelte b/packages/builder/src/components/start/Steps/Info.svelte index 6cb4d94168..0052ff8b20 100644 --- a/packages/builder/src/components/start/Steps/Info.svelte +++ b/packages/builder/src/components/start/Steps/Info.svelte @@ -1,14 +1,25 @@

Create your Web App

- {#if template} + {#if template.fromFile} +
+ +
+ {:else if template}
{template.name} diff --git a/packages/builder/src/pages/index.svelte b/packages/builder/src/pages/index.svelte index fcf0f5af52..5706665190 100644 --- a/packages/builder/src/pages/index.svelte +++ b/packages/builder/src/pages/index.svelte @@ -46,12 +46,18 @@ modal.show() } + function initiateAppImport() { + template = { fromFile: true } + modal.show() + } + checkIfKeysAndApps()
Welcome to the Budibase Beta +
diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 91c79f1961..e899a2dca9 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -105,10 +105,16 @@ async function createInstance(template) { // replicate the template data to the instance DB if (template) { - const templatePath = await downloadTemplate(...template.key.split("/")) - const dbDumpReadStream = fs.createReadStream( - join(templatePath, "db", "dump.txt") - ) + let dbDumpReadStream + + if (template.fileImportPath) { + dbDumpReadStream = fs.createReadStream(template.fileImportPath) + } else { + const templatePath = await downloadTemplate(...template.key.split("/")) + dbDumpReadStream = fs.createReadStream( + join(templatePath, "db", "dump.txt") + ) + } const { ok } = await db.load(dbDumpReadStream) if (!ok) { throw "Error loading database dump from template." diff --git a/packages/server/src/api/controllers/backup.js b/packages/server/src/api/controllers/backup.js new file mode 100644 index 0000000000..9ba122ebb8 --- /dev/null +++ b/packages/server/src/api/controllers/backup.js @@ -0,0 +1,36 @@ +const { performDump } = require("../../utilities/templates") +const path = require("path") +const os = require("os") +const fs = require("fs-extra") + +exports.exportAppDump = async function(ctx) { + const { appId } = ctx.request.body + + const backupsDir = path.join(os.homedir(), ".budibase", "backups") + fs.ensureDirSync(backupsDir) + + const backupIdentifier = `${appId} Backup: ${new Date()}.txt` + + await performDump({ + dir: backupsDir, + appId, + name: backupIdentifier, + }) + + ctx.status = 200 + ctx.body = { + url: `/api/backups/download/${backupIdentifier}`, + } +} + +exports.downloadAppDump = async function(ctx) { + const fileName = ctx.params.fileName + + const backupsDir = path.join(os.homedir(), ".budibase", "backups") + fs.ensureDirSync(backupsDir) + + const backupFile = path.join(backupsDir, fileName) + + ctx.attachment(fileName) + ctx.body = fs.createReadStream(backupFile) +} diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js index c76eb82643..ea0ed4b875 100644 --- a/packages/server/src/api/index.js +++ b/packages/server/src/api/index.js @@ -5,6 +5,7 @@ const zlib = require("zlib") const { budibaseAppsDir } = require("../utilities/budibaseDir") const { isDev } = require("../utilities") const { mainRoutes, authRoutes, staticRoutes } = require("./routes") +const pkg = require("../../package.json") const router = new Router() const env = require("../environment") @@ -32,6 +33,7 @@ router await next() }) .use("/health", ctx => (ctx.status = 200)) + .use("/version", ctx => (ctx.body = pkg.version)) .use(authenticated) // error handling middleware diff --git a/packages/server/src/api/routes/backup.js b/packages/server/src/api/routes/backup.js new file mode 100644 index 0000000000..d5136fb9e6 --- /dev/null +++ b/packages/server/src/api/routes/backup.js @@ -0,0 +1,16 @@ +const Router = require("@koa/router") +const controller = require("../controllers/backup") +const authorized = require("../../middleware/authorized") +const { BUILDER } = require("../../utilities/security/permissions") + +const router = Router() + +router + .post("/api/backups/export", authorized(BUILDER), controller.exportAppDump) + .get( + "/api/backups/download/:fileName", + authorized(BUILDER), + controller.downloadAppDump + ) + +module.exports = router diff --git a/packages/server/src/api/routes/index.js b/packages/server/src/api/routes/index.js index 68b0c3fc2b..002d2ec805 100644 --- a/packages/server/src/api/routes/index.js +++ b/packages/server/src/api/routes/index.js @@ -21,6 +21,7 @@ const permissionRoutes = require("./permission") const datasourceRoutes = require("./datasource") const queryRoutes = require("./query") const hostingRoutes = require("./hosting") +const backupRoutes = require("./backup") exports.mainRoutes = [ deployRoutes, @@ -42,6 +43,7 @@ exports.mainRoutes = [ datasourceRoutes, queryRoutes, hostingRoutes, + backupRoutes, // these need to be handled last as they still use /api/:tableId // this could be breaking as koa may recognise other routes as this tableRoutes, diff --git a/packages/server/src/utilities/templates.js b/packages/server/src/utilities/templates.js index d5b5d5e257..fc38c51626 100644 --- a/packages/server/src/utilities/templates.js +++ b/packages/server/src/utilities/templates.js @@ -56,6 +56,19 @@ exports.downloadTemplate = async function(type, name) { return dirName } +async function performDump({ dir, appId, name = "dump.txt" }) { + const writeStream = fs.createWriteStream(join(dir, name)) + // perform couch dump + const instanceDb = new CouchDB(appId) + await instanceDb.dump(writeStream, { + filter: doc => { + return !doc._id.startsWith(DocumentTypes.USER) + }, + }) +} + +exports.performDump = performDump + exports.exportTemplateFromApp = async function({ templateName, appId }) { // Copy frontend files const templatesDir = join( @@ -67,13 +80,6 @@ exports.exportTemplateFromApp = async function({ templateName, appId }) { "db" ) fs.ensureDirSync(templatesDir) - const writeStream = fs.createWriteStream(join(templatesDir, "dump.txt")) - // perform couch dump - const instanceDb = new CouchDB(appId) - await instanceDb.dump(writeStream, { - filter: doc => { - return !doc._id.startsWith(DocumentTypes.USER) - }, - }) + await performDump({ dir: templatesDir, appId }) return templatesDir }