diff --git a/script.js b/script.js index 916fd47..77fc69c 100644 --- a/script.js +++ b/script.js @@ -28,7 +28,7 @@ async function loadNocStatus() { return; } - statusText.textContent = `${data.nocName} — ${data.status} — ${data.lastUpdated}`; + statusText.textContent = `${data.nocName} — ${data.status} — Last updated: ${data.lastUpdated}`; nodes.textContent = data.summary.nodes; services.textContent = data.summary.services; @@ -48,7 +48,7 @@ async function loadNocStatus() { ? "online" : status.includes("building") || status.includes("tuning") || status.includes("active") ? "warning" - : "planned"; + : "offline"; card.innerHTML = `
${system.type}
${system.address}
Status: ${system.status}
+ Last Check: ${system.lastCheck || "Manual"}
`;
grid.appendChild(card);
diff --git a/scripts/generate-noc-status.py b/scripts/generate-noc-status.py
new file mode 100644
index 0000000..115bcc3
--- /dev/null
+++ b/scripts/generate-noc-status.py
@@ -0,0 +1,123 @@
+import json
+import socket
+from datetime import datetime
+from pathlib import Path
+
+OUTPUT_FILE = Path(__file__).resolve().parent.parent / "data" / "noc-status.json"
+
+SYSTEMS = [
+ {
+ "name": "Proxmox VE",
+ "type": "Virtualization",
+ "host": "10.10.30.250",
+ "port": 8006,
+ "address": "10.10.30.250:8006",
+ "layer": "Infrastructure"
+ },
+ {
+ "name": "TrueNAS",
+ "type": "Storage",
+ "host": "10.10.30.143",
+ "port": 80,
+ "address": "10.10.30.143",
+ "layer": "Storage"
+ },
+ {
+ "name": "Nginx Proxy Manager",
+ "type": "Reverse Proxy",
+ "host": "10.10.30.210",
+ "port": 81,
+ "address": "10.10.30.210:81",
+ "layer": "Access"
+ },
+ {
+ "name": "Dashy",
+ "type": "Dashboard",
+ "host": "10.10.30.61",
+ "port": 4000,
+ "address": "10.10.30.61:4000",
+ "layer": "Monitoring"
+ },
+ {
+ "name": "Jellyfin",
+ "type": "Media Service",
+ "host": "10.10.30.143",
+ "port": 30013,
+ "address": "10.10.30.143:30013",
+ "layer": "Services"
+ },
+ {
+ "name": "K3s API",
+ "type": "Kubernetes API",
+ "host": "10.10.30.72",
+ "port": 6443,
+ "address": "10.10.30.72:6443",
+ "layer": "Compute"
+ },
+ {
+ "name": "APRS-IS",
+ "type": "APRS Internet Service",
+ "host": "noam.aprs2.net",
+ "port": 14580,
+ "address": "noam.aprs2.net:14580",
+ "layer": "Edge"
+ }
+]
+
+
+def check_tcp(host, port, timeout=2):
+ try:
+ with socket.create_connection((host, port), timeout=timeout):
+ return True
+ except OSError:
+ return False
+
+
+def main():
+ checked_at = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+
+ systems = []
+ online_count = 0
+
+ for system in SYSTEMS:
+ online = check_tcp(system["host"], system["port"])
+
+ if online:
+ status = "Online"
+ online_count += 1
+ else:
+ status = "Offline"
+
+ systems.append({
+ "name": system["name"],
+ "type": system["type"],
+ "address": system["address"],
+ "status": status,
+ "layer": system["layer"],
+ "lastCheck": checked_at
+ })
+
+ data = {
+ "nocName": "Ced's NOC",
+ "status": "Operational Build",
+ "lastUpdated": checked_at,
+ "summary": {
+ "nodes": "12+",
+ "services": f"{online_count}/{len(SYSTEMS)}",
+ "vlans": "4",
+ "edgeSystems": "2"
+ },
+ "systems": systems
+ }
+
+ OUTPUT_FILE.parent.mkdir(parents=True, exist_ok=True)
+
+ with open(OUTPUT_FILE, "w", encoding="utf-8") as file:
+ json.dump(data, file, indent=2)
+
+ print(f"NOC status written to {OUTPUT_FILE}")
+ print(f"Online systems: {online_count}/{len(SYSTEMS)}")
+
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/style.css b/style.css
index 2dd3918..5f4eb7c 100644
--- a/style.css
+++ b/style.css
@@ -559,4 +559,25 @@ a:hover {
align-items: flex-start;
gap: 0.5rem;
}
+}
+
+.mini-status.online {
+ background: var(--accent-2);
+ box-shadow: 0 0 16px rgba(142, 240, 207, 0.9);
+}
+
+.mini-status.warning {
+ background: var(--warning);
+ box-shadow: 0 0 16px rgba(242, 204, 96, 0.9);
+}
+
+.mini-status.offline {
+ background: #ff5f56;
+ box-shadow: 0 0 16px rgba(255, 95, 86, 0.9);
+}
+
+.noc-system-card small + small {
+ color: var(--muted);
+ font-weight: 500;
+ margin-top: 0.25rem;
}
\ No newline at end of file