diff --git a/packages/auth/src/environment.js b/packages/auth/src/environment.js
index db24aaafcc..f582bd118a 100644
--- a/packages/auth/src/environment.js
+++ b/packages/auth/src/environment.js
@@ -15,5 +15,6 @@ module.exports = {
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
MINIO_URL: process.env.MINIO_URL,
+ INTERNAL_KEY: process.env.INTERNAL_KEY,
isTest,
}
diff --git a/packages/auth/src/middleware/authenticated.js b/packages/auth/src/middleware/authenticated.js
index 5d8d4e7e13..913e7c8cc8 100644
--- a/packages/auth/src/middleware/authenticated.js
+++ b/packages/auth/src/middleware/authenticated.js
@@ -2,6 +2,7 @@ const { Cookies } = require("../constants")
const database = require("../db")
const { getCookie, clearCookie } = require("../utils")
const { StaticDatabases } = require("../db/utils")
+const env = require("../environment")
const PARAM_REGEX = /\/:(.*?)\//g
@@ -35,10 +36,14 @@ module.exports = (noAuthPatterns = [], opts) => {
return next()
}
try {
+ const apiKey = ctx.request.headers["x-budibase-api-key"]
// check the actual user is authenticated first
const authCookie = getCookie(ctx, Cookies.Auth)
- if (authCookie) {
+ // this is an internal request, no user made it
+ if (apiKey && apiKey === env.INTERNAL_KEY) {
+ ctx.isAuthenticated = true
+ } else if (authCookie) {
try {
const db = database.getDB(StaticDatabases.GLOBAL.name)
const user = await db.get(authCookie.userId)
diff --git a/packages/cli/src/hosting/makeEnv.js b/packages/cli/src/hosting/makeEnv.js
index a4fbce6ee0..e8c4a8c830 100644
--- a/packages/cli/src/hosting/makeEnv.js
+++ b/packages/cli/src/hosting/makeEnv.js
@@ -6,14 +6,11 @@ const randomString = require("randomstring")
const FILE_PATH = path.resolve("./.env")
-function getContents(port, hostingKey) {
+function getContents(port) {
return `
# Use the main port in the builder for your self hosting URL, e.g. localhost:10000
MAIN_PORT=${port}
-# Use this password when configuring your self hosting settings
-HOSTING_KEY=${hostingKey}
-
# This section contains all secrets pertaining to the system
JWT_SECRET=${randomString.generate()}
MINIO_ACCESS_KEY=${randomString.generate()}
@@ -21,6 +18,7 @@ MINIO_SECRET_KEY=${randomString.generate()}
COUCH_DB_PASSWORD=${randomString.generate()}
COUCH_DB_USER=${randomString.generate()}
REDIS_PASSWORD=${randomString.generate()}
+INTERNAL_KEY=${randomString.generate()}
# This section contains variables that do not need to be altered under normal circumstances
APP_PORT=4002
@@ -33,7 +31,6 @@ BUDIBASE_ENVIRONMENT=PRODUCTION`
module.exports.filePath = FILE_PATH
module.exports.ConfigMap = {
- HOSTING_KEY: "key",
MAIN_PORT: "port",
}
module.exports.QUICK_CONFIG = {
@@ -42,18 +39,13 @@ module.exports.QUICK_CONFIG = {
}
module.exports.make = async (inputs = {}) => {
- const hostingKey =
- inputs.key ||
- (await string(
- "Please input the password you'd like to use as your hosting key: "
- ))
const hostingPort =
inputs.port ||
(await number(
"Please enter the port on which you want your installation to run: ",
10000
))
- const fileContents = getContents(hostingPort, hostingKey)
+ const fileContents = getContents(hostingPort)
fs.writeFileSync(FILE_PATH, fileContents)
console.log(
success(
diff --git a/packages/server/scripts/dev/manage.js b/packages/server/scripts/dev/manage.js
index 24cac981cf..5349158036 100644
--- a/packages/server/scripts/dev/manage.js
+++ b/packages/server/scripts/dev/manage.js
@@ -39,6 +39,7 @@ async function init() {
COUCH_DB_URL: "http://budibase:budibase@localhost:10000/db/",
REDIS_URL: "localhost:6379",
WORKER_URL: "http://localhost:4002",
+ INTERNAL_KEY: "budibase",
JWT_SECRET: "testsecret",
REDIS_PASSWORD: "budibase",
MINIO_ACCESS_KEY: "budibase",
diff --git a/packages/server/src/automations/actions.js b/packages/server/src/automations/actions.js
index a83608da30..0f40fd6aae 100644
--- a/packages/server/src/automations/actions.js
+++ b/packages/server/src/automations/actions.js
@@ -1,4 +1,4 @@
-const sendEmail = require("./steps/sendEmail")
+const sendEmail = require("./steps/sendgridEmail")
const createRow = require("./steps/createRow")
const updateRow = require("./steps/updateRow")
const deleteRow = require("./steps/deleteRow")
diff --git a/packages/server/src/automations/steps/sendSmtpEmail.js b/packages/server/src/automations/steps/sendSmtpEmail.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/server/src/automations/steps/sendEmail.js b/packages/server/src/automations/steps/sendgridEmail.js
similarity index 97%
rename from packages/server/src/automations/steps/sendEmail.js
rename to packages/server/src/automations/steps/sendgridEmail.js
index e08a3d8bc4..26c404257e 100644
--- a/packages/server/src/automations/steps/sendEmail.js
+++ b/packages/server/src/automations/steps/sendgridEmail.js
@@ -2,7 +2,7 @@ module.exports.definition = {
description: "Send an email",
tagline: "Send email to {{inputs.to}}",
icon: "ri-mail-open-line",
- name: "Send Email",
+ name: "Send Email (SendGrid)",
type: "ACTION",
stepId: "SEND_EMAIL",
inputs: {},
diff --git a/packages/server/src/environment.js b/packages/server/src/environment.js
index 061f38a985..412b05cab0 100644
--- a/packages/server/src/environment.js
+++ b/packages/server/src/environment.js
@@ -34,6 +34,7 @@ module.exports = {
USE_QUOTAS: process.env.USE_QUOTAS,
REDIS_URL: process.env.REDIS_URL,
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
+ INTERNAL_KEY: process.env.INTERNAL_KEY,
// environment
NODE_ENV: process.env.NODE_ENV,
JEST_WORKER_ID: process.env.JEST_WORKER_ID,
@@ -53,7 +54,6 @@ module.exports = {
BUDIBASE_API_KEY: process.env.BUDIBASE_API_KEY,
USERID_API_KEY: process.env.USERID_API_KEY,
DEPLOYMENT_CREDENTIALS_URL: process.env.DEPLOYMENT_CREDENTIALS_URL,
- HOSTING_KEY: process.env.HOSTING_KEY,
_set(key, value) {
process.env[key] = value
module.exports[key] = value
diff --git a/packages/server/src/utilities/workerRequests.js b/packages/server/src/utilities/workerRequests.js
index f96d48092e..6bf3bf6461 100644
--- a/packages/server/src/utilities/workerRequests.js
+++ b/packages/server/src/utilities/workerRequests.js
@@ -26,7 +26,7 @@ function request(ctx, request) {
? JSON.stringify(request.body)
: request.body
}
- if (ctx.headers) {
+ if (ctx && ctx.headers) {
request.headers.cookie = ctx.headers.cookie
}
return request
@@ -34,6 +34,19 @@ function request(ctx, request) {
exports.request = request
+exports.sendSmtpEmail = async (to, from, contents) => {
+ const response = await fetch(
+ checkSlashesInUrl(env.WORKER_URL + `/api/`),
+ request(null, {
+ method: "POST",
+ headers: {
+ "x-budibase-api-key": env.INTERNAL_KEY,
+ },
+ body: {},
+ })
+ )
+}
+
exports.getDeployedApps = async ctx => {
if (!env.SELF_HOSTED) {
throw "Can only check apps for self hosted environments"
diff --git a/packages/worker/scripts/dev/manage.js b/packages/worker/scripts/dev/manage.js
index 7322349b72..f2784750c6 100644
--- a/packages/worker/scripts/dev/manage.js
+++ b/packages/worker/scripts/dev/manage.js
@@ -8,6 +8,7 @@ async function init() {
SELF_HOSTED: 1,
PORT: 4002,
JWT_SECRET: "testsecret",
+ INTERNAL_KEY: "budibase",
MINIO_ACCESS_KEY: "budibase",
MINIO_SECRET_KEY: "budibase",
COUCH_DB_USER: "budibase",
diff --git a/packages/worker/src/constants/index.js b/packages/worker/src/constants/index.js
index ac0678eb2e..39dad128f5 100644
--- a/packages/worker/src/constants/index.js
+++ b/packages/worker/src/constants/index.js
@@ -27,7 +27,6 @@ const TemplateTypes = {
const EmailTemplatePurpose = {
BASE: "base",
- STYLES: "styles",
PASSWORD_RECOVERY: "password_recovery",
INVITATION: "invitation",
WELCOME: "welcome",
@@ -38,7 +37,6 @@ const TemplateBindings = {
PLATFORM_URL: "platformUrl",
COMPANY: "company",
LOGO_URL: "logoUrl",
- STYLES: "styles",
BODY: "body",
INVITE_URL: "inviteUrl",
EMAIL: "email",
@@ -51,15 +49,11 @@ const TemplateBindings = {
CURRENT_DATE: "currentDate",
RESET_CODE: "resetCode",
INVITE_CODE: "inviteCode",
+ CONTENTS: "contents",
}
const TemplateMetadata = {
[TemplateTypes.EMAIL]: [
- {
- name: "Styling",
- purpose: EmailTemplatePurpose.STYLES,
- bindings: ["url", "company", "companyUrl", "styles", "body"],
- },
{
name: "Base Format",
purpose: EmailTemplatePurpose.BASE,
@@ -76,6 +70,7 @@ const TemplateMetadata = {
{
name: "Custom",
purpose: EmailTemplatePurpose.CUSTOM,
+ bindings: ["contents"],
},
],
}
diff --git a/packages/worker/src/constants/templates/base.hbs b/packages/worker/src/constants/templates/base.hbs
index 38ceff023a..960d6faff1 100644
--- a/packages/worker/src/constants/templates/base.hbs
+++ b/packages/worker/src/constants/templates/base.hbs
@@ -9,7 +9,426 @@
+
+
+
+ {{ contents }}
+
+ |
+
+
+
+
\ No newline at end of file
diff --git a/packages/worker/src/constants/templates/index.js b/packages/worker/src/constants/templates/index.js
index 23e5508341..c677f504c4 100644
--- a/packages/worker/src/constants/templates/index.js
+++ b/packages/worker/src/constants/templates/index.js
@@ -17,10 +17,10 @@ exports.EmailTemplates = {
join(__dirname, "invitation.hbs")
),
[EmailTemplatePurpose.BASE]: readStaticFile(join(__dirname, "base.hbs")),
- [EmailTemplatePurpose.STYLES]: readStaticFile(join(__dirname, "style.hbs")),
[EmailTemplatePurpose.WELCOME]: readStaticFile(
join(__dirname, "welcome.hbs")
),
+ [EmailTemplatePurpose.CUSTOM]: readStaticFile(join(__dirname, "custom.hbs")),
}
exports.addBaseTemplates = (templates, type = null) => {
diff --git a/packages/worker/src/constants/templates/style.hbs b/packages/worker/src/constants/templates/style.hbs
deleted file mode 100644
index 244e901787..0000000000
--- a/packages/worker/src/constants/templates/style.hbs
+++ /dev/null
@@ -1,408 +0,0 @@
-/* Based on templates: https://github.com/wildbit/postmark-templates/blob/master/templates/plain */
-/* Base ------------------------------ */
-
-@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap');
-body {
-width: 100% !important;
-height: 100%;
-margin: 0;
--webkit-text-size-adjust: none;
-}
-
-a {
-color: #3869D4;
-}
-
-a img {
-border: none;
-}
-
-td {
-word-break: break-word;
-}
-
-.preheader {
-display: none !important;
-visibility: hidden;
-mso-hide: all;
-font-size: 1px;
-line-height: 1px;
-max-height: 0;
-max-width: 0;
-opacity: 0;
-overflow: hidden;
-}
-/* Type ------------------------------ */
-
-body,
-td,
-th {
-font-family: "Source Sans Pro", Helvetica, Arial, sans-serif;
-}
-
-h1 {
-margin-top: 0;
-color: #333333;
-font-size: 22px;
-font-weight: bold;
-text-align: left;
-}
-
-h2 {
-margin-top: 0;
-color: #333333;
-font-size: 16px;
-font-weight: bold;
-text-align: left;
-}
-
-h3 {
-margin-top: 0;
-color: #333333;
-font-size: 14px;
-font-weight: bold;
-text-align: left;
-}
-
-td,
-th {
-font-size: 16px;
-}
-
-p,
-ul,
-ol,
-blockquote {
-margin: .4em 0 1.1875em;
-font-size: 16px;
-line-height: 1.625;
-}
-
-p.sub {
-font-size: 13px;
-}
-/* Utilities ------------------------------ */
-
-.align-right {
-text-align: right;
-}
-
-.align-left {
-text-align: left;
-}
-
-.align-center {
-text-align: center;
-}
-/* Buttons ------------------------------ */
-
-.button {
-background-color: #3869D4;
-border-top: 10px solid #3869D4;
-border-right: 18px solid #3869D4;
-border-bottom: 10px solid #3869D4;
-border-left: 18px solid #3869D4;
-display: inline-block;
-color: #FFF;
-text-decoration: none;
-border-radius: 3px;
-box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
--webkit-text-size-adjust: none;
-box-sizing: border-box;
-}
-
-.button--green {
-background-color: #22BC66;
-border-top: 10px solid #22BC66;
-border-right: 18px solid #22BC66;
-border-bottom: 10px solid #22BC66;
-border-left: 18px solid #22BC66;
-}
-
-.button--red {
-background-color: #FF6136;
-border-top: 10px solid #FF6136;
-border-right: 18px solid #FF6136;
-border-bottom: 10px solid #FF6136;
-border-left: 18px solid #FF6136;
-}
-
-@media only screen and (max-width: 500px) {
-.button {
-width: 100% !important;
-text-align: center !important;
-}
-}
-/* Attribute list ------------------------------ */
-
-.attributes {
-margin: 0 0 21px;
-}
-
-.attributes_content {
-background-color: #F4F4F7;
-padding: 16px;
-}
-
-.attributes_item {
-padding: 0;
-}
-/* Related Items ------------------------------ */
-
-.related {
-width: 100%;
-margin: 0;
-padding: 25px 0 0 0;
--premailer-width: 100%;
--premailer-cellpadding: 0;
--premailer-cellspacing: 0;
-}
-
-.related_item {
-padding: 10px 0;
-color: #CBCCCF;
-font-size: 15px;
-line-height: 18px;
-}
-
-.related_item-title {
-display: block;
-margin: .5em 0 0;
-}
-
-.related_item-thumb {
-display: block;
-padding-bottom: 10px;
-}
-
-.related_heading {
-border-top: 1px solid #CBCCCF;
-text-align: center;
-padding: 25px 0 10px;
-}
-/* Discount Code ------------------------------ */
-
-.discount {
-width: 100%;
-margin: 0;
-padding: 24px;
--premailer-width: 100%;
--premailer-cellpadding: 0;
--premailer-cellspacing: 0;
-background-color: #F4F4F7;
-border: 2px dashed #CBCCCF;
-}
-
-.discount_heading {
-text-align: center;
-}
-
-.discount_body {
-text-align: center;
-font-size: 15px;
-}
-/* Social Icons ------------------------------ */
-
-.social {
-width: auto;
-}
-
-.social td {
-padding: 0;
-width: auto;
-}
-
-.social_icon {
-height: 20px;
-margin: 0 8px 10px 8px;
-padding: 0;
-}
-/* Data table ------------------------------ */
-
-.purchase {
-width: 100%;
-margin: 0;
-padding: 35px 0;
--premailer-width: 100%;
--premailer-cellpadding: 0;
--premailer-cellspacing: 0;
-}
-
-.purchase_content {
-width: 100%;
-margin: 0;
-padding: 25px 0 0 0;
--premailer-width: 100%;
--premailer-cellpadding: 0;
--premailer-cellspacing: 0;
-}
-
-.purchase_item {
-padding: 10px 0;
-color: #51545E;
-font-size: 15px;
-line-height: 18px;
-}
-
-.purchase_heading {
-padding-bottom: 8px;
-border-bottom: 1px solid #EAEAEC;
-}
-
-.purchase_heading p {
-margin: 0;
-color: #85878E;
-font-size: 12px;
-}
-
-.purchase_footer {
-padding-top: 15px;
-border-top: 1px solid #EAEAEC;
-}
-
-.purchase_total {
-margin: 0;
-text-align: right;
-font-weight: bold;
-color: #333333;
-}
-
-.purchase_total--label {
-padding: 0 15px 0 0;
-}
-
-body {
-background-color: #FFF;
-color: #333;
-}
-
-p {
-color: #333;
-}
-
-.email-wrapper {
-width: 100%;
-margin: 0;
-padding: 0;
--premailer-width: 100%;
--premailer-cellpadding: 0;
--premailer-cellspacing: 0;
-}
-
-.email-content {
-width: 100%;
-margin: 0;
-padding: 0;
--premailer-width: 100%;
--premailer-cellpadding: 0;
--premailer-cellspacing: 0;
-}
-/* Masthead ----------------------- */
-
-.email-masthead {
-padding: 25px 0;
-text-align: center;
-}
-
-.email-masthead_logo {
-width: 94px;
-}
-
-.email-masthead_name {
-font-size: 16px;
-font-weight: bold;
-color: #A8AAAF;
-text-decoration: none;
-text-shadow: 0 1px 0 white;
-}
-/* Body ------------------------------ */
-
-.email-body {
-width: 100%;
-margin: 0;
-padding: 0;
--premailer-width: 100%;
--premailer-cellpadding: 0;
--premailer-cellspacing: 0;
-}
-
-.email-body_inner {
-width: 570px;
-margin: 0 auto;
-padding: 0;
--premailer-width: 570px;
--premailer-cellpadding: 0;
--premailer-cellspacing: 0;
-}
-
-.email-footer {
-width: 570px;
-margin: 0 auto;
-padding: 0;
--premailer-width: 570px;
--premailer-cellpadding: 0;
--premailer-cellspacing: 0;
-text-align: center;
-}
-
-.email-footer p {
-color: #A8AAAF;
-}
-
-.body-action {
-width: 100%;
-margin: 30px auto;
-padding: 0;
--premailer-width: 100%;
--premailer-cellpadding: 0;
--premailer-cellspacing: 0;
-text-align: center;
-}
-
-.body-sub {
-margin-top: 25px;
-padding-top: 25px;
-border-top: 1px solid #EAEAEC;
-}
-
-.content-cell {
-padding: 35px;
-}
-/*Media Queries ------------------------------ */
-
-@media only screen and (max-width: 600px) {
-.email-body_inner,
-.email-footer {
-width: 100% !important;
-}
-}
-
-@media (prefers-color-scheme: dark) {
-body {
-background-color: #333333 !important;
-color: #FFF !important;
-}
-p,
-ul,
-ol,
-blockquote,
-h1,
-h2,
-h3,
-span,
-.purchase_item {
-color: #FFF !important;
-}
-.attributes_content,
-.discount {
-background-color: #222 !important;
-}
-.email-masthead_name {
-text-shadow: none !important;
-}
-}
-
-:root {
-color-scheme: light dark;
-supported-color-schemes: light dark;
-}
\ No newline at end of file
diff --git a/packages/worker/src/environment.js b/packages/worker/src/environment.js
index 04c010ce16..dda3e842f1 100644
--- a/packages/worker/src/environment.js
+++ b/packages/worker/src/environment.js
@@ -28,8 +28,8 @@ module.exports = {
SALT_ROUNDS: process.env.SALT_ROUNDS,
REDIS_URL: process.env.REDIS_URL,
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
+ INTERNAL_KEY: process.env.INTERNAL_KEY,
/* TODO: to remove - once deployment removed */
- SELF_HOST_KEY: process.env.SELF_HOST_KEY,
COUCH_DB_USERNAME: process.env.COUCH_DB_USERNAME,
COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD,
_set(key, value) {
diff --git a/packages/worker/src/utilities/email.js b/packages/worker/src/utilities/email.js
index e06b98a31e..7a2069bbb4 100644
--- a/packages/worker/src/utilities/email.js
+++ b/packages/worker/src/utilities/email.js
@@ -54,16 +54,14 @@ async function buildEmail(purpose, email, user) {
if (FULL_EMAIL_PURPOSES.indexOf(purpose) === -1) {
throw `Unable to build an email of type ${purpose}`
}
- let [base, styles, body] = await Promise.all([
+ let [base, body] = await Promise.all([
getTemplateByPurpose(TYPE, EmailTemplatePurpose.BASE),
- getTemplateByPurpose(TYPE, EmailTemplatePurpose.STYLES),
getTemplateByPurpose(TYPE, purpose),
])
- if (!base || !styles || !body) {
+ if (!base || !body) {
throw "Unable to build email, missing base components"
}
base = base.contents
- styles = styles.contents
body = body.contents
// if there is a link code needed this will retrieve it
@@ -75,11 +73,9 @@ async function buildEmail(purpose, 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,
})
}
@@ -117,11 +113,17 @@ exports.isEmailConfigured = async (groupId = null) => {
* @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|undefined} groupId If finer grain controls being used then this will lookup config for group.
- * @param {object|undefined} user if sending to an existing user the object can be provided, this is used in the context.
+ * @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.
* @return {Promise