Some worker typescript conversions.

This commit is contained in:
mike12345567 2022-11-16 18:13:34 +00:00
parent 29dd98a7fc
commit cdc25d7032
5 changed files with 192 additions and 161 deletions

View File

@ -1,97 +1,97 @@
const { Config } = require("@budibase/backend-core/constants") import { constants } from "@budibase/backend-core"
exports.LOGO_URL = export const LOGO_URL =
"https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg" "https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg"
exports.UserStatus = { export enum UserStatus {
ACTIVE: "active", ACTIVE = "active",
INACTIVE: "inactive", INACTIVE = "inactive",
} }
exports.Config = Config export const Config = constants.Config
exports.ConfigUploads = { export enum ConfigUpload {
LOGO: "logo", LOGO = "logo",
OIDC_LOGO: "oidc_logo", OIDC_LOGO = "oidc_logo",
} }
const TemplateTypes = { export enum TemplateType {
EMAIL: "email", EMAIL = "email",
} }
const EmailTemplatePurpose = { export enum EmailTemplatePurpose {
BASE: "base", BASE = "base",
PASSWORD_RECOVERY: "password_recovery", PASSWORD_RECOVERY = "password_recovery",
INVITATION: "invitation", INVITATION = "invitation",
WELCOME: "welcome", WELCOME = "welcome",
CUSTOM: "custom", CUSTOM = "custom",
} }
const InternalTemplateBindings = { export enum InternalTemplateBinding {
PLATFORM_URL: "platformUrl", PLATFORM_URL = "platformUrl",
COMPANY: "company", COMPANY = "company",
LOGO_URL: "logoUrl", LOGO_URL = "logoUrl",
EMAIL: "email", EMAIL = "email",
USER: "user", USER = "user",
REQUEST: "request", REQUEST = "request",
DOCS_URL: "docsUrl", DOCS_URL = "docsUrl",
LOGIN_URL: "loginUrl", LOGIN_URL = "loginUrl",
CURRENT_YEAR: "currentYear", CURRENT_YEAR = "currentYear",
CURRENT_DATE: "currentDate", CURRENT_DATE = "currentDate",
BODY: "body", BODY = "body",
STYLES: "styles", STYLES = "styles",
RESET_URL: "resetUrl", RESET_URL = "resetUrl",
RESET_CODE: "resetCode", RESET_CODE = "resetCode",
INVITE_URL: "inviteUrl", INVITE_URL = "inviteUrl",
INVITE_CODE: "inviteUrl", INVITE_CODE = "inviteUrl",
CONTENTS: "contents", CONTENTS = "contents",
} }
const TemplateBindings = { export const TemplateBindings = {
PLATFORM_URL: { PLATFORM_URL: {
name: InternalTemplateBindings.PLATFORM_URL, name: InternalTemplateBinding.PLATFORM_URL,
description: "The URL used to access the budibase platform", description: "The URL used to access the budibase platform",
}, },
COMPANY: { COMPANY: {
name: InternalTemplateBindings.COMPANY, name: InternalTemplateBinding.COMPANY,
description: "The name of your organization", description: "The name of your organization",
}, },
LOGO_URL: { LOGO_URL: {
name: InternalTemplateBindings.LOGO_URL, name: InternalTemplateBinding.LOGO_URL,
description: "The URL of your organizations logo.", description: "The URL of your organizations logo.",
}, },
EMAIL: { EMAIL: {
name: InternalTemplateBindings.EMAIL, name: InternalTemplateBinding.EMAIL,
description: "The recipients email address.", description: "The recipients email address.",
}, },
USER: { USER: {
name: InternalTemplateBindings.USER, name: InternalTemplateBinding.USER,
description: "The recipients user object.", description: "The recipients user object.",
}, },
REQUEST: { REQUEST: {
name: InternalTemplateBindings.REQUEST, name: InternalTemplateBinding.REQUEST,
description: "Additional request metadata.", description: "Additional request metadata.",
}, },
DOCS_URL: { DOCS_URL: {
name: InternalTemplateBindings.DOCS_URL, name: InternalTemplateBinding.DOCS_URL,
description: "Organization documentation URL.", description: "Organization documentation URL.",
}, },
LOGIN_URL: { LOGIN_URL: {
name: InternalTemplateBindings.LOGIN_URL, name: InternalTemplateBinding.LOGIN_URL,
description: "The URL used to log into the organization budibase instance.", description: "The URL used to log into the organization budibase instance.",
}, },
CURRENT_YEAR: { CURRENT_YEAR: {
name: InternalTemplateBindings.CURRENT_YEAR, name: InternalTemplateBinding.CURRENT_YEAR,
description: "The current year.", description: "The current year.",
}, },
CURRENT_DATE: { CURRENT_DATE: {
name: InternalTemplateBindings.CURRENT_DATE, name: InternalTemplateBinding.CURRENT_DATE,
description: "The current date.", description: "The current date.",
}, },
} }
const TemplateMetadata = { export const TemplateMetadata = {
[TemplateTypes.EMAIL]: [ [TemplateType.EMAIL]: [
{ {
name: "Base format", name: "Base format",
description: description:
@ -100,11 +100,11 @@ const TemplateMetadata = {
purpose: EmailTemplatePurpose.BASE, purpose: EmailTemplatePurpose.BASE,
bindings: [ bindings: [
{ {
name: InternalTemplateBindings.BODY, name: InternalTemplateBinding.BODY,
description: "The main body of another email template.", description: "The main body of another email template.",
}, },
{ {
name: InternalTemplateBindings.STYLES, name: InternalTemplateBinding.STYLES,
description: "The contents of the Styling email template.", description: "The contents of the Styling email template.",
}, },
], ],
@ -117,12 +117,12 @@ const TemplateMetadata = {
purpose: EmailTemplatePurpose.PASSWORD_RECOVERY, purpose: EmailTemplatePurpose.PASSWORD_RECOVERY,
bindings: [ bindings: [
{ {
name: InternalTemplateBindings.RESET_URL, name: InternalTemplateBinding.RESET_URL,
description: description:
"The URL the recipient must click to reset their password.", "The URL the recipient must click to reset their password.",
}, },
{ {
name: InternalTemplateBindings.RESET_CODE, name: InternalTemplateBinding.RESET_CODE,
description: description:
"The temporary password reset code used in the recipients password reset URL.", "The temporary password reset code used in the recipients password reset URL.",
}, },
@ -144,12 +144,12 @@ const TemplateMetadata = {
purpose: EmailTemplatePurpose.INVITATION, purpose: EmailTemplatePurpose.INVITATION,
bindings: [ bindings: [
{ {
name: InternalTemplateBindings.INVITE_URL, name: InternalTemplateBinding.INVITE_URL,
description: description:
"The URL the recipient must click to accept the invitation and activate their account.", "The URL the recipient must click to accept the invitation and activate their account.",
}, },
{ {
name: InternalTemplateBindings.INVITE_CODE, name: InternalTemplateBinding.INVITE_CODE,
description: description:
"The temporary invite code used in the recipients invitation URL.", "The temporary invite code used in the recipients invitation URL.",
}, },
@ -163,7 +163,7 @@ const TemplateMetadata = {
purpose: EmailTemplatePurpose.CUSTOM, purpose: EmailTemplatePurpose.CUSTOM,
bindings: [ bindings: [
{ {
name: InternalTemplateBindings.CONTENTS, name: InternalTemplateBinding.CONTENTS,
description: "Custom content body.", description: "Custom content body.",
}, },
], ],
@ -172,12 +172,5 @@ const TemplateMetadata = {
} }
// all purpose combined // all purpose combined
exports.TemplatePurpose = { export { EmailTemplatePurpose as TemplatePurpose }
...EmailTemplatePurpose, export const GLOBAL_OWNER = "global"
}
exports.TemplateTypes = TemplateTypes
exports.EmailTemplatePurpose = EmailTemplatePurpose
exports.TemplateMetadata = TemplateMetadata
exports.TemplateBindings = TemplateBindings
exports.InternalTemplateBindings = InternalTemplateBindings
exports.GLOBAL_OWNER = "global"

View File

@ -1,7 +1,7 @@
const { readStaticFile } = require("../../utilities/fileSystem") const { readStaticFile } = require("../../utilities/fileSystem")
const { const {
EmailTemplatePurpose, EmailTemplatePurpose,
TemplateTypes, TemplateType,
TemplatePurpose, TemplatePurpose,
GLOBAL_OWNER, GLOBAL_OWNER,
} = require("../index") } = require("../index")
@ -26,7 +26,7 @@ exports.EmailTemplates = {
exports.addBaseTemplates = (templates, type = null) => { exports.addBaseTemplates = (templates, type = null) => {
let purposeList let purposeList
switch (type) { switch (type) {
case TemplateTypes.EMAIL: case TemplateType.EMAIL:
purposeList = Object.values(EmailTemplatePurpose) purposeList = Object.values(EmailTemplatePurpose)
break break
default: default:

View File

@ -1,15 +1,33 @@
import env from "../environment"
import { EmailTemplatePurpose, TemplateType, Config } from "../constants"
import { getTemplateByPurpose } from "../constants/templates"
import { getSettingsTemplateContext } from "./templates"
import { processString } from "@budibase/string-templates"
import { getResetPasswordCode, getInviteCode } from "./redis"
import { User } from "@budibase/types"
import { tenancy, db as dbCore, PouchLike } from "@budibase/backend-core"
const nodemailer = require("nodemailer") const nodemailer = require("nodemailer")
const env = require("../environment")
const { getScopedConfig } = require("@budibase/backend-core/db") type SendEmailOpts = {
const { EmailTemplatePurpose, TemplateTypes, Config } = require("../constants") // workspaceId If finer grain controls being used then this will lookup config for workspace.
const { getTemplateByPurpose } = require("../constants/templates") workspaceId?: string
const { getSettingsTemplateContext } = require("./templates") // user If sending to an existing user the object can be provided, this is used in the context.
const { processString } = require("@budibase/string-templates") user: User
const { getResetPasswordCode, getInviteCode } = require("../utilities/redis") // from If sending from an address that is not what is configured in the SMTP config.
const { getGlobalDB } = require("@budibase/backend-core/tenancy") from?: string
// contents If sending a custom email then can supply contents which will be added to it.
contents?: string
// subject A custom subject can be specified if the config one is not desired.
subject?: string
// info Pass in a structure of information to be stored alongside the invitation.
info?: any
cc?: boolean
bcc?: boolean
automation?: boolean
}
const TEST_MODE = false const TEST_MODE = false
const TYPE = TemplateTypes.EMAIL const TYPE = TemplateType.EMAIL
const FULL_EMAIL_PURPOSES = [ const FULL_EMAIL_PURPOSES = [
EmailTemplatePurpose.INVITATION, EmailTemplatePurpose.INVITATION,
@ -18,8 +36,8 @@ const FULL_EMAIL_PURPOSES = [
EmailTemplatePurpose.CUSTOM, EmailTemplatePurpose.CUSTOM,
] ]
function createSMTPTransport(config) { function createSMTPTransport(config: any) {
let options let options: any
let secure = config.secure let secure = config.secure
// default it if not specified // default it if not specified
if (secure == null) { if (secure == null) {
@ -52,10 +70,15 @@ function createSMTPTransport(config) {
return nodemailer.createTransport(options) return nodemailer.createTransport(options)
} }
async function getLinkCode(purpose, email, user, info = null) { async function getLinkCode(
purpose: EmailTemplatePurpose,
email: string,
user: User,
info: any = null
) {
switch (purpose) { switch (purpose) {
case EmailTemplatePurpose.PASSWORD_RECOVERY: case EmailTemplatePurpose.PASSWORD_RECOVERY:
return getResetPasswordCode(user._id, info) return getResetPasswordCode(user._id!, info)
case EmailTemplatePurpose.INVITATION: case EmailTemplatePurpose.INVITATION:
return getInviteCode(email, info) return getInviteCode(email, info)
default: default:
@ -72,7 +95,12 @@ async function getLinkCode(purpose, email, user, info = null) {
* @param {string|null} contents if using a custom template can supply contents for context. * @param {string|null} contents if using a custom template can supply contents for context.
* @return {Promise<string>} returns the built email HTML if all provided parameters were valid. * @return {Promise<string>} returns the built email HTML if all provided parameters were valid.
*/ */
async function buildEmail(purpose, email, context, { user, contents } = {}) { async function buildEmail(
purpose: EmailTemplatePurpose,
email: string,
context: any,
{ user, contents }: any = {}
) {
// this isn't a full email // this isn't a full email
if (FULL_EMAIL_PURPOSES.indexOf(purpose) === -1) { if (FULL_EMAIL_PURPOSES.indexOf(purpose) === -1) {
throw `Unable to build an email of type ${purpose}` throw `Unable to build an email of type ${purpose}`
@ -113,15 +141,19 @@ async function buildEmail(purpose, email, context, { user, contents } = {}) {
* @param {boolean|null} automation Whether or not the configuration is being fetched for an email automation. * @param {boolean|null} automation Whether or not the configuration is being fetched for an email automation.
* @return {Promise<object|null>} returns the SMTP configuration if it exists * @return {Promise<object|null>} returns the SMTP configuration if it exists
*/ */
async function getSmtpConfiguration(db, workspaceId = null, automation) { async function getSmtpConfiguration(
const params = { db: PouchLike,
workspaceId?: string,
automation?: boolean
) {
const params: any = {
type: Config.SMTP, type: Config.SMTP,
} }
if (workspaceId) { if (workspaceId) {
params.workspace = workspaceId params.workspace = workspaceId
} }
const customConfig = await getScopedConfig(db, params) const customConfig = await dbCore.getScopedConfig(db, params)
if (customConfig) { if (customConfig) {
return customConfig return customConfig
@ -146,12 +178,12 @@ async function getSmtpConfiguration(db, workspaceId = null, automation) {
* Checks if a SMTP config exists based on passed in parameters. * Checks if a SMTP config exists based on passed in parameters.
* @return {Promise<boolean>} returns true if there is a configuration that can be used. * @return {Promise<boolean>} returns true if there is a configuration that can be used.
*/ */
exports.isEmailConfigured = async (workspaceId = null) => { export async function isEmailConfigured(workspaceId?: string) {
// when "testing" or smtp fallback is enabled simply return true // when "testing" or smtp fallback is enabled simply return true
if (TEST_MODE || env.SMTP_FALLBACK_ENABLED) { if (TEST_MODE || env.SMTP_FALLBACK_ENABLED) {
return true return true
} }
const db = getGlobalDB() const db = tenancy.getGlobalDB()
const config = await getSmtpConfiguration(db, workspaceId) const config = await getSmtpConfiguration(db, workspaceId)
return config != null return config != null
} }
@ -161,48 +193,49 @@ exports.isEmailConfigured = async (workspaceId = null) => {
* send an email using it. * send an email using it.
* @param {string} email The email address to send to. * @param {string} email The email address to send to.
* @param {string} purpose The purpose of the email being sent (e.g. reset password). * @param {string} purpose The purpose of the email being sent (e.g. reset password).
* @param {string|undefined} workspaceId If finer grain controls being used then this will lookup config for workspace. * @param {object} opts The options for sending the email.
* @param {object|undefined} user If sending to an existing user the object can be provided, this is used in the context.
* @param {string|undefined} from If sending from an address that is not what is configured in the SMTP config.
* @param {string|undefined} contents If sending a custom email then can supply contents which will be added to it.
* @param {string|undefined} subject A custom subject can be specified if the config one is not desired.
* @param {object|undefined} info Pass in a structure of information to be stored alongside the invitation.
* @param {boolean|undefined} disableFallback Prevent email being sent from SMTP fallback to avoid spam.
* @return {Promise<object>} returns details about the attempt to send email, e.g. if it is successful; based on * @return {Promise<object>} returns details about the attempt to send email, e.g. if it is successful; based on
* nodemailer response. * nodemailer response.
*/ */
exports.sendEmail = async ( export async function sendEmail(
email, email: string,
purpose, purpose: EmailTemplatePurpose,
{ workspaceId, user, from, contents, subject, info, cc, bcc, automation } = {} opts: SendEmailOpts
) => { ) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
let config = (await getSmtpConfiguration(db, workspaceId, automation)) || {} let config =
(await getSmtpConfiguration(db, opts?.workspaceId, opts?.automation)) || {}
if (Object.keys(config).length === 0 && !TEST_MODE) { if (Object.keys(config).length === 0 && !TEST_MODE) {
throw "Unable to find SMTP configuration." throw "Unable to find SMTP configuration."
} }
const transport = createSMTPTransport(config) const transport = createSMTPTransport(config)
// if there is a link code needed this will retrieve it // if there is a link code needed this will retrieve it
const code = await getLinkCode(purpose, email, user, info) const code = await getLinkCode(purpose, email, opts.user, opts?.info)
const context = await getSettingsTemplateContext(purpose, code) let context
if (code) {
context = await getSettingsTemplateContext(purpose, code)
}
let message = { let message: any = {
from: from || config.from, from: opts?.from || config.from,
html: await buildEmail(purpose, email, context, { html: await buildEmail(purpose, email, context, {
user, user: opts?.user,
contents, contents: opts?.contents,
}), }),
} }
message = { message = {
...message, ...message,
to: email, to: email,
cc: cc, cc: opts?.cc,
bcc: bcc, bcc: opts?.bcc,
} }
if (subject || config.subject) { if (opts?.subject || config.subject) {
message.subject = await processString(subject || config.subject, context) message.subject = await processString(
opts?.subject || config.subject,
context
)
} }
const response = await transport.sendMail(message) const response = await transport.sendMail(message)
if (TEST_MODE) { if (TEST_MODE) {
@ -216,7 +249,7 @@ exports.sendEmail = async (
* @param {object} config an SMTP configuration - this is based on the nodemailer API. * @param {object} config an SMTP configuration - this is based on the nodemailer API.
* @return {Promise<boolean>} returns true if the configuration is valid. * @return {Promise<boolean>} returns true if the configuration is valid.
*/ */
exports.verifyConfig = async config => { export async function verifyConfig(config: any) {
const transport = createSMTPTransport(config) const transport = createSMTPTransport(config)
await transport.verify() await transport.verify()
} }

View File

@ -1,36 +1,35 @@
const { Client, utils } = require("@budibase/backend-core/redis") import { redis, utils } from "@budibase/backend-core"
const { newid } = require("@budibase/backend-core/utils")
function getExpirySecondsForDB(db) { function getExpirySecondsForDB(db: string) {
switch (db) { switch (db) {
case utils.Databases.PW_RESETS: case redis.utils.Databases.PW_RESETS:
// a hour // a hour
return 3600 return 3600
case utils.Databases.INVITATIONS: case redis.utils.Databases.INVITATIONS:
// a day // a day
return 86400 return 86400
} }
} }
let pwResetClient, invitationClient let pwResetClient: any, invitationClient: any
function getClient(db) { function getClient(db: string) {
switch (db) { switch (db) {
case utils.Databases.PW_RESETS: case redis.utils.Databases.PW_RESETS:
return pwResetClient return pwResetClient
case utils.Databases.INVITATIONS: case redis.utils.Databases.INVITATIONS:
return invitationClient return invitationClient
} }
} }
async function writeACode(db, value) { async function writeACode(db: string, value: any) {
const client = await getClient(db) const client = await getClient(db)
const code = newid() const code = utils.newid()
await client.store(code, value, getExpirySecondsForDB(db)) await client.store(code, value, getExpirySecondsForDB(db))
return code return code
} }
async function getACode(db, code, deleteCode = true) { async function getACode(db: string, code: string, deleteCode = true) {
const client = await getClient(db) const client = await getClient(db)
const value = await client.get(code) const value = await client.get(code)
if (!value) { if (!value) {
@ -42,9 +41,9 @@ async function getACode(db, code, deleteCode = true) {
return value return value
} }
exports.init = async () => { export async function init() {
pwResetClient = new Client(utils.Databases.PW_RESETS) pwResetClient = new redis.Client(redis.utils.Databases.PW_RESETS)
invitationClient = new Client(utils.Databases.INVITATIONS) invitationClient = new redis.Client(redis.utils.Databases.INVITATIONS)
await pwResetClient.init() await pwResetClient.init()
await invitationClient.init() await invitationClient.init()
} }
@ -52,7 +51,7 @@ exports.init = async () => {
/** /**
* make sure redis connection is closed. * make sure redis connection is closed.
*/ */
exports.shutdown = async () => { export async function shutdown() {
if (pwResetClient) await pwResetClient.finish() if (pwResetClient) await pwResetClient.finish()
if (invitationClient) await invitationClient.finish() if (invitationClient) await invitationClient.finish()
console.log("Redis shutdown") console.log("Redis shutdown")
@ -65,8 +64,8 @@ exports.shutdown = async () => {
* @param {object} info Info about the user/the reset process. * @param {object} info Info about the user/the reset process.
* @return {Promise<string>} returns the code that was stored to redis. * @return {Promise<string>} returns the code that was stored to redis.
*/ */
exports.getResetPasswordCode = async (userId, info) => { export async function getResetPasswordCode(userId: string, info: any) {
return writeACode(utils.Databases.PW_RESETS, { userId, info }) return writeACode(redis.utils.Databases.PW_RESETS, { userId, info })
} }
/** /**
@ -75,9 +74,12 @@ exports.getResetPasswordCode = async (userId, info) => {
* @param {boolean} deleteCode If the code is used/finished with this will delete it - defaults to true. * @param {boolean} deleteCode If the code is used/finished with this will delete it - defaults to true.
* @return {Promise<string>} returns the user ID if it is found * @return {Promise<string>} returns the user ID if it is found
*/ */
exports.checkResetPasswordCode = async (resetCode, deleteCode = true) => { export async function checkResetPasswordCode(
resetCode: string,
deleteCode = true
) {
try { try {
return getACode(utils.Databases.PW_RESETS, resetCode, deleteCode) return getACode(redis.utils.Databases.PW_RESETS, resetCode, deleteCode)
} catch (err) { } catch (err) {
throw "Provided information is not valid, cannot reset password - please try again." throw "Provided information is not valid, cannot reset password - please try again."
} }
@ -89,8 +91,8 @@ exports.checkResetPasswordCode = async (resetCode, deleteCode = true) => {
* @param {object|null} info Information to be carried along with the invitation. * @param {object|null} info Information to be carried along with the invitation.
* @return {Promise<string>} returns the code that was stored to redis. * @return {Promise<string>} returns the code that was stored to redis.
*/ */
exports.getInviteCode = async (email, info) => { export async function getInviteCode(email: string, info: any) {
return writeACode(utils.Databases.INVITATIONS, { email, info }) return writeACode(redis.utils.Databases.INVITATIONS, { email, info })
} }
/** /**
@ -99,9 +101,12 @@ exports.getInviteCode = async (email, info) => {
* @param {boolean} deleteCode whether or not the code should be deleted after retrieval - defaults to true. * @param {boolean} deleteCode whether or not the code should be deleted after retrieval - defaults to true.
* @return {Promise<object>} If the code is valid then an email address will be returned. * @return {Promise<object>} If the code is valid then an email address will be returned.
*/ */
exports.checkInviteCode = async (inviteCode, deleteCode = true) => { export async function checkInviteCode(
inviteCode: string,
deleteCode: boolean = true
) {
try { try {
return getACode(utils.Databases.INVITATIONS, inviteCode, deleteCode) return getACode(redis.utils.Databases.INVITATIONS, inviteCode, deleteCode)
} catch (err) { } catch (err) {
throw "Invitation is not valid or has expired, please request a new one." throw "Invitation is not valid or has expired, please request a new one."
} }

View File

@ -1,47 +1,47 @@
const { getScopedConfig } = require("@budibase/backend-core/db") import { db as dbCore, tenancy } from "@budibase/backend-core"
const { import {
Config, Config,
InternalTemplateBindings, InternalTemplateBinding,
LOGO_URL, LOGO_URL,
EmailTemplatePurpose, EmailTemplatePurpose,
} = require("../constants") } from "../constants"
const { checkSlashesInUrl } = require("./index") import { checkSlashesInUrl } from "./index"
const {
getGlobalDB,
addTenantToUrl,
} = require("@budibase/backend-core/tenancy")
const BASE_COMPANY = "Budibase" const BASE_COMPANY = "Budibase"
exports.getSettingsTemplateContext = async (purpose, code = null) => { export async function getSettingsTemplateContext(
const db = getGlobalDB() purpose: EmailTemplatePurpose,
code?: string
) {
const db = tenancy.getGlobalDB()
// TODO: use more granular settings in the future if required // TODO: use more granular settings in the future if required
let settings = (await getScopedConfig(db, { type: Config.SETTINGS })) || {} let settings =
(await dbCore.getScopedConfig(db, { type: Config.SETTINGS })) || {}
const URL = settings.platformUrl const URL = settings.platformUrl
const context = { const context: any = {
[InternalTemplateBindings.LOGO_URL]: [InternalTemplateBinding.LOGO_URL]:
checkSlashesInUrl(`${URL}/${settings.logoUrl}`) || LOGO_URL, checkSlashesInUrl(`${URL}/${settings.logoUrl}`) || LOGO_URL,
[InternalTemplateBindings.PLATFORM_URL]: URL, [InternalTemplateBinding.PLATFORM_URL]: URL,
[InternalTemplateBindings.COMPANY]: settings.company || BASE_COMPANY, [InternalTemplateBinding.COMPANY]: settings.company || BASE_COMPANY,
[InternalTemplateBindings.DOCS_URL]: [InternalTemplateBinding.DOCS_URL]:
settings.docsUrl || "https://docs.budibase.com/", settings.docsUrl || "https://docs.budibase.com/",
[InternalTemplateBindings.LOGIN_URL]: checkSlashesInUrl( [InternalTemplateBinding.LOGIN_URL]: checkSlashesInUrl(
addTenantToUrl(`${URL}/login`) tenancy.addTenantToUrl(`${URL}/login`)
), ),
[InternalTemplateBindings.CURRENT_DATE]: new Date().toISOString(), [InternalTemplateBinding.CURRENT_DATE]: new Date().toISOString(),
[InternalTemplateBindings.CURRENT_YEAR]: new Date().getFullYear(), [InternalTemplateBinding.CURRENT_YEAR]: new Date().getFullYear(),
} }
// attach purpose specific context // attach purpose specific context
switch (purpose) { switch (purpose) {
case EmailTemplatePurpose.PASSWORD_RECOVERY: case EmailTemplatePurpose.PASSWORD_RECOVERY:
context[InternalTemplateBindings.RESET_CODE] = code context[InternalTemplateBinding.RESET_CODE] = code
context[InternalTemplateBindings.RESET_URL] = checkSlashesInUrl( context[InternalTemplateBinding.RESET_URL] = checkSlashesInUrl(
addTenantToUrl(`${URL}/builder/auth/reset?code=${code}`) tenancy.addTenantToUrl(`${URL}/builder/auth/reset?code=${code}`)
) )
break break
case EmailTemplatePurpose.INVITATION: case EmailTemplatePurpose.INVITATION:
context[InternalTemplateBindings.INVITE_CODE] = code context[InternalTemplateBinding.INVITE_CODE] = code
context[InternalTemplateBindings.INVITE_URL] = checkSlashesInUrl( context[InternalTemplateBinding.INVITE_URL] = checkSlashesInUrl(
addTenantToUrl(`${URL}/builder/invite?code=${code}`) tenancy.addTenantToUrl(`${URL}/builder/invite?code=${code}`)
) )
break break
} }