upfynai-code 2.4.1 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -0
- package/client/dist/assets/AppContent-C0CyP3g5.js +513 -0
- package/client/dist/assets/CanvasPanel-0u9QR7U-.js +34 -0
- package/client/dist/assets/CanvasPanel-WhZulBJw.css +1 -0
- package/client/dist/assets/DashboardPanel-Dgqw1yZk.js +1 -0
- package/client/dist/assets/LoginModal-CZDEzqjK.js +19 -0
- package/client/dist/assets/Onboarding-DR6NZ4Vz.js +1 -0
- package/client/dist/assets/{SetupForm-B4p8im5O.js → SetupForm-D49gtWY4.js} +1 -1
- package/client/dist/assets/WorkflowsPanel-CqlbEJA_.js +1 -0
- package/client/dist/assets/{ar-SA-G6X2FPQ2-2gfmdvHk.js → ar-SA-G6X2FPQ2-BWqa1yBH.js} +2 -2
- package/client/dist/assets/{arc-DCZSHhoJ.js → arc-BegSKqEW.js} +1 -1
- package/client/dist/assets/az-AZ-76LH7QW2-DrVlbZDP.js +1 -0
- package/client/dist/assets/{bg-BG-XCXSNQG7-D6__XtOK.js → bg-BG-XCXSNQG7-DdunjBgT.js} +2 -2
- package/client/dist/assets/{blockDiagram-38ab4fdb-Cfbaeyp6.js → blockDiagram-38ab4fdb-BKMbwGHu.js} +2 -2
- package/client/dist/assets/{bn-BD-2XOGV67Q-DHNJw3OG.js → bn-BD-2XOGV67Q-_7DtmvwO.js} +2 -2
- package/client/dist/assets/{c4Diagram-3d4e48cf-BBCnjOTy.js → c4Diagram-3d4e48cf-hJuiHhSn.js} +2 -2
- package/client/dist/assets/{ca-ES-6MX7JW3Y-r5g4o3zQ.js → ca-ES-6MX7JW3Y-BFIrmojG.js} +3 -3
- package/client/dist/assets/channel-Bur-rRTp.js +1 -0
- package/client/dist/assets/classDiagram-70f12bd4-BjiAf9cM.js +2 -0
- package/client/dist/assets/classDiagram-v2-f2320105-pwBewejc.js +2 -0
- package/client/dist/assets/clone-BtqXeoBJ.js +1 -0
- package/client/dist/assets/{createText-2e5e7dd3-B8jCDmF_.js → createText-2e5e7dd3-Dq_acOWe.js} +1 -1
- package/client/dist/assets/{cs-CZ-2BRQDIVT-p08jRLRC.js → cs-CZ-2BRQDIVT-B-x4F6TJ.js} +2 -2
- package/client/dist/assets/{da-DK-5WZEPLOC-CnhOImFf.js → da-DK-5WZEPLOC-Btlc8Dgn.js} +2 -2
- package/client/dist/assets/{de-DE-XR44H4JA-BunSXZ-Y.js → de-DE-XR44H4JA-BVu3ZIoD.js} +2 -2
- package/client/dist/assets/{edges-e0da2a9e-CGBBhG8k.js → edges-e0da2a9e-DH0wVTXR.js} +1 -1
- package/client/dist/assets/{el-GR-BZB4AONW-D4wv1oIz.js → el-GR-BZB4AONW-h2ll8_ZC.js} +2 -2
- package/client/dist/assets/{erDiagram-9861fffd-CYaF3q1I.js → erDiagram-9861fffd-BYezLIR7.js} +2 -2
- package/client/dist/assets/{es-ES-U4NZUMDT-CGeTKXgd.js → es-ES-U4NZUMDT-Cveiulwt.js} +2 -2
- package/client/dist/assets/{eu-ES-A7QVB2H4-Cayx1TxR.js → eu-ES-A7QVB2H4-DQluL2PY.js} +2 -2
- package/client/dist/assets/{fa-IR-HGAKTJCU-CmUg8pmw.js → fa-IR-HGAKTJCU-BJtcMBSv.js} +2 -2
- package/client/dist/assets/{fi-FI-Z5N7JZ37-xvHcPhsU.js → fi-FI-Z5N7JZ37-D8NfbVXV.js} +2 -2
- package/client/dist/assets/{flowDb-956e92f1-C-_LFz70.js → flowDb-956e92f1-scnUykhM.js} +1 -1
- package/client/dist/assets/{flowDiagram-66a62f08-C1sHdSjn.js → flowDiagram-66a62f08-jVyWsfyU.js} +2 -2
- package/client/dist/assets/flowDiagram-v2-96b9c2cf-N6xgi25h.js +1 -0
- package/client/dist/assets/{flowchart-elk-definition-4a651766-CNGfpudb.js → flowchart-elk-definition-4a651766-gKGX3HqR.js} +2 -2
- package/client/dist/assets/{fr-FR-RHASNOE6-DBoHEcNj.js → fr-FR-RHASNOE6-vdj42kC6.js} +2 -2
- package/client/dist/assets/ganttDiagram-c361ad54-C2CiWFUP.js +257 -0
- package/client/dist/assets/{gitGraphDiagram-72cf32ee-DojCDvlS.js → gitGraphDiagram-72cf32ee-C59Yz2LK.js} +2 -2
- package/client/dist/assets/{gl-ES-HMX3MZ6V-p6hrn2cN.js → gl-ES-HMX3MZ6V-DQo0TzoP.js} +2 -2
- package/client/dist/assets/{graph-DXM7lcy1.js → graph-Dx_H43Kv.js} +1 -1
- package/client/dist/assets/{he-IL-6SHJWFNN-y2jEX6-0.js → he-IL-6SHJWFNN-DKXK5e33.js} +2 -2
- package/client/dist/assets/{hi-IN-IWLTKZ5I-99pNfyWr.js → hi-IN-IWLTKZ5I-C2Qgqc0R.js} +2 -2
- package/client/dist/assets/{hu-HU-A5ZG7DT2-hygceGMS.js → hu-HU-A5ZG7DT2-Ss-6vX0m.js} +2 -2
- package/client/dist/assets/{id-ID-SAP4L64H-CyIqi1hv.js → id-ID-SAP4L64H-D7Wsg1S2.js} +2 -2
- package/client/dist/assets/{index-3862675e-4idOQN2N.js → index-3862675e-u8Nv7hHC.js} +1 -1
- package/client/dist/assets/{index-BHZfFT_V.js → index-BVowJdZF.js} +4 -4
- package/client/dist/assets/{index-BGmwbRlb.js → index-ce18TYkg.js} +6 -6
- package/client/dist/assets/index-kQoJx-bc.css +1 -0
- package/client/dist/assets/{infoDiagram-f8f76790-CFLrHqtc.js → infoDiagram-f8f76790-LmoJYsxo.js} +2 -2
- package/client/dist/assets/{it-IT-JPQ66NNP-DzVvVdQI.js → it-IT-JPQ66NNP-CAPTVl7M.js} +2 -2
- package/client/dist/assets/{ja-JP-DBVTYXUO-BI4fPexV.js → ja-JP-DBVTYXUO-eNVPawR2.js} +2 -2
- package/client/dist/assets/{journeyDiagram-49397b02-C3CFDo8z.js → journeyDiagram-49397b02-BaJqehpR.js} +2 -2
- package/client/dist/assets/kaa-6HZHGXH3-tpuNkKhS.js +1 -0
- package/client/dist/assets/{kab-KAB-ZGHBKWFO-DBI_ri48.js → kab-KAB-ZGHBKWFO-Dp83kx4x.js} +2 -2
- package/client/dist/assets/kk-KZ-P5N5QNE5-B9IlC6YN.js +1 -0
- package/client/dist/assets/{km-KH-HSX4SM5Z-DOMFSres.js → km-KH-HSX4SM5Z-B_KMYaMj.js} +2 -2
- package/client/dist/assets/{ko-KR-MTYHY66A-tb08hXzd.js → ko-KR-MTYHY66A-yebnUNdb.js} +3 -3
- package/client/dist/assets/{ku-TR-6OUDTVRD-DlIQCCY4.js → ku-TR-6OUDTVRD-BR6fh6-5.js} +2 -2
- package/client/dist/assets/{layout-B_11mCXA.js → layout-DLl5Jwcl.js} +1 -1
- package/client/dist/assets/{line-B-qmK_vI.js → line-FpB7omSK.js} +1 -1
- package/client/dist/assets/{linear-Ph6uuYcX.js → linear-CkXqUFJ8.js} +1 -1
- package/client/dist/assets/{lt-LT-XHIRWOB4--qWy24_Z.js → lt-LT-XHIRWOB4-SutZSWtR.js} +2 -2
- package/client/dist/assets/{lv-LV-5QDEKY6T-Bnd_1GDb.js → lv-LV-5QDEKY6T-DuAxdcZL.js} +2 -2
- package/client/dist/assets/{mindmap-definition-fc14e90a-Do79tIc0.js → mindmap-definition-fc14e90a-DyxXOExh.js} +2 -2
- package/client/dist/assets/{mr-IN-CRQNXWMA-BsV6HaD9.js → mr-IN-CRQNXWMA-DqDUWM_8.js} +2 -2
- package/client/dist/assets/my-MM-5M5IBNSE-C40kMFMR.js +1 -0
- package/client/dist/assets/{nb-NO-T6EIAALU-Cvf9FdSF.js → nb-NO-T6EIAALU-DVij32Ju.js} +3 -3
- package/client/dist/assets/{nl-NL-IS3SIHDZ-DA1yqpXw.js → nl-NL-IS3SIHDZ-rT84mDYq.js} +2 -2
- package/client/dist/assets/{nn-NO-6E72VCQL-89lm3vku.js → nn-NO-6E72VCQL-BBZXBW8V.js} +2 -2
- package/client/dist/assets/{oc-FR-POXYY2M6-BsrjTJQh.js → oc-FR-POXYY2M6-DzjOugOf.js} +2 -2
- package/client/dist/assets/{pa-IN-N4M65BXN-CczefYaj.js → pa-IN-N4M65BXN-DD1iU8_F.js} +2 -2
- package/client/dist/assets/{percentages-BXMCSKIN-Be6p9phi.js → percentages-BXMCSKIN-WVlHS4wx.js} +7 -7
- package/client/dist/assets/{pieDiagram-8a3498a8-CfblQHdm.js → pieDiagram-8a3498a8-Dd_85qBH.js} +3 -3
- package/client/dist/assets/{pl-PL-T2D74RX3-DdhH-zcK.js → pl-PL-T2D74RX3-ukVXa48G.js} +2 -2
- package/client/dist/assets/{pt-BR-5N22H2LF-gpwlheL6.js → pt-BR-5N22H2LF-BibawarT.js} +2 -2
- package/client/dist/assets/{pt-PT-UZXXM6DQ-Cs87vICi.js → pt-PT-UZXXM6DQ-So3i9l9w.js} +2 -2
- package/client/dist/assets/{quadrantDiagram-120e2f19-CRMSamSP.js → quadrantDiagram-120e2f19-C4dFVDEx.js} +2 -2
- package/client/dist/assets/{requirementDiagram-deff3bca-D3LBN016.js → requirementDiagram-deff3bca-DrTO7yFl.js} +2 -2
- package/client/dist/assets/{ro-RO-JPDTUUEW-CWTSJ1Dt.js → ro-RO-JPDTUUEW-DY0Xq_Hd.js} +2 -2
- package/client/dist/assets/{ru-RU-B4JR7IUQ-Bq7aN2ep.js → ru-RU-B4JR7IUQ-B7u_Zvkd.js} +2 -2
- package/client/dist/assets/sankeyDiagram-04a897e0-D24gfzuS.js +8 -0
- package/client/dist/assets/{sequenceDiagram-704730f1-BRYXVDGX.js → sequenceDiagram-704730f1-Dgji2XLQ.js} +2 -2
- package/client/dist/assets/si-LK-N5RQ5JYF-OejsLzQ_.js +1 -0
- package/client/dist/assets/{sk-SK-C5VTKIMK-ByjKQzUb.js → sk-SK-C5VTKIMK-_vy2Bt-M.js} +2 -2
- package/client/dist/assets/{sl-SI-NN7IZMDC-B8WCyMBU.js → sl-SI-NN7IZMDC-DKOl_u2M.js} +2 -2
- package/client/dist/assets/stateDiagram-587899a1-CJ8eBaiU.js +1 -0
- package/client/dist/assets/stateDiagram-v2-d93cdb3a-C5K3l-Nt.js +1 -0
- package/client/dist/assets/{styles-6aaf32cf-Dr-lfIOW.js → styles-6aaf32cf-DAKE0jbx.js} +1 -1
- package/client/dist/assets/{styles-9a916d00-DS4wRpL7.js → styles-9a916d00-LFAJCgEy.js} +1 -1
- package/client/dist/assets/{styles-c10674c1-nKRF6NrH.js → styles-c10674c1-CllKO8NG.js} +1 -1
- package/client/dist/assets/{subset-shared.chunk-KT79s7KG.js → subset-shared.chunk-Uy-J87FQ.js} +3 -3
- package/client/dist/assets/subset-worker.chunk-dvgDvqt9.js +1 -0
- package/client/dist/assets/{sv-SE-XGPEYMSR-BiIPUVbv.js → sv-SE-XGPEYMSR-CDCB2ZV5.js} +2 -2
- package/client/dist/assets/{svgDrawCommon-08f97a94-C3uP9PYr.js → svgDrawCommon-08f97a94-CObOzbFQ.js} +1 -1
- package/client/dist/assets/{ta-IN-2NMHFXQM-Cidadso2.js → ta-IN-2NMHFXQM-DHUNdO69.js} +2 -2
- package/client/dist/assets/th-TH-HPSO5L25-zI2hnBq3.js +2 -0
- package/client/dist/assets/{timeline-definition-85554ec2-BSsLsIgF.js → timeline-definition-85554ec2-C2XHRmxK.js} +2 -2
- package/client/dist/assets/{tr-TR-DEFEU3FU-DaFcI-KL.js → tr-TR-DEFEU3FU-l-6Hu4-D.js} +2 -2
- package/client/dist/assets/{uk-UA-QMV73CPH-DkBW36St.js → uk-UA-QMV73CPH-CqSOwrl7.js} +3 -3
- package/client/dist/assets/vendor-codemirror-D_s0aGBu.js +35 -0
- package/client/dist/assets/{vendor-icons-Dh9m_Ydt.js → vendor-icons-Lb69KSFJ.js} +167 -117
- package/client/dist/assets/{vi-VN-M7AON7JQ-KrtfxOzl.js → vi-VN-M7AON7JQ-CUL8-mBZ.js} +2 -2
- package/client/dist/assets/{xychartDiagram-e933f94c-CgNgZ4pp.js → xychartDiagram-e933f94c-1fmf6slj.js} +2 -2
- package/client/dist/assets/{zh-CN-LNUGB5OW-BQu12RoD.js → zh-CN-LNUGB5OW-CB5y5VVU.js} +3 -3
- package/client/dist/assets/zh-HK-E62DVLB3-BHcrrEeJ.js +1 -0
- package/client/dist/assets/{zh-TW-RAJ6MFWO-ffJWgVxn.js → zh-TW-RAJ6MFWO-DoDUdkaJ.js} +2 -2
- package/client/dist/index.html +3 -3
- package/package.json +20 -8
- package/server/cli.js +44 -0
- package/server/database/auth.db +0 -0
- package/server/database/db.js +203 -1
- package/server/index.js +111 -18
- package/server/middleware/auth.js +11 -6
- package/server/projects.js +95 -202
- package/server/relay-client.js +47 -10
- package/server/routes/auth.js +6 -0
- package/server/routes/dashboard.js +52 -0
- package/server/routes/projects.js +38 -35
- package/server/routes/voice.js +198 -0
- package/server/routes/webhooks.js +166 -0
- package/server/routes/workflows.js +118 -0
- package/server/services/whisperService.js +84 -0
- package/server/services/workflowScheduler.js +186 -0
- package/client/dist/assets/AppContent-DTZ2FbvM.js +0 -513
- package/client/dist/assets/CanvasPanel-DlTW6Jh6.js +0 -6
- package/client/dist/assets/CanvasPanel-q4HEqNtV.css +0 -1
- package/client/dist/assets/LoginModal-CWoFm0au.js +0 -19
- package/client/dist/assets/Onboarding-CtIoXiTp.js +0 -1
- package/client/dist/assets/az-AZ-76LH7QW2-CDdeucRZ.js +0 -1
- package/client/dist/assets/channel-O3ovC0x9.js +0 -1
- package/client/dist/assets/classDiagram-70f12bd4-D0lhAcxU.js +0 -2
- package/client/dist/assets/classDiagram-v2-f2320105-BuwUsF3F.js +0 -2
- package/client/dist/assets/clone-BG9u7vLi.js +0 -1
- package/client/dist/assets/flowDiagram-v2-96b9c2cf-Cd0Iascd.js +0 -1
- package/client/dist/assets/ganttDiagram-c361ad54-B8HJQqjt.js +0 -257
- package/client/dist/assets/index-B8wwD_Xo.css +0 -1
- package/client/dist/assets/kaa-6HZHGXH3-fwOleoQB.js +0 -1
- package/client/dist/assets/kk-KZ-P5N5QNE5-zpl7uvyF.js +0 -1
- package/client/dist/assets/my-MM-5M5IBNSE-kZQURVIi.js +0 -1
- package/client/dist/assets/sankeyDiagram-04a897e0-CsFqOQZN.js +0 -8
- package/client/dist/assets/si-LK-N5RQ5JYF-BBjcNYQh.js +0 -1
- package/client/dist/assets/stateDiagram-587899a1-BHoy9LtD.js +0 -1
- package/client/dist/assets/stateDiagram-v2-d93cdb3a-BvMUA6bS.js +0 -1
- package/client/dist/assets/subset-worker.chunk-BMx1eyv3.js +0 -1
- package/client/dist/assets/th-TH-HPSO5L25-CFNnJwSv.js +0 -2
- package/client/dist/assets/vendor-codemirror-langs-BH1ZcKHY.js +0 -20
- package/client/dist/assets/vendor-codemirror-rix45NST.js +0 -16
- package/client/dist/assets/zh-HK-E62DVLB3-zx9CvERq.js +0 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import cron from 'node-cron';
|
|
2
|
+
import { workflowDb, webhookDb } from '../database/db.js';
|
|
3
|
+
|
|
4
|
+
const activeJobs = new Map(); // workflowId -> cron task
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Execute a single workflow's steps (same logic as the /run endpoint).
|
|
8
|
+
* Returns { success, results, error }
|
|
9
|
+
*/
|
|
10
|
+
async function executeWorkflow(workflow) {
|
|
11
|
+
const steps = typeof workflow.steps === 'string' ? JSON.parse(workflow.steps) : workflow.steps;
|
|
12
|
+
if (!steps.length) return { success: false, error: 'No steps' };
|
|
13
|
+
|
|
14
|
+
const run = await workflowDb.createRun(workflow.id, workflow.user_id, steps.length);
|
|
15
|
+
const results = [];
|
|
16
|
+
let lastOutput = null;
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < steps.length; i++) {
|
|
19
|
+
const step = steps[i];
|
|
20
|
+
try {
|
|
21
|
+
let stepResult;
|
|
22
|
+
|
|
23
|
+
if (step.type === 'webhook') {
|
|
24
|
+
const webhookId = step.config?.webhookId;
|
|
25
|
+
if (!webhookId) throw new Error('No webhook configured');
|
|
26
|
+
|
|
27
|
+
const webhook = await webhookDb.getById(Number(webhookId), workflow.user_id);
|
|
28
|
+
if (!webhook) throw new Error('Webhook not found');
|
|
29
|
+
|
|
30
|
+
let parsedHeaders = {};
|
|
31
|
+
try { parsedHeaders = JSON.parse(webhook.headers || '{}'); } catch { /* ignore */ }
|
|
32
|
+
|
|
33
|
+
const controller = new AbortController();
|
|
34
|
+
const timeout = setTimeout(() => controller.abort(), 15000);
|
|
35
|
+
|
|
36
|
+
const fetchOptions = {
|
|
37
|
+
method: webhook.method,
|
|
38
|
+
headers: {
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
'User-Agent': 'UpfynAI-Scheduler/1.0',
|
|
41
|
+
...parsedHeaders
|
|
42
|
+
},
|
|
43
|
+
signal: controller.signal
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (['POST', 'PUT', 'PATCH'].includes(webhook.method)) {
|
|
47
|
+
const payload = {
|
|
48
|
+
workflow_id: workflow.id,
|
|
49
|
+
workflow_name: workflow.name,
|
|
50
|
+
step_index: i,
|
|
51
|
+
step_label: step.label,
|
|
52
|
+
previous_output: lastOutput,
|
|
53
|
+
scheduled: true,
|
|
54
|
+
timestamp: new Date().toISOString()
|
|
55
|
+
};
|
|
56
|
+
if (step.config?.payloadTemplate) {
|
|
57
|
+
try { Object.assign(payload, JSON.parse(step.config.payloadTemplate)); } catch { /* ignore */ }
|
|
58
|
+
}
|
|
59
|
+
fetchOptions.body = JSON.stringify(payload);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const response = await fetch(webhook.url, fetchOptions);
|
|
63
|
+
clearTimeout(timeout);
|
|
64
|
+
|
|
65
|
+
const contentType = response.headers.get('content-type') || '';
|
|
66
|
+
let body;
|
|
67
|
+
if (contentType.includes('application/json')) {
|
|
68
|
+
body = await response.json();
|
|
69
|
+
} else {
|
|
70
|
+
body = await response.text();
|
|
71
|
+
if (body.length > 5000) body = body.slice(0, 5000) + '...';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await webhookDb.updateLastTriggered(webhook.id);
|
|
75
|
+
stepResult = { status: response.status, body };
|
|
76
|
+
lastOutput = body;
|
|
77
|
+
|
|
78
|
+
} else if (step.type === 'ai-prompt') {
|
|
79
|
+
stepResult = { type: 'ai-prompt', prompt: step.config?.prompt || '', note: 'Scheduled AI prompts require active session' };
|
|
80
|
+
lastOutput = step.config?.prompt;
|
|
81
|
+
|
|
82
|
+
} else if (step.type === 'delay') {
|
|
83
|
+
const seconds = Math.min(step.config?.seconds || 1, 30);
|
|
84
|
+
await new Promise(resolve => setTimeout(resolve, seconds * 1000));
|
|
85
|
+
stepResult = { delayed: seconds };
|
|
86
|
+
|
|
87
|
+
} else {
|
|
88
|
+
stepResult = { type: step.type, note: 'Unknown step type' };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
results.push({ step: i, label: step.label, type: step.type, success: true, result: stepResult });
|
|
92
|
+
await workflowDb.updateRun(run.id, { status: 'running', stepsCompleted: i + 1 });
|
|
93
|
+
|
|
94
|
+
} catch (stepError) {
|
|
95
|
+
results.push({ step: i, label: step.label, type: step.type, success: false, error: stepError.message });
|
|
96
|
+
await workflowDb.updateRun(run.id, { status: 'failed', stepsCompleted: i, error: `Step ${i + 1} (${step.label}): ${stepError.message}` });
|
|
97
|
+
await workflowDb.updateLastRun(workflow.id);
|
|
98
|
+
return { success: false, run_id: run.id, results, error: `Step ${i + 1} failed: ${stepError.message}` };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await workflowDb.updateRun(run.id, { status: 'completed', stepsCompleted: steps.length, result: results });
|
|
103
|
+
await workflowDb.updateLastRun(workflow.id);
|
|
104
|
+
return { success: true, run_id: run.id, results };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Schedule a single workflow's cron job.
|
|
109
|
+
*/
|
|
110
|
+
function scheduleWorkflow(workflow) {
|
|
111
|
+
const id = workflow.id;
|
|
112
|
+
|
|
113
|
+
// Stop existing job if any
|
|
114
|
+
if (activeJobs.has(id)) {
|
|
115
|
+
activeJobs.get(id).stop();
|
|
116
|
+
activeJobs.delete(id);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!workflow.schedule || !workflow.schedule_enabled) return;
|
|
120
|
+
|
|
121
|
+
// Validate cron expression
|
|
122
|
+
if (!cron.validate(workflow.schedule)) {
|
|
123
|
+
console.warn(`[Scheduler] Invalid cron for workflow ${id}: ${workflow.schedule}`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const task = cron.schedule(workflow.schedule, async () => {
|
|
128
|
+
console.log(`[Scheduler] Running workflow ${id}: ${workflow.name}`);
|
|
129
|
+
try {
|
|
130
|
+
await executeWorkflow(workflow);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
console.error(`[Scheduler] Workflow ${id} execution error:`, err.message);
|
|
133
|
+
}
|
|
134
|
+
}, {
|
|
135
|
+
timezone: workflow.schedule_timezone || 'UTC'
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
activeJobs.set(id, task);
|
|
139
|
+
console.log(`[Scheduler] Scheduled workflow ${id} (${workflow.name}): ${workflow.schedule}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Load all scheduled workflows from DB and start their cron jobs.
|
|
144
|
+
* Call this once at server startup.
|
|
145
|
+
*/
|
|
146
|
+
async function initScheduler() {
|
|
147
|
+
try {
|
|
148
|
+
const workflows = await workflowDb.getScheduled();
|
|
149
|
+
console.log(`[Scheduler] Found ${workflows.length} scheduled workflow(s)`);
|
|
150
|
+
for (const wf of workflows) {
|
|
151
|
+
scheduleWorkflow(wf);
|
|
152
|
+
}
|
|
153
|
+
} catch (err) {
|
|
154
|
+
console.error('[Scheduler] Init error:', err.message);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Re-sync a specific workflow's schedule (call after create/update).
|
|
160
|
+
*/
|
|
161
|
+
async function refreshWorkflowSchedule(workflowId, userId) {
|
|
162
|
+
try {
|
|
163
|
+
const wf = await workflowDb.getById(workflowId, userId);
|
|
164
|
+
if (wf) {
|
|
165
|
+
scheduleWorkflow(wf);
|
|
166
|
+
} else {
|
|
167
|
+
// Workflow deleted — stop its job
|
|
168
|
+
if (activeJobs.has(workflowId)) {
|
|
169
|
+
activeJobs.get(workflowId).stop();
|
|
170
|
+
activeJobs.delete(workflowId);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch { /* ignore */ }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Stop a workflow's cron job.
|
|
178
|
+
*/
|
|
179
|
+
function stopWorkflowSchedule(workflowId) {
|
|
180
|
+
if (activeJobs.has(workflowId)) {
|
|
181
|
+
activeJobs.get(workflowId).stop();
|
|
182
|
+
activeJobs.delete(workflowId);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export { initScheduler, refreshWorkflowSchedule, stopWorkflowSchedule, executeWorkflow };
|