@atom8n/n8n 2.5.2 → 2.5.4

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.
@@ -58,6 +58,7 @@ let CliController = CliController_1 = class CliController {
58
58
  const fileData = body.workflowData;
59
59
  const chatInput = body.chatInput;
60
60
  const inputData = body.inputData;
61
+ const fileModifiedAt = body.fileModifiedAt;
61
62
  this.logger.info(`[cli] inputData: ${inputData ? JSON.stringify(inputData) : '(none)'}`);
62
63
  if (!fileData.nodes || !Array.isArray(fileData.nodes)) {
63
64
  res.status(400).json({ error: 'Workflow does not contain valid nodes' });
@@ -70,13 +71,13 @@ let CliController = CliController_1 = class CliController {
70
71
  this.logger.info(`[cli] Workflow: "${fileData.name}", Nodes: ${fileData.nodes.length}`);
71
72
  try {
72
73
  const user = await this.ownershipService.getInstanceOwner();
73
- const workflowId = await this.syncWorkflow(fileData, user.id);
74
- const workflow = await this.workflowRepository.findOneBy({ id: workflowId });
74
+ const syncResult = await this.syncWorkflow(fileData, user.id, fileModifiedAt);
75
+ const workflow = await this.workflowRepository.findOneBy({ id: syncResult.id });
75
76
  if (!workflow) {
76
77
  res.status(500).json({ error: 'Failed to sync workflow to database' });
77
78
  return;
78
79
  }
79
- this.logger.info(`[cli] Synced workflow: "${workflow.name}" (ID: ${workflowId})`);
80
+ this.logger.info(`[cli] Synced workflow: "${workflow.name}" (ID: ${syncResult.id})`);
80
81
  const triggerNode = workflow.nodes.find((node) => node.type.toLowerCase().includes('trigger') ||
81
82
  node.type.toLowerCase().includes('webhook') ||
82
83
  node.type === 'n8n-nodes-base.start');
@@ -204,13 +205,17 @@ let CliController = CliController_1 = class CliController {
204
205
  includeData: true,
205
206
  unflattenData: true,
206
207
  });
207
- res.json({
208
+ const responsePayload = {
208
209
  success: isSuccess,
209
210
  executionId,
210
211
  status: runResult.status,
211
212
  executionTime: totalTime,
212
213
  data: fullExecution?.data?.resultData ?? runResult.data.resultData,
213
- });
214
+ };
215
+ if (syncResult.syncedWorkflow) {
216
+ responsePayload.syncedWorkflow = syncResult.syncedWorkflow;
217
+ }
218
+ res.json(responsePayload);
214
219
  }
215
220
  catch (error) {
216
221
  if (timeoutId)
@@ -228,17 +233,19 @@ let CliController = CliController_1 = class CliController {
228
233
  });
229
234
  }
230
235
  }
