Merge pull request #12923 from Budibase/fix/very-large-backups

Handling very large backup upload/downloads
This commit is contained in:
Michael Drury 2024-02-01 11:51:08 +00:00 committed by GitHub
commit ebd3c0e790
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 36 additions and 16 deletions

View File

@ -7,7 +7,7 @@ import tar from "tar-fs"
import zlib from "zlib" import zlib from "zlib"
import { promisify } from "util" import { promisify } from "util"
import { join } from "path" import { join } from "path"
import fs from "fs" import fs, { ReadStream } from "fs"
import env from "../environment" import env from "../environment"
import { budibaseTempDir } from "./utils" import { budibaseTempDir } from "./utils"
import { v4 } from "uuid" import { v4 } from "uuid"
@ -184,7 +184,7 @@ export async function upload({
export async function streamUpload( export async function streamUpload(
bucketName: string, bucketName: string,
filename: string, filename: string,
stream: any, stream: ReadStream | ReadableStream,
extra = {} extra = {}
) { ) {
const objectStore = ObjectStore(bucketName) const objectStore = ObjectStore(bucketName)

View File

@ -13,6 +13,7 @@
import CreateRestoreModal from "./CreateRestoreModal.svelte" import CreateRestoreModal from "./CreateRestoreModal.svelte"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { isOnlyUser } from "builderStore" import { isOnlyUser } from "builderStore"
import { BackupType } from "constants/backend/backups"
export let row export let row
@ -42,12 +43,11 @@
</script> </script>
<div class="cell"> <div class="cell">
<ActionMenu align="right"> {#if row.type !== BackupType.RESTORE}
<div slot="control"> <ActionMenu align="right">
<Icon size="M" hoverable name="MoreSmallList" /> <div slot="control">
</div> <Icon size="M" hoverable name="MoreSmallList" />
</div>
{#if row.type !== "restore"}
<AbsTooltip <AbsTooltip
position={TooltipPosition.Left} position={TooltipPosition.Left}
text="Unavailable - another user is editing this app" text="Unavailable - another user is editing this app"
@ -62,8 +62,8 @@
</AbsTooltip> </AbsTooltip>
<MenuItem on:click={deleteDialog.show} icon="Delete">Delete</MenuItem> <MenuItem on:click={deleteDialog.show} icon="Delete">Delete</MenuItem>
<MenuItem on:click={downloadExport} icon="Download">Download</MenuItem> <MenuItem on:click={downloadExport} icon="Download">Download</MenuItem>
{/if} </ActionMenu>
</ActionMenu> {/if}
</div> </div>
<Modal bind:this={restoreBackupModal}> <Modal bind:this={restoreBackupModal}>

@ -1 +1 @@
Subproject commit eb9565f568cfef14b336b14eee753119acfdd43b Subproject commit 4f9616f163039a0eea81319d8e2288340a2ebc79

View File

@ -1,7 +1,13 @@
import fs from "fs"
import { join } from "path"
module AwsMock { module AwsMock {
const aws: any = {} const aws: any = {}
const response = (body: any) => () => ({ promise: () => body }) const response = (body: any, extra?: any) => () => ({
promise: () => body,
...extra,
})
function DocumentClient() { function DocumentClient() {
// @ts-ignore // @ts-ignore
@ -73,9 +79,18 @@ module AwsMock {
// @ts-ignore // @ts-ignore
this.getObject = jest.fn( this.getObject = jest.fn(
response({ response(
Body: "", {
}) Body: "",
},
{
createReadStream: jest
.fn()
.mockReturnValue(
fs.createReadStream(join(__dirname, "aws-sdk.ts"))
),
}
)
) )
} }

View File

@ -165,8 +165,9 @@ export async function importApp(
const isTar = template.file && template?.file?.type?.endsWith("gzip") const isTar = template.file && template?.file?.type?.endsWith("gzip")
const isDirectory = const isDirectory =
template.file && fs.lstatSync(template.file.path).isDirectory() template.file && fs.lstatSync(template.file.path).isDirectory()
let tmpPath: string | undefined = undefined
if (template.file && (isTar || isDirectory)) { if (template.file && (isTar || isDirectory)) {
const tmpPath = isTar ? await untarFile(template.file) : template.file.path tmpPath = isTar ? await untarFile(template.file) : template.file.path
if (isTar && template.file.password) { if (isTar && template.file.password) {
await decryptFiles(tmpPath, template.file.password) await decryptFiles(tmpPath, template.file.password)
} }
@ -208,5 +209,9 @@ export async function importApp(
} }
await updateAttachmentColumns(prodAppId, db) await updateAttachmentColumns(prodAppId, db)
await updateAutomations(prodAppId, db) await updateAutomations(prodAppId, db)
// clear up afterward
if (tmpPath) {
fs.rmSync(tmpPath, { recursive: true, force: true })
}
return ok return ok
} }