From bfd444dbaa879723b4029a5f3fcd40227197cbda Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 Jun 2023 10:47:46 +0100 Subject: [PATCH 1/6] Change the download to be a post --- .../components/start/ExportAppModal.svelte | 52 +++++++++++++++++-- packages/server/src/api/routes/backup.ts | 2 +- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/components/start/ExportAppModal.svelte b/packages/builder/src/components/start/ExportAppModal.svelte index 948416b192..4b2742b609 100644 --- a/packages/builder/src/components/start/ExportAppModal.svelte +++ b/packages/builder/src/components/start/ExportAppModal.svelte @@ -1,5 +1,11 @@ diff --git a/packages/server/src/api/routes/backup.ts b/packages/server/src/api/routes/backup.ts index fb455250a7..94e4cf957f 100644 --- a/packages/server/src/api/routes/backup.ts +++ b/packages/server/src/api/routes/backup.ts @@ -5,7 +5,7 @@ import { permissions } from "@budibase/backend-core" const router: Router = new Router() -router.get( +router.post( "/api/backups/export", authorized(permissions.BUILDER), controller.exportAppDump From a68dc7c96529c0c61d0df478ce1384bd747f134d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 Jun 2023 11:06:46 +0100 Subject: [PATCH 2/6] Remove excludeRows and appName from the querystrings --- .../components/start/ExportAppModal.svelte | 5 ++--- packages/server/src/api/controllers/backup.ts | 20 +++++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/builder/src/components/start/ExportAppModal.svelte b/packages/builder/src/components/start/ExportAppModal.svelte index 4b2742b609..c6f3fceea7 100644 --- a/packages/builder/src/components/start/ExportAppModal.svelte +++ b/packages/builder/src/components/start/ExportAppModal.svelte @@ -16,9 +16,8 @@ const exportApp = async () => { const id = published ? app.prodId : app.devId - const appName = encodeURIComponent(app.name) - const url = `/api/backups/export?appId=${id}&appname=${appName}&excludeRows=${excludeRows}` - await downloadFile(url) + const url = `/api/backups/export?appId=${id}` + await downloadFile(url, { excludeRows }) } export async function downloadFile(url, body) { diff --git a/packages/server/src/api/controllers/backup.ts b/packages/server/src/api/controllers/backup.ts index 53e1bd1792..60312bd36c 100644 --- a/packages/server/src/api/controllers/backup.ts +++ b/packages/server/src/api/controllers/backup.ts @@ -1,14 +1,22 @@ import sdk from "../../sdk" -import { events, context } from "@budibase/backend-core" +import { events, context, db } from "@budibase/backend-core" import { DocumentType } from "../../db/utils" -import { isQsTrue } from "../../utilities" +import { Ctx } from "@budibase/types" + +interface ExportAppDumpRequest { + excludeRows: boolean +} + +export async function exportAppDump(ctx: Ctx) { + const { appId } = ctx.query as any + const { excludeRows } = ctx.request.body + + const [app] = await db.getAppsByIDs([appId]) + const appName = app.name -export async function exportAppDump(ctx: any) { - let { appId, excludeRows } = ctx.query // remove the 120 second limit for the request ctx.req.setTimeout(0) - const appName = decodeURI(ctx.query.appname) - excludeRows = isQsTrue(excludeRows) + const backupIdentifier = `${appName}-export-${new Date().getTime()}.tar.gz` ctx.attachment(backupIdentifier) ctx.body = await sdk.backups.streamExportApp(appId, excludeRows) From d507eb487afc905f837be096fe500090f686e79f Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 Jun 2023 11:21:47 +0100 Subject: [PATCH 3/6] Remove export --- packages/builder/src/components/start/ExportAppModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/start/ExportAppModal.svelte b/packages/builder/src/components/start/ExportAppModal.svelte index c6f3fceea7..49c2e492f3 100644 --- a/packages/builder/src/components/start/ExportAppModal.svelte +++ b/packages/builder/src/components/start/ExportAppModal.svelte @@ -20,7 +20,7 @@ await downloadFile(url, { excludeRows }) } - export async function downloadFile(url, body) { + async function downloadFile(url, body) { try { const response = await fetch(url, { method: "POST", From fbf9b69a6bdd44174166b44ffc1ea9b8046a2034 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 Jun 2023 13:48:10 +0100 Subject: [PATCH 4/6] Simplify notification --- packages/builder/src/components/start/ExportAppModal.svelte | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/builder/src/components/start/ExportAppModal.svelte b/packages/builder/src/components/start/ExportAppModal.svelte index 49c2e492f3..0e5930487d 100644 --- a/packages/builder/src/components/start/ExportAppModal.svelte +++ b/packages/builder/src/components/start/ExportAppModal.svelte @@ -51,11 +51,7 @@ notifications.error("Error exporting the app.") } } catch (error) { - let message = "Error downloading the exported app" - if (error.message) { - message += `: ${error.message}` - } - notifications.error("Error downloading the exported app", message) + notifications.error(error.message ?? "Error downloading the exported app") } } From dec35fcafd4582e7d7d916772c9548a96b6eaf46 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 Jun 2023 13:48:37 +0100 Subject: [PATCH 5/6] Simplify notification --- packages/builder/src/components/start/ExportAppModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/start/ExportAppModal.svelte b/packages/builder/src/components/start/ExportAppModal.svelte index 0e5930487d..5cc3393a89 100644 --- a/packages/builder/src/components/start/ExportAppModal.svelte +++ b/packages/builder/src/components/start/ExportAppModal.svelte @@ -51,7 +51,7 @@ notifications.error("Error exporting the app.") } } catch (error) { - notifications.error(error.message ?? "Error downloading the exported app") + notifications.error(error.message || "Error downloading the exported app") } } From fe1a64964af73fff9b4d798fdc154ac5ed349b1c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 Jun 2023 14:20:20 +0100 Subject: [PATCH 6/6] Fix and improve tests --- .../src/api/routes/tests/backup.spec.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/routes/tests/backup.spec.ts b/packages/server/src/api/routes/tests/backup.spec.ts index ef362ef403..92e0176060 100644 --- a/packages/server/src/api/routes/tests/backup.spec.ts +++ b/packages/server/src/api/routes/tests/backup.spec.ts @@ -1,7 +1,9 @@ +import tk from "timekeeper" import * as setup from "./utilities" import { events } from "@budibase/backend-core" import sdk from "../../../sdk" import { checkBuilderEndpoint } from "./utilities/TestFunctions" +import { mocks } from "@budibase/backend-core/tests" describe("/backups", () => { let request = setup.getRequest() @@ -16,7 +18,7 @@ describe("/backups", () => { describe("exportAppDump", () => { it("should be able to export app", async () => { const res = await request - .get(`/api/backups/export?appId=${config.getAppId()}&appname=test`) + .post(`/api/backups/export?appId=${config.getAppId()}`) .set(config.defaultHeaders()) .expect(200) expect(res.headers["content-type"]).toEqual("application/gzip") @@ -26,10 +28,24 @@ describe("/backups", () => { it("should apply authorization to endpoint", async () => { await checkBuilderEndpoint({ config, - method: "GET", + method: "POST", url: `/api/backups/export?appId=${config.getAppId()}`, }) }) + + it("should infer the app name from the app", async () => { + tk.freeze(mocks.date.MOCK_DATE) + + const res = await request + .post(`/api/backups/export?appId=${config.getAppId()}`) + .set(config.defaultHeaders()) + + expect(res.headers["content-disposition"]).toEqual( + `attachment; filename="${ + config.getApp()!.name + }-export-${mocks.date.MOCK_DATE.getTime()}.tar.gz"` + ) + }) }) describe("calculateBackupStats", () => {