{
  "name": "Daily System Log Summary",
  "nodes": [
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -288,
        -336
      ],
      "id": "8453f0f1-6a5a-4780-8827-f99e5b920526",
      "name": "When clicking ‘Execute workflow’"
    },
    {
      "parameters": {
        "command": "=( echo \"===== System Errors and Warnings =====\"; journalctl --system -p 4 -n 300 --no-pager --output=short-iso | grep -E 'error|fail|warn|critical' || echo 'No system issues found'; echo \"\"; echo \"===== Failed SSH Login Attempts =====\"; grep \"Failed password\" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head -10 || echo 'No failed SSH attempts found'; echo \"\"; echo \"===== Successful SSH Logins =====\"; grep \"Accepted\" /var/log/auth.log | tail -10 || echo 'No successful SSH logins found'; echo \"\"; echo \"===== Kernel Warnings =====\"; dmesg | grep -iE \"error|warn|fail\" | tail -10 || echo 'No kernel warnings found'; echo \"\"; echo \"===== Network Connection Attempts =====\"; ss -tuna | grep ESTAB | head -10 || echo 'No active network connections found'; ) 2>&1"
      },
      "type": "n8n-nodes-base.ssh",
      "typeVersion": 1,
      "position": [
        -16,
        -336
      ],
      "id": "4c78467b-bed0-4f24-98da-2d7db63335e0",
      "name": "Execute a command",
      "credentials": {
        "sshPassword": {
          "id": "4eVZVwyLdUXhfKPX",
          "name": "SSH Media Server"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const now = new Date().toLocaleString(\"en-US\", { timeZone: \"America/Detroit\" });\nconst raw = $json.stdout || \"No logs found\";\n\n// --- Parse Sections ---\nconst cleaned = raw.replace(/\\r/g, \"\").replace(/\\n{2,}/g, \"\\n\");\nconst regex = /={3,}\\s*(.*?)\\s*={3,}\\n?([\\s\\S]*?)(?=(?:={3,}|$))/g;\nconst data = {};\nlet match;\nwhile ((match = regex.exec(cleaned)) !== null) {\n  const title = match[1].trim();\n  const content = match[2].trim().split(\"\\n\").filter(l => l.trim());\n  const filtered = content.filter(l => !/no .*found/i.test(l));\n  data[title] = filtered.length > 0 ? filtered : [];\n}\n\n// --- Render Section ---\nfunction renderSection(title, lines, color) {\n  const hasData = lines && lines.length > 0;\n\n  // Network table\n  if (title === \"Network Connection Attempts\") {\n    if (!hasData)\n      return `<div class=\"section wide\"><h2>${title}</h2><div class=\"entry empty\">No data found</div></div>`;\n    const headers = [\"NetID\", \"State\", \"Recv-Q\", \"Send-Q\", \"Local Address:Port\", \"Peer Address:Port\"];\n    const rows = lines.map(line => {\n      const cols = line.trim().split(/\\s+/).slice(0, 6);\n      return `<tr>${cols.map((c, i) => `<td data-label=\"${headers[i] || \"\"}\">${c}</td>`).join(\"\")}</tr>`;\n    }).join(\"\");\n    return `\n      <div class=\"section wide\">\n        <h2>${title} (${lines.length})</h2>\n        <div class=\"table-wrapper\">\n          <table>\n            <thead><tr>${headers.map(h => `<th>${h}</th>`).join(\"\")}</tr></thead>\n            <tbody>${rows}</tbody>\n          </table>\n        </div>\n      </div>`;\n  }\n\n  if (!hasData)\n    return `<div class=\"section\"><h2>${title} (0)</h2><div class=\"entry empty\">No data found</div></div>`;\n\n  return `\n    <div class=\"section\">\n      <h2>${title} (${lines.length})</h2>\n      ${lines.map((l, i) => {\n        const safeText = l.replace(/</g, \"&lt;\");\n        const id = `${title.replace(/\\s+/g, \"_\")}_${i}`;\n        const copyBtn =\n          title === \"System Errors and Warnings\"\n            ? `<button class=\"copy-btn\" onclick=\"copyText('${id}')\">⧉</button>`\n            : \"\";\n        return `<div class=\"entry\" id=\"${id}\" style=\"border-left-color:${color}\">${safeText}${copyBtn}</div>`;\n      }).join(\"\")}\n    </div>`;\n}\n\n// --- Colors ---\nconst colors = {\n  \"System Errors and Warnings\": \"#ef4444\",\n  \"Failed SSH Login Attempts\": \"#3b82f6\",\n  \"Successful SSH Logins\": \"#22c55e\",\n  \"Kernel Warnings\": \"#f59e0b\",\n  \"Network Connection Attempts\": \"#8b5cf6\",\n};\n\n// --- Count Summary ---\nconst counts = {\n  errors: data[\"System Errors and Warnings\"]?.length || 0,\n  failedSSH: data[\"Failed SSH Login Attempts\"]?.length || 0,\n  successSSH: data[\"Successful SSH Logins\"]?.length || 0,\n  kernel: data[\"Kernel Warnings\"]?.length || 0,\n  network: data[\"Network Connection Attempts\"]?.length || 0,\n};\n\n// --- HTML ---\nlet html = `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<title>System Log Dashboard</title>\n<link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Poppins:wght@500;600&display=swap\" rel=\"stylesheet\">\n<style>\n  body {\n    background: #0d0f13;\n    color: #e5e7eb;\n    font-family: 'Inter', sans-serif;\n    padding: 1.5rem;\n  }\n  h1 {\n    font-family: 'Poppins', sans-serif;\n    font-weight: 600;\n    font-size: 1.7rem;\n    margin-bottom: 1rem;\n    color: #fff;\n  }\n  p {\n    color: #9ca3af;\n    margin-bottom: 1.5rem;\n  }\n  .summary {\n    display: flex;\n    gap: 1rem;\n    flex-wrap: wrap;\n    margin-bottom: 1.5rem;\n  }\n  .card {\n    flex: 1;\n    min-width: 140px;\n    background: rgba(255,255,255,0.04);\n    border: 1px solid rgba(255,255,255,0.08);\n    border-radius: 10px;\n    padding: 1rem;\n    text-align: center;\n    box-shadow: 0 2px 6px rgba(0,0,0,0.3);\n    transition: all 0.2s ease;\n  }\n  .card:hover {\n    background: rgba(255,255,255,0.06);\n  }\n  .count {\n    font-size: 1.4rem;\n    font-weight: 600;\n    margin-bottom: 0.3rem;\n  }\n  .label {\n    font-size: 0.85rem;\n    color: #9ca3af;\n  }\n  .errors .count { color: #ef4444; }\n  .fails .count { color: #3b82f6; }\n  .success .count { color: #22c55e; }\n  .kernel .count { color: #f59e0b; }\n  .network .count { color: #8b5cf6; }\n\n  .grid {\n    display: grid;\n    grid-template-columns: repeat(2, 1fr);\n    gap: 1rem;\n  }\n  .wide { grid-column: 1 / -1; }\n\n  .section {\n    background: rgba(255,255,255,0.04);\n    border-radius: 8px;\n    padding: 1rem;\n    border: 1px solid rgba(255,255,255,0.07);\n  }\n  h2 {\n    font-family: 'Poppins', sans-serif;\n    font-size: 1rem;\n    font-weight: 500;\n    color: #cbd5e1;\n    border-bottom: 1px solid rgba(255,255,255,0.05);\n    margin: 0 0 0.6rem;\n    padding-bottom: 0.3rem;\n  }\n  .entry {\n    background: rgba(0,0,0,0.25);\n    margin: 0.25rem 0;\n    padding: 0.55rem 0.6rem;\n    border-radius: 6px;\n    font-size: 0.85rem;\n    border-left: 3px solid;\n    white-space: pre-wrap;\n    position: relative;\n  }\n  .entry.empty {\n    border-left-color: #4b5563;\n    color: #9ca3af;\n    font-style: italic;\n  }\n  .copy-btn {\n    position: absolute;\n    top: 6px;\n    right: 8px;\n    background: none;\n    border: none;\n    color: #60a5fa;\n    cursor: pointer;\n    font-size: 0.8rem;\n    transition: 0.2s;\n  }\n  .copy-btn:hover { color: #93c5fd; }\n  .table-wrapper { overflow-x: auto; margin-top: 0.4rem; }\n  table {\n    width: 100%;\n    border-collapse: collapse;\n    font-size: 0.85rem;\n  }\n  th, td {\n    padding: 0.3rem 0.5rem;\n    border-bottom: 1px solid rgba(255,255,255,0.05);\n    text-align: left;\n  }\n  th {\n    color: #cbd5e1;\n    background: rgba(255,255,255,0.05);\n    font-family: 'Poppins', sans-serif;\n  }\n  tr:hover td { background-color: rgba(255,255,255,0.03); }\n  footer {\n    color: #9ca3af;\n    font-size: 0.75rem;\n    text-align: center;\n    margin-top: 1.5rem;\n    border-top: 1px solid rgba(255,255,255,0.05);\n    padding-top: 0.5rem;\n  }\n  @media (max-width: 900px) {\n    .grid { grid-template-columns: 1fr; }\n  }\n</style>\n<script>\nfunction copyText(id) {\n  const text = document.getElementById(id).innerText.replace('⧉','').trim();\n  navigator.clipboard.writeText(text).then(() => {\n    const btn = document.querySelector('#' + id + ' .copy-btn');\n    if (btn) {\n      btn.textContent = '✔';\n      setTimeout(() => (btn.textContent = '⧉'), 1500);\n    }\n  });\n}\n</script>\n</head>\n<body>\n<h1>System Log Dashboard</h1>\n<p>Generated: ${now}</p>\n\n<div class=\"summary\">\n  <div class=\"card errors\"><div class=\"count\">${counts.errors}</div><div class=\"label\">System Errors</div></div>\n  <div class=\"card fails\"><div class=\"count\">${counts.failedSSH}</div><div class=\"label\">SSH Failures</div></div>\n  <div class=\"card success\"><div class=\"count\">${counts.successSSH}</div><div class=\"label\">SSH Logins</div></div>\n  <div class=\"card kernel\"><div class=\"count\">${counts.kernel}</div><div class=\"label\">Kernel Warnings</div></div>\n  <div class=\"card network\"><div class=\"count\">${counts.network}</div><div class=\"label\">Network Connections</div></div>\n</div>\n\n<div class=\"grid\">\n  ${renderSection(\"System Errors and Warnings\", data[\"System Errors and Warnings\"], colors[\"System Errors and Warnings\"])}\n  ${renderSection(\"Failed SSH Login Attempts\", data[\"Failed SSH Login Attempts\"], colors[\"Failed SSH Login Attempts\"])}\n  ${renderSection(\"Successful SSH Logins\", data[\"Successful SSH Logins\"], colors[\"Successful SSH Logins\"])}\n  ${renderSection(\"Kernel Warnings\", data[\"Kernel Warnings\"], colors[\"Kernel Warnings\"])}\n  ${renderSection(\"Network Connection Attempts\", data[\"Network Connection Attempts\"], colors[\"Network Connection Attempts\"])}\n</div>\n\n<footer>Generated via n8n • ${now}</footer>\n</body>\n</html>\n`;\n\nreturn [\n  {\n    binary: {\n      data: await this.helpers.prepareBinaryData(\n        Buffer.from(html, \"utf8\"),\n        \"system_log_dashboard.html\",\n        \"text/html\"\n      ),\n    },\n  },\n];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        272,
        -336
      ],
      "id": "febdf1ca-9d8d-4f66-aeff-2fe685215060",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://push.urls.lol/n8n",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Title",
              "value": "Intruder Alert"
            },
            {
              "name": "Actions",
              "value": "view, Open Logs Dashboard, http://192.168.1.76:5000/logs.html"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "text/html",
        "body": "=🚨 Attention! There was a failed SSH login attempt on your server.",
        "options": {}
      },
      "id": "cdcbf666-aedd-4d8c-a113-3d23ef1bc27d",
      "name": "ntfy message",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        544,
        -192
      ],
      "credentials": {
        "httpBasicAuth": {
          "id": "taknAGQzMCToazDX",
          "name": "ntfy creds"
        }
      }
    },
    {
      "parameters": {
        "operation": "put",
        "path": "/logs.html"
      },
      "type": "n8n-nodes-dav.webDav",
      "typeVersion": 1,
      "position": [
        544,
        -336
      ],
      "id": "91db7d6c-8aea-47c5-9e62-94e4f035b435",
      "name": "Upload file",
      "credentials": {
        "davApi": {
          "id": "pEcdUS1P16yASJ8L",
          "name": "DAV account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "288e6872-94a6-420e-b0e3-5e1e4ab1384e",
              "leftValue": "={{ $json[\"stdout\"].match(/===== Failed SSH Login Attempts =====\\n\\s*\\d+/) ? \"true\" : \"\" }}",
              "rightValue": "Failed password",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        272,
        -192
      ],
      "id": "efdc1be0-3585-4829-bee5-3f5d5c047a8a",
      "name": "IF Failed Login Attempts"
    }
  ],
  "pinData": {},
  "connections": {
    "When clicking ‘Execute workflow’": {
      "main": [
        [
          {
            "node": "Execute a command",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute a command": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          },
          {
            "node": "IF Failed Login Attempts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Upload file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Failed Login Attempts": {
      "main": [
        [
          {
            "node": "ntfy message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "42cb8b27-ebe7-4c02-833d-c79c0c84abc4",
  "meta": {
    "instanceId": "5bdcc08ad68cf4e9fe8c1a97ecded79bd057ff173d9a5b775744376d10b0b32d"
  },
  "id": "TDEew4URSgVd0bXK",
  "tags": []
}