{ "name": "Home Monitor", "nodes": [ { "parameters": { "fromEmail": "", "toEmail": "", "subject": "=\ud83c\udf10 [{{ $now.format('MMM dd') }}] Lab Security Brief: {{ $('Zabbix Request').item.json.result.length > 0 ? \"All Systems Operational\" : \"Check Monitoring\" }}", "html": "=
\n \n
\n

\n \ud83c\udf10 Full Network Health Report\n

\n

\n Generated on: {{ $now.format('yyyy-MM-dd HH:mm:ss') }} | \u26a0\ufe0f Status: UP\n

\n\n
\n {{ $json.text }}\n
\n
\n\n
\n

\n ISP Gateway \u2022 DNS \u2022 Proxmox Lab \u2022 Managed by Jeric\n

\n
\n
", "options": {} }, "type": "n8n-nodes-base.emailSend", "typeVersion": 2.1, "position": [ 688, -80 ], "id": "", "name": "Send an Email", "webhookId": "", "credentials": { "smtp": { "id": "", "name": "SMTP account" } } }, { "parameters": { "promptType": "define", "text": "=Role: Senior Infrastructure & Cybersecurity Architect\nTask: Finalize the RAW HTML report by inserting the data and writing the Analysis.\n\n\u26a0\ufe0f CRITICAL MODEL RULES:\n- Output ONLY raw HTML. Do not wrap response in markdown blocks or ```html.\n\n
\n\n

\ud83c\udf10 SYSTEM STATUS OVERVIEW

\n

\n Status: ONLINE \u2705
\n Report Generated: {{ $now.format('yyyy-MM-dd HH:mm:ss') }}
\n MikroTik Uptime: {{ $('MikroTik Request').item.json.uptime }}
\n System Load: {{ $('MikroTik Request').item.json['cpu-load'] }}% CPU\n

\n\n

\ud83d\udda5\ufe0f Hosts Monitored

\n\n\n\n\n\n\n\n\n\n{{ $('Zabbix Request').item.json.result.slice(0, 10).map(h => ``).join('') }}\n\n
Host IDHost NameStatus
${h.hostid}${h.host || h.name}${h.status == 0 ? 'UP' : 'DOWN'}
\n\n

\ud83d\udee1\ufe0f Suricata IDPS Alerts

\n\n\n\n\n\n\n\n\n{{ $('Fix Suricata Logs').all().slice(0, 5).map(i => ``).join('') }}\n\n
AlertSource IP
${i.json.alert || 'No Alert'}${i.json.src_ip || 'N/A'}
\n\n

\ud83d\udcca Wazuh SCA Compliance

\n\n\n\n\n\n\n\n\n{{ $('Fix SCA Table').all().slice(0, 5).map(a => ``).join('') }}\n\n
HostScore
${a.json.host || 'Agent'}${a.json.score || 0}%
\n\n

\ud83e\udde0 ARCHITECT'S ANALYSIS

\n[AI INSTRUCTION: Write a brief 2-sentence infrastructure security analysis based on the tables above.]\n\n
", "batching": {} }, "type": "@n8n/n8n-nodes-langchain.chainLlm", "typeVersion": 1.9, "position": [ 384, -80 ], "id": "", "name": "Basic LLM Chain" }, { "parameters": { "model": "qwen2.5:3b", "options": { "numThread": 2 } }, "type": "@n8n/n8n-nodes-langchain.lmOllama", "typeVersion": 1, "position": [ 384, 192 ], "id": "", "name": "Ollama Model", "retryOnFail": true, "waitBetweenTries": 5000, "credentials": { "ollamaApi": { "id": "", "name": "Ollama account" } } }, { "parameters": { "curlImport": "", "httpVariantWarning": "", "method": "POST", "url": "http:///zabbix/api_jsonrpc.php", "": "", "authentication": "predefinedCredentialType", "nodeCredentialType": "zabbixApi", "provideSslCertificates": "", "sendQuery": false, "sendHeaders": false, "sendBody": true, "contentType": "json", "specifyBody": "json", "jsonBody": "{\n\n \"jsonrpc\": \"2.0\",\n\n \"method\": \"host.get\",\n\n \"params\": {\n\n \"output\": [\"host\", \"status\"],\n\n \"selectInterfaces\": [\"ip\"],\n\n \"filter\": {\n\n \"status\": \"0\" \n\n },\n\n \"monitored_hosts\": true,\n\n \"with_monitored_items\": true\n\n },\n\n \"id\": 1\n\n}", "options": {}, "infoMessage": "" }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ -720, 48 ], "id": "", "name": "Zabbix Request", "extendsCredential": "zabbixApi", "retryOnFail": true, "credentials": { "zabbixApi": { "id": "", "name": "Zabbix account" } } }, { "parameters": { "url": "http:///rest/system/resource", "authentication": "genericCredentialType", "genericAuthType": "httpBasicAuth", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Accept", "value": "application/json" }, { "name": "Content-Type", "value": "application/json" } ] }, "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ -592, -304 ], "id": "", "name": "MikroTik Request", "credentials": { "httpBasicAuth": { "id": "", "name": "Unnamed credential" } } }, { "parameters": { "resource": "file", "operation": "edit", "owner": { "__rl": true, "value": "enricagra", "mode": "name" }, "repository": { "__rl": true, "value": "homelab-status", "mode": "list", "cachedResultName": "homelab-status", "cachedResultUrl": "https://github.com/user/repo/contents/heartbeat.txt" }, "filePath": "heartbeat.txt", "fileContent": "=--- [ HOME LAB MONITOR ] ---\nStatus: ONLINE\nLast Heartbeat: {{ $now.format('yyyy-MM-dd HH:mm:ss') }}\n\n--- Home Router HEALTH ---\nUptime: {{ $json.uptime }}\nCPU Load: {{ $json['cpu-load'] }}\nFree RAM: {{ $json['free-memory'] }}\nFirmware: {{ $json['architecture-name'] }}\n\n--- NETWORK STATUS ---\nGateway: REACHABLE\nISP Connection: ACTIVE \n-----------------------------------------------\nGenerated by: Jeric", "commitMessage": "Connection Status Update" }, "type": "n8n-nodes-base.github", "typeVersion": 1.1, "position": [ -336, -304 ], "id": "", "name": "GitHub Notification", "webhookId": "", "notesInFlow": false, "credentials": { "githubApi": { "id": "", "name": "GitHub account" } } }, { "parameters": { "rule": { "interval": [ { "field": "weeks", "triggerAtDay": [ 0, 1, 2, 3, 4, 5, 6 ], "triggerAtHour": 7 } ] } }, "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.3, "position": [ -1136, -144 ], "id": "", "name": "Schedule Trigger" }, { "parameters": { "mode": "combine", "combineBy": "combineByPosition", "options": {} }, "type": "n8n-nodes-base.merge", "typeVersion": 3.2, "position": [ 192, -80 ], "id": "", "name": "Merge" }, { "parameters": { "curlImport": "", "httpVariantWarning": "", "method": "POST", "url": "http:///zabbix/api_jsonrpc.php", "": "", "authentication": "predefinedCredentialType", "nodeCredentialType": "zabbixApi", "provideSslCertificates": "", "sendQuery": false, "sendHeaders": false, "sendBody": true, "contentType": "json", "specifyBody": "json", "jsonBody": "={\n \"jsonrpc\": \"2.0\",\n \"method\": \"history.get\",\n \"params\": {\n \"output\": [\"value\", \"clock\"],\n \"history\": 2,\n \"itemids\": [\"70652\"],\n \"search\": {\n \"value\": \"Priority: 3\"\n },\n \"sortfield\": \"clock\",\n \"sortorder\": \"DESC\",\n \"limit\": 3\n },\n \"id\": 1\n}", "options": {}, "infoMessage": "" }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ -544, 48 ], "id": "", "name": "Suricata fast logs", "extendsCredential": "zabbixApi", "retryOnFail": true, "credentials": { "zabbixApi": { "id": "", "name": "Zabbix account" } } }, { "parameters": { "curlImport": "", "httpVariantWarning": "", "method": "POST", "url": "http:///zabbix/api_jsonrpc.php", "": "", "authentication": "predefinedCredentialType", "nodeCredentialType": "zabbixApi", "provideSslCertificates": "", "sendQuery": false, "sendHeaders": false, "sendBody": true, "contentType": "json", "specifyBody": "json", "jsonBody": "={\n \"jsonrpc\": \"2.0\",\n \"method\": \"history.get\",\n \"params\": {\n \"output\": \"extend\",\n \"history\": 1, \n \"itemids\": [\"70670\"], \n \"sortfield\": \"clock\",\n \"sortorder\": \"DESC\",\n \"limit\": 1\n },\n \"id\": 1\n}", "options": {}, "infoMessage": "" }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ -208, 48 ], "id": "", "name": "SCA Summary", "extendsCredential": "zabbixApi", "credentials": { "zabbixApi": { "id": "", "name": "Zabbix account" } } }, { "parameters": { "jsCode": "const results = $json.result;\n\nreturn results.map(item => {\n const raw = item.value;\n \n // 1. Timestamp Splitting (Date and Time)\n const fullTimestamp = raw.split(' ')[0]; // 04/14/2026-18:36:22.066470\n const [date, fullTime] = fullTimestamp.split('-');\n const timeOnly = fullTime.split('.')[0]; // Para tanggalin yung milliseconds (0.066470)\n\n // 2. Alert Description\n const alertMatch = raw.match(/\\]\\s+([^*\\[]+)\\s+\\[\\*\\*/);\n const cleanAlert = alertMatch ? alertMatch[1].trim() : \"Protocol Event\";\n\n // 3. Classification & Priority\n const classMatch = raw.match(/\\[Classification: ([^\\]]+)\\]/);\n const priorityMatch = raw.match(/\\[Priority: (\\d+)\\]/);\n\n // 4. IP at Ports (Using Regex)\n const ipPattern = /(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d+)/g;\n const ips = [...raw.matchAll(ipPattern)];\n\n const src_ip = ips[0] ? ips[0][1] : \"Internal\";\n const src_port = ips[0] ? ips[0][2] : \"N/A\";\n const dst_ip = ips[1] ? ips[1][1] : \"N/A\";\n const dst_port = ips[1] ? ips[1][2] : \"N/A\";\n\n return {\n json: {\n date: date,\n time: timeOnly,\n alert: cleanAlert,\n category: classMatch ? classMatch[1] : \"General\",\n priority: priorityMatch ? priorityMatch[1] : \"3\",\n source_ip: src_ip,\n source_port: src_port,\n dest_ip: dst_ip,\n dest_port: dst_port\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -368, 48 ], "id": "", "name": "Fix Suricata Logs" }, { "parameters": { "jsCode": "const raw = $json.result[0].value;\n\n// 1. Hiwalayin ang \"LOW_SCORE\" na line at \"FAILED\" na line\nconst lines = raw.split('\\n');\nconst scoreLine = lines[0].replace('LOW_SCORE: ', '');\nconst failedLine = lines[1].replace('FAILED: ', '');\n\n// 2. Himayin ang Scores per machine\nconst scores = scoreLine.split(' | ').map(item => {\n const parts = item.split('(');\n return {\n hostname: parts[0].trim(),\n score: parts[1].replace('%)', '') + \"%\"\n };\n});\n\n// 3. Himayin ang Failed counts per machine\nconst failed = failedLine.split(' | ').map(item => {\n const parts = item.split('(');\n return {\n hostname: parts[0].trim(),\n failed_checks: parts[1].replace(')', '')\n };\n});\n\n// 4. Pagsamahin sila sa isang malinis na listahan\nreturn scores.map(s => {\n const f = failed.find(item => item.hostname === s.hostname) || { failed_checks: \"0\" };\n return {\n json: {\n host: s.hostname,\n score: s.score,\n failed: f.failed_checks,\n status: parseInt(s.score) < 40 ? \"\u26a0\ufe0f CRITICAL\" : \"OK\"\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -48, 48 ], "id": "", "name": "Fix SCA Table" } ], "pinData": {}, "connections": { "Ollama Model": { "ai_languageModel": [ [ { "node": "Basic LLM Chain", "type": "ai_languageModel", "index": 0 } ] ] }, "Basic LLM Chain": { "main": [ [ { "node": "Send an Email", "type": "main", "index": 0 } ] ] }, "Zabbix Request": { "main": [ [ { "node": "Suricata fast logs", "type": "main", "index": 0 } ] ] }, "MikroTik Request": { "main": [ [ { "node": "GitHub Notification", "type": "main", "index": 0 } ] ] }, "GitHub Notification": { "main": [ [ { "node": "Merge", "type": "main", "index": 0 } ] ] }, "Schedule Trigger": { "main": [ [ { "node": "Zabbix Request", "type": "main", "index": 0 }, { "node": "MikroTik Request", "type": "main", "index": 0 } ] ] }, "Merge": { "main": [ [ { "node": "Basic LLM Chain", "type": "main", "index": 0 } ] ] }, "Suricata fast logs": { "main": [ [ { "node": "Fix Suricata Logs", "type": "main", "index": 0 } ] ] }, "SCA Summary": { "main": [ [ { "node": "Fix SCA Table", "type": "main", "index": 0 } ] ] }, "Fix Suricata Logs": { "main": [ [ { "node": "SCA Summary", "type": "main", "index": 0 } ] ] }, "Fix SCA Table": { "main": [ [ { "node": "Merge", "type": "main", "index": 1 } ] ] } }, "active": false, "settings": { "executionOrder": "v1", "binaryMode": "separate", "availableInMCP": false, "timeSavedMode": "fixed", "timezone": "Asia/Manila", "callerPolicy": "workflowsFromSameOwner" }, "versionId": "", "meta": { "templateCredsSetupCompleted": true, "instanceId": "" }, "id": "", "tags": [] }