Enable encrypting
This commit is contained in:
parent
4acfc623b4
commit
978591e2ba
|
@ -1,4 +1,6 @@
|
||||||
import crypto from "crypto"
|
import crypto from "crypto"
|
||||||
|
import fs from "fs"
|
||||||
|
import zlib from "zlib"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
|
|
||||||
const ALGO = "aes-256-ctr"
|
const ALGO = "aes-256-ctr"
|
||||||
|
@ -60,3 +62,24 @@ export function decrypt(
|
||||||
const final = decipher.final()
|
const final = decipher.final()
|
||||||
return Buffer.concat([base, final]).toString()
|
return Buffer.concat([base, final]).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function encryptFile(filePath: string, secret: string) {
|
||||||
|
const outputFilePath = `${filePath}.enc`
|
||||||
|
|
||||||
|
const inputFile = fs.createReadStream(filePath)
|
||||||
|
const outputFile = fs.createWriteStream(outputFilePath)
|
||||||
|
|
||||||
|
const salt = crypto.randomBytes(RANDOM_BYTES)
|
||||||
|
const stretched = stretchString(secret, salt)
|
||||||
|
const cipher = crypto.createCipheriv(ALGO, stretched, salt)
|
||||||
|
|
||||||
|
const encrypted = inputFile.pipe(cipher).pipe(zlib.createGzip())
|
||||||
|
|
||||||
|
encrypted.pipe(outputFile)
|
||||||
|
|
||||||
|
return new Promise<string>(r => {
|
||||||
|
outputFile.on("finish", () => {
|
||||||
|
r(outputFilePath)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -4,14 +4,19 @@ import { DocumentType } from "../../db/utils"
|
||||||
import { isQsTrue } from "../../utilities"
|
import { isQsTrue } from "../../utilities"
|
||||||
|
|
||||||
export async function exportAppDump(ctx: any) {
|
export async function exportAppDump(ctx: any) {
|
||||||
let { appId, excludeRows } = ctx.query
|
let { appId, excludeRows = false, encryptPassword } = ctx.query
|
||||||
// remove the 120 second limit for the request
|
// remove the 120 second limit for the request
|
||||||
ctx.req.setTimeout(0)
|
ctx.req.setTimeout(0)
|
||||||
const appName = decodeURI(ctx.query.appname)
|
const appName = decodeURI(ctx.query.appname)
|
||||||
excludeRows = isQsTrue(excludeRows)
|
excludeRows = isQsTrue(excludeRows)
|
||||||
const backupIdentifier = `${appName}-export-${new Date().getTime()}.tar.gz`
|
const extension = encryptPassword ? "data" : "tar.gz"
|
||||||
|
const backupIdentifier = `${appName}-export-${new Date().getTime()}.${extension}`
|
||||||
ctx.attachment(backupIdentifier)
|
ctx.attachment(backupIdentifier)
|
||||||
ctx.body = await sdk.backups.streamExportApp(appId, excludeRows)
|
ctx.body = await sdk.backups.streamExportApp({
|
||||||
|
appId,
|
||||||
|
excludeRows,
|
||||||
|
encryptPassword,
|
||||||
|
})
|
||||||
|
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
const appDb = context.getAppDB()
|
const appDb = context.getAppDB()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { db as dbCore, objectStore } from "@budibase/backend-core"
|
import { db as dbCore, encryption, objectStore } from "@budibase/backend-core"
|
||||||
import { budibaseTempDir } from "../../../utilities/budibaseDir"
|
import { budibaseTempDir } from "../../../utilities/budibaseDir"
|
||||||
import { streamFile, createTempFolder } from "../../../utilities/fileSystem"
|
import { streamFile, createTempFolder } from "../../../utilities/fileSystem"
|
||||||
import { ObjectStoreBuckets } from "../../../constants"
|
import { ObjectStoreBuckets } from "../../../constants"
|
||||||
|
@ -31,6 +31,7 @@ interface ExportOpts extends DBDumpOpts {
|
||||||
tar?: boolean
|
tar?: boolean
|
||||||
excludeRows?: boolean
|
excludeRows?: boolean
|
||||||
excludeLogs?: boolean
|
excludeLogs?: boolean
|
||||||
|
encryptPassword?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function tarFilesToTmp(tmpDir: string, files: string[]) {
|
function tarFilesToTmp(tmpDir: string, files: string[]) {
|
||||||
|
@ -150,7 +151,15 @@ export async function exportApp(appId: string, config?: ExportOpts) {
|
||||||
const tarPath = tarFilesToTmp(tmpPath, fs.readdirSync(tmpPath))
|
const tarPath = tarFilesToTmp(tmpPath, fs.readdirSync(tmpPath))
|
||||||
// cleanup the tmp export files as tarball returned
|
// cleanup the tmp export files as tarball returned
|
||||||
fs.rmSync(tmpPath, { recursive: true, force: true })
|
fs.rmSync(tmpPath, { recursive: true, force: true })
|
||||||
return tarPath
|
if (!config.encryptPassword) {
|
||||||
|
return tarPath
|
||||||
|
}
|
||||||
|
|
||||||
|
const encryptedTarPath = await encryption.encryptFile(
|
||||||
|
tarPath,
|
||||||
|
config.encryptPassword
|
||||||
|
)
|
||||||
|
return encryptedTarPath
|
||||||
}
|
}
|
||||||
// tar not requested, turn the directory where export is
|
// tar not requested, turn the directory where export is
|
||||||
else {
|
else {
|
||||||
|
@ -164,11 +173,20 @@ export async function exportApp(appId: string, config?: ExportOpts) {
|
||||||
* @param {boolean} excludeRows Flag to state whether the export should include data.
|
* @param {boolean} excludeRows Flag to state whether the export should include data.
|
||||||
* @returns {*} a readable stream of the backup which is written in real time
|
* @returns {*} a readable stream of the backup which is written in real time
|
||||||
*/
|
*/
|
||||||
export async function streamExportApp(appId: string, excludeRows: boolean) {
|
export async function streamExportApp({
|
||||||
|
appId,
|
||||||
|
excludeRows,
|
||||||
|
encryptPassword,
|
||||||
|
}: {
|
||||||
|
appId: string
|
||||||
|
excludeRows: boolean
|
||||||
|
encryptPassword: string
|
||||||
|
}) {
|
||||||
const tmpPath = await exportApp(appId, {
|
const tmpPath = await exportApp(appId, {
|
||||||
excludeRows,
|
excludeRows,
|
||||||
excludeLogs: true,
|
excludeLogs: true,
|
||||||
tar: true,
|
tar: true,
|
||||||
|
encryptPassword,
|
||||||
})
|
})
|
||||||
return streamFile(tmpPath)
|
return streamFile(tmpPath)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue