{ "name": "Container Actions", "nodes": [ { "parameters": { "inputSource": "passthrough", "schema": { "schemaType": "fromFields", "inputFieldName": "", "fields": [ { "fieldName": "containerId", "fieldType": "string" }, { "fieldName": "containerName", "fieldType": "string" }, { "fieldName": "action", "fieldType": "string" }, { "fieldName": "chatId", "fieldType": "number" }, { "fieldName": "messageId", "fieldType": "number" }, { "fieldName": "responseMode", "fieldType": "string" } ] } }, "id": "trigger-sub-workflow", "name": "When executed by another workflow", "type": "n8n-nodes-base.executeWorkflowTrigger", "typeVersion": 1.1, "position": [ 240, 300 ] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "typeValidation": "loose" }, "conditions": [ { "id": "has-container-id", "leftValue": "={{ $json.containerId }}", "rightValue": "", "operator": { "type": "string", "operation": "notEquals" } } ], "combinator": "and" } }, "id": "if-has-id", "name": "Has Container ID?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ 420, 300 ] }, { "parameters": { "url": "http://docker-socket-proxy:2375/v1.47/containers/json?all=true", "options": { "timeout": 5000 } }, "id": "http-get-containers", "name": "Get All Containers", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 600, 400 ] }, { "parameters": { "jsCode": "// Find container by name and resolve ID\nconst triggerData = $('When executed by another workflow').item.json;\nconst containerName = triggerData.containerName;\nconst containers = $input.all();\n\n// Normalize function to strip leading slash\nconst normalizeName = (name) => name.replace(/^\\//, '').toLowerCase();\nconst searchName = normalizeName(containerName);\n\n// Find matching container\nlet matched = null;\nfor (const item of containers) {\n const c = item.json;\n if (c.Names && c.Names.length > 0) {\n const cName = normalizeName(c.Names[0]);\n if (cName === searchName || cName.includes(searchName)) {\n matched = c;\n break;\n }\n }\n}\n\nif (!matched) {\n // Return error - container not found\n return {\n json: {\n ...triggerData,\n containerId: '',\n error: `Container '${containerName}' not found`\n }\n };\n}\n\nreturn {\n json: {\n ...triggerData,\n containerId: matched.Id\n }\n};" }, "id": "code-resolve-id", "name": "Resolve Container ID", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 780, 400 ] }, { "parameters": { "rules": { "values": [ { "id": "route-start", "conditions": { "options": { "caseSensitive": true, "typeValidation": "loose" }, "conditions": [ { "id": "is-start", "leftValue": "={{ $json.action }}", "rightValue": "start", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "start" }, { "id": "route-stop", "conditions": { "options": { "caseSensitive": true, "typeValidation": "loose" }, "conditions": [ { "id": "is-stop", "leftValue": "={{ $json.action }}", "rightValue": "stop", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "stop" }, { "id": "route-restart", "conditions": { "options": { "caseSensitive": true, "typeValidation": "loose" }, "conditions": [ { "id": "is-restart", "leftValue": "={{ $json.action }}", "rightValue": "restart", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "restart" } ] }, "options": { "fallbackOutput": "none" } }, "id": "switch-action-type", "name": "Route Action", "type": "n8n-nodes-base.switch", "typeVersion": 3.2, "position": [ 960, 300 ] }, { "parameters": { "method": "POST", "url": "=http://docker-socket-proxy:2375/v1.47/containers/{{ $json.containerId }}/start", "options": { "timeout": 15000 } }, "id": "http-start-container", "name": "Start Container", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 1160, 200 ], "onError": "continueRegularOutput" }, { "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": [ 1160, 300 ], "onError": "continueRegularOutput" }, { "parameters": { "method": "POST", "url": "=http://docker-socket-proxy:2375/v1.47/containers/{{ $json.containerId }}/restart?t=10", "options": { "timeout": 15000 } }, "id": "http-restart-container", "name": "Restart Container", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 1160, 400 ], "onError": "continueRegularOutput" }, { "parameters": { "jsCode": "// Format start action result\n// Get data from Route Action (works for both direct and resolved ID paths)\nconst routeData = $('Route Action').item.json;\nconst containerId = routeData.containerId;\nconst containerName = routeData.containerName;\nconst action = routeData.action;\nconst chatId = routeData.chatId;\nconst messageId = routeData.messageId;\nconst responseMode = routeData.responseMode;\n\nconst response = $input.item.json || {};\n\n// Check HTTP status codes first (Docker API with onError:continueRegularOutput)\nif (response.statusCode) {\n if (response.statusCode === 304) {\n // Already in desired state\n return {\n json: {\n success: true,\n message: `\\u2705 ${containerName} is already started`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode === 404) {\n return {\n json: {\n success: false,\n message: `\\u274C Container ${containerName} not found`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode >= 500) {\n return {\n json: {\n success: false,\n message: `\\u274C Server error starting ${containerName}: ${response.statusMessage || 'Unknown error'}`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n }\n}\n\n// Docker API returns 204 No Content on success (empty response body)\n// Error responses contain 'message' field\nconst hasError = response.message || response.error || false;\n\n// Success = no error message in response (empty {} means 204 success)\nconst success = !hasError;\n\nlet message;\nif (success) {\n message = `\\u25B6\\uFE0F ${containerName} started successfully`;\n} else {\n message = `\\u274C Failed to start ${containerName}`;\n}\n\nreturn {\n json: {\n success,\n message,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n};" }, "id": "code-format-start-result", "name": "Format Start Result", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1380, 200 ] }, { "parameters": { "jsCode": "// Format stop action result\n// Get data from Route Action (works for both direct and resolved ID paths)\nconst routeData = $('Route Action').item.json;\nconst containerId = routeData.containerId;\nconst containerName = routeData.containerName;\nconst action = routeData.action;\nconst chatId = routeData.chatId;\nconst messageId = routeData.messageId;\nconst responseMode = routeData.responseMode;\n\nconst response = $input.item.json || {};\n\n// Check HTTP status codes first (Docker API with onError:continueRegularOutput)\nif (response.statusCode) {\n if (response.statusCode === 304) {\n // Already in desired state\n return {\n json: {\n success: true,\n message: `\\u2705 ${containerName} is already stopped`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode === 404) {\n return {\n json: {\n success: false,\n message: `\\u274C Container ${containerName} not found`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode >= 500) {\n return {\n json: {\n success: false,\n message: `\\u274C Server error stopping ${containerName}: ${response.statusMessage || 'Unknown error'}`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n }\n}\n\n// Docker API returns 204 No Content on success (empty response body)\n// Error responses contain 'message' field\nconst hasError = response.message || response.error || false;\n\n// Success = no error message in response (empty {} means 204 success)\nconst success = !hasError;\n\nlet message;\nif (success) {\n message = `\\u23F9\\uFE0F ${containerName} stopped`;\n} else {\n message = `\\u274C Failed to stop ${containerName}`;\n}\n\nreturn {\n json: {\n success,\n message,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n};" }, "id": "code-format-stop-result", "name": "Format Stop Result", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1380, 300 ] }, { "parameters": { "jsCode": "// Format restart action result\n// Get data from Route Action (works for both direct and resolved ID paths)\nconst routeData = $('Route Action').item.json;\nconst containerId = routeData.containerId;\nconst containerName = routeData.containerName;\nconst action = routeData.action;\nconst chatId = routeData.chatId;\nconst messageId = routeData.messageId;\nconst responseMode = routeData.responseMode;\n\nconst response = $input.item.json || {};\n\n// Check HTTP status codes first (Docker API with onError:continueRegularOutput)\nif (response.statusCode) {\n if (response.statusCode === 304) {\n // Already in desired state (running)\n return {\n json: {\n success: true,\n message: `\\u2705 ${containerName} is already started`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode === 404) {\n return {\n json: {\n success: false,\n message: `\\u274C Container ${containerName} not found`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n } else if (response.statusCode >= 500) {\n return {\n json: {\n success: false,\n message: `\\u274C Server error restarting ${containerName}: ${response.statusMessage || 'Unknown error'}`,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n };\n }\n}\n\n// Docker API returns 204 No Content on success (empty response body)\n// Error responses contain 'message' field\nconst hasError = response.message || response.error || false;\n\n// Success = no error message in response (empty {} means 204 success)\nconst success = !hasError;\n\nlet message;\nif (success) {\n message = `\\u{1F504} ${containerName} restarted`;\n} else {\n message = `\\u274C Failed to restart ${containerName}`;\n}\n\nreturn {\n json: {\n success,\n message,\n action,\n containerName,\n containerId,\n chatId,\n messageId,\n responseMode\n }\n};" }, "id": "code-format-restart-result", "name": "Format Restart Result", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1380, 400 ] } ], "connections": { "When executed by another workflow": { "main": [ [ { "node": "Has Container ID?", "type": "main", "index": 0 } ] ] }, "Has Container ID?": { "main": [ [ { "node": "Route Action", "type": "main", "index": 0 } ], [ { "node": "Get All Containers", "type": "main", "index": 0 } ] ] }, "Get All Containers": { "main": [ [ { "node": "Resolve Container ID", "type": "main", "index": 0 } ] ] }, "Resolve Container ID": { "main": [ [ { "node": "Route Action", "type": "main", "index": 0 } ] ] }, "Route Action": { "main": [ [ { "node": "Start Container", "type": "main", "index": 0 } ], [ { "node": "Stop Container", "type": "main", "index": 0 } ], [ { "node": "Restart Container", "type": "main", "index": 0 } ] ] }, "Start Container": { "main": [ [ { "node": "Format Start Result", "type": "main", "index": 0 } ] ] }, "Stop Container": { "main": [ [ { "node": "Format Stop Result", "type": "main", "index": 0 } ] ] }, "Restart Container": { "main": [ [ { "node": "Format Restart Result", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1", "callerPolicy": "any" } }