@budibase/worker 2.4.40 → 2.4.42-alpha.0

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/worker",
3
3
  "email": "hi@budibase.com",
4
- "version": "2.4.40",
4
+ "version": "2.4.42-alpha.0",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -36,10 +36,10 @@
36
36
  "author": "Budibase",
37
37
  "license": "GPL-3.0",
38
38
  "dependencies": {
39
- "@budibase/backend-core": "^2.4.40",
40
- "@budibase/pro": "2.4.39",
41
- "@budibase/string-templates": "^2.4.40",
42
- "@budibase/types": "^2.4.40",
39
+ "@budibase/backend-core": "2.4.42-alpha.0",
40
+ "@budibase/pro": "2.4.40",
41
+ "@budibase/string-templates": "2.4.42-alpha.0",
42
+ "@budibase/types": "2.4.42-alpha.0",
43
43
  "@koa/router": "8.0.8",
44
44
  "@sentry/node": "6.17.7",
45
45
  "@techpass/passport-openidconnect": "0.3.2",
@@ -101,5 +101,5 @@
101
101
  "typescript": "4.7.3",
102
102
  "update-dotenv": "1.1.1"
103
103
  },
104
- "gitHead": "8d1f12dfbecf020366571d81c5dc8db9f968afc7"
104
+ "gitHead": "b31f1ba51b6d82958938a9acc7f8bf1ccef104ac"
105
105
  }
package/scripts/test.sh CHANGED
@@ -3,10 +3,10 @@
3
3
  if [[ -n $CI ]]
4
4
  then
5
5
  # --runInBand performs better in ci where resources are limited
6
- echo "jest --coverage --runInBand"
7
- jest --coverage --runInBand
6
+ echo "jest --coverage --runInBand --forceExit"
7
+ jest --coverage --runInBand --forceExit
8
8
  else
9
9
  # --maxWorkers performs better in development
10
10
  echo "jest --coverage --maxWorkers=2"
11
11
  jest --coverage --maxWorkers=2
12
- fi
12
+ fi
@@ -11,6 +11,7 @@ import {
11
11
  tenancy,
12
12
  } from "@budibase/backend-core"
13
13
  import { checkAnyUserExists } from "../../../utilities/users"
