Add OpenClaw to AI stack
This commit is contained in:
@@ -8,3 +8,6 @@ OPENWEBUI_DB_PASS=changeme
|
||||
|
||||
# Scriberr — Hugging Face token for model downloads
|
||||
HF_TOKEN=changeme
|
||||
|
||||
# OpenClaw — generate with: openssl rand -hex 32
|
||||
OPENCLAW_GATEWAY_TOKEN=changeme
|
||||
|
||||
@@ -128,3 +128,29 @@ services:
|
||||
- driver: nvidia
|
||||
count: 1
|
||||
capabilities: [gpu]
|
||||
|
||||
openclaw:
|
||||
container_name: openclaw
|
||||
image: ghcr.io/openclaw/openclaw:latest
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- ai-net
|
||||
- ai-services
|
||||
- pangolin
|
||||
ports:
|
||||
- "18789:18789"
|
||||
environment:
|
||||
TZ: ${TZ}
|
||||
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
|
||||
OPENCLAW_DISABLE_BONJOUR: "1"
|
||||
volumes:
|
||||
- /mnt/docker-ssd/docker/appdata/openclaw/config:/home/node/.openclaw
|
||||
- /mnt/docker-ssd/docker/appdata/openclaw/workspace:/home/node/.openclaw/workspace
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -sf http://127.0.0.1:18789/healthz || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 90s
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
All homelab services, their hosts, ports, and current status.
|
||||
|
||||
*Last updated: 2026-05-09*
|
||||
*Last updated: 2026-05-10*
|
||||
|
||||
## Shared Databases (PD)
|
||||
|
||||
@@ -32,8 +32,10 @@ All homelab services, their hosts, ports, and current status.
|
||||
| LiteLLM | PD | 4000 | ✅ Active |
|
||||
| OpenWebUI | PD | 8282 | ✅ Active |
|
||||
| Qdrant | PD | 6333 (HTTP) / 6334 (gRPC) | ✅ Active |
|
||||
| Whisper (faster-whisper) | PD | 8786 | ✅ Active |
|
||||
| Whisper (faster-whisper, CPU) | N.O.M.A.D. (10.5.1.16) | 8786 | ✅ Active |
|
||||
| Whisper (faster-whisper, CUDA large-v3) | Rocinante (10.5.1.112) | 8787 | ✅ Active |
|
||||
| SearXNG | PD | 8888 | ✅ Active |
|
||||
| OpenClaw | PD | 18789 | ✅ Active |
|
||||
| Ollama — light tier | N.O.M.A.D. (10.5.1.16) | 11434 | ✅ Active |
|
||||
| Ollama — heavy tier | Rocinante (10.5.1.112) | 11434 | ✅ Active |
|
||||
| Reranker (TEI) | Serenity (10.5.1.5) | 9787 | ✅ Active |
|
||||
|
||||
293
n8n-workflows/14-paperless-inbox-reminder-v1.2.json
Executable file
293
n8n-workflows/14-paperless-inbox-reminder-v1.2.json
Executable file
@@ -0,0 +1,293 @@
|
||||
{
|
||||
"name": "Paperless Inbox Reminder v1.2",
|
||||
"active": true,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"field": "weeks",
|
||||
"weeksInterval": 1,
|
||||
"triggerAtDay": [
|
||||
1
|
||||
],
|
||||
"triggerAtHour": 9,
|
||||
"triggerAtMinute": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "41404371-29ed-4660-9989-a5cbd890555b",
|
||||
"name": "Every Monday 9 AM",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1.2,
|
||||
"position": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://paperless.paccoco.com/api/documents/",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Token {{ $env.PAPERLESS_API_TOKEN }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "is_tagged",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "page_size",
|
||||
"value": "25"
|
||||
},
|
||||
{
|
||||
"name": "ordering",
|
||||
"value": "created"
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"value": "id,title,created,document_type,tags"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "fdcd5728-6d27-445f-961b-18f204297469",
|
||||
"name": "Fetch Inbox Docs",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
224,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const data = $input.first().json;\nconst docs = data.results || [];\nconst total = data.count || 0;\n\n// Oldest 5 for the reminder body\nconst oldest = docs.slice(0, 5).map(d => {\n const date = d.created ? d.created.substring(0, 10) : 'unknown date';\n return ` • ${d.title || 'Untitled'} (added ${date})`;\n});\n\nreturn [{\n json: {\n total,\n hasItems: total > 0,\n hasItemsStr: total > 0 ? 'yes' : 'no',\n oldestList: oldest.join('\\n'),\n remaining: Math.max(0, total - 5)\n }\n}];"
|
||||
},
|
||||
"id": "8fbe6be0-1ed3-4126-8f21-c3f4efb161c6",
|
||||
"name": "Parse Response",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
448,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "b206599d-0c79-4a32-9fb9-91e1c41dcff2",
|
||||
"leftValue": "={{ $json.hasItemsStr }}",
|
||||
"rightValue": "yes",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
}
|
||||
},
|
||||
"id": "e24344a1-e857-462c-b96a-842169e83a6d",
|
||||
"name": "Has Inbox Items?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.2,
|
||||
"position": [
|
||||
672,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const d = $input.first().json;\nconst extra = d.remaining > 0 ? `\\n … and ${d.remaining} more` : '';\nconst body = `${d.total} untagged document${d.total !== 1 ? 's' : ''} in Paperless inbox:\\n${d.oldestList}${extra}\\n\\nhttps://paperless.paccoco.com`;\nreturn [{ json: { title: '📥 Paperless Inbox Needs Attention', message: body, priority: 5 } }];"
|
||||
},
|
||||
"id": "897af9c9-4429-445f-8160-a03a14a318a3",
|
||||
"name": "Format Reminder",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
896,
|
||||
-120
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "return [{ json: { title: '✅ Paperless Inbox Clear', message: 'No untagged documents — inbox is clean.', priority: 1 } }];"
|
||||
},
|
||||
"id": "bd8fe88e-7876-45dd-8344-983674fba79d",
|
||||
"name": "Format Clear",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
896,
|
||||
120
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"message": "={{ $json.message }}",
|
||||
"additionalFields": {
|
||||
"priority": "={{ $json.priority }}",
|
||||
"title": "={{ $json.title }}"
|
||||
},
|
||||
"options": {
|
||||
"contentType": "text/plain"
|
||||
}
|
||||
},
|
||||
"id": "74860e7f-42e3-448f-9e46-5cfb6f12cdc2",
|
||||
"name": "Push to Gotify (Reminder)",
|
||||
"type": "n8n-nodes-base.gotify",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1120,
|
||||
-120
|
||||
],
|
||||
"credentials": {
|
||||
"gotifyApi": {
|
||||
"id": "SETUP_REQUIRED",
|
||||
"name": "Reminders"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"message": "={{ $json.message }}",
|
||||
"additionalFields": {
|
||||
"priority": "={{ $json.priority }}",
|
||||
"title": "={{ $json.title }}"
|
||||
},
|
||||
"options": {
|
||||
"contentType": "text/plain"
|
||||
}
|
||||
},
|
||||
"id": "b1dbd39b-87c3-43ec-9cec-f63eabbd0fa5",
|
||||
"name": "Push to Gotify (Clear)",
|
||||
"type": "n8n-nodes-base.gotify",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1120,
|
||||
120
|
||||
],
|
||||
"credentials": {
|
||||
"gotifyApi": {
|
||||
"id": "SETUP_REQUIRED",
|
||||
"name": "Reminders"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Every Monday 9 AM": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Fetch Inbox Docs",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Fetch Inbox Docs": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse Response": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Has Inbox Items?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Has Inbox Items?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Reminder",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Format Clear",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Format Reminder": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Push to Gotify (Reminder)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Format Clear": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Push to Gotify (Clear)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1",
|
||||
"binaryMode": "separate"
|
||||
},
|
||||
"staticData": null,
|
||||
"meta": null,
|
||||
"pinData": {},
|
||||
"tags": [
|
||||
{
|
||||
"name": "reminders"
|
||||
},
|
||||
{
|
||||
"name": "paperless"
|
||||
},
|
||||
{
|
||||
"name": "scheduled"
|
||||
}
|
||||
],
|
||||
"id": "2b80579b-8d89-42d5-8b8b-9137f59ea206"
|
||||
}
|
||||
302
n8n-workflows/15-n8n-health-monitor-v1.2.json
Executable file
302
n8n-workflows/15-n8n-health-monitor-v1.2.json
Executable file
@@ -0,0 +1,302 @@
|
||||
{
|
||||
"name": "n8n Self-Health Monitor v1.2",
|
||||
"active": true,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"field": "minutes",
|
||||
"minutesInterval": 15
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "dabcb0ae-d513-410e-b779-4570df5c4a35",
|
||||
"name": "Every 15 Minutes",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1.2,
|
||||
"position": [0, 0]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "http://10.5.1.6:5678/api/v1/workflows",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-N8N-API-KEY",
|
||||
"value": "={{ $env.N8N_API_KEY }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "250"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
"name": "Fetch All Workflows",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [224, 0]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "http://10.5.1.6:5678/api/v1/executions",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-N8N-API-KEY",
|
||||
"value": "={{ $env.N8N_API_KEY }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "status",
|
||||
"value": "error"
|
||||
},
|
||||
{
|
||||
"name": "includeData",
|
||||
"value": "false"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "50"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "806d75e9-b95a-499a-b997-a7f7dc046775",
|
||||
"name": "Fetch Failed Executions",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [448, 0]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const data = $input.first().json;\nconst executions = data.data || [];\nconst oneHourAgo = Date.now() - (60 * 60 * 1000);\n\n// Build workflow ID → name lookup from previously fetched workflows\nconst wfResponse = $('Fetch All Workflows').first().json;\nconst nameMap = {};\n(wfResponse.data || []).forEach(wf => { nameMap[wf.id] = wf.name; });\n\n// Filter to failures in the past hour\nconst recent = executions.filter(e => {\n const ts = new Date(e.startedAt || e.stoppedAt || 0).getTime();\n return ts >= oneHourAgo;\n});\n\n// Map executions to failure objects, resolving names from the lookup\nconst failures = recent.map(e => ({\n executionId: e.id,\n workflowId: e.workflowId,\n workflowName: nameMap[e.workflowId] || e.workflowData?.name || `Workflow ${e.workflowId}`,\n startedAt: e.startedAt,\n stoppedAt: e.stoppedAt,\n errorMessage: e.data?.resultData?.error?.message || 'Unknown error'\n}));\n\n// Deduplicate by workflowName (report once per workflow per check)\nconst seen = new Set();\nconst unique = failures.filter(f => {\n if (seen.has(f.workflowName)) return false;\n seen.add(f.workflowName);\n return true;\n});\n\nreturn [{ json: { hasFailures: unique.length > 0, hasFailuresStr: unique.length > 0 ? 'yes' : 'no', count: unique.length, failures: unique } }];"
|
||||
},
|
||||
"id": "f5d47910-5047-4c4f-aa84-a6907aa38551",
|
||||
"name": "Filter Recent Failures",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [672, 0]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "02ff82df-a31d-491b-9cd1-96874c8ae2fe",
|
||||
"leftValue": "={{ $json.hasFailuresStr }}",
|
||||
"rightValue": "yes",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
}
|
||||
},
|
||||
"id": "7c298433-ab89-435e-8955-73da6b986f46",
|
||||
"name": "Has Failures?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.2,
|
||||
"position": [896, 0]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const d = $input.first().json;\n// Fingerprint = sorted workflow names joined — stable across retriggers\nconst fingerprint = (d.failures || [])\n .map(f => f.workflowName)\n .sort()\n .join('|') || 'unknown';\nreturn [{ json: { ...d, fingerprint } }];"
|
||||
},
|
||||
"id": "d3609d45-ba9f-41d3-a9bd-8c558a719f10",
|
||||
"name": "Build Fingerprint",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1120, -120]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "executeQuery",
|
||||
"query": "SELECT fingerprint, last_seen FROM n8n_health_dedup WHERE fingerprint = '{{ $json.fingerprint }}' AND last_seen > NOW() - INTERVAL '1 hour' LIMIT 1;",
|
||||
"options": {}
|
||||
},
|
||||
"id": "15a3dd71-bc5b-4396-9534-63518e097438",
|
||||
"name": "Lookup Dedup",
|
||||
"type": "n8n-nodes-base.postgres",
|
||||
"typeVersion": 2.5,
|
||||
"position": [1344, -120],
|
||||
"credentials": {
|
||||
"postgres": {
|
||||
"id": "n9svoXemqSZoNNUB",
|
||||
"name": "Postgres account"
|
||||
}
|
||||
},
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const alertData = $('Build Fingerprint').first().json;\nconst rows = $input.all().filter(r => r.json && r.json.fingerprint);\nconst alreadyNotified = rows.length > 0;\nreturn [{ json: { ...alertData, alreadyNotified, shouldNotify: !alreadyNotified, shouldNotifyStr: !alreadyNotified ? 'yes' : 'no' } }];"
|
||||
},
|
||||
"id": "a92590e3-7044-4ba3-9b39-f6a9457c5fbe",
|
||||
"name": "Check Already Notified",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1568, -120]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "aa5db0c3-75a7-4ea4-b29e-c12568ccf320",
|
||||
"leftValue": "={{ $json.shouldNotifyStr }}",
|
||||
"rightValue": "yes",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
}
|
||||
},
|
||||
"id": "39ac0eaf-73ce-4fe1-9c4b-d09ac920291d",
|
||||
"name": "Should Notify?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.2,
|
||||
"position": [1792, -120]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "executeQuery",
|
||||
"query": "INSERT INTO n8n_health_dedup (fingerprint, last_seen, workflow_names) VALUES ('{{ $json.fingerprint }}', NOW(), '{{ $json.failures.map(f => f.workflowName).join(\", \") }}') ON CONFLICT (fingerprint) DO UPDATE SET last_seen = NOW(), workflow_names = EXCLUDED.workflow_names;",
|
||||
"options": {}
|
||||
},
|
||||
"id": "0c3be155-0c3a-4354-b863-73a3f5bbf598",
|
||||
"name": "Upsert Dedup Record",
|
||||
"type": "n8n-nodes-base.postgres",
|
||||
"typeVersion": 2.5,
|
||||
"position": [2016, -120],
|
||||
"credentials": {
|
||||
"postgres": {
|
||||
"id": "n9svoXemqSZoNNUB",
|
||||
"name": "Postgres account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const d = $('Build Fingerprint').first().json;\nconst lines = (d.failures || []).map(f =>\n ` • ${f.workflowName}\\n ${f.errorMessage.substring(0, 120)}`\n);\nconst body = `${d.count} workflow${d.count !== 1 ? 's' : ''} failed in the past hour:\\n\\n${lines.join('\\n')}\\n\\nhttps://n8n.paccoco.com`;\nreturn [{ json: { title: `⚠️ n8n: ${d.count} Workflow Failure${d.count !== 1 ? 's' : ''}`, message: body, priority: 7 } }];"
|
||||
},
|
||||
"id": "1c6b42bf-a38e-47b6-82ac-50c43ee9e583",
|
||||
"name": "Format Notification",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [2240, -120]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"message": "={{ $json.message }}",
|
||||
"additionalFields": {
|
||||
"priority": "={{ $json.priority }}",
|
||||
"title": "={{ $json.title }}"
|
||||
},
|
||||
"options": {
|
||||
"contentType": "text/plain"
|
||||
}
|
||||
},
|
||||
"id": "09435c2f-9044-432a-8ca8-dd3ce3262c76",
|
||||
"name": "Push to Gotify",
|
||||
"type": "n8n-nodes-base.gotify",
|
||||
"typeVersion": 1,
|
||||
"position": [2464, -120],
|
||||
"credentials": {
|
||||
"gotifyApi": {
|
||||
"id": "SETUP_REQUIRED",
|
||||
"name": "n8n Health"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Every 15 Minutes": {
|
||||
"main": [[{ "node": "Fetch All Workflows", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Fetch All Workflows": {
|
||||
"main": [[{ "node": "Fetch Failed Executions", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Fetch Failed Executions": {
|
||||
"main": [[{ "node": "Filter Recent Failures", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Filter Recent Failures": {
|
||||
"main": [[{ "node": "Has Failures?", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Has Failures?": {
|
||||
"main": [
|
||||
[{ "node": "Build Fingerprint", "type": "main", "index": 0 }],
|
||||
[]
|
||||
]
|
||||
},
|
||||
"Build Fingerprint": {
|
||||
"main": [[{ "node": "Lookup Dedup", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Lookup Dedup": {
|
||||
"main": [[{ "node": "Check Already Notified", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Check Already Notified": {
|
||||
"main": [[{ "node": "Should Notify?", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Should Notify?": {
|
||||
"main": [
|
||||
[{ "node": "Upsert Dedup Record", "type": "main", "index": 0 }],
|
||||
[]
|
||||
]
|
||||
},
|
||||
"Upsert Dedup Record": {
|
||||
"main": [[{ "node": "Format Notification", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Format Notification": {
|
||||
"main": [[{ "node": "Push to Gotify", "type": "main", "index": 0 }]]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1",
|
||||
"binaryMode": "separate"
|
||||
},
|
||||
"staticData": null,
|
||||
"meta": null,
|
||||
"pinData": {},
|
||||
"tags": [
|
||||
{ "name": "monitoring" },
|
||||
{ "name": "scheduled" },
|
||||
{ "name": "n8n" }
|
||||
],
|
||||
"id": "febfda9b-0f4e-40b5-ab84-82ba9b7fcf29"
|
||||
}
|
||||
88
n8n-workflows/TESTING-STATUS.md
Executable file
88
n8n-workflows/TESTING-STATUS.md
Executable file
@@ -0,0 +1,88 @@
|
||||
# n8n Workflow Testing Status
|
||||
_Last updated: 2026-05-10_
|
||||
|
||||
---
|
||||
|
||||
## ✅ Confirmed Working
|
||||
|
||||
| # | Workflow | Version | Notes |
|
||||
|---|----------|---------|-------|
|
||||
| 01 | Grafana Alert → AI + Gotify | v1.4 | Dedup + recovery branch confirmed |
|
||||
| 08 | Class Recording RAG | v1.2 | Paperless + Gotify end-to-end confirmed |
|
||||
| 09 | Class Transcript Ingest | v1.1 | Gotify confirmed |
|
||||
| 14 | Paperless Inbox Reminder | v1.2 | Fixed: `Bearer` → `Token` auth; fixed: `hasItemsStr` bug |
|
||||
| 15 | n8n Health Monitor | v1.2 | Fixed: workflow names (was showing IDs); Postgres dedup working |
|
||||
| 16 | NFS Watchdog + Auto-Heal | v1.2 | Confirmed working |
|
||||
| 07 | Git Commit → AI Summary → Gotify | v1.0 | Confirmed working |
|
||||
|
||||
## ⏸ Retired
|
||||
|
||||
| # | Workflow | Reason |
|
||||
|---|----------|--------|
|
||||
| 04 | Whisper Transcription | Replaced — transcription now runs via Docker on Rocinante (RTX 4090) and sends directly to n8n |
|
||||
|
||||
---
|
||||
|
||||
## 🔲 Not Yet Tested
|
||||
|
||||
### 02 — RAG Pipeline v1.0
|
||||
**What it does:** Two webhooks — `POST /rag/ingest` chunks + embeds text into Qdrant; `POST /rag/query` embeds query, searches Qdrant, asks LiteLLM, returns answer.
|
||||
|
||||
**Pre-test checklist:**
|
||||
- [ ] `knowledge_base` collection exists in Qdrant (`GET http://10.5.1.6:6333/collections`)
|
||||
- [ ] `nomic-embed-text` model pulled in Ollama (`ollama list` on PD)
|
||||
- [ ] **Hardcoded LiteLLM key** in `LiteLLM Answer` node — should use `$env.LITELLM_API_KEY`
|
||||
|
||||
**Test approach:** POST `{"text": "some test content", "source": "test"}` to `/rag/ingest`, then POST `{"query": "test content question"}` to `/rag/query` and verify an answer comes back.
|
||||
|
||||
---
|
||||
|
||||
### 03 — Paperless AI Processing v1.0
|
||||
**What it does:** Paperless webhook on new document → AI classifies (type/summary/tags) → adds note to doc + sends Gotify + applies tags (parallel branches after `Parse Analysis`).
|
||||
|
||||
**Pre-test checklist:**
|
||||
- [ ] Paperless webhook configured to POST to `https://n8n.paccoco.com/webhook/paperless/new-document` on document add
|
||||
- [ ] **Hardcoded Paperless token** in `Fetch Document`, `Add Note to Document`, and inline in `Apply Suggested Tags to Paperless` code — should use `$env.PAPERLESS_API_TOKEN`
|
||||
- [ ] **Hardcoded LiteLLM key** in `AI Classify & Extract` — should use `$env.LITELLM_API_KEY`
|
||||
- [ ] Gotify credential `ajvRjvj0QldLQYmo` ("Paperless") — already wired ✓
|
||||
|
||||
**Test approach:** Upload a test document to Paperless, then manually POST `{"document_id": <id>}` to the webhook to trigger processing. Check Paperless for note + tags, check Gotify for notification.
|
||||
|
||||
---
|
||||
|
||||
### 05 — Paperless → RAG v1.0
|
||||
**What it does:** Webhook on `POST /paperless/rag-ingest` → fetches Paperless doc → chunks + embeds content → upserts to Qdrant `knowledge_base` → Gotify notification.
|
||||
|
||||
**Pre-test checklist:**
|
||||
- [ ] `knowledge_base` collection exists in Qdrant (same as 02)
|
||||
- [ ] **Hardcoded Paperless token** in `Fetch Document` — should use `$env.PAPERLESS_API_TOKEN`
|
||||
- [ ] Gotify credential `SETUP_REQUIRED` ("Paperless RAG") — needs wiring
|
||||
|
||||
**Test approach:** POST `{"document_id": <id>}` to the webhook. Verify chunks appear in Qdrant and Gotify notification fires.
|
||||
|
||||
---
|
||||
|
||||
### 06 — RSS Digest v1.0
|
||||
**What it does:** Every 6 hours (+ manual webhook + manual trigger), fetches 33 RSS feeds, parses items, deduplicates via Postgres (`rss_seen_items` table), scores by keyword relevance/urgency, AI-summarizes new items per feed, pushes per-feed digests to Gotify.
|
||||
|
||||
**Pre-test checklist:**
|
||||
- [ ] `rss_seen_items` table exists in Postgres
|
||||
- [ ] **Hardcoded LiteLLM key** in `AI Summarize Feed` node — should use `$env.LITELLM_API_KEY`
|
||||
- [ ] Postgres credential `n9svoXemqSZoNNUB` — already wired ✓
|
||||
- [ ] Gotify credential `ExUx3oqYQyhkqZcp` ("Rss Feed") — already wired ✓
|
||||
|
||||
**Test approach:** Use the "When clicking Execute workflow" manual trigger. First run will insert all items as new (expect lots of Gotify notifications). Second run should be silent (all deduped).
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Known Issues / Tech Debt
|
||||
|
||||
| Workflow | Issue | Priority |
|
||||
|----------|-------|----------|
|
||||
| 02 | Hardcoded LiteLLM API key | Medium |
|
||||
| 03 | Hardcoded LiteLLM API key + Paperless token | Medium |
|
||||
| 05 | Hardcoded Paperless token | Medium |
|
||||
| 06 | Hardcoded LiteLLM API key | Medium |
|
||||
| 07 | Hardcoded LiteLLM API key (working but tech debt) | Low |
|
||||
| 15 | `includeData: false` means error messages show as "Unknown error" | Low |
|
||||
| 09 v1.0 | Still active in n8n alongside v1.1 — should be deactivated | Low |
|
||||
Reference in New Issue
Block a user