{ "name": "Garvis Webhook - Bot Actions", "nodes": [ { "parameters": { "httpMethod": "POST", "path": "garvis", "responseMode": "responseNode", "options": {} }, "id": "b2c3d4e5-2222-4000-8000-000000000001", "name": "Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "position": [0, 300], "webhookId": "garvis-webhook" }, { "parameters": { "conditions": { "options": {"caseSensitive": true, "leftValue": "", "typeValidation": "strict"}, "conditions": [ { "id": "auth1", "leftValue": "={{ $json.headers['x-garvis-secret'] }}", "rightValue": "={{ $env.GARVIS_WEBHOOK_SECRET }}", "operator": {"type": "string", "operation": "equals"} } ], "combinator": "and" } }, "id": "b2c3d4e5-2222-4000-8000-000000000002", "name": "IF Auth Valid?", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [220, 300] }, { "parameters": { "respondWith": "json", "responseBody": "={\"error\": \"Unauthorized\", \"message\": \"Invalid or missing x-garvis-secret header\"}", "options": {"responseCode": 401} }, "id": "b2c3d4e5-2222-4000-8000-000000000003", "name": "Respond 401", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [440, 480] }, { "parameters": { "rules": { "rules": [ {"value": "run_pipeline", "output": 0}, {"value": "check_nas", "output": 1}, {"value": "check_services", "output": 2}, {"value": "send_message", "output": 3}, {"value": "get_status", "output": 4} ] }, "value": "={{ $json.body.action }}" }, "id": "b2c3d4e5-2222-4000-8000-000000000004", "name": "Switch Action", "type": "n8n-nodes-base.switch", "typeVersion": 3, "position": [440, 200] }, { "parameters": { "method": "POST", "url": "http://192.168.2.113:5678/api/v1/workflows/{{ $env.CONTENT_PIPELINE_ID }}/activate", "sendHeaders": true, "headerParameters": { "parameters": [ {"name": "X-N8N-API-KEY", "value": "={{ $env.N8N_API_KEY }}"} ] } }, "id": "b2c3d4e5-2222-4000-8000-000000000005", "name": "Trigger Content Pipeline", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [700, 0] }, { "parameters": { "method": "POST", "url": "http://192.168.2.210:5000/webapi/entry.cgi", "sendBody": true, "bodyParameters": { "parameters": [ {"name": "api", "value": "SYNO.FileStation.List"}, {"name": "version", "value": "2"}, {"name": "method", "value": "list"}, {"name": "folder_path", "value": "={{ $json.body.path || '/BlendedFamilyKitchen/DropZone' }}"} ] } }, "id": "b2c3d4e5-2222-4000-8000-000000000006", "name": "Check NAS Files", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [700, 140] }, { "parameters": { "command": "=echo '{\"proxmox\": \"checking...\", \"loki\": \"checking...\", \"nas\": \"checking...\"}' && curl -s -o /dev/null -w '%{http_code}' http://192.168.2.100:8006 && curl -s -o /dev/null -w '%{http_code}' http://192.168.2.114:3100/ready && curl -s -o /dev/null -w '%{http_code}' http://192.168.2.210:5000" }, "id": "b2c3d4e5-2222-4000-8000-000000000007", "name": "Check Services Health", "type": "n8n-nodes-base.executeCommand", "typeVersion": 1, "position": [700, 280] }, { "parameters": { "method": "POST", "url": "=https://api.telegram.org/bot{{ $env.TELEGRAM_BOT_TOKEN }}/sendMessage", "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"chat_id\": \"{{ $json.body.chat_id || $env.JORDAN_CHAT_ID }}\",\n \"text\": \"{{ $json.body.message }}\",\n \"parse_mode\": \"HTML\"\n}" }, "id": "b2c3d4e5-2222-4000-8000-000000000008", "name": "Send Telegram Message", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [700, 420] }, { "parameters": { "mode": "manual", "duplicateItem": false, "assignments": { "assignments": [ {"id": "st1", "name": "status", "value": "operational", "type": "string"}, {"id": "st2", "name": "workflows_active", "value": "={{ $env.ACTIVE_WORKFLOW_COUNT || '0' }}", "type": "string"}, {"id": "st3", "name": "uptime", "value": "={{ $now.toISO() }}", "type": "string"}, {"id": "st4", "name": "version", "value": "1.0.0", "type": "string"} ] } }, "id": "b2c3d4e5-2222-4000-8000-000000000009", "name": "Get System Status", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [700, 560] }, { "parameters": { "respondWith": "json", "responseBody": "={{ JSON.stringify({success: true, action: $('Switch Action').item.json.body.action, result: $json}) }}", "options": {"responseCode": 200} }, "id": "b2c3d4e5-2222-4000-8000-000000000010", "name": "Respond 200 Success", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [960, 280] }, { "parameters": { "respondWith": "json", "responseBody": "={\"error\": \"Unknown action\", \"message\": \"Valid actions: run_pipeline, check_nas, check_services, send_message, get_status\", \"received\": \"{{ $json.body.action }}\"}", "options": {"responseCode": 400} }, "id": "b2c3d4e5-2222-4000-8000-000000000011", "name": "Respond 400 Unknown", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [700, 720] }, { "parameters": { "content": "## 🔐 Webhook Auth & Entry\n\n**Endpoint:** POST http://192.168.2.113:5678/webhook/garvis\n\n**Request format:**\n```json\n{\n \"action\": \"run_pipeline|check_nas|check_services|send_message|get_status\",\n \"message\": \"(for send_message)\",\n \"chat_id\": \"(optional, defaults to Jordan)\",\n \"path\": \"(optional, for check_nas)\"\n}\n```\n\n**Auth:** x-garvis-secret header must match GARVIS_WEBHOOK_SECRET env var\n\n**Test command:**\n```bash\ncurl -X POST http://192.168.2.113:5678/webhook/garvis \\\n -H 'Content-Type: application/json' \\\n -H 'x-garvis-secret: YOUR_SECRET' \\\n -d '{\"action\": \"get_status\"}'\n```\n\n**TODO:**\n- [ ] Generate GARVIS_WEBHOOK_SECRET and add to n8n env\n- [ ] Add webhook URL to Garvis bot config\n- [ ] Test from Garvis agent with real request", "width": 560, "height": 520 }, "id": "b2c3d4e5-2222-4000-8000-000000000012", "name": "Sticky - Webhook Setup", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [-40, -260] }, { "parameters": { "content": "## 🔀 Action Handlers\n\n**run_pipeline** — Triggers the Content Pipeline workflow via n8n API. Use when Garvis needs to manually kick off video processing.\n\n**check_nas** — Lists files in a NAS directory. Defaults to /BlendedFamilyKitchen/DropZone. Pass custom path in request body.\n\n**check_services** — Pings Proxmox (8006), Loki (3100), and NAS (5000) to verify they're responding. Returns HTTP status codes.\n\n**send_message** — Sends a Telegram message to specified chat_id (or Jordan by default). Garvis uses this for notifications.\n\n**get_status** — Returns n8n system status: operational state, active workflow count, uptime, version.\n\n**TODO:**\n- [ ] Add CONTENT_PIPELINE_ID env var after deploying content pipeline\n- [ ] Add N8N_API_KEY env var for internal API calls\n- [ ] Add TELEGRAM_BOT_TOKEN and JORDAN_CHAT_ID env vars\n- [ ] Consider adding: restart_vm, run_backup, deploy_update actions", "width": 560, "height": 480 }, "id": "b2c3d4e5-2222-4000-8000-000000000013", "name": "Sticky - Action Handlers", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [600, -260] } ], "connections": { "Webhook": { "main": [[{"node": "IF Auth Valid?", "type": "main", "index": 0}]] }, "IF Auth Valid?": { "main": [ [{"node": "Switch Action", "type": "main", "index": 0}], [{"node": "Respond 401", "type": "main", "index": 0}] ] }, "Switch Action": { "main": [ [{"node": "Trigger Content Pipeline", "type": "main", "index": 0}], [{"node": "Check NAS Files", "type": "main", "index": 0}], [{"node": "Check Services Health", "type": "main", "index": 0}], [{"node": "Send Telegram Message", "type": "main", "index": 0}], [{"node": "Get System Status", "type": "main", "index": 0}] ] }, "Trigger Content Pipeline": { "main": [[{"node": "Respond 200 Success", "type": "main", "index": 0}]] }, "Check NAS Files": { "main": [[{"node": "Respond 200 Success", "type": "main", "index": 0}]] }, "Check Services Health": { "main": [[{"node": "Respond 200 Success", "type": "main", "index": 0}]] }, "Send Telegram Message": { "main": [[{"node": "Respond 200 Success", "type": "main", "index": 0}]] }, "Get System Status": { "main": [[{"node": "Respond 200 Success", "type": "main", "index": 0}]] } }, "settings": { "executionOrder": "v1" }, "staticData": null, "tags": [], "triggerCount": 1, "active": false }