fix: real registry token
This commit is contained in:
@@ -4,7 +4,7 @@ metadata:
|
|||||||
name: ENC[AES256_GCM,data:tuyp95XjnJ7hmselZQ==,iv:Qdb1EA8uQaWzHE1xcHvf0ht+iyHmya9AaoNsUVeVCnw=,tag:2tYxvILbN41gOA4jYqAGcQ==,type:str]
|
name: ENC[AES256_GCM,data:tuyp95XjnJ7hmselZQ==,iv:Qdb1EA8uQaWzHE1xcHvf0ht+iyHmya9AaoNsUVeVCnw=,tag:2tYxvILbN41gOA4jYqAGcQ==,type:str]
|
||||||
namespace: ENC[AES256_GCM,data:VkkF3Q==,iv:Kegn4sgJEllCpAbn9GMzUknaQIC96ke608qsBNfjgQ4=,tag:cdZv3OkvkPDjFGCKhwuJ8Q==,type:str]
|
namespace: ENC[AES256_GCM,data:VkkF3Q==,iv:Kegn4sgJEllCpAbn9GMzUknaQIC96ke608qsBNfjgQ4=,tag:cdZv3OkvkPDjFGCKhwuJ8Q==,type:str]
|
||||||
stringData:
|
stringData:
|
||||||
token: ENC[AES256_GCM,data:dWN3baAcMkqhWndpz9L5ZG8HvA==,iv:poc+qXxeloPeg0YlMBbnc74GKDg0KsF5bMIZIFQ4XM0=,tag:+2w+RfVShElHQqANpm8xdQ==,type:str]
|
token: ENC[AES256_GCM,data:sjNXGCiW5sKi45zCg+lfNCsxHUD7A0I5/AHuHNM5Vhwlrk+qoTLJxQ==,iv:arjjTTUal/W5jLrarKIMk4bYVWs/PUW5Yr+sn04mNb8=,tag:wbdluuXxrZ4ZvZB1lC1toA==,type:str]
|
||||||
sops:
|
sops:
|
||||||
kms: []
|
kms: []
|
||||||
gcp_kms: []
|
gcp_kms: []
|
||||||
@@ -20,8 +20,8 @@ sops:
|
|||||||
SWM2K2VNRDRaRmpjY3pGdVZjbEM4ZlkKcJ2Ytx9kzQaHbuw8saYIUwwojCqxiOQc
|
SWM2K2VNRDRaRmpjY3pGdVZjbEM4ZlkKcJ2Ytx9kzQaHbuw8saYIUwwojCqxiOQc
|
||||||
gIQe/FlvZ0m8Azfr5BL5NKUcWvMYSQo6si7lngRpYBDIGw/VY6g7AA==
|
gIQe/FlvZ0m8Azfr5BL5NKUcWvMYSQo6si7lngRpYBDIGw/VY6g7AA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2026-04-19T17:37:29Z"
|
lastmodified: "2026-04-19T17:52:35Z"
|
||||||
mac: ENC[AES256_GCM,data:9tj7ezR4Pmp/336Dos7O0znnGQSC5YqHDdukpstN7Ep6dJXB4rStWqgtQgJklVtcqZ8VB2xxqvzI/P945YxLGeWf9EsXpzF2q+eSD94JUPTcRIumaq0HiSWjK74w3MyZFO1SKWOlYC9xpUsrsF4elILyhnnraY+bY/DUpAdKWgA=,iv:oD2HeT+w0Zhzo0w5UzXC1yEsgXiandiMP2JWmmRD79Q=,tag:zqrtW47PDP6tFrXWvzMxVQ==,type:str]
|
mac: ENC[AES256_GCM,data:ZKmWO8CyAp48Bnv10c+kNBksF27oDLfkKEQ9JsBNcGb171Cxf7r21YZ9g5WGXGPxdFntWDZVDmevRVIRIb4KKMeb7UfukqKz9S4yNHaYzCo637XDohl7B9jyr9yN59kk67csYFW3fiptn96l5+NLimSAU6nSaaDEkB4HlCmi7xs=,iv:2hqcGeezi0T4jgqTVGzLNzBQkiig7mUAfSZ6qWVer60=,tag:bKovesylpjQy29lVOvzo4Q==,type:str]
|
||||||
pgp: []
|
pgp: []
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.9.4
|
version: 3.9.4
|
||||||
|
|||||||
@@ -0,0 +1,671 @@
|
|||||||
|
{
|
||||||
|
"name": "Jarvis — MCP Homelab Agent",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"updates": [
|
||||||
|
"message"
|
||||||
|
],
|
||||||
|
"additionalFields": {}
|
||||||
|
},
|
||||||
|
"id": "telegram-trigger",
|
||||||
|
"name": "Telegram Trigger",
|
||||||
|
"type": "n8n-nodes-base.telegramTrigger",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"position": [
|
||||||
|
200,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{ $json.message.text }}",
|
||||||
|
"operation": "startsWith",
|
||||||
|
"value2": "/jarvis"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "check-jarvis-prefix",
|
||||||
|
"name": "Nur /jarvis Nachrichten",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
420,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Extrahiere die eigentliche Frage nach '/jarvis '\nconst text = $input.first().json.message.text || '';\nconst question = text.replace(/^\\/jarvis\\s*/i, '').trim();\nconst chatId = $input.first().json.message.chat.id;\nconst userId = $input.first().json.message.from.id;\n\nif (!question) {\n throw new Error('Keine Frage nach /jarvis gefunden.');\n}\n\nreturn [{ json: { question, chatId, userId } }];"
|
||||||
|
},
|
||||||
|
"id": "extract-question",
|
||||||
|
"name": "Frage extrahieren",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
640,
|
||||||
|
220
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "http://mcp-homelab.mcp-homelab.svc.cluster.local:8000/mcp",
|
||||||
|
"sendHeaders": true,
|
||||||
|
"headerParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sendBody": true,
|
||||||
|
"bodyParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "jsonrpc",
|
||||||
|
"value": "2.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "method",
|
||||||
|
"value": "tools/list"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "mcp-tools-list",
|
||||||
|
"name": "MCP: Tools auflisten",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
860,
|
||||||
|
220
|
||||||
|
],
|
||||||
|
"notes": "Holt die aktuelle Tool-Liste vom MCP-Server. Diese wird dem LLM als Kontext mitgegeben."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Baue den System-Prompt mit Tool-Beschreibungen\nconst tools = $input.first().json.result?.tools || [];\nconst question = $('Frage extrahieren').first().json.question;\n\nconst toolDescriptions = tools.map(t => \n `- ${t.name}: ${t.description}`\n).join('\\n');\n\nconst systemPrompt = `Du bist Jarvis, ein präziser Heimserver-Assistent.\n\nDu hast Zugriff auf folgende MCP-Tools:\n${toolDescriptions}\n\nRegeln:\n- Lies immer erst relevante Daten bevor du antwortest.\n- Antworte auf Deutsch, präzise und ohne Fülltext.\n- Wenn du einen Kubernetes-Fehler siehst, erkläre was ihn verursacht und was als nächstes zu tun ist.\n- Du darfst NIEMALS etwas schreiben oder verändern — nur lesen.\n- Bei Änderungsbedarf sagst du explizit: 'Ich würde empfehlen, folgenden Git-Commit zu machen: ...'`;\n\nreturn [{ json: { systemPrompt, question, tools } }];"
|
||||||
|
},
|
||||||
|
"id": "build-system-prompt",
|
||||||
|
"name": "System-Prompt bauen",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1080,
|
||||||
|
220
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "={{ $env.OLLAMA_BASE_URL }}/api/chat",
|
||||||
|
"sendBody": true,
|
||||||
|
"contentType": "json",
|
||||||
|
"body": "={\n \"model\": \"qwen2.5-coder:14b\",\n \"messages\": [\n { \"role\": \"system\", \"content\": {{ JSON.stringify($json.systemPrompt) }} },\n { \"role\": \"user\", \"content\": {{ JSON.stringify($json.question) }} }\n ],\n \"stream\": false,\n \"tools\": {{ JSON.stringify($json.tools) }}\n}",
|
||||||
|
"options": {
|
||||||
|
"timeout": 120000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "ollama-call",
|
||||||
|
"name": "Ollama (lokal, qwen2.5-coder:14b)",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
1300,
|
||||||
|
100
|
||||||
|
],
|
||||||
|
"notes": "Primär: lokales Ollama auf dem Windows/RTX4070 Rechner. OLLAMA_BASE_URL z.B. http://192.168.2.XXX:11434"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.anthropic.com/v1/messages",
|
||||||
|
"sendHeaders": true,
|
||||||
|
"headerParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "x-api-key",
|
||||||
|
"value": "={{ $env.ANTHROPIC_API_KEY }}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "anthropic-version",
|
||||||
|
"value": "2023-06-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "content-type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sendBody": true,
|
||||||
|
"contentType": "json",
|
||||||
|
"body": "={\n \"model\": \"claude-sonnet-4-20250514\",\n \"max_tokens\": 2048,\n \"system\": {{ JSON.stringify($('Build System-Prompt').first().json.systemPrompt) }},\n \"messages\": [\n { \"role\": \"user\", \"content\": {{ JSON.stringify($('Build System-Prompt').first().json.question) }} }\n ]\n}",
|
||||||
|
"options": {
|
||||||
|
"timeout": 60000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "anthropic-fallback",
|
||||||
|
"name": "Anthropic API (Fallback)",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
1300,
|
||||||
|
340
|
||||||
|
],
|
||||||
|
"notes": "Fallback wenn Ollama nicht erreichbar oder Frage zu komplex. ANTHROPIC_API_KEY als n8n-Variable setzen."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"number": [
|
||||||
|
{
|
||||||
|
"value1": "={{ $('Ollama (lokal, qwen2.5:14b)').first().json.error ? 1 : 0 }}",
|
||||||
|
"operation": "equal",
|
||||||
|
"value2": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "ollama-failed",
|
||||||
|
"name": "Ollama fehlgeschlagen?",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
1520,
|
||||||
|
220
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Normalisiere Antwort aus Ollama oder Anthropic\nlet answer = '';\n\ntry {\n // Ollama-Format\n const ollamaData = $('Ollama (lokal, qwen2.5:14b)').first().json;\n if (ollamaData?.message?.content) {\n answer = ollamaData.message.content;\n }\n} catch(e) {}\n\nif (!answer) {\n try {\n // Anthropic-Format\n const anthropicData = $('Anthropic API (Fallback)').first().json;\n if (anthropicData?.content?.[0]?.text) {\n answer = anthropicData.content[0].text;\n }\n } catch(e) {}\n}\n\nif (!answer) answer = 'Fehler: Keine Antwort von LLM erhalten.';\n\n// Kuerze auf Telegram-Limit (4096 Zeichen)\nif (answer.length > 4000) {\n answer = answer.substring(0, 3990) + '\\n\\n[... gekuerzt]';\n}\n\nconst chatId = $('Frage extrahieren').first().json.chatId;\nreturn [{ json: { answer, chatId } }];"
|
||||||
|
},
|
||||||
|
"id": "normalize-answer",
|
||||||
|
"name": "Antwort normalisieren",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1740,
|
||||||
|
220
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"chatId": "={{ $json.chatId }}",
|
||||||
|
"text": "={{ $json.answer }}",
|
||||||
|
"additionalFields": {
|
||||||
|
"parse_mode": "Markdown"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "telegram-reply",
|
||||||
|
"name": "Telegram Antwort senden",
|
||||||
|
"type": "n8n-nodes-base.telegram",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"position": [
|
||||||
|
1960,
|
||||||
|
220
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"path": "prometheus-alert",
|
||||||
|
"responseMode": "onReceived",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "prometheus-webhook",
|
||||||
|
"name": "Prometheus Alert Webhook",
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
200,
|
||||||
|
600
|
||||||
|
],
|
||||||
|
"webhookId": "prometheus-alert-receiver",
|
||||||
|
"notes": "Webhook-URL in Alertmanager konfigurieren: http://n8n.n8n.svc.cluster.local:5678/webhook/prometheus-alert"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Alertmanager sendet ein Array von Alerts\nconst body = $input.first().json.body || $input.first().json;\nconst alerts = body.alerts || [body];\n\nreturn alerts.map(alert => {\n const severity = alert.labels?.severity || 'unknown';\n const alertname = alert.labels?.alertname || 'UnknownAlert';\n const namespace = alert.labels?.namespace || 'unknown';\n const pod = alert.labels?.pod || alert.labels?.container || '';\n const message = alert.annotations?.message || alert.annotations?.summary || '';\n const status = alert.status || 'firing';\n\n // Severity-Level numerisch fuer spaeteren Vergleich\n const levels = { 'critical': 4, 'high': 3, 'warning': 2, 'info': 1 };\n const level = levels[severity.toLowerCase()] || 2;\n\n return { json: { severity, level, alertname, namespace, pod, message, status } };\n});"
|
||||||
|
},
|
||||||
|
"id": "parse-alert",
|
||||||
|
"name": "Alert parsen",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
420,
|
||||||
|
600
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"number": [
|
||||||
|
{
|
||||||
|
"value1": "={{ $json.level }}",
|
||||||
|
"operation": "largerEqual",
|
||||||
|
"value2": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "is-high-or-critical",
|
||||||
|
"name": "High oder Critical?",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
640,
|
||||||
|
600
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "http://mcp-homelab.mcp-homelab.svc.cluster.local:8000/mcp",
|
||||||
|
"sendHeaders": true,
|
||||||
|
"headerParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sendBody": true,
|
||||||
|
"contentType": "json",
|
||||||
|
"body": "={\n \"jsonrpc\": \"2.0\",\n \"method\": \"tools/call\",\n \"params\": {\n \"name\": \"kubectl_events\",\n \"arguments\": { \"namespace\": {{ JSON.stringify($json.namespace) }} }\n },\n \"id\": \"alert-diag-1\"\n}",
|
||||||
|
"options": {
|
||||||
|
"timeout": 15000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "mcp-get-events",
|
||||||
|
"name": "MCP: Events holen",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
860,
|
||||||
|
500
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "http://mcp-homelab.mcp-homelab.svc.cluster.local:8000/mcp",
|
||||||
|
"sendHeaders": true,
|
||||||
|
"headerParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sendBody": true,
|
||||||
|
"contentType": "json",
|
||||||
|
"body": "={\n \"jsonrpc\": \"2.0\",\n \"method\": \"tools/call\",\n \"params\": {\n \"name\": \"flux_status\",\n \"arguments\": {}\n },\n \"id\": \"alert-diag-2\"\n}",
|
||||||
|
"options": {
|
||||||
|
"timeout": 15000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "mcp-flux-status",
|
||||||
|
"name": "MCP: Flux-Status holen",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
860,
|
||||||
|
620
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.anthropic.com/v1/messages",
|
||||||
|
"sendHeaders": true,
|
||||||
|
"headerParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "x-api-key",
|
||||||
|
"value": "={{ $env.ANTHROPIC_API_KEY }}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "anthropic-version",
|
||||||
|
"value": "2023-06-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "content-type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sendBody": true,
|
||||||
|
"contentType": "json",
|
||||||
|
"body": "={\n \"model\": \"claude-sonnet-4-20250514\",\n \"max_tokens\": 1024,\n \"system\": \"Du bist ein Kubernetes-Experte. Analysiere den Alert und die bereitgestellten Cluster-Daten. Antworte auf Deutsch. Gib aus: 1) Wahrscheinliche Ursache, 2) Empfohlene Untersuchungsschritte, 3) Falls ein GitOps-Fix nötig ist: genaue YAML-Änderung die als PR eingereicht werden sollte.\",\n \"messages\": [{\n \"role\": \"user\",\n \"content\": {{ JSON.stringify(\n 'Alert: ' + $('Alert parsen').first().json.alertname +\n ' (Severity: ' + $('Alert parsen').first().json.severity + ')\\n' +\n 'Namespace: ' + $('Alert parsen').first().json.namespace + '\\n' +\n 'Message: ' + $('Alert parsen').first().json.message + '\\n\\n' +\n 'Kubernetes Events:\\n' + JSON.stringify($('MCP: Events holen').first().json.result, null, 2) + '\\n\\n' +\n 'Flux Status:\\n' + JSON.stringify($('MCP: Flux-Status holen').first().json.result, null, 2)\n ) }}\n }]\n}",
|
||||||
|
"options": {
|
||||||
|
"timeout": 60000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "analyze-alert-llm",
|
||||||
|
"name": "LLM: Alert analysieren",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
1080,
|
||||||
|
560
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const alert = $('Alert parsen').first().json;\nconst analysis = $('LLM: Alert analysieren').first().json;\nconst analysisText = analysis?.content?.[0]?.text || 'Keine LLM-Analyse verfügbar.';\n\n// Severity-Emoji\nconst emoji = { critical: '🔴', high: '🟠', warning: '🟡', info: '🔵' }[alert.severity] || '⚪';\n\nconst msg = `${emoji} *${alert.alertname}* (${alert.severity.toUpperCase()})\n` +\n `Namespace: \\`${alert.namespace}\\`\\n` +\n (alert.pod ? `Pod: \\`${alert.pod}\\`\\n` : '') +\n `\\n${alert.message}\\n\\n` +\n `*Analyse:*\\n${analysisText.substring(0, 2500)}`;\n\nreturn [{ json: { message: msg, severity: alert.severity, level: alert.level } }];"
|
||||||
|
},
|
||||||
|
"id": "format-alert-message",
|
||||||
|
"name": "Alert-Nachricht formatieren",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1300,
|
||||||
|
560
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"chatId": "={{ $env.TELEGRAM_CHAT_ID }}",
|
||||||
|
"text": "={{ $json.message }}",
|
||||||
|
"additionalFields": {
|
||||||
|
"parse_mode": "Markdown"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "telegram-alert",
|
||||||
|
"name": "Telegram Alert senden",
|
||||||
|
"type": "n8n-nodes-base.telegram",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"position": [
|
||||||
|
1520,
|
||||||
|
560
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Warning/Info: einfacher Alert ohne LLM-Analyse\nconst alert = $json;\nconst emoji = { warning: '🟡', info: '🔵' }[alert.severity] || '⚪';\n\nconst msg = `${emoji} *${alert.alertname}* (${alert.severity})\n` +\n `Namespace: \\`${alert.namespace}\\`\\n` +\n alert.message;\n\nreturn [{ json: { message: msg } }];"
|
||||||
|
},
|
||||||
|
"id": "format-low-alert",
|
||||||
|
"name": "Einfacher Alert formatieren",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
860,
|
||||||
|
720
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"chatId": "={{ $env.TELEGRAM_CHAT_ID }}",
|
||||||
|
"text": "={{ $json.message }}",
|
||||||
|
"additionalFields": {
|
||||||
|
"parse_mode": "Markdown"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "telegram-low-alert",
|
||||||
|
"name": "Telegram Low-Alert senden",
|
||||||
|
"type": "n8n-nodes-base.telegram",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"position": [
|
||||||
|
1080,
|
||||||
|
720
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const allowed = ($env.TELEGRAM_ALLOWED_IDS || '').split(',').map(s => s.trim()).filter(Boolean);\nconst chatId = String($input.first().json.message?.chat?.id || '');\nconst userId = String($input.first().json.message?.from?.id || '');\nif (allowed.length > 0 && !allowed.includes(chatId) && !allowed.includes(userId)) {\n return [];\n}\nreturn $input.all();"
|
||||||
|
},
|
||||||
|
"id": "chat-id-whitelist",
|
||||||
|
"name": "Chat-ID Whitelist",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
310,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Telegram Trigger": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Chat-ID Whitelist",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Nur /jarvis Nachrichten": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Frage extrahieren",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Frage extrahieren": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "MCP: Tools auflisten",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"MCP: Tools auflisten": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "System-Prompt bauen",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"System-Prompt bauen": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Ollama (lokal, qwen2.5:14b)",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Ollama (lokal, qwen2.5:14b)": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Ollama fehlgeschlagen?",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Ollama fehlgeschlagen?": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Anthropic API (Fallback)",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Antwort normalisieren",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Anthropic API (Fallback)": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Antwort normalisieren",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Antwort normalisieren": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Telegram Antwort senden",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Prometheus Alert Webhook": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Alert parsen",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Alert parsen": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "High oder Critical?",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"High oder Critical?": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "MCP: Events holen",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "MCP: Flux-Status holen",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Einfacher Alert formatieren",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"MCP: Events holen": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "LLM: Alert analysieren",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"MCP: Flux-Status holen": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "LLM: Alert analysieren",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"LLM: Alert analysieren": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Alert-Nachricht formatieren",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Alert-Nachricht formatieren": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Telegram Alert senden",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Einfacher Alert formatieren": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Telegram Low-Alert senden",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Chat-ID Whitelist": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Nur /jarvis Nachrichten",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1",
|
||||||
|
"saveManualExecutions": true,
|
||||||
|
"saveExecutionProgress": true
|
||||||
|
},
|
||||||
|
"staticData": null,
|
||||||
|
"tags": [
|
||||||
|
"homelab",
|
||||||
|
"jarvis",
|
||||||
|
"mcp"
|
||||||
|
],
|
||||||
|
"pinData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Ergaenze deine bestehende Alertmanager-Konfiguration um diesen Receiver.
|
||||||
|
# Einzufuegen in: clusters/homelab/infrastructure/kube-prometheus-stack/
|
||||||
|
# (wo du Alertmanager konfigurierst — z.B. values.yaml oder alertmanager-config.yaml)
|
||||||
|
|
||||||
|
# alertmanager.yaml Receiver-Abschnitt:
|
||||||
|
receivers:
|
||||||
|
- name: "jarvis-webhook"
|
||||||
|
webhook_configs:
|
||||||
|
- url: "http://n8n.n8n.svc.cluster.local:5678/webhook/prometheus-alert"
|
||||||
|
send_resolved: true
|
||||||
|
max_alerts: 10
|
||||||
|
|
||||||
|
route:
|
||||||
|
receiver: "jarvis-webhook"
|
||||||
|
group_by: ["alertname", "namespace"]
|
||||||
|
group_wait: 30s
|
||||||
|
group_interval: 5m
|
||||||
|
repeat_interval: 4h
|
||||||
|
|
||||||
|
routes:
|
||||||
|
# Critical: sofort, kein Grouping-Delay
|
||||||
|
- matchers:
|
||||||
|
- severity="critical"
|
||||||
|
receiver: "jarvis-webhook"
|
||||||
|
group_wait: 0s
|
||||||
|
repeat_interval: 1h
|
||||||
|
|
||||||
|
# High: 30 Sekunden warten, dann senden
|
||||||
|
- matchers:
|
||||||
|
- severity="high"
|
||||||
|
receiver: "jarvis-webhook"
|
||||||
|
group_wait: 30s
|
||||||
|
repeat_interval: 2h
|
||||||
|
|
||||||
|
# Warning/Info: normale Rate, nicht zu oft
|
||||||
|
- matchers:
|
||||||
|
- severity=~"warning|info"
|
||||||
|
receiver: "jarvis-webhook"
|
||||||
|
group_wait: 2m
|
||||||
|
repeat_interval: 6h
|
||||||
Reference in New Issue
Block a user