From 13ed81cccfbcfb45bfdeaf7e52c74263559f3d3c Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Wed, 4 Feb 2026 21:26:55 -0500 Subject: [PATCH] feat(10.1-03): update main workflow to call status sub-workflow - Removed 10 list/status nodes (Docker queries, keyboard building) - Added 9 integration nodes (Prepare/Execute pairs for status, select, paginate) - Keyword Router status output -> Prepare Status Input -> Execute Container Status - Answer Select Callback -> Execute Select Status -> Send Container Submenu - Answer List Callback -> Execute Paginate Status -> Edit Container List - Prepare Batch Cancel Return -> Execute Batch Cancel Status -> Edit Container List - Added TODO_DEPLOY_STATUS_WORKFLOW placeholder for deployment --- n8n-workflow.json | 681 +++++++++++++++++++++++++--------------------- 1 file changed, 375 insertions(+), 306 deletions(-) diff --git a/n8n-workflow.json b/n8n-workflow.json index 8ebff59..c9c86f1 100644 --- a/n8n-workflow.json +++ b/n8n-workflow.json @@ -356,20 +356,6 @@ 200 ] }, - { - "parameters": { - "command": "curl -s --max-time 5 'http://docker-socket-proxy:2375/v1.47/containers/json?all=true'", - "options": {} - }, - "id": "exec-docker-list", - "name": "Docker List Containers", - "type": "n8n-nodes-base.executeCommand", - "typeVersion": 1, - "position": [ - 1120, - 100 - ] - }, { "parameters": { "jsCode": "// Parse action from message text directly\nconst message = $json.message;\nconst text = message.text.toLowerCase().trim();\nconst chatId = message.chat.id;\nconst messageId = message.message_id;\n\n// Determine action from the text\nlet requestedAction = null;\nlet containerQuery = '';\n\n// Parse action and container name from message\nif (text.startsWith('start ')) {\n requestedAction = 'start';\n containerQuery = text.substring(6).trim();\n} else if (text.startsWith('stop ')) {\n requestedAction = 'stop';\n containerQuery = text.substring(5).trim();\n} else if (text.startsWith('restart ')) {\n requestedAction = 'restart';\n containerQuery = text.substring(8).trim();\n} else if (text.includes('start')) {\n requestedAction = 'start';\n containerQuery = text.replace('start', '').trim();\n} else if (text.includes('stop')) {\n requestedAction = 'stop';\n containerQuery = text.replace('stop', '').trim();\n} else if (text.includes('restart')) {\n requestedAction = 'restart';\n containerQuery = text.replace('restart', '').trim();\n}\n\nif (!requestedAction || !containerQuery) {\n return {\n json: {\n error: true,\n errorMessage: 'Please specify action and container name (e.g., \"start plex\")',\n chatId: chatId\n }\n };\n}\n\nreturn {\n json: {\n action: requestedAction,\n containerQuery: containerQuery,\n chatId: chatId,\n messageId: messageId\n }\n};" @@ -1844,19 +1830,6 @@ } } }, - { - "parameters": { - "jsCode": "// Build Container List Keyboard for /status command\nconst dockerOutput = $input.item.json.stdout;\nconst message = $('Keyword Router').item.json.message;\nconst chatId = message.chat.id;\n\n// Parse JSON response\nlet containers;\ntry {\n if (!dockerOutput || dockerOutput.trim() === '') {\n throw new Error('Empty response');\n }\n containers = JSON.parse(dockerOutput);\n} catch (e) {\n return [{\n json: {\n chatId: chatId,\n error: true,\n text: \"Cannot connect to Docker\",\n isSingleContainer: false\n }\n }];\n}\n\n// Function to normalize container names (strip prefixes)\nfunction normalizeName(name) {\n return name\n .replace(/^\\//, '')\n .replace(/^(linuxserver[-_]|binhex[-_])/i, '')\n .toLowerCase();\n}\n\n// Check if user specified a container name (e.g., \"/status plex\")\nconst text = (message.text || '').toLowerCase().trim();\nlet requestedName = null;\nconst parts = text.split(/\\s+/);\nif (parts.length > 1) {\n requestedName = parts.filter(p => p !== 'status' && p !== '/status').join(' ');\n}\n\n// If specific container requested, route to submenu\nif (requestedName) {\n const matches = containers.filter(c => {\n const containerName = normalizeName(c.Names[0]);\n return containerName.includes(requestedName);\n });\n \n if (matches.length === 1) {\n const container = matches[0];\n return [{\n json: {\n isSingleContainer: true,\n chatId: chatId,\n containerName: normalizeName(container.Names[0]),\n containerId: container.Id,\n containerState: container.State,\n containerStatus: container.Status,\n containerImage: container.Image\n }\n }];\n } else if (matches.length === 0) {\n return [{\n json: {\n chatId: chatId,\n error: true,\n text: 'No container found matching \"' + requestedName + '\"',\n isSingleContainer: false\n }\n }];\n }\n // Multiple matches - show them all in keyboard below\n}\n\n// Build paginated container list keyboard\nconst page = 0; // Initial page\nconst containersPerPage = 6;\n\n// Group by state (running first)\nconst running = containers.filter(c => c.State === 'running');\nconst stopped = containers.filter(c => c.State !== 'running');\nconst sortedContainers = [...running, ...stopped];\n\nconst totalPages = Math.ceil(sortedContainers.length / containersPerPage);\nconst start = page * containersPerPage;\nconst pageContainers = sortedContainers.slice(start, start + containersPerPage);\n\n// Build keyboard rows\nconst keyboard = [];\n\npageContainers.forEach(container => {\n const name = normalizeName(container.Names[0]);\n const stateIcon = container.State === 'running' ? '\\u{1F7E2}' : '\\u26AA'; // Green circle or white circle\n const stateText = container.State === 'running' ? 'Running' : 'Stopped';\n keyboard.push([{\n text: `${stateIcon} ${name} - ${stateText}`,\n callback_data: `select:${name}`\n }]);\n});\n\n// Add navigation row if needed\nif (totalPages > 1) {\n const navRow = [];\n if (page > 0) {\n navRow.push({ text: '\\u25C0\\uFE0F Previous', callback_data: `list:${page - 1}` });\n }\n navRow.push({ text: `${page + 1}/${totalPages}`, callback_data: 'noop' });\n if (page < totalPages - 1) {\n navRow.push({ text: 'Next \\u25B6\\uFE0F', callback_data: `list:${page + 1}` });\n }\n keyboard.push(navRow);\n}\n\n// Add Select Multiple button for batch operations\nkeyboard.push([{ text: \"\u2611\ufe0f Select Multiple\", callback_data: \"batch:mode\" }]);\n\n// Build header text\nconst runningCount = running.length;\nconst totalCount = sortedContainers.length;\nlet headerText = `\\u{1F5C2} Containers (${runningCount}/${totalCount} running)`;\nif (totalPages > 1) {\n headerText += `\\n\\nPage ${page + 1} of ${totalPages}`;\n}\nheaderText += '\\n\\nTap a container to manage it:';\n\nreturn [{\n json: {\n chatId: chatId,\n text: headerText,\n reply_markup: { inline_keyboard: keyboard },\n isSingleContainer: false\n }\n}];" - }, - "id": "code-build-container-list-keyboard", - "name": "Build Container List Keyboard", - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 1340, - 0 - ] - }, { "parameters": { "method": "POST", @@ -1881,51 +1854,6 @@ } } }, - { - "parameters": { - "conditions": { - "options": { - "caseSensitive": true, - "leftValue": "", - "typeValidation": "strict" - }, - "conditions": [ - { - "id": "is-single-container", - "leftValue": "={{ $json.isSingleContainer }}", - "rightValue": true, - "operator": { - "type": "boolean", - "operation": "equals" - } - } - ], - "combinator": "and" - }, - "options": {} - }, - "id": "if-single-container", - "name": "Check Single Container", - "type": "n8n-nodes-base.if", - "typeVersion": 2, - "position": [ - 1560, - -100 - ] - }, - { - "parameters": { - "jsCode": "// Build Container Submenu for direct access (/status plex) or callback selection\nconst data = $input.item.json;\nconst chatId = data.chatId;\nconst containerName = data.containerName;\nconst state = data.containerState;\nconst status = data.containerStatus;\nconst image = data.containerImage;\n\n// Build action keyboard based on container state\nconst keyboard = [];\n\nif (state === 'running') {\n keyboard.push([\n { text: '\\u23F9\\uFE0F Stop', callback_data: `action:stop:${containerName}` },\n { text: '\\u{1F504} Restart', callback_data: `action:restart:${containerName}` }\n ]);\n} else {\n keyboard.push([\n { text: '\\u25B6\\uFE0F Start', callback_data: `action:start:${containerName}` }\n ]);\n}\n\nkeyboard.push([\n { text: '\\u{1F4CB} Logs', callback_data: `action:logs:${containerName}` },\n { text: '\\u2B06\\uFE0F Update', callback_data: `action:update:${containerName}` }\n]);\n\nkeyboard.push([\n { text: '\\u25C0\\uFE0F Back to List', callback_data: 'list:0' }\n]);\n\n// Build status text\nconst stateIcon = state === 'running' ? '\\u{1F7E2}' : '\\u26AA';\nlet text = `${stateIcon} ${containerName}\\n\\n`;\ntext += `State: ${state}\\n`;\ntext += `Status: ${status}\\n`;\ntext += `Image: ${image}`;\n\nreturn [{\n json: {\n chatId: chatId,\n text: text,\n reply_markup: { inline_keyboard: keyboard }\n }\n}];" - }, - "id": "code-build-container-submenu-direct", - "name": "Build Container Submenu Direct", - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 1780, - -100 - ] - }, { "parameters": { "method": "POST", @@ -1974,47 +1902,6 @@ } } }, - { - "parameters": { - "jsCode": "// Prepare container fetch for submenu\nconst data = $(\"Parse Callback Data\").item.json;\nreturn {\n json: {\n queryId: data.queryId,\n chatId: data.chatId,\n messageId: data.messageId,\n containerName: data.containerName\n }\n};" - }, - "id": "code-prepare-container-fetch", - "name": "Prepare Container Fetch", - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 1560, - 900 - ] - }, - { - "parameters": { - "method": "GET", - "url": "=http://docker-socket-proxy:2375/containers/json?all=true", - "options": {} - }, - "id": "http-get-single-container", - "name": "Get Single Container", - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.2, - "position": [ - 1780, - 900 - ] - }, - { - "parameters": { - "jsCode": "// Build Container Submenu for callback selection\nconst containers = $input.all().map(item => item.json);\nconst prevData = $(\"Prepare Container Fetch\").item.json;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\nconst searchName = prevData.containerName.toLowerCase();\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]) === searchName);\n\nif (!container) {\n return [{\n json: {\n chatId,\n messageId,\n text: `Container \\\"${searchName}\\\" not found`,\n reply_markup: { inline_keyboard: [[{ text: \"\\u25C0\\uFE0F Back to List\", callback_data: \"list:0\" }]] }\n }\n }];\n}\n\nconst containerName = normalizeName(container.Names[0]);\nconst state = container.State;\nconst status = container.Status;\nconst image = container.Image;\n\n// Build action keyboard based on container state\nconst keyboard = [];\n\nif (state === \"running\") {\n keyboard.push([\n { text: \"\\u23F9\\uFE0F Stop\", callback_data: `action:stop:${containerName}` },\n { text: \"\\u{1F504} Restart\", callback_data: `action:restart:${containerName}` }\n ]);\n} else {\n keyboard.push([\n { text: \"\\u25B6\\uFE0F Start\", callback_data: `action:start:${containerName}` }\n ]);\n}\n\nkeyboard.push([\n { text: \"\\u{1F4CB} Logs\", callback_data: `action:logs:${containerName}` },\n { text: \"\\u2B06\\uFE0F Update\", callback_data: `action:update:${containerName}` }\n]);\n\nkeyboard.push([\n { text: \"\\u25C0\\uFE0F Back to List\", callback_data: \"list:0\" }\n]);\n\n// Build status text\nconst stateIcon = state === \"running\" ? \"\\u{1F7E2}\" : \"\\u26AA\";\nlet text = `${stateIcon} ${containerName}\\n\\n`;\ntext += `State: ${state}\\n`;\ntext += `Status: ${status}\\n`;\ntext += `Image: ${image}`;\n\nreturn [{\n json: {\n chatId,\n messageId,\n text,\n reply_markup: { inline_keyboard: keyboard }\n }\n}];" - }, - "id": "code-build-container-submenu", - "name": "Build Container Submenu", - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 2000, - 900 - ] - }, { "parameters": { "method": "POST", @@ -2087,47 +1974,6 @@ } } }, - { - "parameters": { - "jsCode": "// Prepare list pagination request\nconst data = $(\"Parse Callback Data\").item.json;\nreturn {\n json: {\n queryId: data.queryId,\n chatId: data.chatId,\n messageId: data.messageId,\n page: data.page || 0\n }\n};" - }, - "id": "code-prepare-list-fetch", - "name": "Prepare List Fetch", - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 1560, - 1000 - ] - }, - { - "parameters": { - "method": "GET", - "url": "=http://docker-socket-proxy:2375/containers/json?all=true", - "options": {} - }, - "id": "http-get-containers-for-list", - "name": "Get Containers For List", - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.2, - "position": [ - 1780, - 1000 - ] - }, - { - "parameters": { - "jsCode": "// Build Paginated Container List Keyboard for callback pagination\nconst containers = $input.all().map(item => item.json);\n// Try to get data from Prepare List Fetch, fallback to Prepare Batch Cancel Return\nlet prevData;\ntry {\n prevData = $(\"Prepare List Fetch\").item.json;\n} catch (e) {\n try {\n prevData = $(\"Prepare Batch Cancel Return\").item.json;\n } catch (e2) {\n prevData = $json; // Fallback to current item\n }\n}\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\nconst page = prevData.page || 0;\n\n// Function to normalize container names\nfunction normalizeName(name) {\n return name\n .replace(/^\\//, \"\")\n .replace(/^(linuxserver[-_]|binhex[-_])/i, \"\")\n .toLowerCase();\n}\n\nconst containersPerPage = 6;\n\n// Group by state (running first)\nconst running = containers.filter(c => c.State === \"running\");\nconst stopped = containers.filter(c => c.State !== \"running\");\nconst sortedContainers = [...running, ...stopped];\n\nconst totalPages = Math.ceil(sortedContainers.length / containersPerPage);\nconst start = page * containersPerPage;\nconst pageContainers = sortedContainers.slice(start, start + containersPerPage);\n\n// Build keyboard rows\nconst keyboard = [];\n\npageContainers.forEach(container => {\n const name = normalizeName(container.Names[0]);\n const stateIcon = container.State === \"running\" ? \"\\u{1F7E2}\" : \"\\u26AA\";\n const stateText = container.State === \"running\" ? \"Running\" : \"Stopped\";\n keyboard.push([{\n text: `${stateIcon} ${name} - ${stateText}`,\n callback_data: `select:${name}`\n }]);\n});\n\n// Add navigation row if needed\nif (totalPages > 1) {\n const navRow = [];\n if (page > 0) {\n navRow.push({ text: \"\\u25C0\\uFE0F Previous\", callback_data: `list:${page - 1}` });\n }\n navRow.push({ text: `${page + 1}/${totalPages}`, callback_data: \"noop\" });\n if (page < totalPages - 1) {\n navRow.push({ text: \"Next \\u25B6\\uFE0F\", callback_data: `list:${page + 1}` });\n }\n keyboard.push(navRow);\n}\n\n// Add Select Multiple button for batch operations\nkeyboard.push([{ text: \"\u2611\ufe0f Select Multiple\", callback_data: \"batch:mode\" }]);\n\n// Build header text\nconst runningCount = running.length;\nconst totalCount = sortedContainers.length;\nlet headerText = `\\u{1F5C2} Containers (${runningCount}/${totalCount} running)`;\nif (totalPages > 1) {\n headerText += `\\n\\nPage ${page + 1} of ${totalPages}`;\n}\nheaderText += \"\\n\\nTap a container to manage it:\";\n\nreturn [{\n json: {\n chatId,\n messageId,\n text: headerText,\n reply_markup: { inline_keyboard: keyboard }\n }\n}];" - }, - "id": "code-build-paginated-list", - "name": "Build Paginated List", - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 2000, - 1000 - ] - }, { "parameters": { "method": "POST", @@ -4736,6 +4582,230 @@ "name": "Telegram account" } } + }, + { + "parameters": { + "jsCode": "// Prepare input for Container Status sub-workflow from /status command\nconst message = $('Keyword Router').item.json.message;\nconst chatId = message.chat.id;\nconst text = (message.text || '').toLowerCase().trim();\n\n// Check if user specified a container name (e.g., \"/status plex\")\nlet searchTerm = null;\nconst parts = text.split(/\\s+/);\nif (parts.length > 1) {\n searchTerm = parts.filter(p => p !== 'status' && p !== '/status').join(' ');\n}\n\nreturn {\n json: {\n chatId: chatId,\n messageId: 0,\n action: 'list',\n containerId: null,\n containerName: null,\n page: 0,\n queryId: null,\n searchTerm: searchTerm\n }\n};" + }, + "id": "code-prepare-status-input", + "name": "Prepare Status Input", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1120, + 100 + ] + }, + { + "parameters": { + "source": "database", + "workflowId": { + "__rl": true, + "mode": "id", + "value": "TODO_DEPLOY_STATUS_WORKFLOW" + }, + "options": { + "waitForSubWorkflow": true + } + }, + "id": "execute-container-status", + "name": "Execute Container Status", + "type": "n8n-nodes-base.executeWorkflow", + "typeVersion": 1.2, + "position": [ + 1340, + 100 + ] + }, + { + "parameters": { + "rules": { + "values": [ + { + "id": "route-list-result", + "conditions": { + "options": { + "caseSensitive": true, + "typeValidation": "loose" + }, + "conditions": [ + { + "id": "is-list", + "leftValue": "={{ $json.action }}", + "rightValue": "list", + "operator": { + "type": "string", + "operation": "equals" + } + } + ], + "combinator": "and" + }, + "renameOutput": true, + "outputKey": "list" + }, + { + "id": "route-status-direct", + "conditions": { + "options": { + "caseSensitive": true, + "typeValidation": "loose" + }, + "conditions": [ + { + "id": "is-status-direct", + "leftValue": "={{ $json.action }}", + "rightValue": "status_direct", + "operator": { + "type": "string", + "operation": "equals" + } + } + ], + "combinator": "and" + }, + "renameOutput": true, + "outputKey": "status_direct" + }, + { + "id": "route-error", + "conditions": { + "options": { + "caseSensitive": true, + "typeValidation": "loose" + }, + "conditions": [ + { + "id": "has-error", + "leftValue": "={{ $json.success }}", + "rightValue": false, + "operator": { + "type": "boolean", + "operation": "equals" + } + } + ], + "combinator": "and" + }, + "renameOutput": true, + "outputKey": "error" + } + ] + }, + "options": { + "fallbackOutput": "extra" + } + }, + "id": "switch-route-status-result", + "name": "Route Status Result", + "type": "n8n-nodes-base.switch", + "typeVersion": 3.2, + "position": [ + 1560, + 100 + ] + }, + { + "parameters": { + "jsCode": "// Prepare input for Container Status sub-workflow from select callback\nconst data = $(\"Parse Callback Data\").item.json;\nreturn {\n json: {\n chatId: data.chatId,\n messageId: data.messageId,\n action: 'status',\n containerId: null,\n containerName: data.containerName,\n page: 0,\n queryId: data.queryId,\n searchTerm: null\n }\n};" + }, + "id": "code-prepare-select-input", + "name": "Prepare Select Status Input", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1340, + 900 + ] + }, + { + "parameters": { + "source": "database", + "workflowId": { + "__rl": true, + "mode": "id", + "value": "TODO_DEPLOY_STATUS_WORKFLOW" + }, + "options": { + "waitForSubWorkflow": true + } + }, + "id": "execute-select-status", + "name": "Execute Select Status", + "type": "n8n-nodes-base.executeWorkflow", + "typeVersion": 1.2, + "position": [ + 1560, + 900 + ] + }, + { + "parameters": { + "jsCode": "// Prepare input for Container Status sub-workflow from list pagination callback\nconst data = $(\"Parse Callback Data\").item.json;\nreturn {\n json: {\n chatId: data.chatId,\n messageId: data.messageId,\n action: 'paginate',\n containerId: null,\n containerName: null,\n page: data.page || 0,\n queryId: data.queryId,\n searchTerm: null\n }\n};" + }, + "id": "code-prepare-paginate-input", + "name": "Prepare Paginate Input", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1340, + 1000 + ] + }, + { + "parameters": { + "source": "database", + "workflowId": { + "__rl": true, + "mode": "id", + "value": "TODO_DEPLOY_STATUS_WORKFLOW" + }, + "options": { + "waitForSubWorkflow": true + } + }, + "id": "execute-paginate-status", + "name": "Execute Paginate Status", + "type": "n8n-nodes-base.executeWorkflow", + "typeVersion": 1.2, + "position": [ + 1560, + 1000 + ] + }, + { + "parameters": { + "jsCode": "// Prepare input for Container Status sub-workflow from batch cancel return\nconst data = $input.item.json;\nreturn {\n json: {\n chatId: data.chatId,\n messageId: data.messageId,\n action: 'paginate',\n containerId: null,\n containerName: null,\n page: data.page || 0,\n queryId: data.queryId || null,\n searchTerm: null\n }\n};" + }, + "id": "code-prepare-batch-cancel-input", + "name": "Prepare Batch Cancel Return Input", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1560, + 1100 + ] + }, + { + "parameters": { + "source": "database", + "workflowId": { + "__rl": true, + "mode": "id", + "value": "TODO_DEPLOY_STATUS_WORKFLOW" + }, + "options": { + "waitForSubWorkflow": true + } + }, + "id": "execute-batch-cancel-status", + "name": "Execute Batch Cancel Status", + "type": "n8n-nodes-base.executeWorkflow", + "typeVersion": 1.2, + "position": [ + 1780, + 1100 + ] } ], "connections": { @@ -4831,7 +4901,6 @@ "index": 0 } ], - [], [ { "node": "Answer Select Callback", @@ -4939,94 +5008,6 @@ ] ] }, - "Answer Select Callback": { - "main": [ - [ - { - "node": "Prepare Container Fetch", - "type": "main", - "index": 0 - } - ] - ] - }, - "Prepare Container Fetch": { - "main": [ - [ - { - "node": "Get Single Container", - "type": "main", - "index": 0 - } - ] - ] - }, - "Get Single Container": { - "main": [ - [ - { - "node": "Build Container Submenu", - "type": "main", - "index": 0 - } - ] - ] - }, - "Build Container Submenu": { - "main": [ - [ - { - "node": "Send Container Submenu", - "type": "main", - "index": 0 - } - ] - ] - }, - "Answer List Callback": { - "main": [ - [ - { - "node": "Prepare List Fetch", - "type": "main", - "index": 0 - } - ] - ] - }, - "Prepare List Fetch": { - "main": [ - [ - { - "node": "Get Containers For List", - "type": "main", - "index": 0 - } - ] - ] - }, - "Get Containers For List": { - "main": [ - [ - { - "node": "Build Paginated List", - "type": "main", - "index": 0 - } - ] - ] - }, - "Build Paginated List": { - "main": [ - [ - { - "node": "Edit Container List", - "type": "main", - "index": 0 - } - ] - ] - }, "Handle Cancel": { "main": [ [ @@ -5126,57 +5107,6 @@ ] ] }, - "Docker List Containers": { - "main": [ - [ - { - "node": "Build Container List Keyboard", - "type": "main", - "index": 0 - } - ] - ] - }, - "Build Container List Keyboard": { - "main": [ - [ - { - "node": "Check Single Container", - "type": "main", - "index": 0 - } - ] - ] - }, - "Check Single Container": { - "main": [ - [ - { - "node": "Build Container Submenu Direct", - "type": "main", - "index": 0 - } - ], - [ - { - "node": "Send Container List", - "type": "main", - "index": 0 - } - ] - ] - }, - "Build Container Submenu Direct": { - "main": [ - [ - { - "node": "Send Container Submenu Direct", - "type": "main", - "index": 0 - } - ] - ] - }, "Docker List for Action": { "main": [ [ @@ -5413,7 +5343,7 @@ ], [ { - "node": "Docker List Containers", + "node": "Prepare Status Input", "type": "main", "index": 0 } @@ -6280,17 +6210,6 @@ ] ] }, - "Prepare Batch Cancel Return": { - "main": [ - [ - { - "node": "Get Containers For List", - "type": "main", - "index": 0 - } - ] - ] - }, "Prepare Text Update Input": { "main": [ [ @@ -6659,6 +6578,156 @@ } ] ] + }, + "Prepare Status Input": { + "main": [ + [ + { + "node": "Execute Container Status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Execute Container Status": { + "main": [ + [ + { + "node": "Route Status Result", + "type": "main", + "index": 0 + } + ] + ] + }, + "Route Status Result": { + "list": [ + [ + { + "node": "Send Container List", + "type": "main", + "index": 0 + } + ] + ], + "status_direct": [ + [ + { + "node": "Send Container Submenu Direct", + "type": "main", + "index": 0 + } + ] + ], + "error": [ + [ + { + "node": "Send Container List", + "type": "main", + "index": 0 + } + ] + ] + }, + "Answer Select Callback": { + "main": [ + [ + { + "node": "Prepare Select Status Input", + "type": "main", + "index": 0 + } + ] + ] + }, + "Prepare Select Status Input": { + "main": [ + [ + { + "node": "Execute Select Status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Execute Select Status": { + "main": [ + [ + { + "node": "Send Container Submenu", + "type": "main", + "index": 0 + } + ] + ] + }, + "Answer List Callback": { + "main": [ + [ + { + "node": "Prepare Paginate Input", + "type": "main", + "index": 0 + } + ] + ] + }, + "Prepare Paginate Input": { + "main": [ + [ + { + "node": "Execute Paginate Status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Execute Paginate Status": { + "main": [ + [ + { + "node": "Edit Container List", + "type": "main", + "index": 0 + } + ] + ] + }, + "Prepare Batch Cancel Return": { + "main": [ + [ + { + "node": "Prepare Batch Cancel Return Input", + "type": "main", + "index": 0 + } + ] + ] + }, + "Prepare Batch Cancel Return Input": { + "main": [ + [ + { + "node": "Execute Batch Cancel Status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Execute Batch Cancel Status": { + "main": [ + [ + { + "node": "Edit Container List", + "type": "main", + "index": 0 + } + ] + ] } }, "pinData": {},