diff --git a/n8n-actions.json b/n8n-actions.json index a6c7ff2..313358d 100644 --- a/n8n-actions.json +++ b/n8n-actions.json @@ -31,6 +31,10 @@ { "fieldName": "responseMode", "fieldType": "string" + }, + { + "fieldName": "correlationId", + "fieldType": "string" } ] } @@ -247,7 +251,7 @@ }, { "parameters": { - "jsCode": "// Format start action result\n// Get data from Route Action (works for both direct and resolved ID paths)\nconst routeData = $('Route Action').item.json;\nconst containerId = routeData.containerId;\nconst containerName = routeData.containerName;\nconst action = routeData.action;\nconst chatId = routeData.chatId;\nconst messageId = routeData.messageId;\nconst responseMode = routeData.responseMode;\n\nconst response = $input.item.json || {};\n\n// Check HTTP status codes first (Docker API with onError:continueRegularOutput)\nif (response.statusCode) {\n if (response.statusCode === 304) {\n // Already in desired state\n return {\n json: {\n success: true,\n message: `\\u2705 ${containerName} is already started`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode === 404) {\n return {\n json: {\n success: false,\n message: `\\u274C Container ${containerName} not found`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode >= 500) {\n return {\n json: {\n success: false,\n message: `\\u274C Server error starting ${containerName}: ${response.statusMessage || 'Unknown error'}`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n }\n}\n\n// Docker API returns 204 No Content on success (empty response body)\n// Error responses contain 'message' field\nconst hasError = response.message || response.error || false;\n\n// Success = no error message in response (empty {} means 204 success)\nconst success = !hasError;\n\nlet message;\nif (success) {\n message = `\\u25B6\\uFE0F ${containerName} started successfully`;\n} else {\n message = `\\u274C Failed to start ${containerName}`;\n}\n\nreturn {\n json: {\n success,\n message,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n};" + "jsCode": "// Format start action result\n// Get data from Route Action (works for both direct and resolved ID paths)\nconst routeData = $('Route Action').item.json;\nconst containerId = routeData.containerId;\nconst containerName = routeData.containerName;\nconst action = routeData.action;\nconst chatId = routeData.chatId;\nconst messageId = routeData.messageId;\nconst responseMode = routeData.responseMode;\nconst correlationId = routeData.correlationId || '';\n\nconst response = $input.item.json || {};\n\n// Check HTTP status codes first (Docker API with onError:continueRegularOutput)\nif (response.statusCode) {\n if (response.statusCode === 304) {\n // Already in desired state\n return {\n json: {\n success: true,\n message: `\\u2705 ${containerName} is already started`,\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode === 404) {\n return {\n json: {\n success: false,\n message: `\\u274C Container ${containerName} not found`,\n error: {\n workflow: 'n8n-actions',\n node: 'Format Start Result',\n message: `Container ${containerName} not found`,\n httpCode: 404,\n rawResponse: JSON.stringify(response).substring(0, 1000)\n },\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode >= 500) {\n return {\n json: {\n success: false,\n message: `\\u274C Server error starting ${containerName}: ${response.statusMessage || 'Unknown error'}`,\n error: {\n workflow: 'n8n-actions',\n node: 'Format Start Result',\n message: `Server error starting container`,\n httpCode: response.statusCode,\n rawResponse: JSON.stringify(response).substring(0, 1000)\n },\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n }\n}\n\n// Docker API returns 204 No Content on success (empty response body)\n// Error responses contain 'message' field\nconst hasError = response.message || response.error || false;\n\n// Success = no error message in response (empty {} means 204 success)\nconst success = !hasError;\n\nlet message;\nif (success) {\n message = `\\u25B6\\uFE0F ${containerName} started successfully`;\n} else {\n message = `\\u274C Failed to start ${containerName}`;\n}\n\nreturn {\n json: {\n success,\n message,\n ...(success ? {} : { error: {\n workflow: 'n8n-actions',\n node: 'Format Start Result',\n message: `Failed to start container`,\n httpCode: response.statusCode || null,\n rawResponse: JSON.stringify(response).substring(0, 1000)\n }}),\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n};" }, "id": "code-format-start-result", "name": "Format Start Result", @@ -260,7 +264,7 @@ }, { "parameters": { - "jsCode": "// Format stop action result\n// Get data from Route Action (works for both direct and resolved ID paths)\nconst routeData = $('Route Action').item.json;\nconst containerId = routeData.containerId;\nconst containerName = routeData.containerName;\nconst action = routeData.action;\nconst chatId = routeData.chatId;\nconst messageId = routeData.messageId;\nconst responseMode = routeData.responseMode;\n\nconst response = $input.item.json || {};\n\n// Check HTTP status codes first (Docker API with onError:continueRegularOutput)\nif (response.statusCode) {\n if (response.statusCode === 304) {\n // Already in desired state\n return {\n json: {\n success: true,\n message: `\\u2705 ${containerName} is already stopped`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode === 404) {\n return {\n json: {\n success: false,\n message: `\\u274C Container ${containerName} not found`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode >= 500) {\n return {\n json: {\n success: false,\n message: `\\u274C Server error stopping ${containerName}: ${response.statusMessage || 'Unknown error'}`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n }\n}\n\n// Docker API returns 204 No Content on success (empty response body)\n// Error responses contain 'message' field\nconst hasError = response.message || response.error || false;\n\n// Success = no error message in response (empty {} means 204 success)\nconst success = !hasError;\n\nlet message;\nif (success) {\n message = `\\u23F9\\uFE0F ${containerName} stopped`;\n} else {\n message = `\\u274C Failed to stop ${containerName}`;\n}\n\nreturn {\n json: {\n success,\n message,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n};" + "jsCode": "// Format stop action result\n// Get data from Route Action (works for both direct and resolved ID paths)\nconst routeData = $('Route Action').item.json;\nconst containerId = routeData.containerId;\nconst containerName = routeData.containerName;\nconst action = routeData.action;\nconst chatId = routeData.chatId;\nconst messageId = routeData.messageId;\nconst responseMode = routeData.responseMode;\nconst correlationId = routeData.correlationId || '';\n\nconst response = $input.item.json || {};\n\n// Check HTTP status codes first (Docker API with onError:continueRegularOutput)\nif (response.statusCode) {\n if (response.statusCode === 304) {\n // Already in desired state\n return {\n json: {\n success: true,\n message: `\\u2705 ${containerName} is already stopped`,\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode === 404) {\n return {\n json: {\n success: false,\n message: `\\u274C Container ${containerName} not found`,\n error: {\n workflow: 'n8n-actions',\n node: 'Format Stop Result',\n message: `Container ${containerName} not found`,\n httpCode: 404,\n rawResponse: JSON.stringify(response).substring(0, 1000)\n },\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode >= 500) {\n return {\n json: {\n success: false,\n message: `\\u274C Server error stopping ${containerName}: ${response.statusMessage || 'Unknown error'}`,\n error: {\n workflow: 'n8n-actions',\n node: 'Format Stop Result',\n message: `Server error stoping container`,\n httpCode: response.statusCode,\n rawResponse: JSON.stringify(response).substring(0, 1000)\n },\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n }\n}\n\n// Docker API returns 204 No Content on success (empty response body)\n// Error responses contain 'message' field\nconst hasError = response.message || response.error || false;\n\n// Success = no error message in response (empty {} means 204 success)\nconst success = !hasError;\n\nlet message;\nif (success) {\n message = `\\u23F9\\uFE0F ${containerName} stopped`;\n} else {\n message = `\\u274C Failed to stop ${containerName}`;\n}\n\nreturn {\n json: {\n success,\n message,\n ...(success ? {} : { error: {\n workflow: 'n8n-actions',\n node: 'Format Stop Result',\n message: `Failed to stop container`,\n httpCode: response.statusCode || null,\n rawResponse: JSON.stringify(response).substring(0, 1000)\n }}),\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n};" }, "id": "code-format-stop-result", "name": "Format Stop Result", @@ -273,7 +277,7 @@ }, { "parameters": { - "jsCode": "// Format restart action result\n// Get data from Route Action (works for both direct and resolved ID paths)\nconst routeData = $('Route Action').item.json;\nconst containerId = routeData.containerId;\nconst containerName = routeData.containerName;\nconst action = routeData.action;\nconst chatId = routeData.chatId;\nconst messageId = routeData.messageId;\nconst responseMode = routeData.responseMode;\n\nconst response = $input.item.json || {};\n\n// Check HTTP status codes first (Docker API with onError:continueRegularOutput)\nif (response.statusCode) {\n if (response.statusCode === 304) {\n // Already in desired state (running)\n return {\n json: {\n success: true,\n message: `\\u2705 ${containerName} is already started`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode === 404) {\n return {\n json: {\n success: false,\n message: `\\u274C Container ${containerName} not found`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode >= 500) {\n return {\n json: {\n success: false,\n message: `\\u274C Server error restarting ${containerName}: ${response.statusMessage || 'Unknown error'}`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n }\n}\n\n// Docker API returns 204 No Content on success (empty response body)\n// Error responses contain 'message' field\nconst hasError = response.message || response.error || false;\n\n// Success = no error message in response (empty {} means 204 success)\nconst success = !hasError;\n\nlet message;\nif (success) {\n message = `\\u{1F504} ${containerName} restarted`;\n} else {\n message = `\\u274C Failed to restart ${containerName}`;\n}\n\nreturn {\n json: {\n success,\n message,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n};" + "jsCode": "// Format restart action result\n// Get data from Route Action (works for both direct and resolved ID paths)\nconst routeData = $('Route Action').item.json;\nconst containerId = routeData.containerId;\nconst containerName = routeData.containerName;\nconst action = routeData.action;\nconst chatId = routeData.chatId;\nconst messageId = routeData.messageId;\nconst responseMode = routeData.responseMode;\nconst correlationId = routeData.correlationId || '';\n\nconst response = $input.item.json || {};\n\n// Check HTTP status codes first (Docker API with onError:continueRegularOutput)\nif (response.statusCode) {\n if (response.statusCode === 304) {\n // Already in desired state (running)\n return {\n json: {\n success: true,\n message: `\\u2705 ${containerName} is already started`,\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode === 404) {\n return {\n json: {\n success: false,\n message: `\\u274C Container ${containerName} not found`,\n error: {\n workflow: 'n8n-actions',\n node: 'Format Restart Result',\n message: `Container ${containerName} not found`,\n httpCode: 404,\n rawResponse: JSON.stringify(response).substring(0, 1000)\n },\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode >= 500) {\n return {\n json: {\n success: false,\n message: `\\u274C Server error restarting ${containerName}: ${response.statusMessage || 'Unknown error'}`,\n error: {\n workflow: 'n8n-actions',\n node: 'Format Restart Result',\n message: `Server error restarting container`,\n httpCode: response.statusCode,\n rawResponse: JSON.stringify(response).substring(0, 1000)\n },\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n }\n}\n\n// Docker API returns 204 No Content on success (empty response body)\n// Error responses contain 'message' field\nconst hasError = response.message || response.error || false;\n\n// Success = no error message in response (empty {} means 204 success)\nconst success = !hasError;\n\nlet message;\nif (success) {\n message = `\\u{1F504} ${containerName} restarted`;\n} else {\n message = `\\u274C Failed to restart ${containerName}`;\n}\n\nreturn {\n json: {\n success,\n message,\n ...(success ? {} : { error: {\n workflow: 'n8n-actions',\n node: 'Format Restart Result',\n message: `Failed to restart container`,\n httpCode: response.statusCode || null,\n rawResponse: JSON.stringify(response).substring(0, 1000)\n }}),\n correlationId,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n};" }, "id": "code-format-restart-result", "name": "Format Restart Result", @@ -400,4 +404,4 @@ "executionOrder": "v1", "callerPolicy": "any" } -} +} \ No newline at end of file diff --git a/n8n-batch-ui.json b/n8n-batch-ui.json index 2a37f4d..2332a80 100644 --- a/n8n-batch-ui.json +++ b/n8n-batch-ui.json @@ -42,6 +42,10 @@ { "fieldName": "batchAction", "fieldType": "string" + }, + { + "fieldName": "correlationId", + "fieldType": "string" } ] } @@ -576,4 +580,4 @@ "executionOrder": "v1", "callerPolicy": "any" } -} +} \ No newline at end of file diff --git a/n8n-confirmation.json b/n8n-confirmation.json index 811851b..0ee5e2b 100644 --- a/n8n-confirmation.json +++ b/n8n-confirmation.json @@ -43,6 +43,10 @@ { "fieldName": "responseMode", "fieldType": "string" + }, + { + "fieldName": "correlationId", + "fieldType": "string" } ] } @@ -328,7 +332,7 @@ }, { "parameters": { - "jsCode": "// Prepare input for container actions sub-workflow (stop action)\nconst data = $('When executed by another workflow').item.json;\n\nreturn {\n json: {\n containerId: data.containerId || '',\n containerName: data.containerName,\n action: 'stop',\n chatId: data.chatId,\n messageId: data.messageId,\n responseMode: data.responseMode || 'inline'\n }\n};" + "jsCode": "// Prepare input for container actions sub-workflow (stop action)\nconst data = $('When executed by another workflow').item.json;\nconst correlationId = data.correlationId || '';\n\nreturn {\n json: {\n containerId: data.containerId || '',\n containerName: data.containerName,\n action: 'stop',\n chatId: data.chatId,\n messageId: data.messageId,\n responseMode: data.responseMode || 'inline',\n correlationId: correlationId\n }\n};" }, "id": "code-prepare-stop-action", "name": "Prepare Stop Action", @@ -600,4 +604,4 @@ "executionOrder": "v1", "callerPolicy": "any" } -} +} \ No newline at end of file diff --git a/n8n-logs.json b/n8n-logs.json index 758a0c2..e91366b 100644 --- a/n8n-logs.json +++ b/n8n-logs.json @@ -14,7 +14,7 @@ }, { "parameters": { - "jsCode": "// Parse and validate input\nconst input = $json;\n\n// Get container identifier (ID or name)\nconst containerId = input.containerId || '';\nconst containerName = input.containerName || '';\nconst lineCount = input.lineCount || 50;\nconst chatId = input.chatId;\nconst messageId = input.messageId || 0;\nconst responseMode = input.responseMode || 'text';\n\nif (!containerId && !containerName) {\n throw new Error('Either containerId or containerName required');\n}\n\nif (!chatId) {\n throw new Error('chatId required');\n}\n\nreturn {\n json: {\n containerId: containerId,\n containerName: containerName,\n lineCount: Math.min(Math.max(parseInt(lineCount), 1), 1000),\n chatId: chatId,\n messageId: messageId,\n responseMode: responseMode\n }\n};" + "jsCode": "// Parse and validate input\nconst input = $json;\n\n// Get container identifier (ID or name)\nconst containerId = input.containerId || '';\nconst containerName = input.containerName || '';\nconst lineCount = input.lineCount || 50;\nconst chatId = input.chatId;\nconst messageId = input.messageId || 0;\nconst responseMode = input.responseMode || 'text';\nconst correlationId = input.correlationId || '';\n\nif (!containerId && !containerName) {\n throw new Error('Either containerId or containerName required');\n}\n\nif (!chatId) {\n throw new Error('chatId required');\n}\n\nreturn {\n json: {\n containerId: containerId,\n containerName: containerName,\n lineCount: Math.min(Math.max(parseInt(lineCount), 1), 1000),\n chatId: chatId,\n messageId: messageId,\n responseMode: responseMode,\n correlationId: correlationId\n }\n};" }, "id": "dac65a64-173a-4536-b3c5-0699704380fa", "name": "Parse Input", @@ -134,7 +134,7 @@ }, { "parameters": { - "jsCode": "// Format logs output for Telegram\nconst rawOutput = $input.item.json.stdout || '';\nconst data = $('Build Logs Command').item.json;\n\n// HTML escape function\nfunction escapeHtml(text) {\n return text.replace(/&/g, '&').replace(//g, '>');\n}\n\n// Handle empty logs\nif (!rawOutput || rawOutput.trim() === '') {\n return {\n json: {\n success: true,\n message: `No logs available for ${data.containerName}`,\n containerName: data.containerName,\n lineCount: 0\n }\n };\n}\n\n// Parse Docker multiplexed stream format\n// Each frame: 8-byte header (byte 0 = stream type, bytes 4-7 = size) + data\nconst logLines = [];\nlet pos = 0;\nwhile (pos < rawOutput.length) {\n const streamType = rawOutput.charCodeAt(pos);\n // Check for valid Docker stream header (0=stdin, 1=stdout, 2=stderr)\n if (streamType <= 2 && pos + 8 <= rawOutput.length) {\n // Parse frame size from bytes 4-7 (big-endian)\n const size = (rawOutput.charCodeAt(pos + 4) << 24) |\n (rawOutput.charCodeAt(pos + 5) << 16) |\n (rawOutput.charCodeAt(pos + 6) << 8) |\n rawOutput.charCodeAt(pos + 7);\n // Extract frame data\n const frameData = rawOutput.substring(pos + 8, pos + 8 + size);\n // Split frame data by newlines and add each line\n frameData.split('\\n').forEach(line => {\n const trimmed = line.replace(/\\r/g, '').trim();\n if (trimmed) logLines.push(trimmed);\n });\n pos += 8 + size;\n } else {\n // Fallback: treat rest as plain text\n rawOutput.substring(pos).split('\\n').forEach(line => {\n const trimmed = line.replace(/\\r/g, '').trim();\n if (trimmed) logLines.push(trimmed);\n });\n break;\n }\n}\n\nconst lines = logLines.join('\\n');\n\n// Truncate for Telegram (4096 char limit, leave room for header)\nconst maxLen = 3800;\nconst truncated = lines.length > maxLen\n ? lines.substring(0, maxLen) + '\\n... (truncated)'\n : lines;\n\n// Escape HTML entities\nconst escaped = escapeHtml(truncated);\n\nconst lineCount = logLines.length;\nconst header = `Logs for ${data.containerName} (last ${lineCount} lines):\\n\\n`;\nconst formatted = header + '
' + escaped + '';\n\nreturn {\n json: {\n success: true,\n message: formatted,\n containerName: data.containerName,\n lineCount: lineCount\n }\n};" + "jsCode": "// Format logs output for Telegram\nconst rawOutput = $input.item.json.stdout || '';\nconst data = $('Build Logs Command').item.json;\nconst correlationId = data.correlationId || '';\n\n// HTML escape function\nfunction escapeHtml(text) {\n return text.replace(/&/g, '&').replace(//g, '>');\n}\n\n// Handle empty logs\nif (!rawOutput || rawOutput.trim() === '') {\n return {\n json: {\n success: true,\n message: `No logs available for ${data.containerName}`,\n containerName: data.containerName,\n lineCount: 0,\n correlationId\n }\n };\n}\n\n// Parse Docker multiplexed stream format\n// Each frame: 8-byte header (byte 0 = stream type, bytes 4-7 = size) + data\nconst logLines = [];\nlet pos = 0;\nwhile (pos < rawOutput.length) {\n const streamType = rawOutput.charCodeAt(pos);\n // Check for valid Docker stream header (0=stdin, 1=stdout, 2=stderr)\n if (streamType <= 2 && pos + 8 <= rawOutput.length) {\n // Parse frame size from bytes 4-7 (big-endian)\n const size = (rawOutput.charCodeAt(pos + 4) << 24) |\n (rawOutput.charCodeAt(pos + 5) << 16) |\n (rawOutput.charCodeAt(pos + 6) << 8) |\n rawOutput.charCodeAt(pos + 7);\n // Extract frame data\n const frameData = rawOutput.substring(pos + 8, pos + 8 + size);\n // Split frame data by newlines and add each line\n frameData.split('\\n').forEach(line => {\n const trimmed = line.replace(/\\r/g, '').trim();\n if (trimmed) logLines.push(trimmed);\n });\n pos += 8 + size;\n } else {\n // Fallback: treat rest as plain text\n rawOutput.substring(pos).split('\\n').forEach(line => {\n const trimmed = line.replace(/\\r/g, '').trim();\n if (trimmed) logLines.push(trimmed);\n });\n break;\n }\n}\n\nconst lines = logLines.join('\\n');\n\n// Truncate for Telegram (4096 char limit, leave room for header)\nconst maxLen = 3800;\nconst truncated = lines.length > maxLen\n ? lines.substring(0, maxLen) + '\\n... (truncated)'\n : lines;\n\n// Escape HTML entities\nconst escaped = escapeHtml(truncated);\n\nconst lineCount = logLines.length;\nconst header = `Logs for ${data.containerName} (last ${lineCount} lines):\\n\\n`;\nconst formatted = header + '
' + escaped + '';\n\nreturn {\n json: {\n success: true,\n message: formatted,\n containerName: data.containerName,\n lineCount: lineCount\n }\n};" }, "id": "e423b026-e619-4e3a-abd9-563e935cd74d", "name": "Format Logs", diff --git a/n8n-matching.json b/n8n-matching.json index e7e2bf9..91ba289 100644 --- a/n8n-matching.json +++ b/n8n-matching.json @@ -31,6 +31,10 @@ { "fieldName": "messageId", "fieldType": "number" + }, + { + "fieldName": "correlationId", + "fieldType": "string" } ] } diff --git a/n8n-status.json b/n8n-status.json index b5303f2..91cb5c5 100644 --- a/n8n-status.json +++ b/n8n-status.json @@ -38,6 +38,10 @@ { "fieldName": "searchTerm", "fieldType": "string" + }, + { + "fieldName": "correlationId", + "fieldType": "string" } ] } diff --git a/n8n-update.json b/n8n-update.json index 0e00ddf..0c7a55a 100644 --- a/n8n-update.json +++ b/n8n-update.json @@ -26,6 +26,10 @@ { "fieldName": "responseMode", "fieldType": "string" + }, + { + "fieldName": "correlationId", + "fieldType": "string" } ] } @@ -117,7 +121,7 @@ }, { "parameters": { - "jsCode": "// Parse container config and prepare for pull\nconst inspectData = $input.item.json;\nconst triggerData = $('When executed by another workflow').item.json;\nconst containerId = triggerData.containerId;\nconst containerName = triggerData.containerName;\nconst chatId = triggerData.chatId;\nconst messageId = triggerData.messageId;\nconst responseMode = triggerData.responseMode;\n\n// Extract image info\nlet imageName = inspectData.Config.Image;\nconst currentImageId = inspectData.Image;\n\n// CRITICAL: Ensure image has a tag, otherwise Docker pulls ALL tags!\nif (imageName && !imageName.includes(':') && !imageName.includes('@')) {\n imageName = imageName + ':latest';\n}\n\n// Extract config for recreation\nconst containerConfig = inspectData.Config;\nconst hostConfig = inspectData.HostConfig;\nconst networkSettings = inspectData.NetworkSettings;\n\n// Get current version from labels or image digest\nconst labels = containerConfig.Labels || {};\nconst currentVersion = labels['org.opencontainers.image.version']\n || labels['version']\n || currentImageId.substring(7, 19);\n\nreturn {\n json: {\n containerId,\n containerName,\n chatId,\n messageId,\n responseMode,\n imageName,\n currentImageId,\n currentVersion,\n containerConfig,\n hostConfig,\n networkSettings\n }\n};" + "jsCode": "// Parse container config and prepare for pull\nconst inspectData = $input.item.json;\nconst triggerData = $('When executed by another workflow').item.json;\nconst containerId = triggerData.containerId;\nconst containerName = triggerData.containerName;\nconst chatId = triggerData.chatId;\nconst messageId = triggerData.messageId;\nconst responseMode = triggerData.responseMode;\nconst correlationId = triggerData.correlationId || '';\n\n// Extract image info\nlet imageName = inspectData.Config.Image;\nconst currentImageId = inspectData.Image;\n\n// CRITICAL: Ensure image has a tag, otherwise Docker pulls ALL tags!\nif (imageName && !imageName.includes(':') && !imageName.includes('@')) {\n imageName = imageName + ':latest';\n}\n\n// Extract config for recreation\nconst containerConfig = inspectData.Config;\nconst hostConfig = inspectData.HostConfig;\nconst networkSettings = inspectData.NetworkSettings;\n\n// Get current version from labels or image digest\nconst labels = containerConfig.Labels || {};\nconst currentVersion = labels['org.opencontainers.image.version']\n || labels['version']\n || currentImageId.substring(7, 19);\n\nreturn {\n json: {\n containerId,\n containerName,\n chatId,\n messageId,\n responseMode,\n imageName,\n currentImageId,\n currentVersion,\n containerConfig,\n hostConfig,\n networkSettings,\n correlationId\n }\n};" }, "id": "code-parse-config", "name": "Parse Container Config", @@ -353,7 +357,7 @@ }, { "parameters": { - "jsCode": "// Format update success result and clean up old image\nconst prevData = $('Parse Create Response').item.json;\nconst containerName = prevData.containerName;\nconst currentVersion = prevData.currentVersion;\nconst newVersion = prevData.newVersion;\nconst currentImageId = prevData.currentImageId;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\nconst responseMode = prevData.responseMode;\n\nconst message = `${containerName} updated: ${currentVersion} \\u2192 ${newVersion}`;\n\nreturn {\n json: {\n success: true,\n updated: true,\n message,\n oldDigest: currentVersion,\n newDigest: newVersion,\n currentImageId,\n chatId,\n messageId,\n responseMode,\n containerName\n }\n};" + "jsCode": "// Format update success result and clean up old image\nconst prevData = $('Parse Create Response').item.json;\nconst containerName = prevData.containerName;\nconst currentVersion = prevData.currentVersion;\nconst newVersion = prevData.newVersion;\nconst currentImageId = prevData.currentImageId;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\nconst responseMode = prevData.responseMode;\nconst correlationId = prevData.correlationId || '';\n\nconst message = `${containerName} updated: ${currentVersion} \\u2192 ${newVersion}`;\n\nreturn {\n json: {\n success: true,\n updated: true,\n message,\n oldDigest: currentVersion,\n newDigest: newVersion,\n currentImageId,\n chatId,\n messageId,\n responseMode,\n containerName,\n correlationId\n }\n};" }, "id": "code-format-success", "name": "Format Update Success", @@ -459,7 +463,7 @@ }, { "parameters": { - "jsCode": "// Return final success result\nconst data = $('Format Update Success').item.json;\nreturn {\n json: {\n success: true,\n updated: true,\n message: data.message,\n oldDigest: data.oldDigest,\n newDigest: data.newDigest\n }\n};" + "jsCode": "// Return final success result\nconst data = $('Format Update Success').item.json;\nreturn {\n json: {\n success: true,\n updated: true,\n message: data.message,\n oldDigest: data.oldDigest,\n newDigest: data.newDigest,\n correlationId: data.correlationId || ''\n }\n};" }, "id": "code-return-success", "name": "Return Success", @@ -472,7 +476,7 @@ }, { "parameters": { - "jsCode": "// Format 'already up to date' result\nconst prevData = $('Check If Update Needed').item.json;\nconst containerName = prevData.containerName;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\nconst responseMode = prevData.responseMode;\n\nconst message = `${containerName} is already up to date`;\n\nreturn {\n json: {\n success: true,\n updated: false,\n message,\n chatId,\n messageId,\n responseMode,\n containerName\n }\n};" + "jsCode": "// Format 'already up to date' result\nconst prevData = $('Check If Update Needed').item.json;\nconst containerName = prevData.containerName;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\nconst responseMode = prevData.responseMode;\nconst correlationId = prevData.correlationId || '';\n\nconst message = `${containerName} is already up to date`;\n\nreturn {\n json: {\n success: true,\n updated: false,\n message,\n chatId,\n messageId,\n responseMode,\n containerName,\n correlationId\n }\n};" }, "id": "code-format-no-update", "name": "Format No Update Needed", @@ -560,7 +564,7 @@ }, { "parameters": { - "jsCode": "// Return no update result\nconst data = $('Format No Update Needed').item.json;\nreturn {\n json: {\n success: true,\n updated: false,\n message: data.message\n }\n};" + "jsCode": "// Return no update result\nconst data = $('Format No Update Needed').item.json;\nreturn {\n json: {\n success: true,\n updated: false,\n message: data.message,\n correlationId: data.correlationId || ''\n }\n};" }, "id": "code-return-no-update", "name": "Return No Update", @@ -573,7 +577,7 @@ }, { "parameters": { - "jsCode": "// Format pull error result\nconst prevData = $('Check Pull Success').item.json;\nconst containerName = prevData.containerName;\nconst errorMessage = prevData.errorMessage;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\nconst responseMode = prevData.responseMode;\n\nconst message = `Failed to update ${containerName}: ${errorMessage}`;\n\nreturn {\n json: {\n success: false,\n updated: false,\n message,\n chatId,\n messageId,\n responseMode,\n containerName\n }\n};" + "jsCode": "// Format pull error result\nconst prevData = $('Check Pull Success').item.json;\nconst containerName = prevData.containerName;\nconst errorMessage = prevData.errorMessage;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\nconst responseMode = prevData.responseMode;\nconst correlationId = prevData.correlationId || '';\n\nconst message = `Failed to update ${containerName}: ${errorMessage}`;\n\nreturn {\n json: {\n success: false,\n updated: false,\n message,\n error: {\n workflow: 'n8n-update',\n node: 'Pull Image',\n message: errorMessage,\n httpCode: null,\n rawResponse: errorMessage\n },\n correlationId,\n chatId,\n messageId,\n responseMode,\n containerName\n }\n};" }, "id": "code-format-pull-error", "name": "Format Pull Error", @@ -1061,4 +1065,4 @@ "settings": { "executionOrder": "v1" } -} +} \ No newline at end of file