diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts
index 321ebd7f58..4926a60150 100644
--- a/packages/backend-core/src/db/utils.ts
+++ b/packages/backend-core/src/db/utils.ts
@@ -254,7 +254,16 @@ export async function getAllApps({ dev, all, idsOnly, efficient }: any = {}) {
return false
})
if (idsOnly) {
- return appDbNames
+ const devAppIds = appDbNames.filter(appId => isDevAppID(appId))
+ const prodAppIds = appDbNames.filter(appId => !isDevAppID(appId))
+ switch (dev) {
+ case true:
+ return devAppIds
+ case false:
+ return prodAppIds
+ default:
+ return appDbNames
+ }
}
const appPromises = appDbNames.map((app: any) =>
// skip setup otherwise databases could be re-created
diff --git a/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte b/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte
index 93b8394b49..f2da863424 100644
--- a/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte
+++ b/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte
@@ -1,6 +1,7 @@
diff --git a/packages/server/src/api/controllers/deploy/index.ts b/packages/server/src/api/controllers/deploy/index.ts
index 86718294de..2ac6c8a15c 100644
--- a/packages/server/src/api/controllers/deploy/index.ts
+++ b/packages/server/src/api/controllers/deploy/index.ts
@@ -125,7 +125,7 @@ async function deployApp(deployment: any) {
const prodAppDoc = await db.get(DocumentType.APP_METADATA)
appDoc._rev = prodAppDoc._rev
} catch (err) {
- // ignore the error - doesn't exist
+ delete appDoc._rev
}
// switch to production app ID
diff --git a/packages/server/src/automations/index.js b/packages/server/src/automations/index.js
index 9c23e35d1c..2baa868890 100644
--- a/packages/server/src/automations/index.js
+++ b/packages/server/src/automations/index.js
@@ -1,16 +1,19 @@
const { processEvent } = require("./utils")
const { queue, shutdown } = require("./bullboard")
-const { TRIGGER_DEFINITIONS } = require("./triggers")
+const { TRIGGER_DEFINITIONS, rebootTrigger } = require("./triggers")
const { ACTION_DEFINITIONS } = require("./actions")
/**
* This module is built purely to kick off the worker farm and manage the inputs/outputs
*/
-exports.init = function () {
+exports.init = async function () {
// this promise will not complete
- return queue.process(async job => {
+ const promise = queue.process(async job => {
await processEvent(job)
})
+ // on init we need to trigger any reboot automations
+ await rebootTrigger()
+ return promise
}
exports.getQueues = () => {
diff --git a/packages/server/src/automations/triggers.js b/packages/server/src/automations/triggers.js
index 216f24be02..395390113a 100644
--- a/packages/server/src/automations/triggers.js
+++ b/packages/server/src/automations/triggers.js
@@ -9,6 +9,7 @@ const { checkTestFlag } = require("../utilities/redis")
const utils = require("./utils")
const env = require("../environment")
const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
+const { getAllApps } = require("@budibase/backend-core/db")
const TRIGGER_DEFINITIONS = definitions
const JOB_OPTS = {
@@ -16,24 +17,27 @@ const JOB_OPTS = {
removeOnFail: true,
}
+async function getAllAutomations() {
+ const db = getAppDB()
+ let automations = await db.allDocs(
+ getAutomationParams(null, { include_docs: true })
+ )
+ return automations.rows.map(row => row.doc)
+}
+
async function queueRelevantRowAutomations(event, eventType) {
if (event.appId == null) {
throw `No appId specified for ${eventType} - check event emitters.`
}
doInAppContext(event.appId, async () => {
- const db = getAppDB()
- let automations = await db.allDocs(
- getAutomationParams(null, { include_docs: true })
- )
+ let automations = await getAllAutomations()
// filter down to the correct event type
- automations = automations.rows
- .map(automation => automation.doc)
- .filter(automation => {
- const trigger = automation.definition.trigger
- return trigger && trigger.event === eventType
- })
+ automations = automations.filter(automation => {
+ const trigger = automation.definition.trigger
+ return trigger && trigger.event === eventType
+ })
for (let automation of automations) {
let automationDef = automation.definition
@@ -110,4 +114,34 @@ exports.externalTrigger = async function (
}
}
+exports.rebootTrigger = async () => {
+ // reboot cron option is only available on the main thread at
+ // startup and only usable in self host
+ if (env.isInThread() || !env.SELF_HOSTED) {
+ return
+ }
+ // iterate through all production apps, find the reboot crons
+ // and trigger events for them
+ const appIds = await getAllApps({ dev: false, idsOnly: true })
+ for (let prodAppId of appIds) {
+ await doInAppContext(prodAppId, async () => {
+ let automations = await getAllAutomations()
+ let rebootEvents = []
+ for (let automation of automations) {
+ if (utils.isRebootTrigger(automation)) {
+ const job = {
+ automation,
+ event: {
+ appId: prodAppId,
+ timestamp: Date.now(),
+ },
+ }
+ rebootEvents.push(queue.add(job, JOB_OPTS))
+ }
+ }
+ await Promise.all(rebootEvents)
+ })
+ }
+}
+
exports.TRIGGER_DEFINITIONS = TRIGGER_DEFINITIONS
diff --git a/packages/server/src/automations/utils.ts b/packages/server/src/automations/utils.ts
index e0979ac0d9..3093f147dc 100644
--- a/packages/server/src/automations/utils.ts
+++ b/packages/server/src/automations/utils.ts
@@ -17,6 +17,7 @@ import { tenancy } from "@budibase/backend-core"
import { quotas } from "@budibase/pro"
import { Automation } from "@budibase/types"
+const REBOOT_CRON = "@reboot"
const WH_STEP_ID = definitions.WEBHOOK.stepId
const CRON_STEP_ID = definitions.CRON.stepId
const Runner = new Thread(ThreadType.AUTOMATION)
@@ -109,22 +110,33 @@ export async function clearMetadata() {
await db.bulkDocs(automationMetadata)
}
+export function isCronTrigger(auto: Automation) {
+ return (
+ auto &&
+ auto.definition.trigger &&
+ auto.definition.trigger.stepId === CRON_STEP_ID
+ )
+}
+
+export function isRebootTrigger(auto: Automation) {
+ const trigger = auto ? auto.definition.trigger : null
+ return isCronTrigger(auto) && trigger?.inputs.cron === REBOOT_CRON
+}
+
/**
* This function handles checking of any cron jobs that need to be enabled/updated.
* @param {string} appId The ID of the app in which we are checking for webhooks
* @param {object|undefined} automation The automation object to be updated.
*/
-export async function enableCronTrigger(appId: any, automation: any) {
+export async function enableCronTrigger(appId: any, automation: Automation) {
const trigger = automation ? automation.definition.trigger : null
- function isCronTrigger(auto: any) {
- return (
- auto &&
- auto.definition.trigger &&
- auto.definition.trigger.stepId === CRON_STEP_ID
- )
- }
+
// need to create cron job
- if (isCronTrigger(automation) && trigger?.inputs.cron) {
+ if (
+ isCronTrigger(automation) &&
+ !isRebootTrigger(automation) &&
+ trigger?.inputs.cron
+ ) {
// make a job id rather than letting Bull decide, makes it easier to handle on way out
const jobId = `${appId}_cron_${newid()}`
const job: any = await queue.add(
diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts
index 3136155869..f64552a92f 100644
--- a/packages/server/src/threads/automation.ts
+++ b/packages/server/src/threads/automation.ts
@@ -458,6 +458,9 @@ class Orchestrator {
export function execute(input: AutomationEvent, callback: WorkerCallback) {
const appId = input.data.event.appId
+ if (!appId) {
+ throw new Error("Unable to execute, event doesn't contain app ID.")
+ }
doInAppContext(appId, async () => {
const automationOrchestrator = new Orchestrator(
input.data.automation,
@@ -475,6 +478,9 @@ export function execute(input: AutomationEvent, callback: WorkerCallback) {
export const removeStalled = async (input: AutomationEvent) => {
const appId = input.data.event.appId
+ if (!appId) {
+ throw new Error("Unable to execute, event doesn't contain app ID.")
+ }
await doInAppContext(appId, async () => {
const automationOrchestrator = new Orchestrator(
input.data.automation,
diff --git a/packages/types/src/documents/app/automation.ts b/packages/types/src/documents/app/automation.ts
index 50562461e4..a038e73d11 100644
--- a/packages/types/src/documents/app/automation.ts
+++ b/packages/types/src/documents/app/automation.ts
@@ -25,6 +25,10 @@ export interface AutomationStep {
export interface AutomationTrigger {
id: string
stepId: string
+ inputs: {
+ [key: string]: any
+ }
+ cronJobId?: string
}
export enum AutomationStatus {