{ "name": "Auto-Backup Verifier", "nodes": [ { "parameters": { "rule": { "interval": [ { "field": "cronExpression", "expression": "20 2 * * *" } ] }, "timezone": "Australia/Perth" }, "id": "cron-trigger", "name": "Daily 02:20 AWST", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.2, "position": [200, 300] }, { "parameters": { "assignments": { "assignments": [ { "name": "repoOwner", "type": "string", "value": "Anthony" }, { "name": "repoName", "type": "string", "value": "openclaw-backups" }, { "name": "branch", "type": "string", "value": "main" }, { "name": "requiredPaths", "type": "string", "value": "AGENTS.md,MEMORY.md,TOOLS.md,state-backup/openclaw.json,state-backup/cron/jobs.json,state-backup/devices/paired.json" } ] } }, "id": "set-context", "name": "Set Context", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [420, 300] }, { "parameters": { "url": "=http://gitea.kangaroo-eel.ts.net:3000/api/v1/repos/{{$json.repoOwner}}/{{$json.repoName}}/branches/{{$json.branch}}", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "options": { "timeout": 10000 } }, "id": "get-branch", "name": "Get Branch Head", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [660, 300], "retryOnFail": true, "maxTries": 3, "waitBetweenTries": 2000 }, { "parameters": { "url": "=http://gitea.kangaroo-eel.ts.net:3000/api/v1/repos/{{$node[\"Set Context\"].json.repoOwner}}/{{$node[\"Set Context\"].json.repoName}}/git/trees/{{$json.commit.id}}?recursive=1", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "options": { "timeout": 10000 } }, "id": "get-tree", "name": "Get Repo Tree", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [900, 300], "retryOnFail": true, "maxTries": 3, "waitBetweenTries": 2000 }, { "parameters": { "jsCode": "const required = $node['Set Context'].json.requiredPaths.split(',').map(s => s.trim());\nconst tree = $json.tree || [];\nconst existing = new Set(tree.map(t => t.path));\nconst missing = required.filter(p => !existing.has(p));\nreturn [{ json: {\n status: missing.length ? 'failed' : 'ok',\n missing,\n checked: required.length,\n headSha: ($node['Get Branch Head'].json.commit || {}).id || '',\n repo: `${$node['Set Context'].json.repoOwner}/${$node['Set Context'].json.repoName}`\n}}];" }, "id": "validate", "name": "Validate Required Files", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1140, 300] }, { "parameters": { "conditions": { "string": [ { "value1": "={{$json.status}}", "operation": "equals", "value2": "ok" } ] } }, "id": "if-ok", "name": "Backup OK?", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [1360, 300] }, { "parameters": { "url": "=http://runtipi.kangaroo-eel.ts.net:8129/message?token={{$env.GOTIFY_TOKEN}}", "sendBody": true, "contentType": "json", "bodyParameters": { "parameters": [ { "name": "title", "value": "✅ Backup Verified" }, { "name": "message", "value": "={{`Repo ${$json.repo} verified. SHA: ${$json.headSha.slice(0,7)}. Checked ${$json.checked} required files.`}}" }, { "name": "priority", "value": "2" } ] }, "options": { "timeout": 10000 } }, "id": "notify-ok", "name": "Gotify Success", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1600, 200] }, { "parameters": { "url": "=http://runtipi.kangaroo-eel.ts.net:8129/message?token={{$env.GOTIFY_TOKEN}}", "sendBody": true, "contentType": "json", "bodyParameters": { "parameters": [ { "name": "title", "value": "🚨 Backup Verification Failed" }, { "name": "message", "value": "={{`Repo ${$json.repo} failed verification. Missing: ${$json.missing.join(', ')}. SHA: ${$json.headSha.slice(0,7)}`}}" }, { "name": "priority", "value": "9" } ] }, "options": { "timeout": 10000 } }, "id": "notify-fail-gotify", "name": "Gotify Failure", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1600, 360] }, { "parameters": { "chatId": "1793951355", "text": "={{`🦀 Backup verifier failed\nRepo: ${$json.repo}\nMissing: ${$json.missing.join(', ')}\nSHA: ${$json.headSha.slice(0,7)}`}}", "additionalFields": {} }, "id": "notify-fail-telegram", "name": "Telegram Failure", "type": "n8n-nodes-base.telegram", "typeVersion": 1.2, "position": [1810, 360], "credentials": { "telegramApi": { "name": "Telegram account" } } } ], "connections": { "Daily 02:20 AWST": { "main": [[{ "node": "Set Context", "type": "main", "index": 0 }]] }, "Set Context": { "main": [[{ "node": "Get Branch Head", "type": "main", "index": 0 }]] }, "Get Branch Head": { "main": [[{ "node": "Get Repo Tree", "type": "main", "index": 0 }]] }, "Get Repo Tree": { "main": [[{ "node": "Validate Required Files", "type": "main", "index": 0 }]] }, "Validate Required Files": { "main": [[{ "node": "Backup OK?", "type": "main", "index": 0 }]] }, "Backup OK?": { "main": [ [{ "node": "Gotify Success", "type": "main", "index": 0 }], [{ "node": "Gotify Failure", "type": "main", "index": 0 }] ] }, "Gotify Failure": { "main": [[{ "node": "Telegram Failure", "type": "main", "index": 0 }]] } }, "settings": { "executionOrder": "v1" }, "active": false }