diff --git a/.github/workflows/release-develop.yml b/.github/workflows/release-develop.yml
index 503fc53194..a7bf041eb5 100644
--- a/.github/workflows/release-develop.yml
+++ b/.github/workflows/release-develop.yml
@@ -48,19 +48,8 @@ jobs:
- run: yarn install --frozen-lockfile
- name: Update versions
- run: |
- version=$(cat lerna.json \
- | grep version \
- | head -1 \
- | awk -F: '{gsub(/"/,"",$2);gsub(/[[:space:]]*/,"",$2); print $2}' \
- | sed 's/[",]//g')
- echo "Setting version $version"
- yarn lerna exec "yarn version --no-git-tag-version --new-version=$version"
- echo "Updating dependencies"
- node scripts/syncLocalDependencies.js $version
- echo "Syncing yarn workspace"
- yarn
- - run: yarn build --configuration=production
+ run: ./scripts/updateVersions.sh
+ - run: yarn build
- run: yarn build:sdk
- name: Publish budibase packages to NPM
diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml
index a4e679e7bf..68625ad7af 100644
--- a/.github/workflows/release-master.yml
+++ b/.github/workflows/release-master.yml
@@ -41,20 +41,9 @@ jobs:
- run: yarn install --frozen-lockfile
- name: Update versions
- run: |
- version=$(cat lerna.json \
- | grep version \
- | head -1 \
- | awk -F: '{gsub(/"/,"",$2);gsub(/[[:space:]]*/,"",$2); print $2}' \
- | sed 's/[",]//g')
- echo "Setting version $version"
- yarn lerna exec "yarn version --no-git-tag-version --new-version=$version"
- echo "Updating dependencies"
- node scripts/syncLocalDependencies.js $version
- echo "Syncing yarn workspace"
- yarn
+ run: ./scripts/updateVersions.sh
- run: yarn lint
- - run: yarn build --configuration=production
+ - run: yarn build
- run: yarn build:sdk
- name: Publish budibase packages to NPM
diff --git a/.github/workflows/release-selfhost.yml b/.github/workflows/release-selfhost.yml
index 7a56748dfb..f4524e99dc 100644
--- a/.github/workflows/release-selfhost.yml
+++ b/.github/workflows/release-selfhost.yml
@@ -17,6 +17,8 @@ jobs:
- uses: actions/checkout@v2
with:
+ submodules: true
+ token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
fetch-depth: 0
- name: Fail if tag is not in master
diff --git a/.github/workflows/release-singleimage.yml b/.github/workflows/release-singleimage.yml
index fbf972a866..5408b48ef8 100644
--- a/.github/workflows/release-singleimage.yml
+++ b/.github/workflows/release-singleimage.yml
@@ -5,7 +5,7 @@ on:
env:
CI: true
- PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }}
+ PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
REGISTRY_URL: registry.hub.docker.com
jobs:
build:
@@ -24,6 +24,8 @@ jobs:
- name: "Checkout"
uses: actions/checkout@v2
with:
+ submodules: true
+ token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
fetch-depth: 0
- name: Fail if tag is not in master
@@ -32,7 +34,7 @@ jobs:
echo "Tag is not in master. This pipeline can only execute tags that are present on the master branch"
exit 1
fi
-
+
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
@@ -44,10 +46,12 @@ jobs:
uses: docker/setup-buildx-action@v1
- name: Run Yarn
run: yarn
- - name: Run Yarn Bootstrap
- run: yarn bootstrap
+ - name: Update versions
+ run: ./scripts/updateVersions.sh
- name: Runt Yarn Lint
run: yarn lint
+ - name: Update versions
+ run: ./scripts/updateVersions.sh
- name: Run Yarn Build
run: yarn build:docker:pre
- name: Login to Docker Hub
diff --git a/hosting/single/Dockerfile b/hosting/single/Dockerfile
index 64a6b01365..56df8185a9 100644
--- a/hosting/single/Dockerfile
+++ b/hosting/single/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:16-slim as build
+FROM node:14-slim as build
# install node-gyp dependencies
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends apt-utils cron g++ make python
@@ -11,12 +11,16 @@ RUN chmod +x /cleanup.sh
# build server
WORKDIR /app
ADD packages/server .
-RUN yarn install --frozen-lockfile --production=true && /cleanup.sh
+COPY yarn.lock .
+RUN yarn install --production=true
+RUN /cleanup.sh
# build worker
WORKDIR /worker
ADD packages/worker .
-RUN yarn install --frozen-lockfile --production=true && /cleanup.sh
+COPY yarn.lock .
+RUN yarn install --production=true
+RUN /cleanup.sh
FROM budibase/couchdb
ARG TARGETARCH
diff --git a/hosting/single/runner.sh b/hosting/single/runner.sh
index 0bd377cd7f..d980202f88 100644
--- a/hosting/single/runner.sh
+++ b/hosting/single/runner.sh
@@ -17,6 +17,7 @@ declare -a DOCKER_VARS=("APP_PORT" "APPS_URL" "ARCHITECTURE" "BUDIBASE_ENVIRONME
[[ -z "${WORKER_PORT}" ]] && export WORKER_PORT=4002
[[ -z "${WORKER_URL}" ]] && export WORKER_URL=http://localhost:4002
[[ -z "${APPS_URL}" ]] && export APPS_URL=http://localhost:4001
+[[ -z "${SERVER_TOP_LEVEL_PATH}" ]] && export SERVER_TOP_LEVEL_PATH=/app
# export CUSTOM_DOMAIN=budi001.custom.com
# Azure App Service customisations
diff --git a/lerna.json b/lerna.json
index 21ffea6191..eded0d214d 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "2.7.1-alpha.1",
+ "version": "2.7.7-alpha.2",
"npmClient": "yarn",
"packages": [
"packages/backend-core",
diff --git a/package.json b/package.json
index ffb3fa775a..56f015f8c0 100644
--- a/package.json
+++ b/package.json
@@ -52,7 +52,7 @@
"dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && lerna run --stream --parallel dev:builder --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker",
"dev:server": "yarn run kill-server && lerna run --stream --parallel dev:builder --scope @budibase/worker --scope @budibase/server",
"dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream --parallel dev:built",
- "dev:docker": "yarn build && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0",
+ "dev:docker": "yarn build:docker:pre && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0",
"test": "lerna run --stream test --stream",
"lint:eslint": "eslint packages && eslint qa-core",
"lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\" && prettier --check \"qa-core/**/*.{js,ts,svelte}\"",
@@ -62,7 +62,7 @@
"lint:fix": "yarn run lint:fix:prettier && yarn run lint:fix:eslint",
"build:specs": "lerna run --stream specs",
"build:docker": "lerna run --stream build:docker && yarn build:docker:proxy && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh $BUDIBASE_RELEASE_VERSION && cd -",
- "build:docker:pre": "lerna run --stream build && lerna run --stream predocker",
+ "build:docker:pre": "yarn build && lerna run --stream predocker",
"build:docker:proxy": "docker build hosting/proxy -t proxy-service",
"build:docker:selfhost": "lerna run --stream build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh latest && cd -",
"build:docker:develop": "node scripts/pinVersions && lerna run --stream build:docker && yarn build:docker:proxy && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh develop && cd -",
diff --git a/packages/backend-core/src/middleware/passport/datasource/google.ts b/packages/backend-core/src/middleware/passport/datasource/google.ts
index 2f91e01d9a..ae6b3b4913 100644
--- a/packages/backend-core/src/middleware/passport/datasource/google.ts
+++ b/packages/backend-core/src/middleware/passport/datasource/google.ts
@@ -1,10 +1,11 @@
import * as google from "../sso/google"
import { Cookie } from "../../../constants"
-import { clearCookie, getCookie } from "../../../utils"
import * as configs from "../../../configs"
-import { BBContext, SSOProfile } from "@budibase/types"
+import * as cache from "../../../cache"
+import * as utils from "../../../utils"
+import { UserCtx, SSOProfile } from "@budibase/types"
import { ssoSaveUserNoOp } from "../sso/sso"
-import { cache, utils } from "../../../"
+
const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy
type Passport = {
@@ -22,7 +23,7 @@ async function fetchGoogleCreds() {
export async function preAuth(
passport: Passport,
- ctx: BBContext,
+ ctx: UserCtx,
next: Function
) {
// get the relevant config
@@ -49,7 +50,7 @@ export async function preAuth(
export async function postAuth(
passport: Passport,
- ctx: BBContext,
+ ctx: UserCtx,
next: Function
) {
// get the relevant config
@@ -57,7 +58,7 @@ export async function postAuth(
const platformUrl = await configs.getPlatformUrl({ tenantAware: false })
let callbackUrl = `${platformUrl}/api/global/auth/datasource/google/callback`
- const authStateCookie = getCookie(ctx, Cookie.DatasourceAuth)
+ const authStateCookie = utils.getCookie(ctx, Cookie.DatasourceAuth)
return passport.authenticate(
new GoogleStrategy(
@@ -72,7 +73,7 @@ export async function postAuth(
_profile: SSOProfile,
done: Function
) => {
- clearCookie(ctx, Cookie.DatasourceAuth)
+ utils.clearCookie(ctx, Cookie.DatasourceAuth)
done(null, { accessToken, refreshToken })
}
),
diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
index 6ab750c3d6..a8fa700b90 100644
--- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
+++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
@@ -13,6 +13,8 @@
Modal,
notifications,
Icon,
+ Checkbox,
+ DatePicker,
} from "@budibase/bbui"
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
import { automationStore, selectedAutomation } from "builderStore"
@@ -306,6 +308,11 @@
drawer.hide()
}
+ function canShowField(key, value) {
+ const dependsOn = value.dependsOn
+ return !dependsOn || !!inputData[dependsOn]
+ }
+
onMount(async () => {
try {
await environment.loadVariables()
@@ -317,210 +324,233 @@
{#each deprecatedSchemaProperties as [key, value]}
-
- {#if key !== "fields"}
-
- {/if}
- {#if value.type === "string" && value.enum}
-
+ {/if}
{/each}
diff --git a/packages/server/Dockerfile b/packages/server/Dockerfile
index 5880a84326..223b96a2ab 100644
--- a/packages/server/Dockerfile
+++ b/packages/server/Dockerfile
@@ -14,7 +14,7 @@ ENV SERVICE=app-service
ENV POSTHOG_TOKEN=phc_bIjZL7oh2GEUd2vqvTBH8WvrX0fWTFQMs6H5KQxiUxU
ENV TENANT_FEATURE_FLAGS=*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR
ENV ACCOUNT_PORTAL_URL=https://account.budibase.app
-
+ENV TOP_LEVEL_PATH=/
# handle node-gyp
RUN apt-get update \
@@ -27,7 +27,8 @@ COPY scripts/integrations/oracle/ scripts/integrations/oracle/
RUN /bin/bash -e scripts/integrations/oracle/instantclient/linux/x86-64/install.sh
COPY package.json .
-RUN yarn install --frozen-lockfile --production=true
+COPY dist/yarn.lock .
+RUN yarn install --production=true
# Remove unneeded data from file system to reduce image size
RUN yarn cache clean && apt-get remove -y --purge --auto-remove g++ make python \
&& rm -rf /tmp/* /root/.node-gyp /usr/local/lib/node_modules/npm/node_modules/node-gyp
diff --git a/packages/server/package.json b/packages/server/package.json
index c73bba0f16..6e74de6afa 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -18,7 +18,7 @@
"test": "bash scripts/test.sh",
"test:memory": "jest --maxWorkers=2 --logHeapUsage --forceExit",
"test:watch": "jest --watch",
- "predocker": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client && yarn build --configuration=production",
+ "predocker": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client && yarn build && cp ../../yarn.lock ./dist/",
"build:docker": "yarn predocker && docker build . -t app-service --label version=$BUDIBASE_RELEASE_VERSION",
"build:docs": "node ./scripts/docs/generate.js open",
"run:docker": "node dist/index.js",
diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts
index bddde485ef..49d2abfc79 100644
--- a/packages/server/src/api/controllers/static/index.ts
+++ b/packages/server/src/api/controllers/static/index.ts
@@ -1,6 +1,6 @@
require("svelte/register")
-import { resolve, join } from "../../../utilities/centralPath"
+import { join } from "../../../utilities/centralPath"
const uuid = require("uuid")
import { ObjectStoreBuckets } from "../../../constants"
import { processString } from "@budibase/string-templates"
@@ -49,7 +49,7 @@ export const toggleBetaUiFeature = async function (ctx: any) {
return
}
- let builderPath = resolve(TOP_LEVEL_PATH, "new_design_ui")
+ let builderPath = join(TOP_LEVEL_PATH, "new_design_ui")
// // download it from S3
if (!fs.existsSync(builderPath)) {
@@ -67,7 +67,7 @@ export const toggleBetaUiFeature = async function (ctx: any) {
}
export const serveBuilder = async function (ctx: any) {
- const builderPath = resolve(TOP_LEVEL_PATH, "builder")
+ const builderPath = join(TOP_LEVEL_PATH, "builder")
await send(ctx, ctx.file, { root: builderPath })
}
diff --git a/packages/server/src/api/controllers/table/external.ts b/packages/server/src/api/controllers/table/external.ts
index a00e65687f..ee789ddd3a 100644
--- a/packages/server/src/api/controllers/table/external.ts
+++ b/packages/server/src/api/controllers/table/external.ts
@@ -208,6 +208,7 @@ export async function save(ctx: UserCtx) {
let tableToSave: TableRequest = {
type: "table",
_id: buildExternalTableId(datasourceId, inputs.name),
+ sourceId: datasourceId,
...inputs,
}
diff --git a/packages/server/src/automations/steps/sendSmtpEmail.ts b/packages/server/src/automations/steps/sendSmtpEmail.ts
index 448c374ce8..f1ce3a85c2 100644
--- a/packages/server/src/automations/steps/sendSmtpEmail.ts
+++ b/packages/server/src/automations/steps/sendSmtpEmail.ts
@@ -48,6 +48,35 @@ export const definition: AutomationStepSchema = {
type: AutomationIOType.STRING,
title: "HTML Contents",
},
+ addInvite: {
+ type: AutomationIOType.BOOLEAN,
+ title: "Add calendar invite",
+ },
+ startTime: {
+ type: AutomationIOType.DATE,
+ title: "Start Time",
+ dependsOn: "addInvite",
+ },
+ endTime: {
+ type: AutomationIOType.DATE,
+ title: "End Time",
+ dependsOn: "addInvite",
+ },
+ summary: {
+ type: AutomationIOType.STRING,
+ title: "Meeting Summary",
+ dependsOn: "addInvite",
+ },
+ location: {
+ type: AutomationIOType.STRING,
+ title: "Location",
+ dependsOn: "addInvite",
+ },
+ url: {
+ type: AutomationIOType.STRING,
+ title: "URL",
+ dependsOn: "addInvite",
+ },
},
required: ["to", "from", "subject", "contents"],
},
@@ -68,21 +97,43 @@ export const definition: AutomationStepSchema = {
}
export async function run({ inputs }: AutomationStepInput) {
- let { to, from, subject, contents, cc, bcc } = inputs
+ let {
+ to,
+ from,
+ subject,
+ contents,
+ cc,
+ bcc,
+ addInvite,
+ startTime,
+ endTime,
+ summary,
+ location,
+ url,
+ } = inputs
if (!contents) {
contents = "No content
"
}
to = to || undefined
try {
- let response = await sendSmtpEmail(
+ let response = await sendSmtpEmail({
to,
from,
subject,
contents,
cc,
bcc,
- true
- )
+ automation: true,
+ invite: addInvite
+ ? {
+ startTime,
+ endTime,
+ summary,
+ location,
+ url,
+ }
+ : undefined,
+ })
return {
success: true,
response,
diff --git a/packages/server/src/automations/tests/sendSmtpEmail.spec.js b/packages/server/src/automations/tests/sendSmtpEmail.spec.js
deleted file mode 100644
index 998a1f54b2..0000000000
--- a/packages/server/src/automations/tests/sendSmtpEmail.spec.js
+++ /dev/null
@@ -1,71 +0,0 @@
-
-function generateResponse(to, from) {
- return {
- "success": true,
- "response": {
- "accepted": [
- to
- ],
- "envelope": {
- "from": from,
- "to": [
- to
- ]
- },
- "message": `Email sent to ${to}.`
- }
-
- }
-}
-
-const mockFetch = jest.fn(() => ({
- headers: {
- raw: () => {
- return { "content-type": ["application/json"] }
- },
- get: () => ["application/json"],
- },
- json: jest.fn(() => response),
- status: 200,
- text: jest.fn(),
-}))
-jest.mock("node-fetch", () => mockFetch)
-const setup = require("./utilities")
-
-
-describe("test the outgoing webhook action", () => {
- let inputs
- let config = setup.getConfig()
- beforeAll(async () => {
- await config.init()
- })
-
- afterAll(setup.afterAll)
-
- it("should be able to run the action", async () => {
- inputs = {
- to: "user1@test.com",
- from: "admin@test.com",
- subject: "hello",
- contents: "testing",
- }
- let resp = generateResponse(inputs.to, inputs.from)
- mockFetch.mockImplementationOnce(() => ({
- headers: {
- raw: () => {
- return { "content-type": ["application/json"] }
- },
- get: () => ["application/json"],
- },
- json: jest.fn(() => resp),
- status: 200,
- text: jest.fn(),
- }))
- const res = await setup.runStep(setup.actions.SEND_EMAIL_SMTP.stepId, inputs)
- expect(res.response).toEqual(resp)
- expect(res.success).toEqual(true)
-
- })
-
-
-})
diff --git a/packages/server/src/automations/tests/sendSmtpEmail.spec.ts b/packages/server/src/automations/tests/sendSmtpEmail.spec.ts
new file mode 100644
index 0000000000..da274dfffc
--- /dev/null
+++ b/packages/server/src/automations/tests/sendSmtpEmail.spec.ts
@@ -0,0 +1,74 @@
+import * as workerRequests from "../../utilities/workerRequests"
+
+jest.mock("../../utilities/workerRequests", () => ({
+ sendSmtpEmail: jest.fn(),
+}))
+
+function generateResponse(to: string, from: string) {
+ return {
+ success: true,
+ response: {
+ accepted: [to],
+ envelope: {
+ from: from,
+ to: [to],
+ },
+ message: `Email sent to ${to}.`,
+ },
+ }
+}
+
+const setup = require("./utilities")
+
+describe("test the outgoing webhook action", () => {
+ let inputs
+ let config = setup.getConfig()
+ beforeAll(async () => {
+ await config.init()
+ })
+
+ afterAll(setup.afterAll)
+
+ it("should be able to run the action", async () => {
+ jest
+ .spyOn(workerRequests, "sendSmtpEmail")
+ .mockImplementationOnce(async () =>
+ generateResponse("user1@test.com", "admin@test.com")
+ )
+ const invite = {
+ startTime: new Date(),
+ endTime: new Date(),
+ summary: "summary",
+ location: "location",
+ url: "url",
+ }
+ inputs = {
+ to: "user1@test.com",
+ from: "admin@test.com",
+ subject: "hello",
+ contents: "testing",
+ cc: "cc",
+ bcc: "bcc",
+ addInvite: true,
+ ...invite,
+ }
+ let resp = generateResponse(inputs.to, inputs.from)
+ const res = await setup.runStep(
+ setup.actions.SEND_EMAIL_SMTP.stepId,
+ inputs
+ )
+ expect(res.response).toEqual(resp)
+ expect(res.success).toEqual(true)
+ expect(workerRequests.sendSmtpEmail).toHaveBeenCalledTimes(1)
+ expect(workerRequests.sendSmtpEmail).toHaveBeenCalledWith({
+ to: "user1@test.com",
+ from: "admin@test.com",
+ subject: "hello",
+ contents: "testing",
+ cc: "cc",
+ bcc: "bcc",
+ invite,
+ automation: true,
+ })
+ })
+})
diff --git a/packages/server/src/environment.ts b/packages/server/src/environment.ts
index 0ba708e7bb..64b342d577 100644
--- a/packages/server/src/environment.ts
+++ b/packages/server/src/environment.ts
@@ -98,7 +98,8 @@ const environment = {
isInThread: () => {
return process.env.FORKED_PROCESS
},
- TOP_LEVEL_PATH: process.env.TOP_LEVEL_PATH,
+ TOP_LEVEL_PATH:
+ process.env.TOP_LEVEL_PATH || process.env.SERVER_TOP_LEVEL_PATH,
}
// threading can cause memory issues with node-ts in development
diff --git a/packages/server/src/utilities/fileSystem/filesystem.ts b/packages/server/src/utilities/fileSystem/filesystem.ts
index 1c6971b1e8..9434f071d4 100644
--- a/packages/server/src/utilities/fileSystem/filesystem.ts
+++ b/packages/server/src/utilities/fileSystem/filesystem.ts
@@ -1,14 +1,14 @@
import { PathLike } from "fs"
import fs from "fs"
import { budibaseTempDir } from "../budibaseDir"
-import { join } from "path"
+import { resolve, join } from "path"
import env from "../../environment"
import tar from "tar"
import environment from "../../environment"
const uuid = require("uuid/v4")
export const TOP_LEVEL_PATH =
- environment.TOP_LEVEL_PATH || join(__dirname, "..", "..", "..")
+ environment.TOP_LEVEL_PATH || resolve(join(__dirname, "..", "..", ".."))
/**
* Upon first startup of instance there may not be everything we need in tmp directory, set it up.
diff --git a/packages/server/src/utilities/workerRequests.ts b/packages/server/src/utilities/workerRequests.ts
index 82e1aac428..5230e25bf7 100644
--- a/packages/server/src/utilities/workerRequests.ts
+++ b/packages/server/src/utilities/workerRequests.ts
@@ -9,7 +9,7 @@ import {
env as coreEnv,
} from "@budibase/backend-core"
import { updateAppRole } from "./global"
-import { BBContext, User } from "@budibase/types"
+import { BBContext, User, EmailInvite } from "@budibase/types"
export function request(ctx?: BBContext, request?: any) {
if (!request.headers) {
@@ -65,15 +65,25 @@ async function checkResponse(
}
// have to pass in the tenant ID as this could be coming from an automation
-export async function sendSmtpEmail(
- to: string,
- from: string,
- subject: string,
- contents: string,
- cc: string,
- bcc: string,
+export async function sendSmtpEmail({
+ to,
+ from,
+ subject,
+ contents,
+ cc,
+ bcc,
+ automation,
+ invite,
+}: {
+ to: string
+ from: string
+ subject: string
+ contents: string
+ cc: string
+ bcc: string
automation: boolean
-) {
+ invite?: EmailInvite
+}) {
// tenant ID will be set in header
const response = await fetch(
checkSlashesInUrl(env.WORKER_URL + `/api/global/email/send`),
@@ -88,6 +98,7 @@ export async function sendSmtpEmail(
bcc,
purpose: "custom",
automation,
+ invite,
},
})
)
diff --git a/packages/types/src/documents/app/automation.ts b/packages/types/src/documents/app/automation.ts
index 193a5a414a..d1dbbec21b 100644
--- a/packages/types/src/documents/app/automation.ts
+++ b/packages/types/src/documents/app/automation.ts
@@ -1,5 +1,6 @@
import { Document } from "../document"
import { EventEmitter } from "events"
+import { User } from "../global"
export enum AutomationIOType {
OBJECT = "object",
@@ -8,6 +9,7 @@ export enum AutomationIOType {
NUMBER = "number",
ARRAY = "array",
JSON = "json",
+ DATE = "date",
}
export enum AutomationCustomIOType {
@@ -66,6 +68,33 @@ export enum AutomationActionStepId {
integromat = "integromat",
}
+export interface EmailInvite {
+ startTime: Date
+ endTime: Date
+ summary: string
+ location?: string
+ url?: string
+}
+
+export interface SendEmailOpts {
+ // workspaceId If finer grain controls being used then this will lookup config for workspace.
+ workspaceId?: string
+ // user If sending to an existing user the object can be provided, this is used in the context.
+ user: User
+ // from If sending from an address that is not what is configured in the SMTP config.
+ 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
+ invite?: EmailInvite
+}
+
export const AutomationStepIdArray = [
...Object.values(AutomationActionStepId),
...Object.values(AutomationTriggerStepId),
@@ -90,6 +119,7 @@ interface BaseIOStructure {
customType?: AutomationCustomIOType
title?: string
description?: string
+ dependsOn?: string
enum?: string[]
pretty?: string[]
properties?: {
diff --git a/packages/worker/Dockerfile b/packages/worker/Dockerfile
index a80ae00af1..dd98f3b7f8 100644
--- a/packages/worker/Dockerfile
+++ b/packages/worker/Dockerfile
@@ -13,7 +13,8 @@ RUN yarn global add pm2
COPY package.json .
-RUN yarn install --frozen-lockfile --production=true
+COPY dist/yarn.lock .
+RUN yarn install --production=true
# Remove unneeded data from file system to reduce image size
RUN apk del .gyp \
&& yarn cache clean
diff --git a/packages/worker/package.json b/packages/worker/package.json
index a90a4eb683..7e8715b162 100644
--- a/packages/worker/package.json
+++ b/packages/worker/package.json
@@ -19,7 +19,7 @@
"run:docker": "node dist/index.js",
"debug": "yarn build && node --expose-gc --inspect=9223 dist/index.js",
"run:docker:cluster": "pm2-runtime start pm2.config.js",
- "predocker": "yarn build --configuration=production",
+ "predocker": "yarn build && cp ../../yarn.lock ./dist/",
"build:docker": "yarn predocker && docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION",
"dev:stack:init": "node ./scripts/dev/manage.js init",
"dev:builder": "npm run dev:stack:init && rimraf dist/ && nodemon",
@@ -53,6 +53,7 @@
"elastic-apm-node": "3.38.0",
"global-agent": "3.0.0",
"got": "11.8.3",
+ "ical-generator": "4.1.0",
"joi": "17.6.0",
"koa": "2.13.4",
"koa-body": "4.2.0",
diff --git a/packages/worker/src/api/controllers/global/email.ts b/packages/worker/src/api/controllers/global/email.ts
index f5acad9a66..e352ef0b87 100644
--- a/packages/worker/src/api/controllers/global/email.ts
+++ b/packages/worker/src/api/controllers/global/email.ts
@@ -14,6 +14,7 @@ export async function sendEmail(ctx: BBContext) {
cc,
bcc,
automation,
+ invite,
} = ctx.request.body
let user
if (userId) {
@@ -29,6 +30,7 @@ export async function sendEmail(ctx: BBContext) {
cc,
bcc,
automation,
+ invite,
})
ctx.body = {
...response,
diff --git a/packages/worker/src/utilities/email.ts b/packages/worker/src/utilities/email.ts
index e47d352f87..5d596b0bde 100644
--- a/packages/worker/src/utilities/email.ts
+++ b/packages/worker/src/utilities/email.ts
@@ -4,28 +4,11 @@ import { getTemplateByPurpose, EmailTemplates } from "../constants/templates"
import { getSettingsTemplateContext } from "./templates"
import { processString } from "@budibase/string-templates"
import { getResetPasswordCode, getInviteCode } from "./redis"
-import { User, SMTPInnerConfig } from "@budibase/types"
+import { User, SendEmailOpts, SMTPInnerConfig } from "@budibase/types"
import { configs } from "@budibase/backend-core"
+import ical from "ical-generator"
const nodemailer = require("nodemailer")
-type SendEmailOpts = {
- // workspaceId If finer grain controls being used then this will lookup config for workspace.
- workspaceId?: string
- // user If sending to an existing user the object can be provided, this is used in the context.
- user: User
- // from If sending from an address that is not what is configured in the SMTP config.
- 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 = env.ENABLE_EMAIL_TEST_MODE && env.isDev()
const TYPE = TemplateType.EMAIL
@@ -200,6 +183,26 @@ export async function sendEmail(
context
)
}
+ if (opts?.invite) {
+ const calendar = ical({
+ name: "Invite",
+ })
+ calendar.createEvent({
+ start: opts.invite.startTime,
+ end: opts.invite.endTime,
+ summary: opts.invite.summary,
+ location: opts.invite.location,
+ url: opts.invite.url,
+ })
+ message = {
+ ...message,
+ icalEvent: {
+ method: "request",
+ content: calendar.toString(),
+ },
+ }
+ }
+
const response = await transport.sendMail(message)
if (TEST_MODE) {
console.log("Test email URL: " + nodemailer.getTestMessageUrl(response))
diff --git a/scripts/resetVersions.sh b/scripts/resetVersions.sh
new file mode 100755
index 0000000000..83411f5153
--- /dev/null
+++ b/scripts/resetVersions.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+echo "Resetting package versions"
+yarn lerna exec "yarn version --no-git-tag-version --new-version=0.0.0"
+echo "Updating dependencies"
+node scripts/syncLocalDependencies.js "0.0.0"
+git checkout package.json
+echo "Package versions reset!"
diff --git a/scripts/updateVersions.sh b/scripts/updateVersions.sh
new file mode 100755
index 0000000000..51ad70366c
--- /dev/null
+++ b/scripts/updateVersions.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+version=$(cat lerna.json \
+ | grep version \
+ | head -1 \
+ | awk -F: '{gsub(/"/,"",$2);gsub(/[[:space:]]*/,"",$2); print $2}' \
+ | sed 's/[",]//g')
+echo "Setting version $version"
+yarn lerna exec "yarn version --no-git-tag-version --new-version=$version"
+echo "Updating dependencies"
+node scripts/syncLocalDependencies.js $version
+echo "Syncing yarn workspace"
+yarn
diff --git a/yarn.lock b/yarn.lock
index 9a57eb4377..ea9823b58b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -13668,6 +13668,13 @@ husky@^8.0.3:
resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
+ical-generator@4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/ical-generator/-/ical-generator-4.1.0.tgz#2a336c951864c5583a2aa715d16f2edcdfd2d90b"
+ integrity sha512-5GrFDJ8SAOj8cB9P1uEZIfKrNxSZ1R2eOQfZePL+CtdWh4RwNXWe8b0goajz+Hu37vcipG3RVldoa2j57Y20IA==
+ dependencies:
+ uuid-random "^1.3.2"
+
iconv-lite@0.4.24, iconv-lite@^0.4.15, iconv-lite@^0.4.24, iconv-lite@^0.4.5:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -25279,6 +25286,11 @@ utils-merge@1.0.1, utils-merge@1.x.x:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
+uuid-random@^1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/uuid-random/-/uuid-random-1.3.2.tgz#96715edbaef4e84b1dcf5024b00d16f30220e2d0"
+ integrity sha512-UOzej0Le/UgkbWEO8flm+0y+G+ljUon1QWTEZOq1rnMAsxo2+SckbiZdKzAHHlVh6gJqI1TjC/xwgR50MuCrBQ==
+
uuid@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"