@budibase/worker 2.13.36 → 2.13.38
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 +12 -12
- package/scripts/dev/manage.js +33 -37
- package/src/api/controllers/global/users.ts +22 -3
- package/src/api/controllers/system/environment.ts +25 -0
- package/src/api/routes/global/tests/users.spec.ts +49 -1
- package/src/api/routes/system/tests/environment.spec.ts +19 -0
- package/src/tests/TestConfiguration.ts +29 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/worker",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "2.13.
|
|
4
|
+
"version": "2.13.38",
|
|
5
5
|
"description": "Budibase background service",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"debug": "yarn build && node --expose-gc --inspect=9223 dist/index.js",
|
|
22
22
|
"run:docker:cluster": "pm2-runtime start pm2.config.js",
|
|
23
23
|
"dev:stack:init": "node ./scripts/dev/manage.js init",
|
|
24
|
-
"dev
|
|
24
|
+
"dev": "npm run dev:stack:init && nodemon",
|
|
25
25
|
"dev:built": "yarn run dev:stack:init && yarn run run:docker",
|
|
26
26
|
"test": "bash scripts/test.sh",
|
|
27
27
|
"test:watch": "jest --watch",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"author": "Budibase",
|
|
38
38
|
"license": "GPL-3.0",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@budibase/backend-core": "2.13.
|
|
41
|
-
"@budibase/pro": "2.13.
|
|
42
|
-
"@budibase/string-templates": "2.13.
|
|
43
|
-
"@budibase/types": "2.13.
|
|
40
|
+
"@budibase/backend-core": "2.13.38",
|
|
41
|
+
"@budibase/pro": "2.13.38",
|
|
42
|
+
"@budibase/string-templates": "2.13.38",
|
|
43
|
+
"@budibase/types": "2.13.38",
|
|
44
44
|
"@koa/router": "8.0.8",
|
|
45
45
|
"@techpass/passport-openidconnect": "0.3.2",
|
|
46
46
|
"@types/global-agent": "2.1.1",
|
|
@@ -75,26 +75,26 @@
|
|
|
75
75
|
"@swc/jest": "0.2.27",
|
|
76
76
|
"@trendyol/jest-testcontainers": "2.1.1",
|
|
77
77
|
"@types/jest": "29.5.5",
|
|
78
|
-
"@types/jsonwebtoken": "
|
|
78
|
+
"@types/jsonwebtoken": "9.0.3",
|
|
79
79
|
"@types/koa": "2.13.4",
|
|
80
80
|
"@types/koa__router": "8.0.8",
|
|
81
81
|
"@types/lodash": "4.14.200",
|
|
82
82
|
"@types/node": "18.17.0",
|
|
83
83
|
"@types/node-fetch": "2.6.4",
|
|
84
84
|
"@types/server-destroy": "1.0.1",
|
|
85
|
-
"@types/supertest": "2.0.
|
|
85
|
+
"@types/supertest": "2.0.14",
|
|
86
86
|
"@types/uuid": "8.3.4",
|
|
87
|
-
"jest": "29.
|
|
87
|
+
"jest": "29.7.0",
|
|
88
88
|
"nodemon": "2.0.15",
|
|
89
89
|
"rimraf": "3.0.2",
|
|
90
|
-
"supertest": "6.
|
|
90
|
+
"supertest": "6.3.3",
|
|
91
91
|
"timekeeper": "2.2.0",
|
|
92
92
|
"typescript": "5.2.2",
|
|
93
93
|
"update-dotenv": "1.1.1"
|
|
94
94
|
},
|
|
95
95
|
"nx": {
|
|
96
96
|
"targets": {
|
|
97
|
-
"dev
|
|
97
|
+
"dev": {
|
|
98
98
|
"dependsOn": [
|
|
99
99
|
{
|
|
100
100
|
"comment": "Required for pro usage when submodule not loaded",
|
|
@@ -107,5 +107,5 @@
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "02e573ced8c766dcac96b29af4eeeab1416e22f4"
|
|
111
111
|
}
|
package/scripts/dev/manage.js
CHANGED
|
@@ -1,44 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const
|
|
3
|
-
const
|
|
2
|
+
const { parsed: existingConfig } = require("dotenv").config()
|
|
3
|
+
const updateDotEnv = require("update-dotenv")
|
|
4
4
|
|
|
5
5
|
async function init() {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
HTTP_LOGGING: 0,
|
|
34
|
-
VERSION: "0.0.0+local",
|
|
35
|
-
}
|
|
36
|
-
let envFile = ""
|
|
37
|
-
Object.keys(envFileJson).forEach(key => {
|
|
38
|
-
envFile += `${key}=${envFileJson[key]}\n`
|
|
39
|
-
})
|
|
40
|
-
fs.writeFileSync(envFilePath, envFile)
|
|
6
|
+
let config = {
|
|
7
|
+
SELF_HOSTED: "1",
|
|
8
|
+
PORT: "4002",
|
|
9
|
+
CLUSTER_PORT: "10000",
|
|
10
|
+
JWT_SECRET: "testsecret",
|
|
11
|
+
INTERNAL_API_KEY: "budibase",
|
|
12
|
+
MINIO_ACCESS_KEY: "budibase",
|
|
13
|
+
MINIO_SECRET_KEY: "budibase",
|
|
14
|
+
REDIS_URL: "localhost:6379",
|
|
15
|
+
REDIS_PASSWORD: "budibase",
|
|
16
|
+
MINIO_URL: "http://localhost:4004",
|
|
17
|
+
COUCH_DB_URL: "http://budibase:budibase@localhost:4005",
|
|
18
|
+
COUCH_DB_USERNAME: "budibase",
|
|
19
|
+
COUCH_DB_PASSWORD: "budibase",
|
|
20
|
+
// empty string is false
|
|
21
|
+
MULTI_TENANCY: "",
|
|
22
|
+
DISABLE_ACCOUNT_PORTAL: "1",
|
|
23
|
+
ACCOUNT_PORTAL_URL: "http://localhost:10001",
|
|
24
|
+
ACCOUNT_PORTAL_API_KEY: "budibase",
|
|
25
|
+
PLATFORM_URL: "http://localhost:10000",
|
|
26
|
+
APPS_URL: "http://localhost:4001",
|
|
27
|
+
SERVICE: "worker-service",
|
|
28
|
+
DEPLOYMENT_ENVIRONMENT: "development",
|
|
29
|
+
TENANT_FEATURE_FLAGS: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR",
|
|
30
|
+
ENABLE_EMAIL_TEST_MODE: "1",
|
|
31
|
+
HTTP_LOGGING: "0",
|
|
32
|
+
VERSION: "0.0.0+local",
|
|
41
33
|
}
|
|
34
|
+
|
|
35
|
+
config = { ...config, ...existingConfig }
|
|
36
|
+
|
|
37
|
+
await updateDotEnv(config)
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
// if more than init required use this to determine the command type
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
migrations,
|
|
27
27
|
platform,
|
|
28
28
|
tenancy,
|
|
29
|
+
db,
|
|
29
30
|
} from "@budibase/backend-core"
|
|
30
31
|
import { checkAnyUserExists } from "../../../utilities/users"
|
|
31
32
|
import { isEmailConfigured } from "../../../utilities/email"
|
|
@@ -185,9 +186,27 @@ export const getAppUsers = async (ctx: Ctx<SearchUsersRequest>) => {
|
|
|
185
186
|
export const search = async (ctx: Ctx<SearchUsersRequest>) => {
|
|
186
187
|
const body = ctx.request.body
|
|
187
188
|
|
|
188
|
-
// TODO: for now only
|
|
189
|
-
if (body?.query
|
|
190
|
-
|
|
189
|
+
// TODO: for now only two supported search keys; string.email and equal._id
|
|
190
|
+
if (body?.query) {
|
|
191
|
+
// Clean numeric prefixing. This will overwrite duplicate search fields,
|
|
192
|
+
// but this is fine because we only support a single custom search on
|
|
193
|
+
// email and id
|
|
194
|
+
for (let filters of Object.values(body.query)) {
|
|
195
|
+
if (filters && typeof filters === "object") {
|
|
196
|
+
for (let [field, value] of Object.entries(filters)) {
|
|
197
|
+
delete filters[field]
|
|
198
|
+
const cleanedField = db.removeKeyNumbering(field)
|
|
199
|
+
if (filters[cleanedField] !== undefined) {
|
|
200
|
+
ctx.throw(400, "Only 1 filter per field is supported")
|
|
201
|
+
}
|
|
202
|
+
filters[cleanedField] = value
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Validate we aren't trying to search on any illegal fields
|
|
207
|
+
if (!userSdk.core.isSupportedUserSearch(body.query)) {
|
|
208
|
+
ctx.throw(400, "Can only search by string.email or equal._id")
|
|
209
|
+
}
|
|
191
210
|
}
|
|
192
211
|
|
|
193
212
|
if (body.paginate === false) {
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import { Ctx } from "@budibase/types"
|
|
2
2
|
import env from "../../../environment"
|
|
3
3
|
import { env as coreEnv } from "@budibase/backend-core"
|
|
4
|
+
import nodeFetch from "node-fetch"
|
|
5
|
+
|
|
6
|
+
let sqsAvailable: boolean
|
|
7
|
+
async function isSqsAvailable() {
|
|
8
|
+
if (sqsAvailable !== undefined) {
|
|
9
|
+
return sqsAvailable
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
await nodeFetch(coreEnv.COUCH_DB_SQL_URL, {
|
|
14
|
+
timeout: 1000,
|
|
15
|
+
})
|
|
16
|
+
sqsAvailable = true
|
|
17
|
+
return true
|
|
18
|
+
} catch (e) {
|
|
19
|
+
sqsAvailable = false
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
}
|
|
4
23
|
|
|
5
24
|
export const fetch = async (ctx: Ctx) => {
|
|
6
25
|
ctx.body = {
|
|
@@ -12,4 +31,10 @@ export const fetch = async (ctx: Ctx) => {
|
|
|
12
31
|
baseUrl: env.PLATFORM_URL,
|
|
13
32
|
isDev: env.isDev() && !env.isTest(),
|
|
14
33
|
}
|
|
34
|
+
|
|
35
|
+
if (env.SELF_HOSTED) {
|
|
36
|
+
ctx.body.infrastructure = {
|
|
37
|
+
sqs: await isSqsAvailable(),
|
|
38
|
+
}
|
|
39
|
+
}
|
|
15
40
|
}
|
|
@@ -590,6 +590,15 @@ describe("/api/global/users", () => {
|
|
|
590
590
|
expect(response.body.data[0].email).toBe(user.email)
|
|
591
591
|
})
|
|
592
592
|
|
|
593
|
+
it("should be able to search by email with numeric prefixing", async () => {
|
|
594
|
+
const user = await config.createUser()
|
|
595
|
+
const response = await config.api.users.searchUsers({
|
|
596
|
+
query: { string: { ["999:email"]: user.email } },
|
|
597
|
+
})
|
|
598
|
+
expect(response.body.data.length).toBe(1)
|
|
599
|
+
expect(response.body.data[0].email).toBe(user.email)
|
|
600
|
+
})
|
|
601
|
+
|
|
593
602
|
it("should be able to search by _id", async () => {
|
|
594
603
|
const user = await config.createUser()
|
|
595
604
|
const response = await config.api.users.searchUsers({
|
|
@@ -599,13 +608,52 @@ describe("/api/global/users", () => {
|
|
|
599
608
|
expect(response.body.data[0]._id).toBe(user._id)
|
|
600
609
|
})
|
|
601
610
|
|
|
611
|
+
it("should be able to search by _id with numeric prefixing", async () => {
|
|
612
|
+
const user = await config.createUser()
|
|
613
|
+
const response = await config.api.users.searchUsers({
|
|
614
|
+
query: { equal: { ["1:_id"]: user._id } },
|
|
615
|
+
})
|
|
616
|
+
expect(response.body.data.length).toBe(1)
|
|
617
|
+
expect(response.body.data[0]._id).toBe(user._id)
|
|
618
|
+
})
|
|
619
|
+
|
|
620
|
+
it("should throw an error when using multiple filters on the same field", async () => {
|
|
621
|
+
const user = await config.createUser()
|
|
622
|
+
await config.api.users.searchUsers(
|
|
623
|
+
{
|
|
624
|
+
query: {
|
|
625
|
+
string: {
|
|
626
|
+
["1:email"]: user.email,
|
|
627
|
+
["2:email"]: "something else",
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
},
|
|
631
|
+
{ status: 400 }
|
|
632
|
+
)
|
|
633
|
+
})
|
|
634
|
+
|
|
635
|
+
it("should throw an error when using multiple filters on the same field without prefixes", async () => {
|
|
636
|
+
const user = await config.createUser()
|
|
637
|
+
await config.api.users.searchUsers(
|
|
638
|
+
{
|
|
639
|
+
query: {
|
|
640
|
+
string: {
|
|
641
|
+
["_id"]: user.email,
|
|
642
|
+
["999:_id"]: "something else",
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
{ status: 400 }
|
|
647
|
+
)
|
|
648
|
+
})
|
|
649
|
+
|
|
602
650
|
it("should throw an error when unimplemented options used", async () => {
|
|
603
651
|
const user = await config.createUser()
|
|
604
652
|
await config.api.users.searchUsers(
|
|
605
653
|
{
|
|
606
654
|
query: { equal: { firstName: user.firstName } },
|
|
607
655
|
},
|
|
608
|
-
{ status:
|
|
656
|
+
{ status: 400 }
|
|
609
657
|
)
|
|
610
658
|
})
|
|
611
659
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { TestConfiguration } from "../../../../tests"
|
|
2
2
|
|
|
3
|
+
jest.unmock("node-fetch")
|
|
4
|
+
|
|
3
5
|
describe("/api/system/environment", () => {
|
|
4
6
|
const config = new TestConfiguration()
|
|
5
7
|
|
|
@@ -27,5 +29,22 @@ describe("/api/system/environment", () => {
|
|
|
27
29
|
offlineMode: false,
|
|
28
30
|
})
|
|
29
31
|
})
|
|
32
|
+
|
|
33
|
+
it("returns the expected environment for self hosters", async () => {
|
|
34
|
+
await config.withEnv({ SELF_HOSTED: true }, async () => {
|
|
35
|
+
const env = await config.api.environment.getEnvironment()
|
|
36
|
+
expect(env.body).toEqual({
|
|
37
|
+
cloud: false,
|
|
38
|
+
disableAccountPortal: 0,
|
|
39
|
+
isDev: false,
|
|
40
|
+
multiTenancy: true,
|
|
41
|
+
baseUrl: "http://localhost:10000",
|
|
42
|
+
offlineMode: false,
|
|
43
|
+
infrastructure: {
|
|
44
|
+
sqs: false,
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
})
|
|
30
49
|
})
|
|
31
50
|
})
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
} from "@budibase/types"
|
|
37
37
|
import API from "./api"
|
|
38
38
|
import jwt, { Secret } from "jsonwebtoken"
|
|
39
|
+
import cloneDeep from "lodash/fp/cloneDeep"
|
|
39
40
|
|
|
40
41
|
class TestConfiguration {
|
|
41
42
|
server: any
|
|
@@ -240,6 +241,34 @@ class TestConfiguration {
|
|
|
240
241
|
return { message: "Admin user only endpoint.", status: 403 }
|
|
241
242
|
}
|
|
242
243
|
|
|
244
|
+
async withEnv(newEnvVars: Partial<typeof env>, f: () => Promise<void>) {
|
|
245
|
+
let cleanup = this.setEnv(newEnvVars)
|
|
246
|
+
try {
|
|
247
|
+
await f()
|
|
248
|
+
} finally {
|
|
249
|
+
cleanup()
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/*
|
|
254
|
+
* Sets the environment variables to the given values and returns a function
|
|
255
|
+
* that can be called to reset the environment variables to their original values.
|
|
256
|
+
*/
|
|
257
|
+
setEnv(newEnvVars: Partial<typeof env>): () => void {
|
|
258
|
+
const oldEnv = cloneDeep(env)
|
|
259
|
+
|
|
260
|
+
let key: keyof typeof newEnvVars
|
|
261
|
+
for (key in newEnvVars) {
|
|
262
|
+
env._set(key, newEnvVars[key])
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return () => {
|
|
266
|
+
for (const [key, value] of Object.entries(oldEnv)) {
|
|
267
|
+
env._set(key, value)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
243
272
|
// USERS
|
|
244
273
|
|
|
245
274
|
async createDefaultUser() {
|