14
+ import { getLicensedConfig } from "../../../utilities/configs"
14
15
  import {
15
16
  Config,
16
17
  ConfigType,
@@ -211,6 +212,38 @@ export async function save(ctx: UserCtx<Config>) {
211
212
  ctx.throw(400, err)
212
213
  }
213
214
 
215
+ // Ignore branding changes if the license does not permit it
216
+ // Favicon and Logo Url are excluded.
217
+ try {
218
+ const brandingEnabled = await pro.features.isBrandingEnabled()
219
+ if (existingConfig?.config && !brandingEnabled) {
220
+ const {
221
+ emailBrandingEnabled,
222
+ testimonialsEnabled,
223
+ platformTitle,
224
+ metaDescription,
225
+ loginHeading,
226
+ loginButton,
227
+ metaImageUrl,
228
+ metaTitle,
229
+ } = existingConfig.config
230
+
231
+ body.config = {
232
+ ...body.config,
233
+ emailBrandingEnabled,
234
+ testimonialsEnabled,
235
+ platformTitle,
236
+ metaDescription,
237
+ loginHeading,
238
+ loginButton,
239
+ metaImageUrl,
240
+ metaTitle,
241
+ }
242
+ }
243
+ } catch (e) {
244
+ console.error("There was an issue retrieving the license", e)
245
+ }
246
+
214
247
  try {
215
248
  body._id = configs.generateConfigID(type)
216
249
  const response = await configs.save(body)
@@ -276,6 +309,9 @@ export async function publicSettings(
276
309
  // settings
277
310
  const configDoc = await configs.getSettingsConfigDoc()
278
311
  const config = configDoc.config
312
+
313
+ const branding = await pro.branding.getBrandingConfig(config)
314
+
279
315
  // enrich the logo url - empty url means deleted
280
316
  if (config.logoUrl && config.logoUrl !== "") {
281
317
  config.logoUrl = objectStore.getGlobalFileUrl(
@@ -285,6 +321,15 @@ export async function publicSettings(
285
321
  )
286
322
  }
287
323
 
324
+ if (branding.faviconUrl && branding.faviconUrl !== "") {
325
+ // @ts-ignore
326
+ config.faviconUrl = objectStore.getGlobalFileUrl(
327
+ "settings",
328
+ "faviconUrl",
329
+ branding.faviconUrl
330
+ )
331
+ }
332
+
288
333
  // google
289
334
  const googleConfig = await configs.getGoogleConfig()
290
335
  const preActivated = googleConfig && googleConfig.activated == null
@@ -305,6 +350,7 @@ export async function publicSettings(
305
350
  _rev: configDoc._rev,
306
351
  config: {
307
352
  ...config,
353
+ ...branding,
308
354
  google,
309
355
  oidc,
310
356
  isSSOEnforced,
@@ -3,7 +3,6 @@ import {
3
3
  getInviteCodes,
4
4
  updateInviteCode,
5
5
  } from "../../../utilities/redis"
6
- // import sdk from "../../../sdk"
7
6
  import * as userSdk from "../../../sdk/users"
8
7
  import env from "../../../environment"
9
8
  import {
@@ -26,11 +25,11 @@ import {
26
25
  import {
27
26
  accounts,
28
27
  cache,
29
- errors,
30
28
  events,
31
29
  migrations,
32
30
  tenancy,
33
31
  platform,
32
+ ErrorCode,
34
33
  } from "@budibase/backend-core"
35
34
  import { checkAnyUserExists } from "../../../utilities/users"
36
35
  import { isEmailConfigured } from "../../../utilities/email"
@@ -421,7 +420,7 @@ export const inviteAccept = async (
421
420
  email: user.email,
422
421
  }
423
422
  } catch (err: any) {
424
- if (err.code === errors.codes.USAGE_LIMIT_EXCEEDED) {
423
+ if (err.code === ErrorCode.USAGE_LIMIT_EXCEEDED) {
425
424
  // explicitly re-throw limit exceeded errors
426
425
  ctx.throw(400, err)
427
426
  }
@@ -286,6 +286,7 @@ describe("configs", () => {
286
286
  type: "settings",
287
287
  config: {
288
288
  company: "Budibase",
289
+ emailBrandingEnabled: true,
289
290
  logoUrl: "",
290
291
  analyticsEnabled: false,
291
292
  google: false,
@@ -294,6 +295,7 @@ describe("configs", () => {
294
295
  oidc: false,
295
296
  oidcCallbackUrl: `http://localhost:10000/api/global/auth/${config.tenantId}/oidc/callback`,
296
297
  platformUrl: "http://localhost:10000",
298
+ testimonialsEnabled: true,
297
299
  },
298
300
  }
299
301
  delete body._rev
@@ -20,6 +20,7 @@ export enum TemplateType {
20
20
  }
21
21
 
22
22
  export enum EmailTemplatePurpose {
23
+ CORE = "core",
23
24
  BASE = "base",
24
25
  PASSWORD_RECOVERY = "password_recovery",
25
26
  INVITATION = "invitation",
@@ -0,0 +1,33 @@
1
+ {{#if enableEmailBranding}}
2
+ <tr>
3
+ <td cellpadding="0" cellspacing="0">
4
+ <table
5
+ align="center"
6
+ width="570"
7
+ cellpadding="0"
8
+ cellspacing="0"
9
+ role="presentation"
10
+ >
11
+ <tbody>
12
+ <tr>
13
+ <td
14
+ style="padding:0 35px; padding-top: 15px;"
15
+ cellpadding="0"
16
+ cellspacing="0"
17
+ >
18
+ <img
19
+ width="32px"
20
+ style="margin-right:16px; vertical-align: middle;"
21
+ alt="Budibase Logo"
22
+ src="https://i.imgur.com/Xhdt1YP.png"
23
+ />
24
+ <strong style="vertical-align: middle; font-size: 1.1em">
25
+ Budibase
26
+ </strong>
27
+ </td>
28
+ </tr>
29
+ </tbody>
30
+ </table>
31
+ </td>
32
+ </tr>
33
+ {{/if}}
@@ -21,6 +21,7 @@ export const EmailTemplates = {
21
21
  join(__dirname, "welcome.hbs")
22
22
  ),
23
23
  [EmailTemplatePurpose.CUSTOM]: readStaticFile(join(__dirname, "custom.hbs")),
24
+ [EmailTemplatePurpose.CORE]: readStaticFile(join(__dirname, "core.hbs")),
24
25
  }
25
26
 
26
27
  export function addBaseTemplates(templates: Template[], type?: string) {
package/src/index.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  middleware,
22
22
  queue,
23
23
  env as coreEnv,
24
+ timers,
24
25
  } from "@budibase/backend-core"
25
26
  db.init()
26
27
  import Koa from "koa"
@@ -91,6 +92,7 @@ server.on("close", async () => {
91
92
  }
92
93
  shuttingDown = true
93
94
  console.log("Server Closed")
95
+ timers.cleanup()
94
96
  await redis.shutdown()
95
97
  await events.shutdown()
96
98
  await queue.shutdown()
@@ -106,6 +106,8 @@ class TestConfiguration {
106
106
  async afterAll() {
107
107
  if (this.server) {
108
108
  await this.server.close()
109
+ } else {
110
+ await require("../index").default.close()
109
111
  }
110
112
  }
111
113
 
@@ -10,3 +10,4 @@ process.env.MINIO_SECRET_KEY = "test"
10
10
  process.env.PLATFORM_URL = "http://localhost:10000"
11
11
  process.env.INTERNAL_API_KEY = "tet"
12
12
  process.env.DISABLE_ACCOUNT_PORTAL = "0"
13
+ process.env.REDIS_PASSWORD = "budibase"
@@ -2,7 +2,7 @@ import "./logging"
2
2
 
3
3
  import { mocks, testContainerUtils } from "@budibase/backend-core/tests"
4
4
  import env from "../environment"
5
- import { env as coreEnv } from "@budibase/backend-core"
5
+ import { env as coreEnv, timers } from "@budibase/backend-core"
6
6
 
7
7
  // must explicitly enable fetch mock
8
8
  mocks.fetch.enable()
@@ -21,3 +21,7 @@ if (!process.env.CI) {
21
21
  }
22
22
 
23
23
  testContainerUtils.setupEnv(env, coreEnv)
24
+
25
+ afterAll(() => {
26
+ timers.cleanup()
27
+ })
@@ -0,0 +1,27 @@
1
+ import * as pro from "@budibase/pro"
2
+
3
+ export async function getLicensedConfig() {
4
+ let licensedConfig: object = {}
5
+ const defaults = {
6
+ emailBrandingEnabled: true,
7
+ testimonialsEnabled: true,
8
+ platformTitle: undefined,
9
+ metaDescription: undefined,
10
+ loginHeading: undefined,
11
+ loginButton: undefined,
12
+ metaImageUrl: undefined,
13
+ metaTitle: undefined,
14
+ }
15
+
16
+ try {
17
+ // License/Feature Checks
18
+ const enabled = await pro.features.isBrandingEnabled()
19
+ if (!enabled) {
20
+ licensedConfig = { ...defaults }
21
+ }
22
+ } catch (e) {
23
+ licensedConfig = { ...defaults }
24
+ console.info("Could not retrieve license", e)
25
+ }
26
+ return licensedConfig
27
+ }
@@ -1,6 +1,6 @@
1
1
  import env from "../environment"
2
2
  import { EmailTemplatePurpose, TemplateType } from "../constants"
3
- import { getTemplateByPurpose } from "../constants/templates"
3
+ import { getTemplateByPurpose, EmailTemplates } from "../constants/templates"
4
4
  import { getSettingsTemplateContext } from "./templates"
5
5
  import { processString } from "@budibase/string-templates"
6
6
  import { getResetPasswordCode, getInviteCode } from "./redis"
@@ -109,11 +109,16 @@ async function buildEmail(
109
109
  getTemplateByPurpose(TYPE, EmailTemplatePurpose.BASE),
110
110
  getTemplateByPurpose(TYPE, purpose),
111
111
  ])
112
- if (!base || !body) {
112
+
113
+ // Change from branding to core
114
+ let core = EmailTemplates[EmailTemplatePurpose.CORE]
115
+
116
+ if (!base || !body || !core) {
113
117
  throw "Unable to build email, missing base components"
114
118
  }
115
119
  base = base.contents
116
120
  body = body.contents
121
+
117
122
  let name = user ? user.name : undefined
118
123
  if (user && !name && user.firstName) {
119
124
  name = user.lastName ? `${user.firstName} ${user.lastName}` : user.firstName
@@ -126,8 +131,12 @@ async function buildEmail(
126
131
  user: user || {},
127
132
  }
128
133
 
129
- body = await processString(body, context)
130
- // this should now be the complete email HTML
134
+ // Prepend the core template
135
+ const fullBody = core + body
136
+
137
+ body = await processString(fullBody, context)
138
+
139
+ // this should now be the core email HTML
131
140
  return processString(base, {
132
141
  ...context,
133
142
  body,
@@ -1,17 +1,22 @@
1
1
  import { tenancy, configs } from "@budibase/backend-core"
2
+ import { SettingsInnerConfig } from "@budibase/types"
2
3
  import {
3
4
  InternalTemplateBinding,
4
5
  LOGO_URL,
5
6
  EmailTemplatePurpose,
6
7
  } from "../constants"
7
8
  import { checkSlashesInUrl } from "./index"
9
+ import { getLicensedConfig } from "./configs"
10
+
8
11
  const BASE_COMPANY = "Budibase"
12
+ import * as pro from "@budibase/pro"
9
13
 
10
14
  export async function getSettingsTemplateContext(
11
15
  purpose: EmailTemplatePurpose,
12
16
  code?: string | null
13
17
  ) {
14
- let settings = await configs.getSettingsConfig()
18
+ const settings = await configs.getSettingsConfig()
19
+ const branding = await pro.branding.getBrandingConfig(settings)
15
20
  const URL = settings.platformUrl
16
21
  const context: any = {
17
22
  [InternalTemplateBinding.LOGO_URL]:
@@ -25,6 +30,9 @@ export async function getSettingsTemplateContext(
25
30
  [InternalTemplateBinding.CURRENT_DATE]: new Date().toISOString(),
26
31
  [InternalTemplateBinding.CURRENT_YEAR]: new Date().getFullYear(),
27
32
  }
33
+
34
+ context["enableEmailBranding"] = branding.emailBrandingEnabled === true
35
+
28
36
  // attach purpose specific context
29
37
  switch (purpose) {
30
38
  case EmailTemplatePurpose.PASSWORD_RECOVERY: