@budibase/worker 2.3.18-alpha.3 → 2.3.18-alpha.30
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/jest.config.ts +1 -1
- package/package.json +8 -7
- package/scripts/test.sh +12 -0
- package/src/api/controllers/global/auth.ts +38 -47
- package/src/api/controllers/global/configs.ts +150 -142
- package/src/api/controllers/global/users.ts +149 -10
- package/src/api/routes/global/configs.ts +6 -12
- package/src/api/routes/global/tests/auditLogs.spec.ts +111 -0
- package/src/api/routes/global/tests/auth.spec.ts +5 -15
- package/src/api/routes/global/tests/configs.spec.ts +88 -60
- package/src/api/routes/global/tests/realEmail.spec.ts +1 -0
- package/src/api/routes/global/tests/users.spec.ts +5 -1
- package/src/api/routes/global/users.ts +19 -14
- package/src/api/routes/index.ts +3 -0
- package/src/db/index.ts +8 -2
- package/src/environment.ts +1 -11
- package/src/index.ts +18 -2
- package/src/sdk/auth/auth.ts +2 -2
- package/src/sdk/users/tests/users.spec.ts +31 -7
- package/src/sdk/users/users.ts +37 -11
- package/src/tests/api/auditLogs.ts +26 -0
- package/src/tests/api/auth.ts +7 -5
- package/src/tests/api/configs.ts +8 -0
- package/src/tests/api/email.ts +1 -0
- package/src/tests/api/index.ts +3 -0
- package/src/tests/structures/configs.ts +20 -13
- package/src/utilities/email.ts +19 -65
- package/src/utilities/redis.ts +40 -2
- package/src/utilities/templates.ts +4 -9
package/src/utilities/email.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import env from "../environment"
|
|
2
|
-
import { EmailTemplatePurpose, TemplateType
|
|
2
|
+
import { EmailTemplatePurpose, TemplateType } from "../constants"
|
|
3
3
|
import { getTemplateByPurpose } from "../constants/templates"
|
|
4
4
|
import { getSettingsTemplateContext } from "./templates"
|
|
5
5
|
import { processString } from "@budibase/string-templates"
|
|
6
6
|
import { getResetPasswordCode, getInviteCode } from "./redis"
|
|
7
|
-
import { User,
|
|
8
|
-
import {
|
|
7
|
+
import { User, SMTPInnerConfig } from "@budibase/types"
|
|
8
|
+
import { configs } from "@budibase/backend-core"
|
|
9
9
|
const nodemailer = require("nodemailer")
|
|
10
10
|
|
|
11
11
|
type SendEmailOpts = {
|
|
@@ -36,24 +36,24 @@ const FULL_EMAIL_PURPOSES = [
|
|
|
36
36
|
EmailTemplatePurpose.CUSTOM,
|
|
37
37
|
]
|
|
38
38
|
|
|
39
|
-
function createSMTPTransport(config
|
|
39
|
+
function createSMTPTransport(config?: SMTPInnerConfig) {
|
|
40
40
|
let options: any
|
|
41
|
-
let secure = config
|
|
41
|
+
let secure = config?.secure
|
|
42
42
|
// default it if not specified
|
|
43
43
|
if (secure == null) {
|
|
44
|
-
secure = config
|
|
44
|
+
secure = config?.port === 465
|
|
45
45
|
}
|
|
46
46
|
if (!TEST_MODE) {
|
|
47
47
|
options = {
|
|
48
|
-
port: config
|
|
49
|
-
host: config
|
|
48
|
+
port: config?.port,
|
|
49
|
+
host: config?.host,
|
|
50
50
|
secure: secure,
|
|
51
|
-
auth: config
|
|
51
|
+
auth: config?.auth,
|
|
52
52
|
}
|
|
53
53
|
options.tls = {
|
|
54
54
|
rejectUnauthorized: false,
|
|
55
55
|
}
|
|
56
|
-
if (config
|
|
56
|
+
if (config?.connectionTimeout) {
|
|
57
57
|
options.connectionTimeout = config.connectionTimeout
|
|
58
58
|
}
|
|
59
59
|
} else {
|
|
@@ -134,57 +134,16 @@ async function buildEmail(
|
|
|
134
134
|
})
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
/**
|
|
138
|
-
* Utility function for finding most valid SMTP configuration.
|
|
139
|
-
* @param {object} db The CouchDB database which is to be looked up within.
|
|
140
|
-
* @param {string|null} workspaceId If using finer grain control of configs a workspace can be used.
|
|
141
|
-
* @param {boolean|null} automation Whether or not the configuration is being fetched for an email automation.
|
|
142
|
-
* @return {Promise<object|null>} returns the SMTP configuration if it exists
|
|
143
|
-
*/
|
|
144
|
-
async function getSmtpConfiguration(
|
|
145
|
-
db: Database,
|
|
146
|
-
workspaceId?: string,
|
|
147
|
-
automation?: boolean
|
|
148
|
-
) {
|
|
149
|
-
const params: any = {
|
|
150
|
-
type: Config.SMTP,
|
|
151
|
-
}
|
|
152
|
-
if (workspaceId) {
|
|
153
|
-
params.workspace = workspaceId
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const customConfig = await dbCore.getScopedConfig(db, params)
|
|
157
|
-
|
|
158
|
-
if (customConfig) {
|
|
159
|
-
return customConfig
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Use an SMTP fallback configuration from env variables
|
|
163
|
-
if (!automation && env.SMTP_FALLBACK_ENABLED) {
|
|
164
|
-
return {
|
|
165
|
-
port: env.SMTP_PORT,
|
|
166
|
-
host: env.SMTP_HOST,
|
|
167
|
-
secure: false,
|
|
168
|
-
from: env.SMTP_FROM_ADDRESS,
|
|
169
|
-
auth: {
|
|
170
|
-
user: env.SMTP_USER,
|
|
171
|
-
pass: env.SMTP_PASSWORD,
|
|
172
|
-
},
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
137
|
/**
|
|
178
138
|
* Checks if a SMTP config exists based on passed in parameters.
|
|
179
139
|
* @return {Promise<boolean>} returns true if there is a configuration that can be used.
|
|
180
140
|
*/
|
|
181
|
-
export async function isEmailConfigured(
|
|
141
|
+
export async function isEmailConfigured() {
|
|
182
142
|
// when "testing" or smtp fallback is enabled simply return true
|
|
183
143
|
if (TEST_MODE || env.SMTP_FALLBACK_ENABLED) {
|
|
184
144
|
return true
|
|
185
145
|
}
|
|
186
|
-
const
|
|
187
|
-
const config = await getSmtpConfiguration(db, workspaceId)
|
|
146
|
+
const config = await configs.getSMTPConfig()
|
|
188
147
|
return config != null
|
|
189
148
|
}
|
|
190
149
|
|
|
@@ -202,22 +161,17 @@ export async function sendEmail(
|
|
|
202
161
|
purpose: EmailTemplatePurpose,
|
|
203
162
|
opts: SendEmailOpts
|
|
204
163
|
) {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
(await getSmtpConfiguration(db, opts?.workspaceId, opts?.automation)) || {}
|
|
208
|
-
if (Object.keys(config).length === 0 && !TEST_MODE) {
|
|
164
|
+
const config = await configs.getSMTPConfig(opts?.automation)
|
|
165
|
+
if (!config && !TEST_MODE) {
|
|
209
166
|
throw "Unable to find SMTP configuration."
|
|
210
167
|
}
|
|
211
168
|
const transport = createSMTPTransport(config)
|
|
212
169
|
// if there is a link code needed this will retrieve it
|
|
213
170
|
const code = await getLinkCode(purpose, email, opts.user, opts?.info)
|
|
214
|
-
let context
|
|
215
|
-
if (code) {
|
|
216
|
-
context = await getSettingsTemplateContext(purpose, code)
|
|
217
|
-
}
|
|
171
|
+
let context = await getSettingsTemplateContext(purpose, code)
|
|
218
172
|
|
|
219
173
|
let message: any = {
|
|
220
|
-
from: opts?.from || config
|
|
174
|
+
from: opts?.from || config?.from,
|
|
221
175
|
html: await buildEmail(purpose, email, context, {
|
|
222
176
|
user: opts?.user,
|
|
223
177
|
contents: opts?.contents,
|
|
@@ -231,9 +185,9 @@ export async function sendEmail(
|
|
|
231
185
|
bcc: opts?.bcc,
|
|
232
186
|
}
|
|
233
187
|
|
|
234
|
-
if (opts?.subject || config
|
|
188
|
+
if (opts?.subject || config?.subject) {
|
|
235
189
|
message.subject = await processString(
|
|
236
|
-
opts?.subject || config
|
|
190
|
+
(opts?.subject || config?.subject) as string,
|
|
237
191
|
context
|
|
238
192
|
)
|
|
239
193
|
}
|
|
@@ -249,7 +203,7 @@ export async function sendEmail(
|
|
|
249
203
|
* @param {object} config an SMTP configuration - this is based on the nodemailer API.
|
|
250
204
|
* @return {Promise<boolean>} returns true if the configuration is valid.
|
|
251
205
|
*/
|
|
252
|
-
export async function verifyConfig(config:
|
|
206
|
+
export async function verifyConfig(config: SMTPInnerConfig) {
|
|
253
207
|
const transport = createSMTPTransport(config)
|
|
254
208
|
await transport.verify()
|
|
255
209
|
}
|
package/src/utilities/redis.ts
CHANGED
|
@@ -7,7 +7,7 @@ function getExpirySecondsForDB(db: string) {
|
|
|
7
7
|
return 3600
|
|
8
8
|
case redis.utils.Databases.INVITATIONS:
|
|
9
9
|
// a day
|
|
10
|
-
return
|
|
10
|
+
return 604800
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -29,11 +29,25 @@ async function writeACode(db: string, value: any) {
|
|
|
29
29
|
return code
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
async function updateACode(db: string, code: string, value: any) {
|
|
33
|
+
const client = await getClient(db)
|
|
34
|
+
await client.store(code, value, getExpirySecondsForDB(db))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Given an invite code and invite body, allow the update an existing/valid invite in redis
|
|
39
|
+
* @param {string} inviteCode The invite code for an invite in redis
|
|
40
|
+
* @param {object} value The body of the updated user invitation
|
|
41
|
+
*/
|
|
42
|
+
export async function updateInviteCode(inviteCode: string, value: string) {
|
|
43
|
+
await updateACode(redis.utils.Databases.INVITATIONS, inviteCode, value)
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
async function getACode(db: string, code: string, deleteCode = true) {
|
|
33
47
|
const client = await getClient(db)
|
|
34
48
|
const value = await client.get(code)
|
|
35
49
|
if (!value) {
|
|
36
|
-
throw "Invalid code."
|
|
50
|
+
throw new Error("Invalid code.")
|
|
37
51
|
}
|
|
38
52
|
if (deleteCode) {
|
|
39
53
|
await client.delete(code)
|
|
@@ -113,3 +127,27 @@ export async function checkInviteCode(
|
|
|
113
127
|
throw "Invitation is not valid or has expired, please request a new one."
|
|
114
128
|
}
|
|
115
129
|
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
Get all currently available user invitations.
|
|
133
|
+
@return {Object[]} A list of all objects containing invite metadata
|
|
134
|
+
**/
|
|
135
|
+
export async function getInviteCodes(tenantIds?: string[]) {
|
|
136
|
+
const client = await getClient(redis.utils.Databases.INVITATIONS)
|
|
137
|
+
const invites: any[] = await client.scan()
|
|
138
|
+
|
|
139
|
+
const results = invites.map(invite => {
|
|
140
|
+
return {
|
|
141
|
+
...invite.value,
|
|
142
|
+
code: invite.key,
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
return results.reduce((acc, invite) => {
|
|
146
|
+
if (tenantIds?.length && tenantIds.includes(invite.info.tenantId)) {
|
|
147
|
+
acc.push(invite)
|
|
148
|
+
} else {
|
|
149
|
+
acc.push(invite)
|
|
150
|
+
}
|
|
151
|
+
return acc
|
|
152
|
+
}, [])
|
|
153
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { tenancy, configs } from "@budibase/backend-core"
|
|
2
2
|
import {
|
|
3
|
-
Config,
|
|
4
3
|
InternalTemplateBinding,
|
|
5
4
|
LOGO_URL,
|
|
6
5
|
EmailTemplatePurpose,
|
|
@@ -10,20 +9,16 @@ const BASE_COMPANY = "Budibase"
|
|
|
10
9
|
|
|
11
10
|
export async function getSettingsTemplateContext(
|
|
12
11
|
purpose: EmailTemplatePurpose,
|
|
13
|
-
code?: string
|
|
12
|
+
code?: string | null
|
|
14
13
|
) {
|
|
15
|
-
|
|
16
|
-
// TODO: use more granular settings in the future if required
|
|
17
|
-
let settings =
|
|
18
|
-
(await dbCore.getScopedConfig(db, { type: Config.SETTINGS })) || {}
|
|
14
|
+
let settings = await configs.getSettingsConfig()
|
|
19
15
|
const URL = settings.platformUrl
|
|
20
16
|
const context: any = {
|
|
21
17
|
[InternalTemplateBinding.LOGO_URL]:
|
|
22
18
|
checkSlashesInUrl(`${URL}/${settings.logoUrl}`) || LOGO_URL,
|
|
23
19
|
[InternalTemplateBinding.PLATFORM_URL]: URL,
|
|
24
20
|
[InternalTemplateBinding.COMPANY]: settings.company || BASE_COMPANY,
|
|
25
|
-
[InternalTemplateBinding.DOCS_URL]:
|
|
26
|
-
settings.docsUrl || "https://docs.budibase.com/",
|
|
21
|
+
[InternalTemplateBinding.DOCS_URL]: "https://docs.budibase.com/",
|
|
27
22
|
[InternalTemplateBinding.LOGIN_URL]: checkSlashesInUrl(
|
|
28
23
|
tenancy.addTenantToUrl(`${URL}/login`)
|
|
29
24
|
),
|