feat(10-03): wire main workflow to use container-actions sub-workflow

- Added 9 nodes for sub-workflow integration
- Text path: Check Match Count -> Prepare Text Action Input -> Execute Container Action
- Inline path: Get Container For Action -> Prepare Inline Action Input -> Execute Inline Action
- Confirmed stop: Get Container For Stop -> Prepare Confirmed Stop Input -> Execute Confirmed Stop Action
- Confirmation dialogs remain in main workflow (stop, batch stop)
- Uses CONTAINER_ACTIONS_WORKFLOW_ID env var for sub-workflow reference
This commit is contained in:
Lucas Berger
2026-02-04 13:10:14 -05:00
parent d0b03b7159
commit d07932f69f
+234 -3
View File
@@ -5042,6 +5042,138 @@
2880, 2880,
1650 1650
] ]
},
{
"parameters": {
"jsCode": "// Prepare input for container actions sub-workflow\nconst data = $input.item.json;\nconst containerId = data.matches[0].Id;\nconst containerName = data.matches[0].Name;\nconst action = data.action;\nconst chatId = data.chatId;\n\nreturn {\n json: {\n containerId: containerId,\n containerName: containerName,\n action: action,\n chatId: chatId,\n messageId: 0, // Text mode doesn't have a message to edit\n responseMode: 'text'\n }\n};"
},
"id": "code-prepare-text-action-rr53pd94",
"name": "Prepare Text Action Input",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1780,
500
]
},
{
"parameters": {
"source": "database",
"workflowId": "={{ $env.CONTAINER_ACTIONS_WORKFLOW_ID }}",
"mode": "once",
"options": {
"waitForSubWorkflow": true
}
},
"id": "exec-container-action-qokchnw8",
"name": "Execute Container Action",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
2000,
500
]
},
{
"parameters": {
"jsCode": "// Handle sub-workflow result for text command path\nconst result = $input.item.json;\nconst success = result.success;\nconst message = result.message;\nconst chatId = result.chatId;\n\nreturn {\n json: {\n success: success,\n chatId: chatId,\n text: message\n }\n};"
},
"id": "code-handle-text-result-c6ha90fh",
"name": "Handle Text Action Result",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2220,
500
]
},
{
"parameters": {
"jsCode": "// Prepare input for container actions sub-workflow from inline keyboard\n// Container lookup already done by Get Container For Action\nconst containers = $input.all().map(item => item.json);\nconst prevData = $('Prepare Immediate Action').item.json;\nconst containerName = prevData.containerName.toLowerCase();\nconst action = prevData.action;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\n\n// Function to normalize container names\nfunction normalizeName(name) {\n return name\n .replace(/^\\//, '')\n .replace(/^(linuxserver[-_]|binhex[-_])/i, '')\n .toLowerCase();\n}\n\n// Find the matching container\nconst container = containers.find(c => normalizeName(c.Names[0]) === containerName);\n\nif (!container) {\n return {\n json: {\n error: true,\n chatId,\n messageId,\n text: 'Container not found'\n }\n };\n}\n\nreturn {\n json: {\n containerId: container.Id,\n containerName: normalizeName(container.Names[0]),\n action: action,\n chatId: chatId,\n messageId: messageId,\n responseMode: 'inline'\n }\n};"
},
"id": "code-prepare-inline-action-tyjn5pb1",
"name": "Prepare Inline Action Input",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2220,
1200
]
},
{
"parameters": {
"source": "database",
"workflowId": "={{ $env.CONTAINER_ACTIONS_WORKFLOW_ID }}",
"mode": "once",
"options": {
"waitForSubWorkflow": true
}
},
"id": "exec-inline-action-8aoev7xt",
"name": "Execute Inline Action",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
2440,
1200
]
},
{
"parameters": {
"jsCode": "// Handle sub-workflow result for inline keyboard path\nconst result = $input.item.json;\nconst success = result.success;\nconst message = result.message;\nconst action = result.action;\nconst containerName = result.containerName;\nconst chatId = result.chatId;\nconst messageId = result.messageId;\n\n// Build keyboard based on result\nlet keyboard;\nif (success) {\n // Success: only back button\n keyboard = {\n inline_keyboard: [\n [{ text: '\\u25C0\\uFE0F Back to Containers', callback_data: 'list:0' }]\n ]\n };\n} else {\n // Error: retry and back buttons\n keyboard = {\n inline_keyboard: [\n [{ text: '\\u{1F504} Try Again', callback_data: 'action:' + action + ':' + containerName }],\n [{ text: '\\u25C0\\uFE0F Back to Containers', callback_data: 'list:0' }]\n ]\n };\n}\n\nreturn {\n json: {\n chatId,\n messageId,\n text: message,\n reply_markup: keyboard\n }\n};"
},
"id": "code-handle-inline-result-x19h97t3",
"name": "Handle Inline Action Result",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2660,
1200
]
},
{
"parameters": {
"jsCode": "// Prepare input for container actions sub-workflow from confirmed stop\nconst containers = $input.all().map(item => item.json);\nconst prevData = $('Prepare Confirmed Stop').item.json;\nconst containerName = prevData.containerName.toLowerCase();\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\n\n// Function to normalize container names\nfunction normalizeName(name) {\n return name\n .replace(/^\\//, '')\n .replace(/^(linuxserver[-_]|binhex[-_])/i, '')\n .toLowerCase();\n}\n\n// Find the matching container\nconst container = containers.find(c => normalizeName(c.Names[0]) === containerName);\n\nif (!container) {\n return {\n json: {\n error: true,\n chatId,\n messageId,\n text: 'Container not found'\n }\n };\n}\n\nreturn {\n json: {\n containerId: container.Id,\n containerName: normalizeName(container.Names[0]),\n action: 'stop',\n chatId: chatId,\n messageId: messageId,\n responseMode: 'inline'\n }\n};"
},
"id": "code-prepare-confirmed-stop-vt9cw9tl",
"name": "Prepare Confirmed Stop Input",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2440,
1650
]
},
{
"parameters": {
"source": "database",
"workflowId": "={{ $env.CONTAINER_ACTIONS_WORKFLOW_ID }}",
"mode": "once",
"options": {
"waitForSubWorkflow": true
}
},
"id": "exec-confirmed-stop-sub-qmm011fk",
"name": "Execute Confirmed Stop Action",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
2660,
1650
]
},
{
"parameters": {
"jsCode": "// Handle sub-workflow result for confirmed stop path\nconst result = $input.item.json;\nconst success = result.success;\nconst message = result.message;\nconst containerName = result.containerName;\nconst chatId = result.chatId;\nconst messageId = result.messageId;\n\n// Build keyboard based on result\nlet keyboard;\nif (success) {\n // Success: only back button\n keyboard = {\n inline_keyboard: [\n [{ text: '\\u25C0\\uFE0F Back to Containers', callback_data: 'list:0' }]\n ]\n };\n} else {\n // Error: retry and back buttons\n const timestamp = Math.floor(Date.now() / 1000);\n keyboard = {\n inline_keyboard: [\n [{ text: '\\u{1F504} Try Again', callback_data: 'confirm:stop:' + containerName + ':' + timestamp }],\n [{ text: '\\u25C0\\uFE0F Back to Containers', callback_data: 'list:0' }]\n ]\n };\n}\n\nreturn {\n json: {\n chatId,\n messageId,\n text: message,\n reply_markup: keyboard\n }\n};"
},
"id": "code-handle-confirmed-stop-f2r86fwr",
"name": "Handle Confirmed Stop Result",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2880,
1650
]
} }
], ],
"connections": { "connections": {
@@ -5531,7 +5663,7 @@
], ],
[ [
{ {
"node": "Build Action Command", "node": "Prepare Text Action Input",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -6268,7 +6400,7 @@
"main": [ "main": [
[ [
{ {
"node": "Build Immediate Action Command", "node": "Prepare Inline Action Input",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -6458,7 +6590,7 @@
"main": [ "main": [
[ [
{ {
"node": "Build Confirmed Stop Command", "node": "Prepare Confirmed Stop Input",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -7197,6 +7329,105 @@
} }
] ]
] ]
},
"Prepare Text Action Input": {
"main": [
[
{
"node": "Execute Container Action",
"type": "main",
"index": 0
}
]
]
},
"Execute Container Action": {
"main": [
[
{
"node": "Handle Text Action Result",
"type": "main",
"index": 0
}
]
]
},
"Handle Text Action Result": {
"main": [
[
{
"node": "Send Action Result",
"type": "main",
"index": 0
}
]
]
},
"Prepare Inline Action Input": {
"main": [
[
{
"node": "Execute Inline Action",
"type": "main",
"index": 0
}
]
]
},
"Execute Inline Action": {
"main": [
[
{
"node": "Handle Inline Action Result",
"type": "main",
"index": 0
}
]
]
},
"Handle Inline Action Result": {
"main": [
[
{
"node": "Send Immediate Result",
"type": "main",
"index": 0
}
]
]
},
"Prepare Confirmed Stop Input": {
"main": [
[
{
"node": "Execute Confirmed Stop Action",
"type": "main",
"index": 0
}
]
]
},
"Execute Confirmed Stop Action": {
"main": [
[
{
"node": "Handle Confirmed Stop Result",
"type": "main",
"index": 0
}
]
]
},
"Handle Confirmed Stop Result": {
"main": [
[
{
"node": "Send Confirmed Stop Result",
"type": "main",
"index": 0
}
]
]
} }
}, },
"pinData": {}, "pinData": {},