@budibase/server 2.7.7-alpha.7 → 2.7.7-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "2.7.7-alpha.7",
4
+ "version": "2.7.7-alpha.8",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -46,12 +46,12 @@
46
46
  "license": "GPL-3.0",
47
47
  "dependencies": {
48
48
  "@apidevtools/swagger-parser": "10.0.3",
49
- "@budibase/backend-core": "2.7.7-alpha.7",
50
- "@budibase/client": "2.7.7-alpha.7",
51
- "@budibase/pro": "2.7.7-alpha.7",
52
- "@budibase/shared-core": "2.7.7-alpha.7",
53
- "@budibase/string-templates": "2.7.7-alpha.7",
54
- "@budibase/types": "2.7.7-alpha.7",
49
+ "@budibase/backend-core": "2.7.7-alpha.8",
50
+ "@budibase/client": "2.7.7-alpha.8",
51
+ "@budibase/pro": "2.7.7-alpha.8",
52
+ "@budibase/shared-core": "2.7.7-alpha.8",
53
+ "@budibase/string-templates": "2.7.7-alpha.8",
54
+ "@budibase/types": "2.7.7-alpha.8",
55
55
  "@bull-board/api": "3.7.0",
56
56
  "@bull-board/koa": "3.9.4",
57
57
  "@elastic/elasticsearch": "7.10.0",
@@ -117,7 +117,7 @@
117
117
  "socket.io": "4.6.1",
118
118
  "svelte": "3.49.0",
119
119
  "swagger-parser": "10.0.3",
120
- "tar": "6.1.11",
120
+ "tar": "6.1.15",
121
121
  "to-json-schema": "0.2.5",
122
122
  "uuid": "3.3.2",
123
123
  "validate.js": "0.13.1",
@@ -150,7 +150,7 @@
150
150
  "@types/redis": "4.0.11",
151
151
  "@types/server-destroy": "1.0.1",
152
152
  "@types/supertest": "2.0.12",
153
- "@types/tar": "6.1.3",
153
+ "@types/tar": "6.1.5",
154
154
  "@typescript-eslint/parser": "5.45.0",
155
155
  "apidoc": "0.50.4",
156
156
  "babel-jest": "29.5.0",
@@ -195,5 +195,5 @@
195
195
  }
196
196
  }
197
197
  },
198
- "gitHead": "9d9022ad8b10316acdce141458bfb434140cdde6"
198
+ "gitHead": "f64e9dbe99612a5c43f962cd95309fde1431cbad"
199
199
  }
@@ -5,11 +5,12 @@ import { Ctx } from "@budibase/types"
5
5
 
6
6
  interface ExportAppDumpRequest {
7
7
  excludeRows: boolean
8
+ encryptPassword?: string
8
9
  }
9
10
 
10
11
  export async function exportAppDump(ctx: Ctx<ExportAppDumpRequest>) {
11
12
  const { appId } = ctx.query as any
12
- const { excludeRows } = ctx.request.body
13
+ const { excludeRows, encryptPassword } = ctx.request.body
13
14
 
14
15
  const [app] = await db.getAppsByIDs([appId])
15
16
  const appName = app.name
@@ -17,9 +18,14 @@ export async function exportAppDump(ctx: Ctx<ExportAppDumpRequest>) {
17
18
  // remove the 120 second limit for the request
18
19
  ctx.req.setTimeout(0)
19
20
 
20
- const backupIdentifier = `${appName}-export-${new Date().getTime()}.tar.gz`
21
+ const extension = encryptPassword ? "enc.tar.gz" : "tar.gz"
22
+ const backupIdentifier = `${appName}-export-${new Date().getTime()}.${extension}`
21
23
  ctx.attachment(backupIdentifier)
22
- ctx.body = await sdk.backups.streamExportApp(appId, excludeRows)
24
+ ctx.body = await sdk.backups.streamExportApp({
25
+ appId,
26
+ excludeRows,
27
+ encryptPassword,
28
+ })
23
29
 
24
30
  await context.doInAppContext(appId, async () => {
25
31
  const appDb = context.getAppDB()
@@ -1,4 +1,4 @@
1
- import { db as dbCore, objectStore } from "@budibase/backend-core"
1
+ import { db as dbCore, encryption, objectStore } from "@budibase/backend-core"
2
2
  import { budibaseTempDir } from "../../../utilities/budibaseDir"
3
3
  import { streamFile, createTempFolder } from "../../../utilities/fileSystem"
4
4
  import { ObjectStoreBuckets } from "../../../constants"
@@ -18,7 +18,8 @@ import { join } from "path"
18
18
  import env from "../../../environment"
19
19
 
20
20
  const uuid = require("uuid/v4")
21
- const tar = require("tar")
21
+ import tar from "tar"
22
+
22
23
  const MemoryStream = require("memorystream")
23
24
 
24
25
  interface DBDumpOpts {
@@ -30,16 +31,18 @@ interface ExportOpts extends DBDumpOpts {
30
31
  tar?: boolean
31
32
  excludeRows?: boolean
32
33
  excludeLogs?: boolean
34
+ encryptPassword?: string
33
35
  }
34
36
 
35
37
  function tarFilesToTmp(tmpDir: string, files: string[]) {
36
- const exportFile = join(budibaseTempDir(), `${uuid()}.tar.gz`)
38
+ const fileName = `${uuid()}.tar.gz`
39
+ const exportFile = join(budibaseTempDir(), fileName)
37
40
  tar.create(
38
41
  {
39
42
  sync: true,
40
43
  gzip: true,
41
44
  file: exportFile,
42
- recursive: true,
45
+ noDirRecurse: false,
43
46
  cwd: tmpDir,
44
47
  },
45
48
  files
@@ -124,6 +127,7 @@ export async function exportApp(appId: string, config?: ExportOpts) {
124
127
  )
125
128
  }
126
129
  }
130
+
127
131
  const downloadedPath = join(tmpPath, appPath)
128
132
  if (fs.existsSync(downloadedPath)) {
129
133
  const allFiles = fs.readdirSync(downloadedPath)
@@ -141,12 +145,27 @@ export async function exportApp(appId: string, config?: ExportOpts) {
141
145
  filter: defineFilter(config?.excludeRows, config?.excludeLogs),
142
146
  exportPath: dbPath,
143
147
  })
148
+
149
+ if (config?.encryptPassword) {
150
+ for (let file of fs.readdirSync(tmpPath)) {
151
+ const path = join(tmpPath, file)
152
+
153
+ await encryption.encryptFile(
154
+ { dir: tmpPath, filename: file },
155
+ config.encryptPassword
156
+ )
157
+
158
+ fs.rmSync(path)
159
+ }
160
+ }
161
+
144
162
  // if tar requested, return where the tarball is
145
163
  if (config?.tar) {
146
164
  // now the tmpPath contains both the DB export and attachments, tar this
147
165
  const tarPath = tarFilesToTmp(tmpPath, fs.readdirSync(tmpPath))
148
166
  // cleanup the tmp export files as tarball returned
149
167
  fs.rmSync(tmpPath, { recursive: true, force: true })
168
+
150
169
  return tarPath
151
170
  }
152
171
  // tar not requested, turn the directory where export is
@@ -161,11 +180,20 @@ export async function exportApp(appId: string, config?: ExportOpts) {
161
180
  * @param {boolean} excludeRows Flag to state whether the export should include data.
162
181
  * @returns {*} a readable stream of the backup which is written in real time
163
182
  */
164
- export async function streamExportApp(appId: string, excludeRows: boolean) {
183
+ export async function streamExportApp({
184
+ appId,
185
+ excludeRows,
186
+ encryptPassword,
187
+ }: {
188
+ appId: string
189
+ excludeRows: boolean
190
+ encryptPassword?: string
191
+ }) {
165
192
  const tmpPath = await exportApp(appId, {
166
193
  excludeRows,
167
194
  excludeLogs: true,
168
195
  tar: true,
196
+ encryptPassword,
169
197
  })
170
198
  return streamFile(tmpPath)
171
199
  }
@@ -1,4 +1,4 @@
1
- import { db as dbCore, objectStore } from "@budibase/backend-core"
1
+ import { db as dbCore, encryption, objectStore } from "@budibase/backend-core"
2
2
  import { Database, Row } from "@budibase/types"
3
3
  import { getAutomationParams, TABLE_ROW_PREFIX } from "../../../db/utils"
4
4
  import { budibaseTempDir } from "../../../utilities/budibaseDir"
@@ -20,6 +20,7 @@ type TemplateType = {
20
20
  file?: {
21
21
  type: string
22
22
  path: string
23
+ password?: string
23
24
  }
24
25
  key?: string
25
26
  }
@@ -123,6 +124,22 @@ export function untarFile(file: { path: string }) {
123
124
  return tmpPath
124
125
  }
125
126
 
127
+ async function decryptFiles(path: string, password: string) {
128
+ try {
129
+ for (let file of fs.readdirSync(path)) {
130
+ const inputPath = join(path, file)
131
+ const outputPath = inputPath.replace(/\.enc$/, "")
132
+ await encryption.decryptFile(inputPath, outputPath, password)
133
+ fs.rmSync(inputPath)
134
+ }
135
+ } catch (err: any) {
136
+ if (err.message === "incorrect header check") {
137
+ throw new Error("File cannot be imported")
138
+ }
139
+ throw err
140
+ }
141
+ }
142
+
126
143
  export function getGlobalDBFile(tmpPath: string) {
127
144
  return fs.readFileSync(join(tmpPath, GLOBAL_DB_EXPORT_FILE), "utf8")
128
145
  }
@@ -143,6 +160,9 @@ export async function importApp(
143
160
  template.file && fs.lstatSync(template.file.path).isDirectory()
144
161
  if (template.file && (isTar || isDirectory)) {
145
162
  const tmpPath = isTar ? untarFile(template.file) : template.file.path
163
+ if (isTar && template.file.password) {
164
+ await decryptFiles(tmpPath, template.file.password)
165
+ }
146
166
  const contents = fs.readdirSync(tmpPath)
147
167
  // have to handle object import
148
168
  if (contents.length) {