From 5b1ae778da0b76b6880b9a1de1490427d51a7338 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 27 Sep 2021 14:57:22 +0100 Subject: [PATCH] fallback SMTP configuration for cloud --- .../api/controllers/row/ExternalRequest.ts | 2 +- .../src/automations/tests/automation.spec.js | 2 -- .../src/automations/tests/createRow.spec.js | 2 +- .../src/automations/tests/deleteRow.spec.js | 2 +- packages/server/src/definitions/datasource.ts | 4 ++-- packages/server/src/integrations/base/sql.ts | 2 +- packages/server/src/integrations/mysql.ts | 6 +++--- .../src/middleware/tests/usageQuota.spec.js | 6 +++--- packages/server/src/middleware/usageQuota.js | 16 +++++++------- packages/server/src/utilities/usageQuota.js | 4 +++- .../src/api/controllers/global/users.js | 2 ++ packages/worker/src/environment.js | 5 +++++ packages/worker/src/utilities/email.js | 21 ++++++++++++++++--- 13 files changed, 48 insertions(+), 26 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 12db55efdc..75c3e9b492 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -546,7 +546,7 @@ module External { }, meta: { table, - } + }, } // can't really use response right now const response = await makeExternalQuery(appId, json) diff --git a/packages/server/src/automations/tests/automation.spec.js b/packages/server/src/automations/tests/automation.spec.js index 83b7b81a75..9444995ca1 100644 --- a/packages/server/src/automations/tests/automation.spec.js +++ b/packages/server/src/automations/tests/automation.spec.js @@ -13,8 +13,6 @@ const { makePartial } = require("../../tests/utilities") const { cleanInputValues } = require("../automationUtils") const setup = require("./utilities") -usageQuota.getAPIKey.mockReturnValue({ apiKey: "test" }) - describe("Run through some parts of the automations system", () => { let config = setup.getConfig() diff --git a/packages/server/src/automations/tests/createRow.spec.js b/packages/server/src/automations/tests/createRow.spec.js index 1004711d87..a04fc7aad4 100644 --- a/packages/server/src/automations/tests/createRow.spec.js +++ b/packages/server/src/automations/tests/createRow.spec.js @@ -46,7 +46,7 @@ describe("test the create row action", () => { await setup.runStep(setup.actions.CREATE_ROW.stepId, { row }) - expect(usageQuota.update).toHaveBeenCalledWith(setup.apiKey, "rows", 1) + expect(usageQuota.update).toHaveBeenCalledWith("rows", 1) }) }) diff --git a/packages/server/src/automations/tests/deleteRow.spec.js b/packages/server/src/automations/tests/deleteRow.spec.js index a3d73d3bf6..21246f22d0 100644 --- a/packages/server/src/automations/tests/deleteRow.spec.js +++ b/packages/server/src/automations/tests/deleteRow.spec.js @@ -37,7 +37,7 @@ describe("test the delete row action", () => { it("check usage quota attempts", async () => { await setup.runInProd(async () => { await setup.runStep(setup.actions.DELETE_ROW.stepId, inputs) - expect(usageQuota.update).toHaveBeenCalledWith(setup.apiKey, "rows", -1) + expect(usageQuota.update).toHaveBeenCalledWith("rows", -1) }) }) diff --git a/packages/server/src/definitions/datasource.ts b/packages/server/src/definitions/datasource.ts index d7d4e77961..2daef8eda7 100644 --- a/packages/server/src/definitions/datasource.ts +++ b/packages/server/src/definitions/datasource.ts @@ -1,4 +1,4 @@ -import {Table} from "./common"; +import { Table } from "./common" export enum Operation { CREATE = "CREATE", @@ -139,7 +139,7 @@ export interface QueryJson { paginate?: PaginationJson body?: object meta?: { - table?: Table, + table?: Table } extra?: { idFilter?: SearchFilters diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 91af3e1a85..c5e9bdb0bb 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -148,7 +148,7 @@ function buildRead(knex: Knex, json: QueryJson, limit: number): KnexQuery { if (!resource) { resource = { fields: [] } } - let selectStatement: string|string[] = "*" + let selectStatement: string | string[] = "*" // handle select if (resource.fields && resource.fields.length > 0) { // select the resources as the format "table.columnName" - this is what is provided diff --git a/packages/server/src/integrations/mysql.ts b/packages/server/src/integrations/mysql.ts index d43ae86bea..c17cca0745 100644 --- a/packages/server/src/integrations/mysql.ts +++ b/packages/server/src/integrations/mysql.ts @@ -108,7 +108,7 @@ module MySQLModule { client: any, query: SqlQuery, connect: boolean = true - ): Promise { + ): Promise { // Node MySQL is callback based, so we must wrap our call in a promise return new Promise((resolve, reject) => { if (connect) { @@ -252,9 +252,9 @@ module MySQLModule { json.extra = { idFilter: { equal: { - [primaryKey]: results.insertId + [primaryKey]: results.insertId, }, - } + }, } return json } diff --git a/packages/server/src/middleware/tests/usageQuota.spec.js b/packages/server/src/middleware/tests/usageQuota.spec.js index 97d9c7794a..d828f2ca60 100644 --- a/packages/server/src/middleware/tests/usageQuota.spec.js +++ b/packages/server/src/middleware/tests/usageQuota.spec.js @@ -39,7 +39,7 @@ class TestConfiguration { if (bool) { env.isDev = () => false env.isProd = () => true - this.ctx.auth = { apiKey: "test" } + this.ctx.user = { tenantId: "test" } } else { env.isDev = () => true env.isProd = () => false @@ -114,7 +114,7 @@ describe("usageQuota middleware", () => { await config.executeMiddleware() - expect(usageQuota.update).toHaveBeenCalledWith("test", "rows", 1) + expect(usageQuota.update).toHaveBeenCalledWith("rows", 1) expect(config.next).toHaveBeenCalled() }) @@ -131,7 +131,7 @@ describe("usageQuota middleware", () => { ]) await config.executeMiddleware() - expect(usageQuota.update).toHaveBeenCalledWith("test", "storage", 10100) + expect(usageQuota.update).toHaveBeenCalledWith("storage", 10100) expect(config.next).toHaveBeenCalled() }) }) \ No newline at end of file diff --git a/packages/server/src/middleware/usageQuota.js b/packages/server/src/middleware/usageQuota.js index d18ffae205..d56a960615 100644 --- a/packages/server/src/middleware/usageQuota.js +++ b/packages/server/src/middleware/usageQuota.js @@ -50,15 +50,15 @@ module.exports = async (ctx, next) => { } // update usage for uploads to be the total size - // if (property === usageQuota.Properties.UPLOAD) { - // const files = - // ctx.request.files.file.length > 1 - // ? Array.from(ctx.request.files.file) - // : [ctx.request.files.file] - // usage = files.map(file => file.size).reduce((total, size) => total + size) - // } + if (property === usageQuota.Properties.UPLOAD) { + const files = + ctx.request.files.file.length > 1 + ? Array.from(ctx.request.files.file) + : [ctx.request.files.file] + usage = files.map(file => file.size).reduce((total, size) => total + size) + } try { - await usageQuota.update(ctx.user.tenantId, property, usage) + await usageQuota.update(property, usage) return next() } catch (err) { ctx.throw(400, err) diff --git a/packages/server/src/utilities/usageQuota.js b/packages/server/src/utilities/usageQuota.js index b98fb923cd..fb3f61f2f6 100644 --- a/packages/server/src/utilities/usageQuota.js +++ b/packages/server/src/utilities/usageQuota.js @@ -12,6 +12,7 @@ exports.Properties = { USER: "users", AUTOMATION: "automationRuns", APPS: "apps", + EMAILS: "emails", } /** @@ -28,7 +29,8 @@ exports.update = async (property, usage) => { try { const db = getGlobalDB() const quota = await db.get("usage_quota") - // TODO: check if the quota needs reset + + // Check if the quota needs reset if (Date.now() >= quota.quotaReset) { quota.quotaReset = getNewQuotaReset() for (let prop of Object.keys(quota.usageQuota)) { diff --git a/packages/worker/src/api/controllers/global/users.js b/packages/worker/src/api/controllers/global/users.js index 61c2981280..c714f2b1ca 100644 --- a/packages/worker/src/api/controllers/global/users.js +++ b/packages/worker/src/api/controllers/global/users.js @@ -151,6 +151,7 @@ exports.adminUser = async ctx => { apps: 0, users: 0, views: 0, + emails: 0, }, usageLimits: { automationRuns: 1000, @@ -158,6 +159,7 @@ exports.adminUser = async ctx => { apps: 4, storage: 1000, users: 10, + emails: 50, }, }) // } diff --git a/packages/worker/src/environment.js b/packages/worker/src/environment.js index 646536f292..5f0556efc4 100644 --- a/packages/worker/src/environment.js +++ b/packages/worker/src/environment.js @@ -33,6 +33,11 @@ module.exports = { INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, MULTI_TENANCY: process.env.MULTI_TENANCY, ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL, + SMTP_USER: process.env.SMTP_USER, + SMTP_PASSWORD: process.env.SMTP_PASSWORD, + SMTP_HOST: process.env.SMTP_HOST, + SMTP_PORT: process.env.SMTP_PORT, + SMTP_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS, _set(key, value) { process.env[key] = value module.exports[key] = value diff --git a/packages/worker/src/utilities/email.js b/packages/worker/src/utilities/email.js index d22933ef36..5843cb28ea 100644 --- a/packages/worker/src/utilities/email.js +++ b/packages/worker/src/utilities/email.js @@ -1,4 +1,5 @@ const nodemailer = require("nodemailer") +const env = require("../environment") const { getScopedConfig } = require("@budibase/auth/db") const { EmailTemplatePurpose, TemplateTypes, Configs } = require("../constants") const { getTemplateByPurpose } = require("../constants/templates") @@ -110,7 +111,21 @@ async function getSmtpConfiguration(db, workspaceId = null) { if (workspaceId) { params.workspace = workspaceId } - return getScopedConfig(db, params) + + if (!env.SMTP_FALLBACK_ENABLED) { + return getScopedConfig(db, params) + } else { + // Use an SMTP fallback configuration from env variables + return { + port: env.SMTP_PORT, + host: env.SMTP_HOST, + secure: false, + auth: { + user: env.SMTP_USER, + pass: env.SMTP_PASSWORD, + }, + } + } } /** @@ -118,8 +133,8 @@ async function getSmtpConfiguration(db, workspaceId = null) { * @return {Promise} returns true if there is a configuration that can be used. */ exports.isEmailConfigured = async (workspaceId = null) => { - // when "testing" simply return true - if (TEST_MODE) { + // when "testing" or smtp fallback is enabled simply return true + if (TEST_MODE || env.SMTP_FALLBACK_ENABLED) { return true } const db = getGlobalDB()