Some worker typescript conversions.
This commit is contained in:
parent
29dd98a7fc
commit
cdc25d7032
|
@ -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"
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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."
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue