@fsai-flow/core 0.0.4 → 0.1.0
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/dist/index.d.ts +17 -0
- package/dist/index.js +61 -0
- package/dist/{src/lib → lib}/ActiveWebhooks.d.ts +1 -1
- package/dist/lib/ActiveWebhooks.js +177 -0
- package/dist/{src/lib → lib}/ActiveWorkflows.d.ts +3 -3
- package/dist/lib/ActiveWorkflows.js +465 -0
- package/dist/{src/lib → lib}/BinaryDataManager/FileSystem.d.ts +1 -1
- package/dist/lib/BinaryDataManager/FileSystem.js +180 -0
- package/dist/{src/lib → lib}/BinaryDataManager/index.d.ts +2 -2
- package/dist/lib/BinaryDataManager/index.js +129 -0
- package/dist/{src/lib → lib}/ChangeCase.js +11 -11
- package/dist/lib/Constants.js +18 -0
- package/dist/{src/lib → lib}/Credentials.d.ts +1 -1
- package/dist/{src/lib → lib}/Credentials.js +7 -8
- package/dist/{src/lib → lib}/FileSystem.d.ts +1 -1
- package/dist/lib/FileSystem.js +180 -0
- package/dist/{src/lib → lib}/InputConnectionDataLegacy.d.ts +1 -1
- package/dist/lib/InputConnectionDataLegacy.js +72 -0
- package/dist/{src/lib → lib}/Interfaces.d.ts +47 -48
- package/dist/{src/lib → lib}/Interfaces.js +0 -1
- package/dist/{src/lib → lib}/LoadNodeParameterOptions.d.ts +1 -1
- package/dist/lib/LoadNodeParameterOptions.js +152 -0
- package/dist/{src/lib → lib}/NodeExecuteFunctions.d.ts +9 -10
- package/dist/lib/NodeExecuteFunctions.js +2467 -0
- package/dist/{src/lib → lib}/NodesLoader/constants.d.ts +1 -1
- package/dist/lib/NodesLoader/constants.js +105 -0
- package/dist/{src/lib → lib}/NodesLoader/custom-directory-loader.d.ts +1 -1
- package/dist/lib/NodesLoader/custom-directory-loader.js +35 -0
- package/dist/{src/lib → lib}/NodesLoader/directory-loader.d.ts +1 -1
- package/dist/{src/lib → lib}/NodesLoader/directory-loader.js +80 -38
- package/dist/lib/NodesLoader/index.d.ts +5 -0
- package/dist/{src/lib → lib}/NodesLoader/index.js +5 -6
- package/dist/{src/lib → lib}/NodesLoader/lazy-package-directory-loader.d.ts +1 -1
- package/dist/lib/NodesLoader/lazy-package-directory-loader.js +44 -0
- package/dist/{src/lib → lib}/NodesLoader/load-class-in-isolation.js +6 -11
- package/dist/{src/lib → lib}/NodesLoader/package-directory-loader.d.ts +2 -2
- package/dist/{src/lib → lib}/NodesLoader/package-directory-loader.js +28 -36
- package/dist/{src/lib → lib}/NodesLoader/types.js +0 -1
- package/dist/{src/lib → lib}/RedisLeaderElectionManager.d.ts +1 -1
- package/dist/lib/RedisLeaderElectionManager.js +279 -0
- package/dist/lib/RequestTypes.d.ts +58 -0
- package/dist/lib/RequestTypes.js +8 -0
- package/dist/{src/lib → lib}/UserSettings.d.ts +1 -1
- package/dist/lib/UserSettings.js +269 -0
- package/dist/{src/lib → lib}/WorkflowExecute.d.ts +4 -4
- package/dist/{src/lib → lib}/WorkflowExecute.js +230 -178
- package/dist/{src/lib → lib}/index.d.ts +2 -2
- package/dist/lib/index.js +129 -0
- package/dist/{src/utils → utils}/crypto.js +2 -3
- package/package.json +59 -52
- package/dist/README.md +0 -31
- package/dist/package.json +0 -54
- package/dist/src/index.d.ts +0 -16
- package/dist/src/index.js +0 -30
- package/dist/src/index.js.map +0 -1
- package/dist/src/lib/ActiveWebhooks.js +0 -184
- package/dist/src/lib/ActiveWebhooks.js.map +0 -1
- package/dist/src/lib/ActiveWorkflows.js +0 -456
- package/dist/src/lib/ActiveWorkflows.js.map +0 -1
- package/dist/src/lib/BinaryDataManager/FileSystem.js +0 -179
- package/dist/src/lib/BinaryDataManager/FileSystem.js.map +0 -1
- package/dist/src/lib/BinaryDataManager/index.js +0 -146
- package/dist/src/lib/BinaryDataManager/index.js.map +0 -1
- package/dist/src/lib/ChangeCase.js.map +0 -1
- package/dist/src/lib/Constants.js +0 -19
- package/dist/src/lib/Constants.js.map +0 -1
- package/dist/src/lib/Credentials.js.map +0 -1
- package/dist/src/lib/FileSystem.js +0 -179
- package/dist/src/lib/FileSystem.js.map +0 -1
- package/dist/src/lib/InputConnectionDataLegacy.js +0 -79
- package/dist/src/lib/InputConnectionDataLegacy.js.map +0 -1
- package/dist/src/lib/Interfaces.js.map +0 -1
- package/dist/src/lib/LoadNodeParameterOptions.js +0 -150
- package/dist/src/lib/LoadNodeParameterOptions.js.map +0 -1
- package/dist/src/lib/NodeExecuteFunctions.js +0 -2479
- package/dist/src/lib/NodeExecuteFunctions.js.map +0 -1
- package/dist/src/lib/NodesLoader/constants.js +0 -106
- package/dist/src/lib/NodesLoader/constants.js.map +0 -1
- package/dist/src/lib/NodesLoader/custom-directory-loader.js +0 -36
- package/dist/src/lib/NodesLoader/custom-directory-loader.js.map +0 -1
- package/dist/src/lib/NodesLoader/directory-loader.js.map +0 -1
- package/dist/src/lib/NodesLoader/index.d.ts +0 -5
- package/dist/src/lib/NodesLoader/index.js.map +0 -1
- package/dist/src/lib/NodesLoader/lazy-package-directory-loader.js +0 -52
- package/dist/src/lib/NodesLoader/lazy-package-directory-loader.js.map +0 -1
- package/dist/src/lib/NodesLoader/load-class-in-isolation.js.map +0 -1
- package/dist/src/lib/NodesLoader/package-directory-loader.js.map +0 -1
- package/dist/src/lib/NodesLoader/types.js.map +0 -1
- package/dist/src/lib/RedisLeaderElectionManager.js +0 -294
- package/dist/src/lib/RedisLeaderElectionManager.js.map +0 -1
- package/dist/src/lib/UserSettings.js +0 -261
- package/dist/src/lib/UserSettings.js.map +0 -1
- package/dist/src/lib/WorkflowExecute.js.map +0 -1
- package/dist/src/lib/index.js +0 -146
- package/dist/src/lib/index.js.map +0 -1
- package/dist/src/utils/crypto.js.map +0 -1
- package/eslint.config.js +0 -19
- package/jest.config.ts +0 -10
- package/project.json +0 -19
- package/src/index.ts +0 -28
- package/src/lib/ActiveWebhooks.ts +0 -245
- package/src/lib/ActiveWorkflows.ts +0 -575
- package/src/lib/BinaryDataManager/FileSystem.ts +0 -214
- package/src/lib/BinaryDataManager/index.ts +0 -187
- package/src/lib/ChangeCase.ts +0 -45
- package/src/lib/Constants.ts +0 -16
- package/src/lib/Credentials.ts +0 -108
- package/src/lib/FileSystem.ts +0 -214
- package/src/lib/InputConnectionDataLegacy.ts +0 -123
- package/src/lib/Interfaces.ts +0 -338
- package/src/lib/LoadNodeParameterOptions.ts +0 -235
- package/src/lib/NodeExecuteFunctions.ts +0 -3700
- package/src/lib/NodesLoader/constants.ts +0 -112
- package/src/lib/NodesLoader/custom-directory-loader.ts +0 -31
- package/src/lib/NodesLoader/directory-loader.ts +0 -458
- package/src/lib/NodesLoader/index.ts +0 -5
- package/src/lib/NodesLoader/lazy-package-directory-loader.ts +0 -55
- package/src/lib/NodesLoader/load-class-in-isolation.ts +0 -19
- package/src/lib/NodesLoader/package-directory-loader.ts +0 -107
- package/src/lib/NodesLoader/types.ts +0 -14
- package/src/lib/RedisLeaderElectionManager.ts +0 -334
- package/src/lib/UserSettings.ts +0 -292
- package/src/lib/WorkflowExecute.ts +0 -1128
- package/src/lib/index.ts +0 -187
- package/src/utils/crypto.ts +0 -5
- package/tests/Credentials.test.ts +0 -88
- package/tests/Helpers.ts +0 -808
- package/tests/WorkflowExecute.test.ts +0 -1242
- package/tsconfig.json +0 -41
- package/tsconfig.lib.json +0 -10
- package/tsconfig.spec.json +0 -14
- /package/dist/{src/lib → lib}/ChangeCase.d.ts +0 -0
- /package/dist/{src/lib → lib}/Constants.d.ts +0 -0
- /package/dist/{src/lib → lib}/NodesLoader/load-class-in-isolation.d.ts +0 -0
- /package/dist/{src/lib → lib}/NodesLoader/types.d.ts +0 -0
- /package/dist/{src/utils → utils}/crypto.d.ts +0 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable no-continue */
|
|
3
|
+
/* eslint-disable no-await-in-loop */
|
|
4
|
+
/* eslint-disable no-restricted-syntax */
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ActiveWorkflows = void 0;
|
|
7
|
+
const workflow_1 = require("@fsai-flow/workflow");
|
|
8
|
+
const cron_1 = require("cron");
|
|
9
|
+
const crypto_1 = require("../utils/crypto");
|
|
10
|
+
const RedisLeaderElectionManager_1 = require("./RedisLeaderElectionManager");
|
|
11
|
+
class ActiveWorkflows {
|
|
12
|
+
constructor(redisConfig) {
|
|
13
|
+
this.workflowData = {};
|
|
14
|
+
this.instanceLeaderElection = null;
|
|
15
|
+
this.pollingWorkflows = new Map();
|
|
16
|
+
this.isLeader = false;
|
|
17
|
+
this.isInitializingLeaderElection = false;
|
|
18
|
+
this.leaderElectionInitialized = false;
|
|
19
|
+
this.leaderElectionEnabled = false;
|
|
20
|
+
this.redisConfig = redisConfig;
|
|
21
|
+
this.leaderElectionEnabled = redisConfig !== undefined;
|
|
22
|
+
if (!this.leaderElectionEnabled) {
|
|
23
|
+
workflow_1.LoggerProxy.info("📴 Redis leader election disabled - workflows will run on all instances");
|
|
24
|
+
this.isLeader = true; // In non-queue mode, every instance is a "leader"
|
|
25
|
+
this.leaderElectionInitialized = true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Returns if the workflow is active
|
|
30
|
+
*
|
|
31
|
+
* @param {string} id The id of the workflow to check
|
|
32
|
+
* @returns {boolean}
|
|
33
|
+
* @memberof ActiveWorkflows
|
|
34
|
+
*/
|
|
35
|
+
isActive(id) {
|
|
36
|
+
return Object.hasOwn(this.workflowData, id);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Returns the ids of the currently active workflows
|
|
40
|
+
*
|
|
41
|
+
* @returns {string[]}
|
|
42
|
+
* @memberof ActiveWorkflows
|
|
43
|
+
*/
|
|
44
|
+
allActiveWorkflows() {
|
|
45
|
+
return Object.keys(this.workflowData);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Returns the Workflow data for the workflow with
|
|
49
|
+
* the given id if it is currently active
|
|
50
|
+
*
|
|
51
|
+
* @param {string} id
|
|
52
|
+
* @returns {(WorkflowData | undefined)}
|
|
53
|
+
* @memberof ActiveWorkflows
|
|
54
|
+
*/
|
|
55
|
+
get(id) {
|
|
56
|
+
return this.workflowData[id];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Makes a workflow active
|
|
60
|
+
*
|
|
61
|
+
* @param {string} id The id of the workflow to activate
|
|
62
|
+
* @param {Workflow} workflow The workflow to activate
|
|
63
|
+
* @param {IWorkflowExecuteAdditionalData} additionalData The additional data which is needed to run workflows
|
|
64
|
+
* @returns {Promise<void>}
|
|
65
|
+
* @memberof ActiveWorkflows
|
|
66
|
+
*/
|
|
67
|
+
async add(id, workflow, additionalData, mode, activation, getTriggerFunctions, getPollFunctions) {
|
|
68
|
+
this.workflowData[id] = {};
|
|
69
|
+
const triggerNodes = workflow.getTriggerNodes();
|
|
70
|
+
let triggerResponse;
|
|
71
|
+
this.workflowData[id].triggerResponses = [];
|
|
72
|
+
for (const triggerNode of triggerNodes) {
|
|
73
|
+
triggerResponse = await workflow.runTrigger(triggerNode, getTriggerFunctions, additionalData, mode, activation);
|
|
74
|
+
if (triggerResponse !== undefined) {
|
|
75
|
+
// If a response was given save it
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
77
|
+
this.workflowData[id].triggerResponses?.push(triggerResponse);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const pollNodes = workflow.getPollNodes();
|
|
81
|
+
if (pollNodes.length) {
|
|
82
|
+
this.workflowData[id].pollResponses = [];
|
|
83
|
+
for (const pollNode of pollNodes) {
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
85
|
+
this.workflowData[id].pollResponses?.push(await this.activatePolling(pollNode, workflow, additionalData, getPollFunctions, mode, activation));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Initialize global leader election (called once per instance)
|
|
91
|
+
*/
|
|
92
|
+
async initializeLeaderElection() {
|
|
93
|
+
// Skip leader election if disabled
|
|
94
|
+
if (!this.leaderElectionEnabled) {
|
|
95
|
+
workflow_1.LoggerProxy.debug("📴 Leader election disabled - skipping initialization");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// Prevent race condition - only one initialization at a time
|
|
99
|
+
if (this.leaderElectionInitialized || this.isInitializingLeaderElection) {
|
|
100
|
+
workflow_1.LoggerProxy.debug("⏳ Leader election already initialized or in progress, waiting...");
|
|
101
|
+
// Wait for initialization to complete
|
|
102
|
+
while (this.isInitializingLeaderElection) {
|
|
103
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.isInitializingLeaderElection = true;
|
|
108
|
+
try {
|
|
109
|
+
const nodeId = process.env["POD_NAME"] ||
|
|
110
|
+
process.env["HOSTNAME"] ||
|
|
111
|
+
`flowx-instance-${Math.random().toString(36).substr(2, 9)}`;
|
|
112
|
+
const lockName = "global-polling-leadership";
|
|
113
|
+
workflow_1.LoggerProxy.info(`🌟 Initializing global polling leadership for instance ${nodeId}`);
|
|
114
|
+
this.instanceLeaderElection = new RedisLeaderElectionManager_1.RedisLeaderElectionManager(lockName, this.redisConfig, // We know it's not null because leaderElectionEnabled is true
|
|
115
|
+
{
|
|
116
|
+
onStartedLeading: () => {
|
|
117
|
+
workflow_1.LoggerProxy.info(`🏆 Instance ${nodeId} became GLOBAL POLLING LEADER - starting all registered workflows (current count: ${this.pollingWorkflows.size})`);
|
|
118
|
+
this.isLeader = true;
|
|
119
|
+
// Set initialized flag before starting workflows to prevent timing issue
|
|
120
|
+
this.leaderElectionInitialized = true;
|
|
121
|
+
this.startAllPollingWorkflows();
|
|
122
|
+
},
|
|
123
|
+
onStoppedLeading: () => {
|
|
124
|
+
workflow_1.LoggerProxy.info(`📉 Instance ${nodeId} lost GLOBAL POLLING LEADERSHIP - stopping all workflows`);
|
|
125
|
+
this.isLeader = false;
|
|
126
|
+
this.stopAllPollingWorkflows();
|
|
127
|
+
},
|
|
128
|
+
onNewLeader: (_identity) => {
|
|
129
|
+
//Logger.info(`👑 New global polling leader: ${identity}`);
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
await this.instanceLeaderElection.start();
|
|
133
|
+
// Only set this if we didn't become leader during start() - prevent double-setting
|
|
134
|
+
if (!this.leaderElectionInitialized) {
|
|
135
|
+
this.leaderElectionInitialized = true;
|
|
136
|
+
}
|
|
137
|
+
workflow_1.LoggerProxy.info(`✅ Global leader election initialized for ${nodeId}`);
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
this.isInitializingLeaderElection = false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Start all registered polling workflows (called when becoming leader)
|
|
145
|
+
*/
|
|
146
|
+
startAllPollingWorkflows() {
|
|
147
|
+
workflow_1.LoggerProxy.debug(`🔍 startAllPollingWorkflows called - isLeader: ${this.isLeader}, initialized: ${this.leaderElectionInitialized}, workflows: ${this.pollingWorkflows.size}`);
|
|
148
|
+
// Safety check: Only start if we're truly the leader and initialization is complete
|
|
149
|
+
if (!this.isLeader || !this.leaderElectionInitialized) {
|
|
150
|
+
workflow_1.LoggerProxy.warn(`⚠️ Cannot start workflows - not leader (isLeader: ${this.isLeader}, initialized: ${this.leaderElectionInitialized})`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
workflow_1.LoggerProxy.info(`🚀 Starting all ${this.pollingWorkflows.size} registered polling workflows`);
|
|
154
|
+
workflow_1.LoggerProxy.debug(`📋 Registered workflow keys: ${Array.from(this.pollingWorkflows.keys()).join(", ")}`);
|
|
155
|
+
let startedCount = 0;
|
|
156
|
+
for (const [workflowKey, pollingWorkflow] of this.pollingWorkflows) {
|
|
157
|
+
try {
|
|
158
|
+
// Skip if already running (double-check safety)
|
|
159
|
+
if (pollingWorkflow.activeCronJobs.length > 0) {
|
|
160
|
+
workflow_1.LoggerProxy.warn(`⚠️ Workflow ${workflowKey} already has active cron jobs - skipping`);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
workflow_1.LoggerProxy.info(`⏰ Starting cron jobs for workflow "${pollingWorkflow.workflow.name}", node "${pollingWorkflow.node.name}" (${pollingWorkflow.cronTimes.length} schedules)`);
|
|
164
|
+
// Create and start cron jobs for this workflow
|
|
165
|
+
pollingWorkflow.activeCronJobs = pollingWorkflow.cronTimes.map((cronTime) => new cron_1.CronJob(cronTime, pollingWorkflow.executeTrigger, undefined, true, pollingWorkflow.timezone));
|
|
166
|
+
startedCount++;
|
|
167
|
+
workflow_1.LoggerProxy.info(`✅ Started ${pollingWorkflow.activeCronJobs.length} cron jobs for workflow "${pollingWorkflow.workflow.name}"`);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
workflow_1.LoggerProxy.error(`❌ Failed to start polling for ${workflowKey}: ${error instanceof Error ? error.message : String(error)}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
workflow_1.LoggerProxy.info(`🎯 Successfully started ${startedCount} of ${this.pollingWorkflows.size} polling workflows`);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Stop all polling workflows (called when losing leadership)
|
|
177
|
+
*/
|
|
178
|
+
stopAllPollingWorkflows() {
|
|
179
|
+
workflow_1.LoggerProxy.info(`🛑 Stopping all ${this.pollingWorkflows.size} polling workflows`);
|
|
180
|
+
for (const [workflowKey, pollingWorkflow] of this.pollingWorkflows) {
|
|
181
|
+
try {
|
|
182
|
+
// Skip if already stopped (safety check)
|
|
183
|
+
if (pollingWorkflow.activeCronJobs.length === 0) {
|
|
184
|
+
workflow_1.LoggerProxy.debug(`⏭️ Workflow ${workflowKey} already stopped - skipping`);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
workflow_1.LoggerProxy.info(`⏹️ Stopping cron jobs for workflow "${pollingWorkflow.workflow.name}", node "${pollingWorkflow.node.name}"`);
|
|
188
|
+
// Stop all cron jobs for this workflow
|
|
189
|
+
for (const job of pollingWorkflow.activeCronJobs) {
|
|
190
|
+
job.stop();
|
|
191
|
+
}
|
|
192
|
+
pollingWorkflow.activeCronJobs = [];
|
|
193
|
+
workflow_1.LoggerProxy.debug(`✅ Stopped polling for ${workflowKey}`);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
workflow_1.LoggerProxy.error(`❌ Failed to stop polling for ${workflowKey}: ${error instanceof Error ? error.message : String(error)}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Activates polling for the given node with global Redis leader election
|
|
202
|
+
*
|
|
203
|
+
* @param {INode} node
|
|
204
|
+
* @param {Workflow} workflow
|
|
205
|
+
* @param {IWorkflowExecuteAdditionalData} additionalData
|
|
206
|
+
* @param {IGetExecutePollFunctions} getPollFunctions
|
|
207
|
+
* @returns {Promise<IPollResponse>}
|
|
208
|
+
* @memberof ActiveWorkflows
|
|
209
|
+
*/
|
|
210
|
+
async activatePolling(node, workflow, additionalData, getPollFunctions, mode, activation) {
|
|
211
|
+
const pollFunctions = getPollFunctions(workflow, node, additionalData, mode, activation);
|
|
212
|
+
// Get polling configuration
|
|
213
|
+
const pollTimes = pollFunctions.getNodeParameter("pollTimes");
|
|
214
|
+
// Build cron times
|
|
215
|
+
const cronTimes = this.buildCronTimes(pollTimes);
|
|
216
|
+
const timezone = pollFunctions.getTimezone();
|
|
217
|
+
// Validate cron times
|
|
218
|
+
this.validateCronTimes(cronTimes);
|
|
219
|
+
// Create unique workflow key
|
|
220
|
+
const workflowKey = `${workflow.id}-${node.name}`;
|
|
221
|
+
// Check if this workflow is already registered (prevents duplicate registrations)
|
|
222
|
+
if (this.pollingWorkflows.has(workflowKey)) {
|
|
223
|
+
workflow_1.LoggerProxy.warn(`⚠️ Workflow ${workflowKey} is already registered - returning existing closeFunction`);
|
|
224
|
+
const _existingWorkflow = this.pollingWorkflows.get(workflowKey);
|
|
225
|
+
return {
|
|
226
|
+
closeFunction: async () => {
|
|
227
|
+
workflow_1.LoggerProxy.info(`🗑️ Removing duplicate registration for: "${workflow.name}" (node: ${node.name})`);
|
|
228
|
+
// Just log - don't actually remove since this is a duplicate
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
// Create the polling execution function
|
|
233
|
+
const executeTrigger = async () => {
|
|
234
|
+
try {
|
|
235
|
+
workflow_1.LoggerProxy.info(`Polling trigger initiated for workflow "${workflow.name}"`, {
|
|
236
|
+
workflowName: workflow.name,
|
|
237
|
+
workflowId: workflow.id,
|
|
238
|
+
});
|
|
239
|
+
const pollResponse = await workflow.runPoll(node, pollFunctions);
|
|
240
|
+
if (pollResponse !== null) {
|
|
241
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
242
|
+
pollFunctions.__emit(pollResponse);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
const err = error;
|
|
247
|
+
workflow_1.LoggerProxy.error(`Polling trigger failed for workflow "${workflow.name}"`, {
|
|
248
|
+
workflowName: workflow.name,
|
|
249
|
+
workflowId: workflow.id,
|
|
250
|
+
nodeName: node.name,
|
|
251
|
+
error: err.message,
|
|
252
|
+
statusCode: err.statusCode,
|
|
253
|
+
httpCode: err.httpCode,
|
|
254
|
+
description: err.description,
|
|
255
|
+
cause: typeof err.cause === "object" &&
|
|
256
|
+
err.cause instanceof Error
|
|
257
|
+
? err.cause.message
|
|
258
|
+
: err.cause,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
// Register this polling workflow
|
|
263
|
+
const pollingWorkflow = {
|
|
264
|
+
workflow,
|
|
265
|
+
node,
|
|
266
|
+
cronTimes,
|
|
267
|
+
timezone,
|
|
268
|
+
executeTrigger,
|
|
269
|
+
activeCronJobs: [],
|
|
270
|
+
};
|
|
271
|
+
this.pollingWorkflows.set(workflowKey, pollingWorkflow);
|
|
272
|
+
workflow_1.LoggerProxy.info(`📋 Registered polling workflow: "${workflow.name}" (node: ${node.name}) [Key: ${workflowKey}] - Total workflows: ${this.pollingWorkflows.size}`);
|
|
273
|
+
// Initialize global leader election if enabled
|
|
274
|
+
await this.initializeLeaderElection();
|
|
275
|
+
// Start workflow if we're the leader (or if leader election is disabled)
|
|
276
|
+
if (this.isLeader &&
|
|
277
|
+
this.leaderElectionInitialized &&
|
|
278
|
+
pollingWorkflow.activeCronJobs.length === 0) {
|
|
279
|
+
const reason = this.leaderElectionEnabled
|
|
280
|
+
? "Leader established"
|
|
281
|
+
: "Leader election disabled";
|
|
282
|
+
workflow_1.LoggerProxy.info(`🔥 Post-registration start: "${workflow.name}" [Key: ${workflowKey}] - ${reason}, starting immediately`);
|
|
283
|
+
try {
|
|
284
|
+
pollingWorkflow.activeCronJobs = pollingWorkflow.cronTimes.map((cronTime) => new cron_1.CronJob(cronTime, pollingWorkflow.executeTrigger, undefined, true, pollingWorkflow.timezone));
|
|
285
|
+
workflow_1.LoggerProxy.info(`✅ Post-registration: Started ${pollingWorkflow.activeCronJobs.length} cron jobs for "${workflow.name}"`);
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
workflow_1.LoggerProxy.error(`❌ Failed to start newly registered workflow ${workflowKey}: ${error instanceof Error ? error.message : String(error)}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
const leaderElectionStatus = this.leaderElectionEnabled
|
|
293
|
+
? "enabled"
|
|
294
|
+
: "disabled";
|
|
295
|
+
workflow_1.LoggerProxy.debug(`⏳ Post-registration wait: "${workflow.name}" [Key: ${workflowKey}] (isLeader: ${this.isLeader}, initialized: ${this.leaderElectionInitialized}, activeCronJobs: ${pollingWorkflow.activeCronJobs.length}, leaderElection: ${leaderElectionStatus})`);
|
|
296
|
+
}
|
|
297
|
+
// Return cleanup function
|
|
298
|
+
return {
|
|
299
|
+
closeFunction: async () => {
|
|
300
|
+
workflow_1.LoggerProxy.info(`🗑️ Removing polling workflow: "${workflow.name}" (node: ${node.name})`);
|
|
301
|
+
// Stop cron jobs for this workflow
|
|
302
|
+
const workflow_to_remove = this.pollingWorkflows.get(workflowKey);
|
|
303
|
+
if (workflow_to_remove) {
|
|
304
|
+
for (const job of workflow_to_remove.activeCronJobs) {
|
|
305
|
+
job.stop();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Remove from registered workflows
|
|
309
|
+
this.pollingWorkflows.delete(workflowKey);
|
|
310
|
+
workflow_1.LoggerProxy.debug(`✅ Removed polling workflow ${workflowKey}`);
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Extract cron time building logic (same as original)
|
|
316
|
+
*/
|
|
317
|
+
buildCronTimes(pollTimes) {
|
|
318
|
+
const parameterOrder = [
|
|
319
|
+
"second",
|
|
320
|
+
"minute",
|
|
321
|
+
"hour",
|
|
322
|
+
"dayOfMonth",
|
|
323
|
+
"month",
|
|
324
|
+
"weekday",
|
|
325
|
+
];
|
|
326
|
+
const cronTimes = [];
|
|
327
|
+
if (pollTimes.item !== undefined) {
|
|
328
|
+
for (const item of pollTimes.item) {
|
|
329
|
+
let cronTime;
|
|
330
|
+
if (item.mode === "custom") {
|
|
331
|
+
cronTimes.push(item["cronExpression"].trim());
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (item.mode === "everyMinute") {
|
|
335
|
+
cronTimes.push(`${(0, crypto_1.secureRandomNumber)(1, 60).toString()} * * * * *`);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (item.mode === "everyX") {
|
|
339
|
+
if (item["unit"] === "minutes") {
|
|
340
|
+
cronTimes.push(`${(0, crypto_1.secureRandomNumber)(1, 60).toString()} */${item["value"]} * * * *`);
|
|
341
|
+
}
|
|
342
|
+
else if (item["unit"] === "hours") {
|
|
343
|
+
cronTimes.push(`${(0, crypto_1.secureRandomNumber)(1, 60).toString()} 0 */${item["value"]} * * *`);
|
|
344
|
+
}
|
|
345
|
+
else if (item["unit"] === "seconds") {
|
|
346
|
+
cronTimes.push(`*/${item["value"]} * * * * *`);
|
|
347
|
+
}
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
cronTime = [];
|
|
351
|
+
for (const parameterName of parameterOrder) {
|
|
352
|
+
if (item[parameterName] !== undefined) {
|
|
353
|
+
cronTime.push(item[parameterName]);
|
|
354
|
+
}
|
|
355
|
+
else if (parameterName === "second") {
|
|
356
|
+
cronTime.push((0, crypto_1.secureRandomNumber)(1, 60).toString());
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
cronTime.push("*");
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
cronTimes.push(cronTime.join(" "));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return cronTimes;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Validate cron times (same as original)
|
|
369
|
+
*/
|
|
370
|
+
validateCronTimes(cronTimes) {
|
|
371
|
+
for (const cronTime of cronTimes) {
|
|
372
|
+
const cronTimeParts = cronTime.split(" ");
|
|
373
|
+
if (cronTimeParts.length === 6 && cronTimeParts[0].includes("/")) {
|
|
374
|
+
const secondsInterval = Number.parseInt(cronTimeParts[0].replace("*/", ""), 10);
|
|
375
|
+
if (secondsInterval < 1) {
|
|
376
|
+
throw new Error("The polling interval is too short. It has to be at least 1 second!");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
else if (cronTimeParts.length > 0 &&
|
|
380
|
+
cronTimeParts[0].includes("*") &&
|
|
381
|
+
!cronTimeParts[0].includes("/")) {
|
|
382
|
+
throw new Error("The polling interval is too short. It has to be at least a minute!");
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Makes a workflow inactive
|
|
388
|
+
*
|
|
389
|
+
* @param {string} id The id of the workflow to deactivate
|
|
390
|
+
* @returns {Promise<void>}
|
|
391
|
+
* @memberof ActiveWorkflows
|
|
392
|
+
*/
|
|
393
|
+
async remove(id) {
|
|
394
|
+
if (!this.isActive(id)) {
|
|
395
|
+
// Workflow is currently not registered
|
|
396
|
+
throw new Error(`The workflow with the id "${id}" is currently not active and can so not be removed`);
|
|
397
|
+
}
|
|
398
|
+
const workflowData = this.workflowData[id];
|
|
399
|
+
if (workflowData.triggerResponses) {
|
|
400
|
+
for (const triggerResponse of workflowData.triggerResponses) {
|
|
401
|
+
if (triggerResponse.closeFunction) {
|
|
402
|
+
try {
|
|
403
|
+
await triggerResponse.closeFunction();
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
workflow_1.LoggerProxy.error(`There was a problem deactivating trigger of workflow "${id}": "${error instanceof Error ? error.message : String(error)}"`, {
|
|
407
|
+
workflowId: id,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (workflowData.pollResponses) {
|
|
414
|
+
for (const pollResponse of workflowData.pollResponses) {
|
|
415
|
+
if (pollResponse.closeFunction) {
|
|
416
|
+
try {
|
|
417
|
+
await pollResponse.closeFunction();
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
workflow_1.LoggerProxy.error(`There was a problem deactivating polling trigger of workflow "${id}": "${error instanceof Error ? error.message : String(error)}"`, {
|
|
421
|
+
workflowId: id,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// Remove polling workflows from the global map (closeFunction will handle stopping cron jobs)
|
|
428
|
+
const pollingWorkflowsToRemove = [];
|
|
429
|
+
for (const [workflowKey, pollingWorkflow] of this.pollingWorkflows) {
|
|
430
|
+
if (pollingWorkflow.workflow.id === id) {
|
|
431
|
+
pollingWorkflowsToRemove.push(workflowKey);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
for (const workflowKey of pollingWorkflowsToRemove) {
|
|
435
|
+
const pollingWorkflow = this.pollingWorkflows.get(workflowKey);
|
|
436
|
+
if (pollingWorkflow) {
|
|
437
|
+
workflow_1.LoggerProxy.info(`🗑️ Removing polling workflow: "${pollingWorkflow.workflow.name}" (node: ${pollingWorkflow.node.name})`);
|
|
438
|
+
// Stop cron jobs for this workflow
|
|
439
|
+
for (const job of pollingWorkflow.activeCronJobs) {
|
|
440
|
+
job.stop();
|
|
441
|
+
}
|
|
442
|
+
// Remove from registered workflows
|
|
443
|
+
this.pollingWorkflows.delete(workflowKey);
|
|
444
|
+
workflow_1.LoggerProxy.debug(`✅ Removed polling workflow ${workflowKey}`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
// If no more polling workflows are registered and leader election is enabled, stop it
|
|
448
|
+
if (this.pollingWorkflows.size === 0 &&
|
|
449
|
+
this.instanceLeaderElection &&
|
|
450
|
+
this.leaderElectionEnabled) {
|
|
451
|
+
workflow_1.LoggerProxy.info("📴 No more polling workflows - stopping global leader election");
|
|
452
|
+
try {
|
|
453
|
+
await this.instanceLeaderElection.stop();
|
|
454
|
+
this.instanceLeaderElection = null;
|
|
455
|
+
this.isLeader = false;
|
|
456
|
+
this.leaderElectionInitialized = false;
|
|
457
|
+
}
|
|
458
|
+
catch (error) {
|
|
459
|
+
workflow_1.LoggerProxy.error(`There was a problem stopping global leader election: "${error instanceof Error ? error.message : String(error)}"`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
delete this.workflowData[id];
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
exports.ActiveWorkflows = ActiveWorkflows;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IBinaryDataConfig, IBinaryDataManager } from
|
|
1
|
+
import type { IBinaryDataConfig, IBinaryDataManager } from "../Interfaces";
|
|
2
2
|
export declare class BinaryDataFileSystem implements IBinaryDataManager {
|
|
3
3
|
private storagePath;
|
|
4
4
|
private binaryDataTTL;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.BinaryDataFileSystem = void 0;
|
|
37
|
+
const node_fs_1 = require("node:fs");
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const uuid_1 = require("uuid");
|
|
40
|
+
const PREFIX_METAFILE = "binarymeta";
|
|
41
|
+
const PREFIX_PERSISTED_METAFILE = "persistedmeta";
|
|
42
|
+
class BinaryDataFileSystem {
|
|
43
|
+
constructor(config) {
|
|
44
|
+
this.storagePath = config.localStoragePath;
|
|
45
|
+
this.binaryDataTTL = config.binaryDataTTL;
|
|
46
|
+
this.persistedBinaryDataTTL = config.persistedBinaryDataTTL;
|
|
47
|
+
}
|
|
48
|
+
async init(startPurger = false) {
|
|
49
|
+
if (startPurger) {
|
|
50
|
+
setInterval(async () => {
|
|
51
|
+
await this.deleteMarkedFiles();
|
|
52
|
+
}, this.binaryDataTTL * 30000);
|
|
53
|
+
setInterval(async () => {
|
|
54
|
+
await this.deleteMarkedPersistedFiles();
|
|
55
|
+
}, this.persistedBinaryDataTTL * 30000);
|
|
56
|
+
}
|
|
57
|
+
return node_fs_1.promises
|
|
58
|
+
.readdir(this.storagePath)
|
|
59
|
+
.catch(async () => node_fs_1.promises.mkdir(this.storagePath, { recursive: true }))
|
|
60
|
+
.then(async () => node_fs_1.promises.readdir(this.getBinaryDataMetaPath()))
|
|
61
|
+
.catch(async () => node_fs_1.promises.mkdir(this.getBinaryDataMetaPath(), { recursive: true }))
|
|
62
|
+
.then(async () => node_fs_1.promises.readdir(this.getBinaryDataPersistMetaPath()))
|
|
63
|
+
.catch(async () => node_fs_1.promises.mkdir(this.getBinaryDataPersistMetaPath(), { recursive: true }))
|
|
64
|
+
.then(async () => this.deleteMarkedFiles())
|
|
65
|
+
.then(async () => this.deleteMarkedPersistedFiles())
|
|
66
|
+
.then(() => { });
|
|
67
|
+
}
|
|
68
|
+
async storeBinaryData(binaryBuffer, executionId) {
|
|
69
|
+
const binaryDataId = this.generateFileName(executionId);
|
|
70
|
+
return this.addBinaryIdToPersistMeta(executionId, binaryDataId).then(async () => this.saveToLocalStorage(binaryBuffer, binaryDataId).then(() => binaryDataId));
|
|
71
|
+
}
|
|
72
|
+
async retrieveBinaryDataByIdentifier(identifier) {
|
|
73
|
+
return this.retrieveFromLocalStorage(identifier);
|
|
74
|
+
}
|
|
75
|
+
async markDataForDeletionByExecutionId(executionId) {
|
|
76
|
+
const tt = new Date(Date.now() + this.binaryDataTTL * 60000);
|
|
77
|
+
return node_fs_1.promises.writeFile(path.join(this.getBinaryDataMetaPath(), `${PREFIX_METAFILE}_${executionId}_${tt.valueOf()}`), "");
|
|
78
|
+
}
|
|
79
|
+
async deleteMarkedFiles() {
|
|
80
|
+
return this.deleteMarkedFilesByMeta(this.getBinaryDataMetaPath(), PREFIX_METAFILE);
|
|
81
|
+
}
|
|
82
|
+
async deleteMarkedPersistedFiles() {
|
|
83
|
+
return this.deleteMarkedFilesByMeta(this.getBinaryDataPersistMetaPath(), PREFIX_PERSISTED_METAFILE);
|
|
84
|
+
}
|
|
85
|
+
async addBinaryIdToPersistMeta(executionId, identifier) {
|
|
86
|
+
const currentTime = Date.now();
|
|
87
|
+
const timeAtNextHour = currentTime + 3600000 - (currentTime % 3600000);
|
|
88
|
+
const timeoutTime = timeAtNextHour + this.persistedBinaryDataTTL * 60000;
|
|
89
|
+
const filePath = path.join(this.getBinaryDataPersistMetaPath(), `${PREFIX_PERSISTED_METAFILE}_${executionId}_${timeoutTime}`);
|
|
90
|
+
return node_fs_1.promises
|
|
91
|
+
.readFile(filePath)
|
|
92
|
+
.catch(async () => node_fs_1.promises.writeFile(filePath, identifier))
|
|
93
|
+
.then(() => { });
|
|
94
|
+
}
|
|
95
|
+
async deleteMarkedFilesByMeta(metaPath, filePrefix) {
|
|
96
|
+
const currentTimeValue = Date.now();
|
|
97
|
+
const metaFileNames = await node_fs_1.promises.readdir(metaPath);
|
|
98
|
+
const execsAdded = {};
|
|
99
|
+
const proms = metaFileNames.reduce((prev, curr) => {
|
|
100
|
+
const [prefix, executionId, ts] = curr.split("_");
|
|
101
|
+
if (prefix !== filePrefix) {
|
|
102
|
+
return prev;
|
|
103
|
+
}
|
|
104
|
+
const execTimestamp = Number.parseInt(ts, 10);
|
|
105
|
+
if (execTimestamp < currentTimeValue) {
|
|
106
|
+
if (execsAdded[executionId]) {
|
|
107
|
+
// do not delete data, only meta file
|
|
108
|
+
prev.push(this.deleteMetaFileByPath(path.join(metaPath, curr)));
|
|
109
|
+
return prev;
|
|
110
|
+
}
|
|
111
|
+
execsAdded[executionId] = 1;
|
|
112
|
+
prev.push(this.deleteBinaryDataByExecutionId(executionId).then(async () => this.deleteMetaFileByPath(path.join(metaPath, curr))));
|
|
113
|
+
}
|
|
114
|
+
return prev;
|
|
115
|
+
}, [Promise.resolve()]);
|
|
116
|
+
return Promise.all(proms).then(() => { });
|
|
117
|
+
}
|
|
118
|
+
async duplicateBinaryDataByIdentifier(binaryDataId, prefix) {
|
|
119
|
+
const newBinaryDataId = this.generateFileName(prefix);
|
|
120
|
+
return node_fs_1.promises
|
|
121
|
+
.copyFile(path.join(this.storagePath, binaryDataId), path.join(this.storagePath, newBinaryDataId))
|
|
122
|
+
.then(() => newBinaryDataId);
|
|
123
|
+
}
|
|
124
|
+
async deleteBinaryDataByExecutionId(executionId) {
|
|
125
|
+
const regex = new RegExp(`${executionId}_*`);
|
|
126
|
+
const filenames = await node_fs_1.promises.readdir(path.join(this.storagePath));
|
|
127
|
+
const proms = filenames.reduce((allProms, filename) => {
|
|
128
|
+
if (regex.test(filename)) {
|
|
129
|
+
allProms.push(node_fs_1.promises.rm(path.join(this.storagePath, filename)));
|
|
130
|
+
}
|
|
131
|
+
return allProms;
|
|
132
|
+
}, [Promise.resolve()]);
|
|
133
|
+
return Promise.all(proms).then(async () => Promise.resolve());
|
|
134
|
+
}
|
|
135
|
+
async deleteBinaryDataByIdentifier(identifier) {
|
|
136
|
+
return this.deleteFromLocalStorage(identifier);
|
|
137
|
+
}
|
|
138
|
+
async persistBinaryDataForExecutionId(executionId) {
|
|
139
|
+
return node_fs_1.promises
|
|
140
|
+
.readdir(this.getBinaryDataPersistMetaPath())
|
|
141
|
+
.then(async (metafiles) => {
|
|
142
|
+
const proms = metafiles.reduce((prev, curr) => {
|
|
143
|
+
if (curr.startsWith(`${PREFIX_PERSISTED_METAFILE}_${executionId}_`)) {
|
|
144
|
+
prev.push(node_fs_1.promises.rm(path.join(this.getBinaryDataPersistMetaPath(), curr)));
|
|
145
|
+
return prev;
|
|
146
|
+
}
|
|
147
|
+
return prev;
|
|
148
|
+
}, [Promise.resolve()]);
|
|
149
|
+
return Promise.all(proms).then(() => { });
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
generateFileName(prefix) {
|
|
153
|
+
return `${prefix}_${(0, uuid_1.v4)()}`;
|
|
154
|
+
}
|
|
155
|
+
getBinaryDataMetaPath() {
|
|
156
|
+
return path.join(this.storagePath, "meta");
|
|
157
|
+
}
|
|
158
|
+
getBinaryDataPersistMetaPath() {
|
|
159
|
+
return path.join(this.storagePath, "persistMeta");
|
|
160
|
+
}
|
|
161
|
+
async deleteMetaFileByPath(metafilePath) {
|
|
162
|
+
return node_fs_1.promises.rm(metafilePath);
|
|
163
|
+
}
|
|
164
|
+
async deleteFromLocalStorage(identifier) {
|
|
165
|
+
return node_fs_1.promises.rm(path.join(this.storagePath, identifier));
|
|
166
|
+
}
|
|
167
|
+
async saveToLocalStorage(data, identifier) {
|
|
168
|
+
await node_fs_1.promises.writeFile(path.join(this.storagePath, identifier), data);
|
|
169
|
+
}
|
|
170
|
+
async retrieveFromLocalStorage(identifier) {
|
|
171
|
+
const filePath = path.join(this.storagePath, identifier);
|
|
172
|
+
try {
|
|
173
|
+
return await node_fs_1.promises.readFile(filePath);
|
|
174
|
+
}
|
|
175
|
+
catch (_e) {
|
|
176
|
+
throw new Error(`Error finding file: ${filePath}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
exports.BinaryDataFileSystem = BinaryDataFileSystem;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { IBinaryData, INodeExecutionData } from
|
|
2
|
-
import { IBinaryDataConfig } from
|
|
1
|
+
import type { IBinaryData, INodeExecutionData } from "@fsai-flow/workflow";
|
|
2
|
+
import type { IBinaryDataConfig } from "../Interfaces";
|
|
3
3
|
export declare class BinaryDataManager {
|
|
4
4
|
private static instance;
|
|
5
5
|
private managers;
|