feat(10-02): create container-update sub-workflow
- New sub-workflow with 31 nodes encapsulating entire update flow - Input contract: containerId, containerName, chatId, messageId, responseMode - Output contract: success, updated, message, oldDigest, newDigest - Handles both text and inline response modes - Preserves :latest tag protection and old image cleanup
This commit is contained in:
@@ -0,0 +1,965 @@
|
||||
{
|
||||
"name": "Container Update",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"inputSource": "passthrough",
|
||||
"schema": {
|
||||
"schemaType": "fromFields",
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "containerId",
|
||||
"fieldType": "string"
|
||||
},
|
||||
{
|
||||
"fieldName": "containerName",
|
||||
"fieldType": "string"
|
||||
},
|
||||
{
|
||||
"fieldName": "chatId",
|
||||
"fieldType": "number"
|
||||
},
|
||||
{
|
||||
"fieldName": "messageId",
|
||||
"fieldType": "number"
|
||||
},
|
||||
{
|
||||
"fieldName": "responseMode",
|
||||
"fieldType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "sub-trigger",
|
||||
"name": "When executed by another workflow",
|
||||
"type": "n8n-nodes-base.executeWorkflowTrigger",
|
||||
"typeVersion": 1.1,
|
||||
"position": [
|
||||
240,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "=http://docker-socket-proxy:2375/containers/{{ $json.containerId }}/json",
|
||||
"options": {
|
||||
"timeout": 5000
|
||||
}
|
||||
},
|
||||
"id": "http-inspect-container",
|
||||
"name": "Inspect Container",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
460,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"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};"
|
||||
},
|
||||
"id": "code-parse-config",
|
||||
"name": "Parse Container Config",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
680,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"command": "=curl -s --max-time 600 -X POST 'http://docker-socket-proxy:2375/v1.47/images/create?fromImage={{ encodeURIComponent($json.imageName) }}' | tail -c 10000",
|
||||
"options": {
|
||||
"timeout": 660
|
||||
}
|
||||
},
|
||||
"id": "exec-pull-image",
|
||||
"name": "Pull Image",
|
||||
"type": "n8n-nodes-base.executeCommand",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
900,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Check pull response for errors\nconst stdout = $input.item.json.stdout || '';\nconst prevData = $('Parse Container Config').item.json;\n\n// Docker pull streams JSON objects, check for error messages\nif (stdout.includes('\"message\"') && (stdout.includes('toomanyrequests') || stdout.includes('error') || stdout.includes('denied'))) {\n // Extract error message\n let errorMsg = 'Pull failed';\n try {\n const match = stdout.match(/\"message\"\\s*:\\s*\"([^\"]+)\"/);\n if (match) errorMsg = match[1];\n } catch (e) {}\n \n return {\n json: {\n pullError: true,\n errorMessage: errorMsg.substring(0, 100),\n ...prevData\n }\n };\n}\n\n// Success - pass through data\nreturn {\n json: {\n pullError: false,\n ...prevData\n }\n};"
|
||||
},
|
||||
"id": "code-check-pull",
|
||||
"name": "Check Pull Response",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1120,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "pull-success",
|
||||
"leftValue": "={{ $json.pullError }}",
|
||||
"rightValue": false,
|
||||
"operator": {
|
||||
"type": "boolean",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "if-pull-success",
|
||||
"name": "Check Pull Success",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1340,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "=http://docker-socket-proxy:2375/v1.47/images/{{ encodeURIComponent($json.imageName) }}/json",
|
||||
"options": {
|
||||
"timeout": 5000
|
||||
}
|
||||
},
|
||||
"id": "http-inspect-new-image",
|
||||
"name": "Inspect New Image",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
1560,
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Compare old and new image IDs\nconst newImage = $input.item.json;\nconst prevData = $('Check Pull Success').item.json;\nconst currentImageId = prevData.currentImageId;\n\nconst newImageId = newImage.Id;\n\nif (currentImageId === newImageId) {\n // No update needed\n return {\n json: {\n needsUpdate: false,\n ...prevData\n }\n };\n}\n\n// Extract new version from labels\nconst labels = newImage.Config?.Labels || {};\nconst newVersion = labels['org.opencontainers.image.version']\n || labels['version']\n || newImageId.substring(7, 19);\n\nreturn {\n json: {\n needsUpdate: true,\n newImageId,\n newVersion,\n ...prevData\n }\n};"
|
||||
},
|
||||
"id": "code-compare-digests",
|
||||
"name": "Compare Digests",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1780,
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "needs-update",
|
||||
"leftValue": "={{ $json.needsUpdate }}",
|
||||
"rightValue": true,
|
||||
"operator": {
|
||||
"type": "boolean",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "if-needs-update",
|
||||
"name": "Check If Update Needed",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
2000,
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "=http://docker-socket-proxy:2375/v1.47/containers/{{ $json.containerId }}/stop?t=10",
|
||||
"options": {
|
||||
"timeout": 15000
|
||||
}
|
||||
},
|
||||
"id": "http-stop-container",
|
||||
"name": "Stop Container",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
2220,
|
||||
100
|
||||
],
|
||||
"onError": "continueRegularOutput"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "=http://docker-socket-proxy:2375/v1.47/containers/{{ $('Check If Update Needed').item.json.containerId }}",
|
||||
"options": {
|
||||
"timeout": 5000
|
||||
}
|
||||
},
|
||||
"id": "http-remove-container",
|
||||
"name": "Remove Container",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
2440,
|
||||
100
|
||||
],
|
||||
"onError": "continueRegularOutput"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Build container create request body from saved config\nconst prevData = $('Check If Update Needed').item.json;\nconst config = prevData.containerConfig;\nconst hostConfig = prevData.hostConfig;\nconst networkSettings = prevData.networkSettings;\nconst containerName = prevData.containerName;\n\n// Build NetworkingConfig from NetworkSettings\nconst networks = {};\nfor (const [name, netConfig] of Object.entries(networkSettings.Networks || {})) {\n networks[name] = {\n IPAMConfig: netConfig.IPAMConfig,\n Links: netConfig.Links,\n Aliases: netConfig.Aliases\n };\n}\n\nconst createBody = {\n ...config,\n HostConfig: hostConfig,\n NetworkingConfig: {\n EndpointsConfig: networks\n }\n};\n\n// Remove fields that shouldn't be in create request\ndelete createBody.Hostname;\ndelete createBody.Domainname;\n\nreturn {\n json: {\n createBody,\n containerName,\n ...prevData\n }\n};"
|
||||
},
|
||||
"id": "code-build-create-body",
|
||||
"name": "Build Create Body",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
2660,
|
||||
100
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "=http://docker-socket-proxy:2375/v1.47/containers/create?name={{ encodeURIComponent($json.containerName) }}",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify($json.createBody) }}",
|
||||
"options": {
|
||||
"timeout": 5000
|
||||
}
|
||||
},
|
||||
"id": "http-create-container",
|
||||
"name": "Create Container",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
2880,
|
||||
100
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Parse create response and extract new container ID\nconst response = $input.item.json;\nconst prevData = $('Build Create Body').item.json;\n\nif (response.message) {\n // Error response from Docker\n return {\n json: {\n createError: true,\n errorMessage: response.message,\n ...prevData\n }\n };\n}\n\nreturn {\n json: {\n createError: false,\n newContainerId: response.Id,\n ...prevData\n }\n};"
|
||||
},
|
||||
"id": "code-parse-create",
|
||||
"name": "Parse Create Response",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
3100,
|
||||
100
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "=http://docker-socket-proxy:2375/v1.47/containers/{{ $json.newContainerId }}/start",
|
||||
"options": {
|
||||
"timeout": 5000
|
||||
}
|
||||
},
|
||||
"id": "http-start-container",
|
||||
"name": "Start Container",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
3320,
|
||||
100
|
||||
],
|
||||
"onError": "continueRegularOutput"
|
||||
},
|
||||
{
|
||||
"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 = `<b>${containerName}</b> 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};"
|
||||
},
|
||||
"id": "code-format-success",
|
||||
"name": "Format Update Success",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
3540,
|
||||
100
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "is-inline",
|
||||
"leftValue": "={{ $json.responseMode }}",
|
||||
"rightValue": "inline",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "if-response-mode-success",
|
||||
"name": "Check Response Mode (Success)",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
3760,
|
||||
100
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "=https://api.telegram.org/bot{{ $env.TELEGRAM_BOT_TOKEN }}/editMessageText",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ chat_id: $json.chatId, message_id: $json.messageId, text: '\\u2705 ' + $json.message, parse_mode: 'HTML', reply_markup: { inline_keyboard: [[{ text: '\\u25C0\\uFE0F Back to Containers', callback_data: 'list:0' }]] } }) }}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "http-send-inline-success",
|
||||
"name": "Send Inline Success",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
3980,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "message",
|
||||
"operation": "sendMessage",
|
||||
"chatId": "={{ $json.chatId }}",
|
||||
"text": "={{ $json.message }}",
|
||||
"additionalFields": {
|
||||
"parse_mode": "HTML"
|
||||
}
|
||||
},
|
||||
"id": "telegram-send-text-success",
|
||||
"name": "Send Text Success",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.2,
|
||||
"position": [
|
||||
3980,
|
||||
200
|
||||
],
|
||||
"credentials": {
|
||||
"telegramApi": {
|
||||
"id": "I0xTTiASl7C1NZhJ",
|
||||
"name": "Telegram account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "=http://docker-socket-proxy:2375/v1.47/images/{{ $('Format Update Success').item.json.currentImageId }}?force=false",
|
||||
"options": {
|
||||
"timeout": 5000
|
||||
}
|
||||
},
|
||||
"id": "http-remove-old-image-success",
|
||||
"name": "Remove Old Image (Success)",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
4200,
|
||||
100
|
||||
],
|
||||
"onError": "continueRegularOutput"
|
||||
},
|
||||
{
|
||||
"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};"
|
||||
},
|
||||
"id": "code-return-success",
|
||||
"name": "Return Success",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
4420,
|
||||
100
|
||||
]
|
||||
},
|
||||
{
|
||||
"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 = `<b>${containerName}</b> 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};"
|
||||
},
|
||||
"id": "code-format-no-update",
|
||||
"name": "Format No Update Needed",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
2220,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "is-inline-no-update",
|
||||
"leftValue": "={{ $json.responseMode }}",
|
||||
"rightValue": "inline",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "if-response-mode-no-update",
|
||||
"name": "Check Response Mode (No Update)",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
2440,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "=https://api.telegram.org/bot{{ $env.TELEGRAM_BOT_TOKEN }}/editMessageText",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ chat_id: $json.chatId, message_id: $json.messageId, text: '\\u2705 ' + $json.message, parse_mode: 'HTML', reply_markup: { inline_keyboard: [[{ text: '\\u25C0\\uFE0F Back to Containers', callback_data: 'list:0' }]] } }) }}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "http-send-inline-no-update",
|
||||
"name": "Send Inline No Update",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
2660,
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "message",
|
||||
"operation": "sendMessage",
|
||||
"chatId": "={{ $json.chatId }}",
|
||||
"text": "={{ $json.message }}",
|
||||
"additionalFields": {
|
||||
"parse_mode": "HTML"
|
||||
}
|
||||
},
|
||||
"id": "telegram-send-text-no-update",
|
||||
"name": "Send Text No Update",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.2,
|
||||
"position": [
|
||||
2660,
|
||||
400
|
||||
],
|
||||
"credentials": {
|
||||
"telegramApi": {
|
||||
"id": "I0xTTiASl7C1NZhJ",
|
||||
"name": "Telegram account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"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};"
|
||||
},
|
||||
"id": "code-return-no-update",
|
||||
"name": "Return No Update",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
2880,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"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 <b>${containerName}</b>: ${errorMessage}`;\n\nreturn {\n json: {\n success: false,\n updated: false,\n message,\n chatId,\n messageId,\n responseMode,\n containerName\n }\n};"
|
||||
},
|
||||
"id": "code-format-pull-error",
|
||||
"name": "Format Pull Error",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1560,
|
||||
400
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "is-inline-error",
|
||||
"leftValue": "={{ $json.responseMode }}",
|
||||
"rightValue": "inline",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "if-response-mode-error",
|
||||
"name": "Check Response Mode (Error)",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1780,
|
||||
400
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "=https://api.telegram.org/bot{{ $env.TELEGRAM_BOT_TOKEN }}/editMessageText",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ chat_id: $json.chatId, message_id: $json.messageId, text: '\\u274C ' + $json.message, parse_mode: 'HTML', reply_markup: { inline_keyboard: [[{ text: '\\u25C0\\uFE0F Back to Containers', callback_data: 'list:0' }]] } }) }}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "http-send-inline-error",
|
||||
"name": "Send Inline Error",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
2000,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "message",
|
||||
"operation": "sendMessage",
|
||||
"chatId": "={{ $json.chatId }}",
|
||||
"text": "={{ $json.message }}",
|
||||
"additionalFields": {
|
||||
"parse_mode": "HTML"
|
||||
}
|
||||
},
|
||||
"id": "telegram-send-text-error",
|
||||
"name": "Send Text Error",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.2,
|
||||
"position": [
|
||||
2000,
|
||||
500
|
||||
],
|
||||
"credentials": {
|
||||
"telegramApi": {
|
||||
"id": "I0xTTiASl7C1NZhJ",
|
||||
"name": "Telegram account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Return error result\nconst data = $('Format Pull Error').item.json;\nreturn {\n json: {\n success: false,\n updated: false,\n message: data.message\n }\n};"
|
||||
},
|
||||
"id": "code-return-error",
|
||||
"name": "Return Error",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
2220,
|
||||
400
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When executed by another workflow": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Inspect Container",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Inspect Container": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse Container Config",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse Container Config": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Pull Image",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Pull Image": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check Pull Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check Pull Response": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check Pull Success",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check Pull Success": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Inspect New Image",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Format Pull Error",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Inspect New Image": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Compare Digests",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Compare Digests": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check If Update Needed",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check If Update Needed": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Stop Container",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Format No Update Needed",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Stop Container": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Remove Container",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Remove Container": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Build Create Body",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Build Create Body": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Create Container",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Create Container": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse Create Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse Create Response": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Start Container",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Start Container": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Update Success",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Format Update Success": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check Response Mode (Success)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check Response Mode (Success)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send Inline Success",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Send Text Success",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Send Inline Success": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Remove Old Image (Success)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Send Text Success": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Remove Old Image (Success)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Remove Old Image (Success)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Return Success",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Format No Update Needed": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check Response Mode (No Update)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check Response Mode (No Update)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send Inline No Update",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Send Text No Update",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Send Inline No Update": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Return No Update",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Send Text No Update": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Return No Update",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Format Pull Error": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check Response Mode (Error)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check Response Mode (Error)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send Inline Error",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Send Text Error",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Send Inline Error": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Return Error",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Send Text Error": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Return Error",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user