diff --git a/n8n-workflow.json b/n8n-workflow.json index 3002acf..f6014e5 100644 --- a/n8n-workflow.json +++ b/n8n-workflow.json @@ -3371,7 +3371,15 @@ "mode": "list", "value": "7AvTzLtKXM2hZTio92_mC" }, - "options": {} + "options": { + "timeout": 120000, + "response": { + "response": { + "errorRedirection": "continue", + "fullResponse": false + } + } + } }, "id": "720b6da1-60e6-4ec4-8a5f-d403ad05994c", "name": "Execute Batch Update", @@ -5291,6 +5299,137 @@ 2600, 700 ] + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict" + }, + "conditions": [ + { + "id": "check-count", + "leftValue": "={{ $json.totalCount }}", + "rightValue": 5, + "operator": { + "type": "number", + "operation": "lte" + } + } + ], + "combinator": "and" + } + }, + "id": "if-check-batch-size", + "name": "Check Batch Size", + "type": "n8n-nodes-base.if", + "typeVersion": 2, + "position": [ + 2600, + 2600 + ] + }, + { + "parameters": { + "jsCode": "// Build updateContainers (plural) mutation for small batches\nconst batchState = $input.item.json;\nconst containers = batchState.containers || [];\nconst chatId = batchState.chatId;\n\n// Get Container ID Registry to look up PrefixedIDs\nconst staticData = $getWorkflowStaticData('global');\nconst registry = JSON.parse(staticData._containerIdRegistry || '{}');\n\n// Build array of PrefixedIDs and name mapping\nconst ids = [];\nconst nameMap = {};\nconst notFound = [];\n\nfor (const container of containers) {\n const name = container.Name;\n const entry = registry[name];\n \n if (entry && entry.prefixedId) {\n ids.push(entry.prefixedId);\n nameMap[entry.prefixedId] = name;\n } else {\n // Container not in registry - this shouldn't happen but handle gracefully\n notFound.push(name);\n }\n}\n\nif (notFound.length > 0) {\n return {\n json: {\n error: true,\n errorMessage: `Containers not found in registry: ${notFound.join(', ')}`,\n chatId: chatId\n }\n };\n}\n\n// Build GraphQL mutation\nconst idsJson = JSON.stringify(ids);\nconst query = `mutation { docker { updateContainers(ids: ${idsJson}) { id state image imageId } } }`;\n\nreturn {\n json: {\n query: query,\n ids: ids,\n nameMap: nameMap,\n containerCount: ids.length,\n chatId: chatId,\n batchState: batchState\n }\n};\n" + }, + "id": "code-build-batch-update-mutation", + "name": "Build Batch Update Mutation", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2800, + 2500 + ] + }, + { + "parameters": { + "method": "POST", + "url": "={{ $env.UNRAID_HOST }}/graphql", + "authentication": "none", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "x-api-key", + "value": "={{ $env.UNRAID_API_KEY }}" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ {\"query\": $json.query} }}", + "options": { + "timeout": 120000, + "response": { + "response": { + "errorRedirection": "continue", + "fullResponse": false + } + } + } + }, + "id": "http-execute-batch-update", + "name": "Execute Batch Update", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 3000, + 2500 + ] + }, + { + "parameters": { + "jsCode": "// Handle updateContainers mutation response\nconst response = $input.item.json;\nconst prevData = $('Build Batch Update Mutation').item.json;\nconst chatId = prevData.chatId;\nconst nameMap = prevData.nameMap;\n\n// Check for GraphQL errors\nif (response.errors) {\n return {\n json: {\n success: false,\n error: true,\n errorMessage: response.errors[0].message,\n chatId: chatId,\n batchMode: 'parallel'\n }\n };\n}\n\n// Extract updated containers\nconst updated = response.data?.docker?.updateContainers || [];\n\nif (updated.length === 0) {\n return {\n json: {\n success: false,\n error: true,\n errorMessage: 'No containers returned from updateContainers mutation',\n chatId: chatId,\n batchMode: 'parallel'\n }\n };\n}\n\n// Map results back to container names\nconst results = updated.map(container => ({\n name: nameMap[container.id] || container.id,\n imageId: container.imageId,\n state: container.state,\n success: true\n}));\n\n// Update Container ID Registry with new IDs\nconst staticData = $getWorkflowStaticData('global');\nconst registry = JSON.parse(staticData._containerIdRegistry || '{}');\n\nupdated.forEach(container => {\n const name = nameMap[container.id];\n if (name && registry[name]) {\n registry[name].prefixedId = container.id;\n registry[name].unraidId = container.id;\n registry[name].lastSeen = Date.now();\n }\n});\n\nstaticData._containerIdRegistry = JSON.stringify(registry);\n\nreturn {\n json: {\n success: true,\n batchMode: 'parallel',\n updatedCount: results.length,\n results: results,\n chatId: chatId\n }\n};\n" + }, + "id": "code-handle-batch-update-response", + "name": "Handle Batch Update Response", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3200, + 2500 + ] + }, + { + "parameters": { + "jsCode": "// Format batch update result for Telegram message\nconst result = $input.item.json;\nconst chatId = result.chatId;\n\nif (result.error) {\n return {\n json: {\n chatId: chatId,\n text: `\u274c Batch update failed: ${result.errorMessage}`,\n success: false\n }\n };\n}\n\nconst mode = result.batchMode || 'parallel';\nconst count = result.updatedCount || 0;\nconst results = result.results || [];\n\n// Build summary message\nlet message = `\u2705 Batch update complete (${mode} mode)\\n\\n`;\nmessage += `Updated ${count} container${count !== 1 ? 's' : ''}:\\n`;\n\nresults.forEach(r => {\n const icon = r.success ? '\u2713' : '\u2717';\n message += ` ${icon} ${r.name}\\n`;\n});\n\nreturn {\n json: {\n chatId: chatId,\n text: message,\n success: true\n }\n};\n" + }, + "id": "code-format-batch-result", + "name": "Format Batch Result", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3400, + 2600 + ] + }, + { + "parameters": { + "chatId": "={{ $json.chatId }}", + "text": "={{ $json.text }}", + "additionalFields": {} + }, + "id": "telegram-send-batch-result", + "name": "Send Batch Result", + "type": "n8n-nodes-base.telegram", + "typeVersion": 1.2, + "position": [ + 3600, + 2600 + ], + "credentials": { + "telegramApi": { + "id": "I0xTTiASl7C1NZhJ", + "name": "Telegram account" + } + } } ], "connections": { @@ -7332,7 +7471,80 @@ ] ] }, - "registry-update-bitmap-stop": {} + "registry-update-bitmap-stop": {}, + "code-prepare-update-all-batch": { + "main": [ + [ + { + "node": "if-check-batch-size", + "type": "main", + "index": 0 + } + ] + ] + }, + "if-check-batch-size": { + "main": [ + [ + { + "node": "code-build-batch-update-mutation", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "code-prepare-batch-loop", + "type": "main", + "index": 0 + } + ] + ] + }, + "code-build-batch-update-mutation": { + "main": [ + [ + { + "node": "http-execute-batch-update", + "type": "main", + "index": 0 + } + ] + ] + }, + "http-execute-batch-update": { + "main": [ + [ + { + "node": "code-handle-batch-update-response", + "type": "main", + "index": 0 + } + ] + ] + }, + "code-handle-batch-update-response": { + "main": [ + [ + { + "node": "code-format-batch-result", + "type": "main", + "index": 0 + } + ] + ] + }, + "code-format-batch-result": { + "main": [ + [ + { + "node": "telegram-send-batch-result", + "type": "main", + "index": 0 + } + ] + ] + } }, "pinData": {}, "settings": {