diff --git a/n8n-workflow.json b/n8n-workflow.json index 4cd7fc9..ea9e73c 100644 --- a/n8n-workflow.json +++ b/n8n-workflow.json @@ -5042,6 +5042,138 @@ 2880, 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": { @@ -5531,7 +5663,7 @@ ], [ { - "node": "Build Action Command", + "node": "Prepare Text Action Input", "type": "main", "index": 0 } @@ -6268,7 +6400,7 @@ "main": [ [ { - "node": "Build Immediate Action Command", + "node": "Prepare Inline Action Input", "type": "main", "index": 0 } @@ -6458,7 +6590,7 @@ "main": [ [ { - "node": "Build Confirmed Stop Command", + "node": "Prepare Confirmed Stop Input", "type": "main", "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": {},