Fleshing out the main work behind the email generation.
This commit is contained in:
parent
27846e1bee
commit
737dd356c3
|
@ -20,6 +20,7 @@
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/auth": "0.0.1",
|
"@budibase/auth": "0.0.1",
|
||||||
|
"@budibase/string-templates": "^0.8.16",
|
||||||
"@koa/router": "^8.0.0",
|
"@koa/router": "^8.0.0",
|
||||||
"aws-sdk": "^2.811.0",
|
"aws-sdk": "^2.811.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
const CouchDB = require("../../../db")
|
const CouchDB = require("../../../db")
|
||||||
const authPkg = require("@budibase/auth")
|
const {
|
||||||
const { utils, StaticDatabases } = authPkg
|
generateConfigID,
|
||||||
|
StaticDatabases,
|
||||||
|
getConfigParams,
|
||||||
|
determineScopedConfig,
|
||||||
|
} = require("@budibase/auth").db
|
||||||
|
|
||||||
const GLOBAL_DB = StaticDatabases.GLOBAL.name
|
const GLOBAL_DB = StaticDatabases.GLOBAL.name
|
||||||
|
|
||||||
|
@ -11,7 +15,7 @@ exports.save = async function(ctx) {
|
||||||
|
|
||||||
// Config does not exist yet
|
// Config does not exist yet
|
||||||
if (!configDoc._id) {
|
if (!configDoc._id) {
|
||||||
configDoc._id = utils.generateConfigID({
|
configDoc._id = generateConfigID({
|
||||||
type,
|
type,
|
||||||
group,
|
group,
|
||||||
user,
|
user,
|
||||||
|
@ -33,12 +37,11 @@ exports.save = async function(ctx) {
|
||||||
exports.fetch = async function(ctx) {
|
exports.fetch = async function(ctx) {
|
||||||
const db = new CouchDB(GLOBAL_DB)
|
const db = new CouchDB(GLOBAL_DB)
|
||||||
const response = await db.allDocs(
|
const response = await db.allDocs(
|
||||||
utils.getConfigParams(undefined, {
|
getConfigParams(undefined, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
const groups = response.rows.map(row => row.doc)
|
ctx.body = response.rows.map(row => row.doc)
|
||||||
ctx.body = groups
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,7 +63,7 @@ exports.find = async function(ctx) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Find the config with the most granular scope based on context
|
// Find the config with the most granular scope based on context
|
||||||
const scopedConfig = await authPkg.db.determineScopedConfig(db, {
|
const scopedConfig = await determineScopedConfig(db, {
|
||||||
type: ctx.params.type,
|
type: ctx.params.type,
|
||||||
user: userId,
|
user: userId,
|
||||||
group,
|
group,
|
||||||
|
|
|
@ -1,59 +1,17 @@
|
||||||
const {
|
const {
|
||||||
generateTemplateID,
|
generateTemplateID,
|
||||||
getTemplateParams,
|
|
||||||
StaticDatabases,
|
StaticDatabases,
|
||||||
} = require("@budibase/auth").db
|
} = require("@budibase/auth").db
|
||||||
const { CouchDB } = require("../../../db")
|
const { CouchDB } = require("../../../db")
|
||||||
const {
|
const {
|
||||||
TemplatePurposePretty,
|
TemplateMetadata,
|
||||||
TemplateTypes,
|
TemplateBindings,
|
||||||
EmailTemplatePurpose,
|
|
||||||
TemplatePurpose,
|
|
||||||
} = require("../../../constants")
|
} = require("../../../constants")
|
||||||
const { getTemplateByPurpose } = require("../../../constants/templates")
|
const { getTemplates } = require("../../../constants/templates")
|
||||||
|
|
||||||
const GLOBAL_DB = StaticDatabases.GLOBAL.name
|
const GLOBAL_DB = StaticDatabases.GLOBAL.name
|
||||||
const GLOBAL_OWNER = "global"
|
const GLOBAL_OWNER = "global"
|
||||||
|
|
||||||
function addBaseTemplates(templates, type = null) {
|
|
||||||
let purposeList
|
|
||||||
switch (type) {
|
|
||||||
case TemplateTypes.EMAIL:
|
|
||||||
purposeList = Object.values(EmailTemplatePurpose)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
purposeList = Object.values(TemplatePurpose)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
for (let purpose of purposeList) {
|
|
||||||
// check if a template exists already for purpose
|
|
||||||
if (templates.find(template => template.purpose === purpose)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
templates.push(getTemplateByPurpose(purpose))
|
|
||||||
}
|
|
||||||
return templates
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getTemplates({ ownerId, type, id } = {}) {
|
|
||||||
const db = new CouchDB(GLOBAL_DB)
|
|
||||||
const response = await db.allDocs(
|
|
||||||
getTemplateParams(ownerId, id, {
|
|
||||||
include_docs: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
let templates = response.rows.map(row => row.doc)
|
|
||||||
// should only be one template with ID
|
|
||||||
if (id) {
|
|
||||||
return templates[0]
|
|
||||||
}
|
|
||||||
if (type) {
|
|
||||||
templates = templates.filter(template => template.type === type)
|
|
||||||
}
|
|
||||||
|
|
||||||
return addBaseTemplates(templates, type)
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.save = async ctx => {
|
exports.save = async ctx => {
|
||||||
const db = new CouchDB(GLOBAL_DB)
|
const db = new CouchDB(GLOBAL_DB)
|
||||||
const type = ctx.params.type
|
const type = ctx.params.type
|
||||||
|
@ -77,7 +35,8 @@ exports.save = async ctx => {
|
||||||
|
|
||||||
exports.definitions = async ctx => {
|
exports.definitions = async ctx => {
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
purpose: TemplatePurposePretty,
|
purpose: TemplateMetadata,
|
||||||
|
bindings: Object.values(TemplateBindings),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
exports.LOGO_URL =
|
||||||
|
"https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg"
|
||||||
|
|
||||||
exports.UserStatus = {
|
exports.UserStatus = {
|
||||||
ACTIVE: "active",
|
ACTIVE: "active",
|
||||||
INACTIVE: "inactive",
|
INACTIVE: "inactive",
|
||||||
|
@ -19,39 +22,48 @@ const TemplateTypes = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmailTemplatePurpose = {
|
const EmailTemplatePurpose = {
|
||||||
HEADER: "header",
|
BASE: "base",
|
||||||
FOOTER: "footer",
|
|
||||||
STYLES: "styles",
|
STYLES: "styles",
|
||||||
PASSWORD_RECOVERY: "password_recovery",
|
PASSWORD_RECOVERY: "password_recovery",
|
||||||
INVITATION: "invitation",
|
INVITATION: "invitation",
|
||||||
CUSTOM: "custom",
|
CUSTOM: "custom",
|
||||||
}
|
}
|
||||||
|
|
||||||
const TemplatePurposePretty = {
|
const TemplateBindings = {
|
||||||
|
URL: "url",
|
||||||
|
COMPANY: "company",
|
||||||
|
LOGO_URL: "logoUrl",
|
||||||
|
STYLES: "styles",
|
||||||
|
BODY: "body",
|
||||||
|
REGISTRATION_URL: "registrationUrl",
|
||||||
|
EMAIL: "email",
|
||||||
|
RESET_URL: "resetUrl",
|
||||||
|
USER: "user",
|
||||||
|
}
|
||||||
|
|
||||||
|
const TemplateMetadata = {
|
||||||
[TemplateTypes.EMAIL]: [
|
[TemplateTypes.EMAIL]: [
|
||||||
{
|
{
|
||||||
name: "Styling",
|
name: "Styling",
|
||||||
value: EmailTemplatePurpose.STYLES,
|
purpose: EmailTemplatePurpose.STYLES,
|
||||||
|
bindings: ["url", "company", "companyUrl", "styles", "body"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Header",
|
name: "Base Format",
|
||||||
value: EmailTemplatePurpose.HEADER,
|
purpose: EmailTemplatePurpose.BASE,
|
||||||
},
|
bindings: ["company", "registrationUrl"]
|
||||||
{
|
|
||||||
name: "Footer",
|
|
||||||
value: EmailTemplatePurpose.FOOTER,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Password Recovery",
|
name: "Password Recovery",
|
||||||
value: EmailTemplatePurpose.PASSWORD_RECOVERY,
|
purpose: EmailTemplatePurpose.PASSWORD_RECOVERY,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "New User Invitation",
|
name: "New User Invitation",
|
||||||
value: EmailTemplatePurpose.INVITATION,
|
purpose: EmailTemplatePurpose.INVITATION,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Custom",
|
name: "Custom",
|
||||||
value: EmailTemplatePurpose.CUSTOM,
|
purpose: EmailTemplatePurpose.CUSTOM,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
@ -62,4 +74,5 @@ exports.TemplatePurpose = {
|
||||||
}
|
}
|
||||||
exports.TemplateTypes = TemplateTypes
|
exports.TemplateTypes = TemplateTypes
|
||||||
exports.EmailTemplatePurpose = EmailTemplatePurpose
|
exports.EmailTemplatePurpose = EmailTemplatePurpose
|
||||||
exports.TemplatePurposePretty = TemplatePurposePretty
|
exports.TemplateMetadata = TemplateMetadata
|
||||||
|
exports.TemplateBindings = TemplateBindings
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="x-apple-disable-message-reformatting">
|
||||||
|
<style>
|
||||||
|
{{ styles }}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin: auto;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="bg_white logo" style="padding: 1em 2.5em; text-align: center">
|
||||||
|
<h1><a href="{{ companyUrl }}">{{ company }}</a></h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="bg_white logo" style="padding: 1em 2.5em; text-align: center">
|
||||||
|
{{ body }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="bg_white logo" style="padding: 1em 2.5em; text-align: center">
|
||||||
|
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin: auto;">
|
||||||
|
<tbody><tr>
|
||||||
|
<td valign="middle" class="bg_black footer email-section">
|
||||||
|
<table>
|
||||||
|
<tbody><tr>
|
||||||
|
<td valign="top" width="50%" style="padding-top: 20px;">
|
||||||
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||||
|
<tbody><tr>
|
||||||
|
<td style="text-align: left; padding-right: 10px;">
|
||||||
|
<h3 class="heading">{{ company }}</h3>
|
||||||
|
<p>Company information.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
</td>
|
||||||
|
<td valign="top" width="50%" style="padding-top: 20px;">
|
||||||
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||||
|
<tbody><tr>
|
||||||
|
<td style="text-align: left; padding-left: 10px;">
|
||||||
|
<h3 class="heading"><a href="{{ url }}">Budibase Platform</a></h3>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td valign="middle" class="bg_black footer email-section">
|
||||||
|
<table>
|
||||||
|
<tbody><tr>
|
||||||
|
<td valign="top" width="50%">
|
||||||
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||||
|
<tbody><tr>
|
||||||
|
<td style="text-align: left; padding-right: 10px;">
|
||||||
|
<p>© 2021 Restobar. All Rights Reserved</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
|
@ -1,48 +0,0 @@
|
||||||
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin: auto;">
|
|
||||||
<tbody><tr>
|
|
||||||
<td valign="middle" class="bg_black footer email-section">
|
|
||||||
<table>
|
|
||||||
<tbody><tr>
|
|
||||||
<td valign="top" width="50%" style="padding-top: 20px;">
|
|
||||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
|
||||||
<tbody><tr>
|
|
||||||
<td style="text-align: left; padding-right: 10px;">
|
|
||||||
<h3 class="heading">{{ company }}</h3>
|
|
||||||
<p>Company information.</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody></table>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="50%" style="padding-top: 20px;">
|
|
||||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
|
||||||
<tbody><tr>
|
|
||||||
<td style="text-align: left; padding-left: 10px;">
|
|
||||||
<h3 class="heading">Budibase Portal</h3>
|
|
||||||
{{ portalUrl }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody></table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody></table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="middle" class="bg_black footer email-section">
|
|
||||||
<table>
|
|
||||||
<tbody><tr>
|
|
||||||
<td valign="top" width="50%">
|
|
||||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
|
||||||
<tbody><tr>
|
|
||||||
<td style="text-align: left; padding-right: 10px;">
|
|
||||||
<p>© 2021 Restobar. All Rights Reserved</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody></table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody></table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
|
@ -1,36 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="x-apple-disable-message-reformatting">
|
|
||||||
<style>
|
|
||||||
{{ styles }}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin: auto;">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td class="bg_white logo" style="padding: 1em 2.5em; text-align: center">
|
|
||||||
<h1><a href="{{ companyUrl }}">{{ company }}</a></h1>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="bg_white logo" style="padding: 1em 2.5em; text-align: center">
|
|
||||||
{{ body }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="bg_white logo" style="padding: 1em 2.5em; text-align: center">
|
|
||||||
{{ footer }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
|
@ -1,6 +1,11 @@
|
||||||
const { readStaticFile } = require("../../utilities/fileSystem")
|
const { readStaticFile } = require("../../utilities/fileSystem")
|
||||||
const { EmailTemplatePurpose } = require("../index")
|
const { EmailTemplatePurpose, TemplateTypes, TemplatePurpose } = require("../index")
|
||||||
const { join } = require("path")
|
const { join } = require("path")
|
||||||
|
const CouchDB = require("../../db")
|
||||||
|
const {
|
||||||
|
getTemplateParams,
|
||||||
|
StaticDatabases,
|
||||||
|
} = require("@budibase/auth").db
|
||||||
|
|
||||||
const TEMPLATE_PATH = join(__dirname, "..", "constants", "templates")
|
const TEMPLATE_PATH = join(__dirname, "..", "constants", "templates")
|
||||||
|
|
||||||
|
@ -11,19 +16,56 @@ exports.EmailTemplates = {
|
||||||
[EmailTemplatePurpose.INVITATION]: readStaticFile(
|
[EmailTemplatePurpose.INVITATION]: readStaticFile(
|
||||||
join(TEMPLATE_PATH, "invitation.html")
|
join(TEMPLATE_PATH, "invitation.html")
|
||||||
),
|
),
|
||||||
[EmailTemplatePurpose.HEADER]: readStaticFile(
|
[EmailTemplatePurpose.BASE]: readStaticFile(
|
||||||
join(TEMPLATE_PATH, "header.html")
|
join(TEMPLATE_PATH, "base.html")
|
||||||
),
|
|
||||||
[EmailTemplatePurpose.FOOTER]: readStaticFile(
|
|
||||||
join(TEMPLATE_PATH, "footer.html")
|
|
||||||
),
|
),
|
||||||
[EmailTemplatePurpose.STYLES]: readStaticFile(
|
[EmailTemplatePurpose.STYLES]: readStaticFile(
|
||||||
join(TEMPLATE_PATH, "style.css")
|
join(TEMPLATE_PATH, "style.css")
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getTemplateByPurpose = purpose => {
|
exports.addBaseTemplates = (templates, type = null) => {
|
||||||
if (exports.EmailTemplates[purpose]) {
|
let purposeList
|
||||||
return exports.EmailTemplates[purpose]
|
switch (type) {
|
||||||
|
case TemplateTypes.EMAIL:
|
||||||
|
purposeList = Object.values(EmailTemplatePurpose)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
purposeList = Object.values(TemplatePurpose)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
for (let purpose of purposeList) {
|
||||||
|
// check if a template exists already for purpose
|
||||||
|
if (templates.find(template => template.purpose === purpose)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (exports.EmailTemplates[purpose]) {
|
||||||
|
templates.push(exports.EmailTemplates[purpose])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return templates
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.getTemplates = async ({ ownerId, type, id } = {}) => {
|
||||||
|
const db = new CouchDB(StaticDatabases.GLOBAL.name)
|
||||||
|
const response = await db.allDocs(
|
||||||
|
getTemplateParams(ownerId, id, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
let templates = response.rows.map(row => row.doc)
|
||||||
|
// should only be one template with ID
|
||||||
|
if (id) {
|
||||||
|
return templates[0]
|
||||||
|
}
|
||||||
|
if (type) {
|
||||||
|
templates = templates.filter(template => template.type === type)
|
||||||
|
}
|
||||||
|
return exports.addBaseTemplates(templates, type)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getTemplateByPurpose = async (type, purpose) => {
|
||||||
|
const templates = await exports.getTemplates({ type })
|
||||||
|
return templates.find(template => template.purpose === purpose)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<span class="subheading">Budibase Password reset</span>
|
<span class="subheading">Budibase Password reset</span>
|
||||||
<h2>Please follow the below link to reset your password.</h2>
|
<h2>Please follow the below link to reset your password.</h2>
|
||||||
<p><a href="{{ resetUrl }}" class="btn btn-primary">Reset password</a></p>
|
<p><a href="{{ resetUrl }}" class="btn btn-primary">Reset password</a></p>
|
||||||
<p>This password reset was required for {{ user }} if you did not
|
<p>This password reset was required for {{ email }} if you did not
|
||||||
request this then please contact your administrator.</p>
|
request this then please contact your administrator.</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
const { EmailTemplatePurpose, TemplateTypes } = require("../constants")
|
||||||
|
const { getTemplateByPurpose } = require("../constants/templates")
|
||||||
|
const { processString } = require("@budibase/string-templates")
|
||||||
|
const { getSettingsTemplateContext } = require("./templates")
|
||||||
|
|
||||||
|
const TYPE = TemplateTypes.EMAIL
|
||||||
|
|
||||||
|
const FULL_EMAIL_PURPOSES = [EmailTemplatePurpose.INVITATION, EmailTemplatePurpose.PASSWORD_RECOVERY]
|
||||||
|
|
||||||
|
exports.buildEmail = async (email, user, purpose) => {
|
||||||
|
// this isn't a full email
|
||||||
|
if (FULL_EMAIL_PURPOSES.indexOf(purpose) === -1) {
|
||||||
|
throw `Unable to build an email of type ${purpose}`
|
||||||
|
}
|
||||||
|
let [base, styles, body] = await Promise.all([
|
||||||
|
getTemplateByPurpose(TYPE, EmailTemplatePurpose.BASE),
|
||||||
|
getTemplateByPurpose(TYPE, EmailTemplatePurpose.STYLES),
|
||||||
|
getTemplateByPurpose(TYPE, purpose),
|
||||||
|
])
|
||||||
|
|
||||||
|
// TODO: need to extend the context as much as possible
|
||||||
|
const context = {
|
||||||
|
...await getSettingsTemplateContext(),
|
||||||
|
email,
|
||||||
|
user
|
||||||
|
}
|
||||||
|
|
||||||
|
body = await processString(body, context)
|
||||||
|
styles = await processString(styles, context)
|
||||||
|
// this should now be the complete email HTML
|
||||||
|
return processString(base, {
|
||||||
|
...context,
|
||||||
|
styles,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Makes sure that a URL has the correct number of slashes, while maintaining the
|
||||||
|
* http(s):// double slashes.
|
||||||
|
* @param {string} url The URL to test and remove any extra double slashes.
|
||||||
|
* @return {string} The updated url.
|
||||||
|
*/
|
||||||
|
exports.checkSlashesInUrl = url => {
|
||||||
|
return url.replace(/(https?:\/\/)|(\/)+/g, "$1$2")
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
const CouchDB = require("../../../db")
|
||||||
|
const { getConfigParams, StaticDatabases } = require("@budibase/auth").db
|
||||||
|
const { Configs, TemplateBindings, LOGO_URL } = require("../constants")
|
||||||
|
const { checkSlashesInUrl } = require("./index")
|
||||||
|
const env = require("../environment")
|
||||||
|
|
||||||
|
const LOCAL_URL = `http://localhost:${env.PORT}`
|
||||||
|
const BASE_COMPANY = "Budibase"
|
||||||
|
|
||||||
|
exports.getSettingsTemplateContext = async () => {
|
||||||
|
const db = new CouchDB(StaticDatabases.GLOBAL.name)
|
||||||
|
const response = await db.allDocs(
|
||||||
|
getConfigParams(Configs.SETTINGS, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
let settings = response.rows.map(row => row.doc)[0] || {}
|
||||||
|
if (!settings.url) {
|
||||||
|
settings.url = LOCAL_URL
|
||||||
|
}
|
||||||
|
// TODO: need to fully spec out the context
|
||||||
|
return {
|
||||||
|
[TemplateBindings.LOGO_URL]: settings.logoUrl || LOGO_URL,
|
||||||
|
[TemplateBindings.URL]: settings.url,
|
||||||
|
[TemplateBindings.REGISTRATION_URL]: checkSlashesInUrl(`${settings.url}/registration`),
|
||||||
|
[TemplateBindings.RESET_URL]: checkSlashesInUrl(`${settings.url}/reset`),
|
||||||
|
[TemplateBindings.COMPANY]: settings.company || BASE_COMPANY,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue