@budibase/server 2.7.7-alpha.7 → 2.7.7-alpha.9
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/dist/automation.js +249 -125
- package/dist/automation.js.map +3 -3
- package/dist/index.js +323 -194
- package/dist/index.js.map +3 -3
- package/dist/query.js +233 -109
- package/dist/query.js.map +3 -3
- package/package.json +10 -10
- package/src/api/controllers/backup.ts +9 -3
- package/src/api/controllers/role.ts +5 -5
- package/src/api/controllers/routing.ts +3 -3
- package/src/middleware/currentapp.ts +1 -1
- package/src/sdk/app/backups/exports.ts +33 -5
- package/src/sdk/app/backups/imports.ts +21 -1
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.
|
|
4
|
+
"version": "2.7.7-alpha.9",
|
|
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.
|
|
50
|
-
"@budibase/client": "2.7.7-alpha.
|
|
51
|
-
"@budibase/pro": "2.7.7-alpha.
|
|
52
|
-
"@budibase/shared-core": "2.7.7-alpha.
|
|
53
|
-
"@budibase/string-templates": "2.7.7-alpha.
|
|
54
|
-
"@budibase/types": "2.7.7-alpha.
|
|
49
|
+
"@budibase/backend-core": "2.7.7-alpha.9",
|
|
50
|
+
"@budibase/client": "2.7.7-alpha.9",
|
|
51
|
+
"@budibase/pro": "2.7.7-alpha.9",
|
|
52
|
+
"@budibase/shared-core": "2.7.7-alpha.9",
|
|
53
|
+
"@budibase/string-templates": "2.7.7-alpha.9",
|
|
54
|
+
"@budibase/types": "2.7.7-alpha.9",
|
|
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.
|
|
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.
|
|
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": "
|
|
198
|
+
"gitHead": "31aefecfb14cd5995975c076fa4cbb6be09db535"
|
|
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
|
|
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(
|
|
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()
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getUserMetadataParams,
|
|
5
5
|
InternalTables,
|
|
6
6
|
} from "../../db/utils"
|
|
7
|
-
import {
|
|
7
|
+
import { UserCtx, Database } from "@budibase/types"
|
|
8
8
|
|
|
9
9
|
const UpdateRolesOptions = {
|
|
10
10
|
CREATED: "created",
|
|
@@ -38,15 +38,15 @@ async function updateRolesOnUserTable(
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export async function fetch(ctx:
|
|
41
|
+
export async function fetch(ctx: UserCtx) {
|
|
42
42
|
ctx.body = await roles.getAllRoles()
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export async function find(ctx:
|
|
45
|
+
export async function find(ctx: UserCtx) {
|
|
46
46
|
ctx.body = await roles.getRole(ctx.params.roleId)
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
export async function save(ctx:
|
|
49
|
+
export async function save(ctx: UserCtx) {
|
|
50
50
|
const db = context.getAppDB()
|
|
51
51
|
let { _id, name, inherits, permissionId } = ctx.request.body
|
|
52
52
|
let isCreate = false
|
|
@@ -72,7 +72,7 @@ export async function save(ctx: BBContext) {
|
|
|
72
72
|
ctx.message = `Role '${role.name}' created successfully.`
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
export async function destroy(ctx:
|
|
75
|
+
export async function destroy(ctx: UserCtx) {
|
|
76
76
|
const db = context.getAppDB()
|
|
77
77
|
const roleId = ctx.params.roleId
|
|
78
78
|
const role = await db.get(roleId)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getRoutingInfo } from "../../utilities/routing"
|
|
2
2
|
import { roles } from "@budibase/backend-core"
|
|
3
|
-
import {
|
|
3
|
+
import { UserCtx } from "@budibase/types"
|
|
4
4
|
|
|
5
5
|
const URL_SEPARATOR = "/"
|
|
6
6
|
|
|
@@ -56,11 +56,11 @@ async function getRoutingStructure() {
|
|
|
56
56
|
return { routes: routing.json }
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export async function fetch(ctx:
|
|
59
|
+
export async function fetch(ctx: UserCtx) {
|
|
60
60
|
ctx.body = await getRoutingStructure()
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
export async function clientFetch(ctx:
|
|
63
|
+
export async function clientFetch(ctx: UserCtx) {
|
|
64
64
|
const routing = await getRoutingStructure()
|
|
65
65
|
let roleId = ctx.user?.role?._id
|
|
66
66
|
const roleIds = (await roles.getUserRoleHierarchy(roleId, {
|
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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) {
|