231
- async syncWorkflow(fileData, userId) {
236
+ async syncWorkflow(fileData, userId, fileModifiedAt) {
232
237
  if (fileData.id && (0, utils_1.isWorkflowIdValid)(fileData.id)) {
233
238
  const existing = await this.workflowRepository.findOneBy({ id: fileData.id });
234
239
  if (existing) {
235
- const fileUpdatedAt = fileData.updatedAt
236
- ? new Date(fileData.updatedAt)
237
- : null;
240
+ const fileUpdatedAt = fileModifiedAt ? new Date(fileModifiedAt) : null;
238
241
  const serverUpdatedAt = existing.updatedAt
239
242
  ? new Date(existing.updatedAt)
240
243
  : null;
241
- if (fileUpdatedAt && serverUpdatedAt && fileUpdatedAt > serverUpdatedAt) {
244
+ this.logger.info(`[cli] Found existing workflow (ID match): ${existing.id}, ` +
245
+ `fileUpdatedAt=${fileUpdatedAt?.toISOString() ?? 'null'}, ` +
246
+ `serverUpdatedAt=${serverUpdatedAt?.toISOString() ?? 'null'}`);
247
+ const shouldUpdate = !fileUpdatedAt || !serverUpdatedAt || fileUpdatedAt >= serverUpdatedAt;
248
+ if (shouldUpdate) {
242
249
  this.logger.info(`[cli] Updating existing workflow (ID match): ${existing.id}`);
243
250
  await this.workflowRepository.update(existing.id, {
244
251
  nodes: fileData.nodes,
@@ -247,23 +254,36 @@ let CliController = CliController_1 = class CliController {
247
254
  name: fileData.name,
248
255
  updatedAt: new Date(),
249
256
  });
257
+ return { id: existing.id };
250
258
  }
251
259
  else {
252
- this.logger.info(`[cli] Using existing workflow (ID match): ${existing.id}`);
260
+ this.logger.info(`[cli] Using existing workflow (ID match): ${existing.id} ` +
261
+ `(server is newer: ${serverUpdatedAt.toISOString()} > ${fileUpdatedAt.toISOString()})`);
262
+ return {
263
+ id: existing.id,
264
+ syncedWorkflow: {
265
+ name: existing.name,
266
+ nodes: existing.nodes,
267
+ connections: existing.connections,
268
+ settings: existing.settings,
269
+ pinData: existing.pinData ?? {},
270
+ },
271
+ };
253
272
  }
254
- return existing.id;
255
273
  }
256
274
  }
257
275
  if (fileData.name) {
258
276
  const existing = await this.workflowRepository.findOneBy({ name: fileData.name });
259
277
  if (existing) {
260
- const fileUpdatedAt = fileData.updatedAt
261
- ? new Date(fileData.updatedAt)
262
- : null;
278
+ const fileUpdatedAt = fileModifiedAt ? new Date(fileModifiedAt) : null;
263
279
  const serverUpdatedAt = existing.updatedAt
264
280
  ? new Date(existing.updatedAt)
265
281
  : null;
266
- if (fileUpdatedAt && serverUpdatedAt && fileUpdatedAt > serverUpdatedAt) {
282
+ this.logger.info(`[cli] Found existing workflow (name match): ${existing.id}, ` +
283
+ `fileUpdatedAt=${fileUpdatedAt?.toISOString() ?? 'null'}, ` +
284
+ `serverUpdatedAt=${serverUpdatedAt?.toISOString() ?? 'null'}`);
285
+ const shouldUpdate = !fileUpdatedAt || !serverUpdatedAt || fileUpdatedAt >= serverUpdatedAt;
286
+ if (shouldUpdate) {
267
287
  this.logger.info(`[cli] Updating existing workflow (name match): ${existing.id}`);
268
288
  await this.workflowRepository.update(existing.id, {
269
289
  nodes: fileData.nodes,
@@ -272,11 +292,22 @@ let CliController = CliController_1 = class CliController {
272
292
  name: fileData.name,
273
293
  updatedAt: new Date(),
274
294
  });
295
+ return { id: existing.id };
275
296
  }
276
297
  else {
277
- this.logger.info(`[cli] Using existing workflow (name match): ${existing.id}`);
298
+ this.logger.info(`[cli] Using existing workflow (name match): ${existing.id} ` +
299
+ `(server is newer: ${serverUpdatedAt.toISOString()} > ${fileUpdatedAt.toISOString()})`);
300
+ return {
301
+ id: existing.id,
302
+ syncedWorkflow: {
303
+ name: existing.name,
304
+ nodes: existing.nodes,
305
+ connections: existing.connections,
306
+ settings: existing.settings,
307
+ pinData: existing.pinData ?? {},
308
+ },
309
+ };
278
310
  }
279
- return existing.id;
280
311
  }
281
312
  }
282
313
  const workflowId = fileData.id && (0, utils_1.isWorkflowIdValid)(fileData.id) ? fileData.id : (0, db_1.generateNanoId)();
@@ -301,7 +332,7 @@ let CliController = CliController_1 = class CliController {
301
332
  });
302
333
  await transactionManager.save(sharedWorkflow);
303
334
  });
304
- return workflowId;
335
+ return { id: workflowId };
305
336
  }
306
337
  async health() {
307
338
  return { status: 'ok', timestamp: new Date().toISOString() };