@budibase/worker 2.1.39 → 2.1.40-alpha.1
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/__mocks__/aws-sdk.ts +15 -0
- package/__mocks__/oauth.ts +57 -0
- package/coverage/clover.xml +2153 -0
- package/coverage/coverage-final.json +78 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +431 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/src/api/controllers/global/auth.ts.html +925 -0
- package/coverage/lcov-report/src/api/controllers/global/configs.ts.html +1405 -0
- package/coverage/lcov-report/src/api/controllers/global/email.ts.html +196 -0
- package/coverage/lcov-report/src/api/controllers/global/index.html +236 -0
- package/coverage/lcov-report/src/api/controllers/global/license.ts.html +187 -0
- package/coverage/lcov-report/src/api/controllers/global/roles.ts.html +283 -0
- package/coverage/lcov-report/src/api/controllers/global/self.ts.html +577 -0
- package/coverage/lcov-report/src/api/controllers/global/templates.ts.html +316 -0
- package/coverage/lcov-report/src/api/controllers/global/users.ts.html +796 -0
- package/coverage/lcov-report/src/api/controllers/global/workspaces.ts.html +244 -0
- package/coverage/lcov-report/src/api/controllers/system/accounts.ts.html +148 -0
- package/coverage/lcov-report/src/api/controllers/system/environment.ts.html +124 -0
- package/coverage/lcov-report/src/api/controllers/system/index.html +191 -0
- package/coverage/lcov-report/src/api/controllers/system/migrations.ts.html +124 -0
- package/coverage/lcov-report/src/api/controllers/system/restore.ts.html +124 -0
- package/coverage/lcov-report/src/api/controllers/system/status.ts.html +133 -0
- package/coverage/lcov-report/src/api/controllers/system/tenants.ts.html +154 -0
- package/coverage/lcov-report/src/api/index.html +116 -0
- package/coverage/lcov-report/src/api/index.ts.html +583 -0
- package/coverage/lcov-report/src/api/routes/global/auth.ts.html +349 -0
- package/coverage/lcov-report/src/api/routes/global/configs.ts.html +457 -0
- package/coverage/lcov-report/src/api/routes/global/email.ts.html +193 -0
- package/coverage/lcov-report/src/api/routes/global/index.html +236 -0
- package/coverage/lcov-report/src/api/routes/global/license.ts.html +124 -0
- package/coverage/lcov-report/src/api/routes/global/roles.ts.html +133 -0
- package/coverage/lcov-report/src/api/routes/global/self.ts.html +139 -0
- package/coverage/lcov-report/src/api/routes/global/templates.ts.html +196 -0
- package/coverage/lcov-report/src/api/routes/global/users.ts.html +409 -0
- package/coverage/lcov-report/src/api/routes/global/workspaces.ts.html +196 -0
- package/coverage/lcov-report/src/api/routes/index.html +116 -0
- package/coverage/lcov-report/src/api/routes/index.ts.html +196 -0
- package/coverage/lcov-report/src/api/routes/system/accounts.ts.html +142 -0
- package/coverage/lcov-report/src/api/routes/system/environment.ts.html +109 -0
- package/coverage/lcov-report/src/api/routes/system/index.html +191 -0
- package/coverage/lcov-report/src/api/routes/system/migrations.ts.html +142 -0
- package/coverage/lcov-report/src/api/routes/system/restore.ts.html +109 -0
- package/coverage/lcov-report/src/api/routes/system/status.ts.html +109 -0
- package/coverage/lcov-report/src/api/routes/system/tenants.ts.html +124 -0
- package/coverage/lcov-report/src/api/routes/validation/index.html +131 -0
- package/coverage/lcov-report/src/api/routes/validation/index.ts.html +88 -0
- package/coverage/lcov-report/src/api/routes/validation/users.ts.html +235 -0
- package/coverage/lcov-report/src/constants/index.html +116 -0
- package/coverage/lcov-report/src/constants/index.ts.html +613 -0
- package/coverage/lcov-report/src/constants/templates/index.html +116 -0
- package/coverage/lcov-report/src/constants/templates/index.ts.html +316 -0
- package/coverage/lcov-report/src/db/index.html +116 -0
- package/coverage/lcov-report/src/db/index.ts.html +115 -0
- package/coverage/lcov-report/src/environment.ts.html +373 -0
- package/coverage/lcov-report/src/index.html +131 -0
- package/coverage/lcov-report/src/index.ts.html +394 -0
- package/coverage/lcov-report/src/middleware/cloudRestricted.ts.html +139 -0
- package/coverage/lcov-report/src/middleware/index.html +116 -0
- package/coverage/lcov-report/src/migrations/functions/globalInfoSyncUsers.ts.html +145 -0
- package/coverage/lcov-report/src/migrations/functions/index.html +116 -0
- package/coverage/lcov-report/src/migrations/index.html +116 -0
- package/coverage/lcov-report/src/migrations/index.ts.html +271 -0
- package/coverage/lcov-report/src/sdk/accounts/accounts.ts.html +262 -0
- package/coverage/lcov-report/src/sdk/accounts/index.html +131 -0
- package/coverage/lcov-report/src/sdk/accounts/index.ts.html +88 -0
- package/coverage/lcov-report/src/sdk/index.html +116 -0
- package/coverage/lcov-report/src/sdk/index.ts.html +106 -0
- package/coverage/lcov-report/src/sdk/users/events.ts.html +577 -0
- package/coverage/lcov-report/src/sdk/users/index.html +146 -0
- package/coverage/lcov-report/src/sdk/users/index.ts.html +88 -0
- package/coverage/lcov-report/src/sdk/users/users.ts.html +1903 -0
- package/coverage/lcov-report/src/tests/TestConfiguration.ts.html +1051 -0
- package/coverage/lcov-report/src/tests/api/accounts.ts.html +160 -0
- package/coverage/lcov-report/src/tests/api/auth.ts.html +220 -0
- package/coverage/lcov-report/src/tests/api/base.ts.html +133 -0
- package/coverage/lcov-report/src/tests/api/configs.ts.html +226 -0
- package/coverage/lcov-report/src/tests/api/email.ts.html +148 -0
- package/coverage/lcov-report/src/tests/api/environment.ts.html +130 -0
- package/coverage/lcov-report/src/tests/api/index.html +296 -0
- package/coverage/lcov-report/src/tests/api/index.ts.html +205 -0
- package/coverage/lcov-report/src/tests/api/migrations.ts.html +151 -0
- package/coverage/lcov-report/src/tests/api/restore.ts.html +127 -0
- package/coverage/lcov-report/src/tests/api/self.ts.html +163 -0
- package/coverage/lcov-report/src/tests/api/status.ts.html +121 -0
- package/coverage/lcov-report/src/tests/api/tenants.ts.html +130 -0
- package/coverage/lcov-report/src/tests/api/users.ts.html +511 -0
- package/coverage/lcov-report/src/tests/controllers.ts.html +100 -0
- package/coverage/lcov-report/src/tests/index.html +146 -0
- package/coverage/lcov-report/src/tests/index.ts.html +139 -0
- package/coverage/lcov-report/src/tests/mocks/email.js.html +115 -0
- package/coverage/lcov-report/src/tests/mocks/index.html +131 -0
- package/coverage/lcov-report/src/tests/mocks/index.ts.html +106 -0
- package/coverage/lcov-report/src/tests/structures/configs.ts.html +313 -0
- package/coverage/lcov-report/src/tests/structures/groups.ts.html +118 -0
- package/coverage/lcov-report/src/tests/structures/index.html +161 -0
- package/coverage/lcov-report/src/tests/structures/index.ts.html +151 -0
- package/coverage/lcov-report/src/tests/structures/users.ts.html +196 -0
- package/coverage/lcov-report/src/utilities/appService.ts.html +181 -0
- package/coverage/lcov-report/src/utilities/email.ts.html +850 -0
- package/coverage/lcov-report/src/utilities/fileSystem.ts.html +100 -0
- package/coverage/lcov-report/src/utilities/index.html +206 -0
- package/coverage/lcov-report/src/utilities/index.ts.html +112 -0
- package/coverage/lcov-report/src/utilities/redis.ts.html +424 -0
- package/coverage/lcov-report/src/utilities/templates.ts.html +232 -0
- package/coverage/lcov-report/src/utilities/users.ts.html +133 -0
- package/coverage/lcov.info +3958 -0
- package/jest.config.ts +29 -0
- package/package.json +14 -24
- package/scripts/localdomain.js +33 -7
- package/src/api/controllers/global/auth.ts +51 -43
- package/src/api/controllers/global/{configs.js → configs.ts} +119 -114
- package/src/api/controllers/global/{email.js → email.ts} +6 -5
- package/src/api/controllers/global/roles.ts +66 -0
- package/src/api/controllers/global/self.ts +2 -2
- package/src/api/controllers/global/users.ts +4 -2
- package/src/api/controllers/global/{workspaces.js → workspaces.ts} +15 -18
- package/src/api/controllers/system/{environment.js → environment.ts} +3 -2
- package/src/api/controllers/system/restore.ts +1 -1
- package/src/api/controllers/system/{status.js → status.ts} +4 -3
- package/src/api/controllers/system/tenants.ts +8 -51
- package/src/api/index.ts +31 -8
- package/src/api/routes/global/{auth.js → auth.ts} +31 -48
- package/src/api/routes/global/{configs.js → configs.ts} +22 -23
- package/src/api/routes/global/{email.js → email.ts} +9 -10
- package/src/api/routes/global/license.ts +1 -1
- package/src/api/routes/global/roles.ts +16 -0
- package/src/api/routes/global/self.ts +2 -2
- package/src/api/routes/global/templates.ts +4 -4
- package/src/api/routes/global/tests/auth.spec.ts +134 -64
- package/src/api/routes/global/tests/configs.spec.ts +34 -35
- package/src/api/routes/global/tests/email.spec.ts +4 -3
- package/src/api/routes/global/tests/license.spec.ts +31 -0
- package/src/api/routes/global/tests/realEmail.spec.ts +2 -3
- package/src/api/routes/global/tests/roles.spec.ts +27 -0
- package/src/api/routes/global/tests/self.spec.ts +3 -4
- package/src/api/routes/global/tests/templates.spec.ts +35 -0
- package/src/api/routes/global/tests/users.spec.ts +51 -43
- package/src/api/routes/global/tests/workspaces.spec.ts +29 -0
- package/src/api/routes/global/{users.js → users.ts} +26 -24
- package/src/api/routes/global/{workspaces.js → workspaces.ts} +9 -10
- package/src/api/routes/index.ts +2 -1
- package/src/api/routes/system/accounts.ts +1 -1
- package/src/api/routes/system/environment.ts +8 -0
- package/src/api/routes/system/migrations.ts +1 -1
- package/src/api/routes/system/restore.ts +1 -1
- package/src/api/routes/system/status.ts +8 -0
- package/src/api/routes/system/tenants.ts +13 -0
- package/src/api/routes/system/tests/accounts.spec.ts +5 -6
- package/src/api/routes/system/tests/environment.spec.ts +29 -0
- package/src/api/routes/system/tests/migrations.spec.ts +63 -0
- package/src/api/routes/system/tests/restore.spec.ts +36 -0
- package/src/api/routes/system/tests/status.spec.ts +48 -0
- package/src/api/routes/system/tests/tenants.spec.ts +61 -0
- package/src/api/routes/validation/users.ts +3 -3
- package/src/constants/{index.js → index.ts} +57 -64
- package/src/constants/templates/{index.js → index.ts} +23 -19
- package/src/db/index.ts +10 -0
- package/src/environment.ts +8 -6
- package/src/index.ts +15 -16
- package/src/middleware/{cloudRestricted.js → cloudRestricted.ts} +5 -4
- package/src/middleware/tests/tenancy.spec.ts +73 -0
- package/src/tests/TestConfiguration.ts +99 -50
- package/src/tests/api/accounts.ts +5 -8
- package/src/tests/api/auth.ts +3 -6
- package/src/tests/api/base.ts +16 -0
- package/src/tests/api/configs.ts +15 -8
- package/src/tests/api/email.ts +3 -6
- package/src/tests/api/environment.ts +15 -0
- package/src/tests/api/index.ts +15 -0
- package/src/tests/api/migrations.ts +22 -0
- package/src/tests/api/restore.ts +14 -0
- package/src/tests/api/self.ts +11 -6
- package/src/tests/api/status.ts +12 -0
- package/src/tests/api/tenants.ts +15 -0
- package/src/tests/api/users.ts +38 -6
- package/src/tests/controllers.ts +5 -0
- package/src/tests/index.ts +5 -1
- package/{scripts/jestSetup.js → src/tests/jestSetup.ts} +12 -3
- package/src/tests/structures/{configs.js → configs.ts} +11 -11
- package/src/tests/structures/index.ts +5 -3
- package/src/tests/structures/users.ts +1 -0
- package/src/utilities/appService.ts +32 -0
- package/src/utilities/{email.js → email.ts} +78 -45
- package/src/utilities/fileSystem.ts +5 -0
- package/src/utilities/{index.js → index.ts} +1 -1
- package/src/utilities/{redis.js → redis.ts} +29 -24
- package/src/utilities/templates.ts +49 -0
- package/src/utilities/users.ts +16 -0
- package/tsconfig.build.json +2 -3
- package/tsconfig.json +3 -1
- package/src/api/controllers/global/roles.js +0 -68
- package/src/api/routes/global/roles.js +0 -12
- package/src/api/routes/system/environment.js +0 -8
- package/src/api/routes/system/status.js +0 -8
- package/src/api/routes/system/tenants.js +0 -12
- package/src/db/index.js +0 -11
- package/src/tests/controllers.js +0 -7
- package/src/tests/structures/accounts.ts +0 -24
- package/src/utilities/appService.js +0 -33
- package/src/utilities/fileSystem.js +0 -5
- package/src/utilities/templates.js +0 -49
- package/src/utilities/users.js +0 -17
package/src/tests/api/users.ts
CHANGED
|
@@ -3,16 +3,16 @@ import {
|
|
|
3
3
|
BulkUserRequest,
|
|
4
4
|
InviteUsersRequest,
|
|
5
5
|
User,
|
|
6
|
+
CreateAdminUserRequest,
|
|
6
7
|
} from "@budibase/types"
|
|
8
|
+
import * as structures from "../structures"
|
|
9
|
+
import { generator } from "@budibase/backend-core/tests"
|
|
7
10
|
import TestConfiguration from "../TestConfiguration"
|
|
11
|
+
import { TestAPI, TestAPIOpts } from "./base"
|
|
8
12
|
|
|
9
|
-
export class UserAPI {
|
|
10
|
-
config: TestConfiguration
|
|
11
|
-
request: any
|
|
12
|
-
|
|
13
|
+
export class UserAPI extends TestAPI {
|
|
13
14
|
constructor(config: TestConfiguration) {
|
|
14
|
-
|
|
15
|
-
this.request = config.request
|
|
15
|
+
super(config)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// INVITE
|
|
@@ -91,6 +91,30 @@ export class UserAPI {
|
|
|
91
91
|
|
|
92
92
|
// USER
|
|
93
93
|
|
|
94
|
+
createAdminUser = async (
|
|
95
|
+
request?: CreateAdminUserRequest,
|
|
96
|
+
opts?: TestAPIOpts
|
|
97
|
+
) => {
|
|
98
|
+
if (!request) {
|
|
99
|
+
request = {
|
|
100
|
+
email: structures.email(),
|
|
101
|
+
password: generator.string(),
|
|
102
|
+
tenantId: structures.uuid(),
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const res = await this.request
|
|
106
|
+
.post(`/api/global/users/init`)
|
|
107
|
+
.send(request)
|
|
108
|
+
.set(this.config.internalAPIHeaders())
|
|
109
|
+
.expect("Content-Type", /json/)
|
|
110
|
+
.expect(opts?.status ? opts.status : 200)
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
...request,
|
|
114
|
+
userId: res.body._id,
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
94
118
|
saveUser = (user: User, status?: number) => {
|
|
95
119
|
return this.request
|
|
96
120
|
.post(`/api/global/users`)
|
|
@@ -107,4 +131,12 @@ export class UserAPI {
|
|
|
107
131
|
.expect("Content-Type", /json/)
|
|
108
132
|
.expect(status ? status : 200)
|
|
109
133
|
}
|
|
134
|
+
|
|
135
|
+
getUser = (userId: string, opts?: TestAPIOpts) => {
|
|
136
|
+
return this.request
|
|
137
|
+
.get(`/api/global/users/${userId}`)
|
|
138
|
+
.set(opts?.headers ? opts.headers : this.config.defaultHeaders())
|
|
139
|
+
.expect("Content-Type", /json/)
|
|
140
|
+
.expect(opts?.status ? opts.status : 200)
|
|
141
|
+
}
|
|
110
142
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * as email from "../api/controllers/global/email"
|
|
2
|
+
export * as workspaces from "../api/controllers/global/workspaces"
|
|
3
|
+
export * as config from "../api/controllers/global/configs"
|
|
4
|
+
export * as templates from "../api/controllers/global/templates"
|
|
5
|
+
export * as users from "../api/controllers/global/users"
|
package/src/tests/index.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import mocks from "./mocks"
|
|
2
|
+
import { generator } from "@budibase/backend-core/tests"
|
|
1
3
|
import TestConfiguration from "./TestConfiguration"
|
|
2
4
|
import structures from "./structures"
|
|
3
|
-
import mocks from "./mocks"
|
|
4
5
|
import API from "./api"
|
|
6
|
+
import { v4 as uuid } from "uuid"
|
|
5
7
|
|
|
6
8
|
const pkg = {
|
|
7
9
|
structures,
|
|
10
|
+
generator,
|
|
11
|
+
uuid,
|
|
8
12
|
TENANT_1: structures.TENANT_1,
|
|
9
13
|
mocks,
|
|
10
14
|
TestConfiguration,
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import env from "../environment"
|
|
2
2
|
|
|
3
|
-
env._set("SELF_HOSTED", "
|
|
3
|
+
env._set("SELF_HOSTED", "0")
|
|
4
4
|
env._set("NODE_ENV", "jest")
|
|
5
5
|
env._set("JWT_SECRET", "test-jwtsecret")
|
|
6
6
|
env._set("LOG_LEVEL", "silent")
|
|
7
7
|
env._set("MULTI_TENANCY", true)
|
|
8
|
+
env._set("MINIO_URL", "http://localhost")
|
|
9
|
+
env._set("MINIO_ACCESS_KEY", "test")
|
|
10
|
+
env._set("MINIO_SECRET_KEY", "test")
|
|
11
|
+
env._set("PLATFORM_URL", "http://localhost:10000")
|
|
12
|
+
env._set("INTERNAL_API_KEY", "test")
|
|
13
|
+
env._set("DISABLE_ACCOUNT_PORTAL", false)
|
|
8
14
|
|
|
9
|
-
|
|
15
|
+
import { mocks } from "@budibase/backend-core/tests"
|
|
16
|
+
|
|
17
|
+
// must explicitly enable fetch mock
|
|
18
|
+
mocks.fetch.enable()
|
|
10
19
|
|
|
11
20
|
// mock all dates to 2020-01-01T00:00:00.000Z
|
|
12
21
|
// use tk.reset() to use real dates in individual tests
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { Config } = require("../../constants")
|
|
2
2
|
const { utils } = require("@budibase/backend-core")
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
export function oidc(conf?: any) {
|
|
5
5
|
return {
|
|
6
|
-
type:
|
|
6
|
+
type: Config.OIDC,
|
|
7
7
|
config: {
|
|
8
8
|
configs: [
|
|
9
9
|
{
|
|
@@ -21,9 +21,9 @@ exports.oidc = conf => {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
export function google(conf?: any) {
|
|
25
25
|
return {
|
|
26
|
-
type:
|
|
26
|
+
type: Config.GOOGLE,
|
|
27
27
|
config: {
|
|
28
28
|
clientID: "clientId",
|
|
29
29
|
clientSecret: "clientSecret",
|
|
@@ -33,9 +33,9 @@ exports.google = conf => {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
export function smtp(conf?: any) {
|
|
37
37
|
return {
|
|
38
|
-
type:
|
|
38
|
+
type: Config.SMTP,
|
|
39
39
|
config: {
|
|
40
40
|
port: 12345,
|
|
41
41
|
host: "smtptesthost.com",
|
|
@@ -47,9 +47,9 @@ exports.smtp = conf => {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
export function smtpEthereal() {
|
|
51
51
|
return {
|
|
52
|
-
type:
|
|
52
|
+
type: Config.SMTP,
|
|
53
53
|
config: {
|
|
54
54
|
port: 587,
|
|
55
55
|
host: "smtp.ethereal.email",
|
|
@@ -63,9 +63,9 @@ exports.smtpEthereal = () => {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
export function settings(conf?: any) {
|
|
67
67
|
return {
|
|
68
|
-
type:
|
|
68
|
+
type: Config.SETTINGS,
|
|
69
69
|
config: {
|
|
70
70
|
platformUrl: "http://localhost:10000",
|
|
71
71
|
logoUrl: "",
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { structures } from "@budibase/backend-core/tests"
|
|
2
|
+
import * as configs from "./configs"
|
|
2
3
|
import * as users from "./users"
|
|
3
4
|
import * as groups from "./groups"
|
|
4
|
-
import
|
|
5
|
+
import { v4 as uuid } from "uuid"
|
|
5
6
|
|
|
6
7
|
const TENANT_ID = "default"
|
|
7
8
|
const TENANT_1 = "tenant1"
|
|
8
9
|
const CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306"
|
|
9
10
|
|
|
10
11
|
const pkg = {
|
|
12
|
+
...structures,
|
|
13
|
+
uuid,
|
|
11
14
|
configs,
|
|
12
15
|
users,
|
|
13
|
-
accounts,
|
|
14
16
|
TENANT_ID,
|
|
15
17
|
TENANT_1,
|
|
16
18
|
CSRF_TOKEN,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fetch from "node-fetch"
|
|
2
|
+
import { constants, tenancy } from "@budibase/backend-core"
|
|
3
|
+
import { checkSlashesInUrl } from "../utilities"
|
|
4
|
+
import env from "../environment"
|
|
5
|
+
|
|
6
|
+
async function makeAppRequest(url: string, method: string, body: any) {
|
|
7
|
+
if (env.isTest()) {
|
|
8
|
+
return
|
|
9
|
+
}
|
|
10
|
+
const request: any = { headers: {} }
|
|
11
|
+
request.headers[constants.Header.API_KEY] = env.INTERNAL_API_KEY
|
|
12
|
+
if (tenancy.isTenantIdSet()) {
|
|
13
|
+
request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId()
|
|
14
|
+
}
|
|
15
|
+
if (body) {
|
|
16
|
+
request.headers["Content-Type"] = "application/json"
|
|
17
|
+
request.body = JSON.stringify(body)
|
|
18
|
+
}
|
|
19
|
+
request.method = method
|
|
20
|
+
return fetch(checkSlashesInUrl(env.APPS_URL + url), request)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function syncUserInApps(userId: string) {
|
|
24
|
+
const response = await makeAppRequest(
|
|
25
|
+
`/api/users/metadata/sync/${userId}`,
|
|
26
|
+
"POST",
|
|
27
|
+
{}
|
|
28
|
+
)
|
|
29
|
+
if (response && response.status !== 200) {
|
|
30
|
+
throw "Unable to sync user."
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,15 +1,33 @@
|
|
|
1
|
+
import env from "../environment"
|
|
2
|
+
import { EmailTemplatePurpose, TemplateType, Config } from "../constants"
|
|
3
|
+
import { getTemplateByPurpose } from "../constants/templates"
|
|
4
|
+
import { getSettingsTemplateContext } from "./templates"
|
|
5
|
+
import { processString } from "@budibase/string-templates"
|
|
6
|
+
import { getResetPasswordCode, getInviteCode } from "./redis"
|
|
7
|
+
import { User, Database } from "@budibase/types"
|
|
8
|
+
import { tenancy, db as dbCore } from "@budibase/backend-core"
|
|
1
9
|
const nodemailer = require("nodemailer")
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
|
|
11
|
+
type SendEmailOpts = {
|
|
12
|
+
// workspaceId If finer grain controls being used then this will lookup config for workspace.
|
|
13
|
+
workspaceId?: string
|
|
14
|
+
// user If sending to an existing user the object can be provided, this is used in the context.
|
|
15
|
+
user: User
|
|
16
|
+
// from If sending from an address that is not what is configured in the SMTP config.
|
|
17
|
+
from?: string
|
|
18
|
+
// contents If sending a custom email then can supply contents which will be added to it.
|
|
19
|
+
contents?: string
|
|
20
|
+
// subject A custom subject can be specified if the config one is not desired.
|
|
21
|
+
subject?: string
|
|
22
|
+
// info Pass in a structure of information to be stored alongside the invitation.
|
|
23
|
+
info?: any
|
|
24
|
+
cc?: boolean
|
|
25
|
+
bcc?: boolean
|
|
26
|
+
automation?: boolean
|
|
27
|
+
}
|
|
10
28
|
|
|
11
29
|
const TEST_MODE = false
|
|
12
|
-
const TYPE =
|
|
30
|
+
const TYPE = TemplateType.EMAIL
|
|
13
31
|
|
|
14
32
|
const FULL_EMAIL_PURPOSES = [
|
|
15
33
|
EmailTemplatePurpose.INVITATION,
|
|
@@ -18,8 +36,8 @@ const FULL_EMAIL_PURPOSES = [
|
|
|
18
36
|
EmailTemplatePurpose.CUSTOM,
|
|
19
37
|
]
|
|
20
38
|
|
|
21
|
-
function createSMTPTransport(config) {
|
|
22
|
-
let options
|
|
39
|
+
function createSMTPTransport(config: any) {
|
|
40
|
+
let options: any
|
|
23
41
|
let secure = config.secure
|
|
24
42
|
// default it if not specified
|
|
25
43
|
if (secure == null) {
|
|
@@ -52,10 +70,15 @@ function createSMTPTransport(config) {
|
|
|
52
70
|
return nodemailer.createTransport(options)
|
|
53
71
|
}
|
|
54
72
|
|
|
55
|
-
async function getLinkCode(
|
|
73
|
+
async function getLinkCode(
|
|
74
|
+
purpose: EmailTemplatePurpose,
|
|
75
|
+
email: string,
|
|
76
|
+
user: User,
|
|
77
|
+
info: any = null
|
|
78
|
+
) {
|
|
56
79
|
switch (purpose) {
|
|
57
80
|
case EmailTemplatePurpose.PASSWORD_RECOVERY:
|
|
58
|
-
return getResetPasswordCode(user._id
|
|
81
|
+
return getResetPasswordCode(user._id!, info)
|
|
59
82
|
case EmailTemplatePurpose.INVITATION:
|
|
60
83
|
return getInviteCode(email, info)
|
|
61
84
|
default:
|
|
@@ -72,7 +95,12 @@ async function getLinkCode(purpose, email, user, info = null) {
|
|
|
72
95
|
* @param {string|null} contents if using a custom template can supply contents for context.
|
|
73
96
|
* @return {Promise<string>} returns the built email HTML if all provided parameters were valid.
|
|
74
97
|
*/
|
|
75
|
-
async function buildEmail(
|
|
98
|
+
async function buildEmail(
|
|
99
|
+
purpose: EmailTemplatePurpose,
|
|
100
|
+
email: string,
|
|
101
|
+
context: any,
|
|
102
|
+
{ user, contents }: any = {}
|
|
103
|
+
) {
|
|
76
104
|
// this isn't a full email
|
|
77
105
|
if (FULL_EMAIL_PURPOSES.indexOf(purpose) === -1) {
|
|
78
106
|
throw `Unable to build an email of type ${purpose}`
|
|
@@ -113,15 +141,19 @@ async function buildEmail(purpose, email, context, { user, contents } = {}) {
|
|
|
113
141
|
* @param {boolean|null} automation Whether or not the configuration is being fetched for an email automation.
|
|
114
142
|
* @return {Promise<object|null>} returns the SMTP configuration if it exists
|
|
115
143
|
*/
|
|
116
|
-
async function getSmtpConfiguration(
|
|
117
|
-
|
|
118
|
-
|
|
144
|
+
async function getSmtpConfiguration(
|
|
145
|
+
db: Database,
|
|
146
|
+
workspaceId?: string,
|
|
147
|
+
automation?: boolean
|
|
148
|
+
) {
|
|
149
|
+
const params: any = {
|
|
150
|
+
type: Config.SMTP,
|
|
119
151
|
}
|
|
120
152
|
if (workspaceId) {
|
|
121
153
|
params.workspace = workspaceId
|
|
122
154
|
}
|
|
123
155
|
|
|
124
|
-
const customConfig = await getScopedConfig(db, params)
|
|
156
|
+
const customConfig = await dbCore.getScopedConfig(db, params)
|
|
125
157
|
|
|
126
158
|
if (customConfig) {
|
|
127
159
|
return customConfig
|
|
@@ -146,12 +178,12 @@ async function getSmtpConfiguration(db, workspaceId = null, automation) {
|
|
|
146
178
|
* Checks if a SMTP config exists based on passed in parameters.
|
|
147
179
|
* @return {Promise<boolean>} returns true if there is a configuration that can be used.
|
|
148
180
|
*/
|
|
149
|
-
|
|
181
|
+
export async function isEmailConfigured(workspaceId?: string) {
|
|
150
182
|
// when "testing" or smtp fallback is enabled simply return true
|
|
151
183
|
if (TEST_MODE || env.SMTP_FALLBACK_ENABLED) {
|
|
152
184
|
return true
|
|
153
185
|
}
|
|
154
|
-
const db = getGlobalDB()
|
|
186
|
+
const db = tenancy.getGlobalDB()
|
|
155
187
|
const config = await getSmtpConfiguration(db, workspaceId)
|
|
156
188
|
return config != null
|
|
157
189
|
}
|
|
@@ -161,48 +193,49 @@ exports.isEmailConfigured = async (workspaceId = null) => {
|
|
|
161
193
|
* send an email using it.
|
|
162
194
|
* @param {string} email The email address to send to.
|
|
163
195
|
* @param {string} purpose The purpose of the email being sent (e.g. reset password).
|
|
164
|
-
* @param {
|
|
165
|
-
* @param {object|undefined} user If sending to an existing user the object can be provided, this is used in the context.
|
|
166
|
-
* @param {string|undefined} from If sending from an address that is not what is configured in the SMTP config.
|
|
167
|
-
* @param {string|undefined} contents If sending a custom email then can supply contents which will be added to it.
|
|
168
|
-
* @param {string|undefined} subject A custom subject can be specified if the config one is not desired.
|
|
169
|
-
* @param {object|undefined} info Pass in a structure of information to be stored alongside the invitation.
|
|
170
|
-
* @param {boolean|undefined} disableFallback Prevent email being sent from SMTP fallback to avoid spam.
|
|
196
|
+
* @param {object} opts The options for sending the email.
|
|
171
197
|
* @return {Promise<object>} returns details about the attempt to send email, e.g. if it is successful; based on
|
|
172
198
|
* nodemailer response.
|
|
173
199
|
*/
|
|
174
|
-
|
|
175
|
-
email,
|
|
176
|
-
purpose,
|
|
177
|
-
|
|
178
|
-
)
|
|
179
|
-
const db = getGlobalDB()
|
|
180
|
-
let config =
|
|
200
|
+
export async function sendEmail(
|
|
201
|
+
email: string,
|
|
202
|
+
purpose: EmailTemplatePurpose,
|
|
203
|
+
opts: SendEmailOpts
|
|
204
|
+
) {
|
|
205
|
+
const db = tenancy.getGlobalDB()
|
|
206
|
+
let config =
|
|
207
|
+
(await getSmtpConfiguration(db, opts?.workspaceId, opts?.automation)) || {}
|
|
181
208
|
if (Object.keys(config).length === 0 && !TEST_MODE) {
|
|
182
209
|
throw "Unable to find SMTP configuration."
|
|
183
210
|
}
|
|
184
211
|
const transport = createSMTPTransport(config)
|
|
185
212
|
// if there is a link code needed this will retrieve it
|
|
186
|
-
const code = await getLinkCode(purpose, email, user, info)
|
|
187
|
-
|
|
213
|
+
const code = await getLinkCode(purpose, email, opts.user, opts?.info)
|
|
214
|
+
let context
|
|
215
|
+
if (code) {
|
|
216
|
+
context = await getSettingsTemplateContext(purpose, code)
|
|
217
|
+
}
|
|
188
218
|
|
|
189
|
-
let message = {
|
|
190
|
-
from: from || config.from,
|
|
219
|
+
let message: any = {
|
|
220
|
+
from: opts?.from || config.from,
|
|
191
221
|
html: await buildEmail(purpose, email, context, {
|
|
192
|
-
user,
|
|
193
|
-
contents,
|
|
222
|
+
user: opts?.user,
|
|
223
|
+
contents: opts?.contents,
|
|
194
224
|
}),
|
|
195
225
|
}
|
|
196
226
|
|
|
197
227
|
message = {
|
|
198
228
|
...message,
|
|
199
229
|
to: email,
|
|
200
|
-
cc: cc,
|
|
201
|
-
bcc: bcc,
|
|
230
|
+
cc: opts?.cc,
|
|
231
|
+
bcc: opts?.bcc,
|
|
202
232
|
}
|
|
203
233
|
|
|
204
|
-
if (subject || config.subject) {
|
|
205
|
-
message.subject = await processString(
|
|
234
|
+
if (opts?.subject || config.subject) {
|
|
235
|
+
message.subject = await processString(
|
|
236
|
+
opts?.subject || config.subject,
|
|
237
|
+
context
|
|
238
|
+
)
|
|
206
239
|
}
|
|
207
240
|
const response = await transport.sendMail(message)
|
|
208
241
|
if (TEST_MODE) {
|
|
@@ -216,7 +249,7 @@ exports.sendEmail = async (
|
|
|
216
249
|
* @param {object} config an SMTP configuration - this is based on the nodemailer API.
|
|
217
250
|
* @return {Promise<boolean>} returns true if the configuration is valid.
|
|
218
251
|
*/
|
|
219
|
-
|
|
252
|
+
export async function verifyConfig(config: any) {
|
|
220
253
|
const transport = createSMTPTransport(config)
|
|
221
254
|
await transport.verify()
|
|
222
255
|
}
|
|
@@ -1,36 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
const { newid } = require("@budibase/backend-core/utils")
|
|
1
|
+
import { redis, utils } from "@budibase/backend-core"
|
|
3
2
|
|
|
4
|
-
function getExpirySecondsForDB(db) {
|
|
3
|
+
function getExpirySecondsForDB(db: string) {
|
|
5
4
|
switch (db) {
|
|
6
|
-
case utils.Databases.PW_RESETS:
|
|
5
|
+
case redis.utils.Databases.PW_RESETS:
|
|
7
6
|
// a hour
|
|
8
7
|
return 3600
|
|
9
|
-
case utils.Databases.INVITATIONS:
|
|
8
|
+
case redis.utils.Databases.INVITATIONS:
|
|
10
9
|
// a day
|
|
11
10
|
return 86400
|
|
12
11
|
}
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
let pwResetClient, invitationClient
|
|
14
|
+
let pwResetClient: any, invitationClient: any
|
|
16
15
|
|
|
17
|
-
function getClient(db) {
|
|
16
|
+
function getClient(db: string) {
|
|
18
17
|
switch (db) {
|
|
19
|
-
case utils.Databases.PW_RESETS:
|
|
18
|
+
case redis.utils.Databases.PW_RESETS:
|
|
20
19
|
return pwResetClient
|
|
21
|
-
case utils.Databases.INVITATIONS:
|
|
20
|
+
case redis.utils.Databases.INVITATIONS:
|
|
22
21
|
return invitationClient
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
async function writeACode(db, value) {
|
|
25
|
+
async function writeACode(db: string, value: any) {
|
|
27
26
|
const client = await getClient(db)
|
|
28
|
-
const code = newid()
|
|
27
|
+
const code = utils.newid()
|
|
29
28
|
await client.store(code, value, getExpirySecondsForDB(db))
|
|
30
29
|
return code
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
async function getACode(db, code, deleteCode = true) {
|
|
32
|
+
async function getACode(db: string, code: string, deleteCode = true) {
|
|
34
33
|
const client = await getClient(db)
|
|
35
34
|
const value = await client.get(code)
|
|
36
35
|
if (!value) {
|
|
@@ -42,9 +41,9 @@ async function getACode(db, code, deleteCode = true) {
|
|
|
42
41
|
return value
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
pwResetClient = new Client(utils.Databases.PW_RESETS)
|
|
47
|
-
invitationClient = new Client(utils.Databases.INVITATIONS)
|
|
44
|
+
export async function init() {
|
|
45
|
+
pwResetClient = new redis.Client(redis.utils.Databases.PW_RESETS)
|
|
46
|
+
invitationClient = new redis.Client(redis.utils.Databases.INVITATIONS)
|
|
48
47
|
await pwResetClient.init()
|
|
49
48
|
await invitationClient.init()
|
|
50
49
|
}
|
|
@@ -52,7 +51,7 @@ exports.init = async () => {
|
|
|
52
51
|
/**
|
|
53
52
|
* make sure redis connection is closed.
|
|
54
53
|
*/
|
|
55
|
-
|
|
54
|
+
export async function shutdown() {
|
|
56
55
|
if (pwResetClient) await pwResetClient.finish()
|
|
57
56
|
if (invitationClient) await invitationClient.finish()
|
|
58
57
|
console.log("Redis shutdown")
|
|
@@ -65,8 +64,8 @@ exports.shutdown = async () => {
|
|
|
65
64
|
* @param {object} info Info about the user/the reset process.
|
|
66
65
|
* @return {Promise<string>} returns the code that was stored to redis.
|
|
67
66
|
*/
|
|
68
|
-
|
|
69
|
-
return writeACode(utils.Databases.PW_RESETS, { userId, info })
|
|
67
|
+
export async function getResetPasswordCode(userId: string, info: any) {
|
|
68
|
+
return writeACode(redis.utils.Databases.PW_RESETS, { userId, info })
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
/**
|
|
@@ -75,9 +74,12 @@ exports.getResetPasswordCode = async (userId, info) => {
|
|
|
75
74
|
* @param {boolean} deleteCode If the code is used/finished with this will delete it - defaults to true.
|
|
76
75
|
* @return {Promise<string>} returns the user ID if it is found
|
|
77
76
|
*/
|
|
78
|
-
|
|
77
|
+
export async function checkResetPasswordCode(
|
|
78
|
+
resetCode: string,
|
|
79
|
+
deleteCode = true
|
|
80
|
+
) {
|
|
79
81
|
try {
|
|
80
|
-
return getACode(utils.Databases.PW_RESETS, resetCode, deleteCode)
|
|
82
|
+
return getACode(redis.utils.Databases.PW_RESETS, resetCode, deleteCode)
|
|
81
83
|
} catch (err) {
|
|
82
84
|
throw "Provided information is not valid, cannot reset password - please try again."
|
|
83
85
|
}
|
|
@@ -89,8 +91,8 @@ exports.checkResetPasswordCode = async (resetCode, deleteCode = true) => {
|
|
|
89
91
|
* @param {object|null} info Information to be carried along with the invitation.
|
|
90
92
|
* @return {Promise<string>} returns the code that was stored to redis.
|
|
91
93
|
*/
|
|
92
|
-
|
|
93
|
-
return writeACode(utils.Databases.INVITATIONS, { email, info })
|
|
94
|
+
export async function getInviteCode(email: string, info: any) {
|
|
95
|
+
return writeACode(redis.utils.Databases.INVITATIONS, { email, info })
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
/**
|
|
@@ -99,9 +101,12 @@ exports.getInviteCode = async (email, info) => {
|
|
|
99
101
|
* @param {boolean} deleteCode whether or not the code should be deleted after retrieval - defaults to true.
|
|
100
102
|
* @return {Promise<object>} If the code is valid then an email address will be returned.
|
|
101
103
|
*/
|
|
102
|
-
|
|
104
|
+
export async function checkInviteCode(
|
|
105
|
+
inviteCode: string,
|
|
106
|
+
deleteCode: boolean = true
|
|
107
|
+
) {
|
|
103
108
|
try {
|
|
104
|
-
return getACode(utils.Databases.INVITATIONS, inviteCode, deleteCode)
|
|
109
|
+
return getACode(redis.utils.Databases.INVITATIONS, inviteCode, deleteCode)
|
|
105
110
|
} catch (err) {
|
|
106
111
|
throw "Invitation is not valid or has expired, please request a new one."
|
|
107
112
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { db as dbCore, tenancy } from "@budibase/backend-core"
|
|
2
|
+
import {
|
|
3
|
+
Config,
|
|
4
|
+
InternalTemplateBinding,
|
|
5
|
+
LOGO_URL,
|
|
6
|
+
EmailTemplatePurpose,
|
|
7
|
+
} from "../constants"
|
|
8
|
+
import { checkSlashesInUrl } from "./index"
|
|
9
|
+
const BASE_COMPANY = "Budibase"
|
|
10
|
+
|
|
11
|
+
export async function getSettingsTemplateContext(
|
|
12
|
+
purpose: EmailTemplatePurpose,
|
|
13
|
+
code?: string
|
|
14
|
+
) {
|
|
15
|
+
const db = tenancy.getGlobalDB()
|
|
16
|
+
// TODO: use more granular settings in the future if required
|
|
17
|
+
let settings =
|
|
18
|
+
(await dbCore.getScopedConfig(db, { type: Config.SETTINGS })) || {}
|
|
19
|
+
const URL = settings.platformUrl
|
|
20
|
+
const context: any = {
|
|
21
|
+
[InternalTemplateBinding.LOGO_URL]:
|
|
22
|
+
checkSlashesInUrl(`${URL}/${settings.logoUrl}`) || LOGO_URL,
|
|
23
|
+
[InternalTemplateBinding.PLATFORM_URL]: URL,
|
|
24
|
+
[InternalTemplateBinding.COMPANY]: settings.company || BASE_COMPANY,
|
|
25
|
+
[InternalTemplateBinding.DOCS_URL]:
|
|
26
|
+
settings.docsUrl || "https://docs.budibase.com/",
|
|
27
|
+
[InternalTemplateBinding.LOGIN_URL]: checkSlashesInUrl(
|
|
28
|
+
tenancy.addTenantToUrl(`${URL}/login`)
|
|
29
|
+
),
|
|
30
|
+
[InternalTemplateBinding.CURRENT_DATE]: new Date().toISOString(),
|
|
31
|
+
[InternalTemplateBinding.CURRENT_YEAR]: new Date().getFullYear(),
|
|
32
|
+
}
|
|
33
|
+
// attach purpose specific context
|
|
34
|
+
switch (purpose) {
|
|
35
|
+
case EmailTemplatePurpose.PASSWORD_RECOVERY:
|
|
36
|
+
context[InternalTemplateBinding.RESET_CODE] = code
|
|
37
|
+
context[InternalTemplateBinding.RESET_URL] = checkSlashesInUrl(
|
|
38
|
+
tenancy.addTenantToUrl(`${URL}/builder/auth/reset?code=${code}`)
|
|
39
|
+
)
|
|
40
|
+
break
|
|
41
|
+
case EmailTemplatePurpose.INVITATION:
|
|
42
|
+
context[InternalTemplateBinding.INVITE_CODE] = code
|
|
43
|
+
context[InternalTemplateBinding.INVITE_URL] = checkSlashesInUrl(
|
|
44
|
+
tenancy.addTenantToUrl(`${URL}/builder/invite?code=${code}`)
|
|
45
|
+
)
|
|
46
|
+
break
|
|
47
|
+
}
|
|
48
|
+
return context
|
|
49
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { tenancy, db as dbCore } from "@budibase/backend-core"
|
|
2
|
+
|
|
3
|
+
export async function checkAnyUserExists() {
|
|
4
|
+
try {
|
|
5
|
+
const db = tenancy.getGlobalDB()
|
|
6
|
+
const users = await db.allDocs(
|
|
7
|
+
dbCore.getGlobalUserParams(null, {
|
|
8
|
+
include_docs: true,
|
|
9
|
+
limit: 1,
|
|
10
|
+
})
|
|
11
|
+
)
|
|
12
|
+
return users && users.rows.length >= 1
|
|
13
|
+
} catch (err) {
|
|
14
|
+
throw new Error("Unable to retrieve user list")
|
|
15
|
+
}
|
|
16
|
+
}
|
package/tsconfig.build.json
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
"target": "es6",
|
|
4
4
|
"module": "commonjs",
|
|
5
5
|
"lib": ["es2020"],
|
|
6
|
-
"allowJs": true,
|
|
7
6
|
"strict": true,
|
|
8
7
|
"noImplicitAny": true,
|
|
9
8
|
"esModuleInterop": true,
|
|
@@ -20,7 +19,7 @@
|
|
|
20
19
|
"node_modules",
|
|
21
20
|
"dist",
|
|
22
21
|
"src/tests",
|
|
23
|
-
"**/*.spec.
|
|
24
|
-
"**/*.spec.
|
|
22
|
+
"**/*.spec.js",
|
|
23
|
+
"**/*.spec.ts"
|
|
25
24
|
]
|
|
26
25
|
}
|