feat(09-01): add batch command detection and parsing

- Add "Detect Batch Command" code node to parse multi-container commands
- Add "Is Batch Command" IF node to route batch vs single container
- Add "Route Single Action" switch to route single commands to appropriate flow
- Add "Get Containers for Batch" node for Docker API container list
- Modify Keyword Router to route restart/start/stop/update through batch detection
- Single container commands route to existing Parse Action/Update Command flows
- Batch commands (2+ containers) route to new batch processing flow

Batch detection logic:
- Pattern: {action} {name1} {name2} ... where action is update/start/stop/restart
- Single container = not batch (existing flow)
- Two or more containers = batch (new flow)
This commit is contained in:
Lucas Berger
2026-02-03 21:16:41 -05:00
parent 59ed1245c2
commit 9e7ff2ab08
+175 -4
View File
@@ -3800,6 +3800,130 @@
"name": "Telegram account" "name": "Telegram account"
} }
} }
},
{
"parameters": {
"jsCode": "// Detect if this is a batch command (multiple container names)\n// Batch commands: {action} {name1} {name2} ... where action is update/start/stop/restart\nconst message = $json.message;\nconst text = message.text.toLowerCase().trim();\nconst chatId = message.chat.id;\nconst messageId = message.message_id;\n\n// Determine action type from the routed keyword\n// This node receives from Keyword Router which already matched the action\nlet action = null;\nlet containerPart = '';\n\nif (text.startsWith('restart ')) {\n action = 'restart';\n containerPart = text.substring(8).trim();\n} else if (text.includes('restart')) {\n action = 'restart';\n containerPart = text.replace('restart', '').trim();\n} else if (text.startsWith('start ')) {\n action = 'start';\n containerPart = text.substring(6).trim();\n} else if (text.includes('start')) {\n action = 'start';\n containerPart = text.replace('start', '').trim();\n} else if (text.startsWith('stop ')) {\n action = 'stop';\n containerPart = text.substring(5).trim();\n} else if (text.includes('stop')) {\n action = 'stop';\n containerPart = text.replace('stop', '').trim();\n} else if (text.startsWith('update ')) {\n action = 'update';\n containerPart = text.substring(7).trim();\n} else if (text.includes('update')) {\n action = 'update';\n containerPart = text.replace('update', '').trim();\n}\n\nif (!action || !containerPart) {\n // No valid action found, pass through as non-batch (will be handled by existing flow)\n return {\n json: {\n isBatch: false,\n action: action,\n containerNames: [],\n originalMessage: text,\n chatId: chatId,\n messageId: messageId,\n message: message\n }\n };\n}\n\n// Split container names by whitespace\nconst containerNames = containerPart.split(/\\s+/).filter(name => name.length > 0);\n\n// Batch = 2 or more containers\nconst isBatch = containerNames.length >= 2;\n\nreturn {\n json: {\n isBatch: isBatch,\n action: action,\n containerNames: containerNames,\n originalMessage: text,\n chatId: chatId,\n messageId: messageId,\n message: message\n }\n};"
},
"id": "code-detect-batch",
"name": "Detect Batch Command",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
900,
-200
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "is-batch-check",
"leftValue": "={{ $json.isBatch }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-is-batch",
"name": "Is Batch Command",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1120,
-200
]
},
{
"parameters": {
"rules": {
"values": [
{
"id": "action-start-stop-restart",
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "loose"
},
"conditions": [
{
"id": "is-action-type",
"leftValue": "={{ $json.action }}",
"rightValue": "update",
"operator": {
"type": "string",
"operation": "notEquals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "action"
},
{
"id": "action-update",
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "loose"
},
"conditions": [
{
"id": "is-update-type",
"leftValue": "={{ $json.action }}",
"rightValue": "update",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "update"
}
]
},
"options": {
"fallbackOutput": "none"
}
},
"id": "switch-route-single-action",
"name": "Route Single Action",
"type": "n8n-nodes-base.switch",
"typeVersion": 3.2,
"position": [
1340,
-100
]
},
{
"parameters": {
"command": "curl -s --max-time 5 'http://docker-socket-proxy:2375/v1.47/containers/json?all=true'",
"options": {}
},
"id": "exec-docker-list-batch",
"name": "Get Containers for Batch",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 1,
"position": [
1340,
-300
]
} }
], ],
"connections": { "connections": {
@@ -4921,28 +5045,28 @@
], ],
[ [
{ {
"node": "Parse Action Command", "node": "Detect Batch Command",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
], ],
[ [
{ {
"node": "Parse Action Command", "node": "Detect Batch Command",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
], ],
[ [
{ {
"node": "Parse Action Command", "node": "Detect Batch Command",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
], ],
[ [
{ {
"node": "Parse Update Command", "node": "Detect Batch Command",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -4963,6 +5087,53 @@
] ]
] ]
}, },
"Detect Batch Command": {
"main": [
[
{
"node": "Is Batch Command",
"type": "main",
"index": 0
}
]
]
},
"Is Batch Command": {
"main": [
[
{
"node": "Get Containers for Batch",
"type": "main",
"index": 0
}
],
[
{
"node": "Route Single Action",
"type": "main",
"index": 0
}
]
]
},
"Route Single Action": {
"main": [
[
{
"node": "Parse Action Command",
"type": "main",
"index": 0
}
],
[
{
"node": "Parse Update Command",
"type": "main",
"index": 0
}
]
]
},
"Parse Action Command": { "Parse Action Command": {
"main": [ "main": [
[ [