""" Network Operations Center โ Real-time monitoring dashboard for all 8 publisher sites. Hacker aesthetic, live data, comprehensive fleet overview. """ import json import time import sqlite3 import threading from datetime import datetime, timedelta from pathlib import Path from collections import defaultdict from flask import Flask, render_template_string, jsonify, request app = Flask(__name__) SITES = { "ai": {"name": "AI Insights", "ip": "10.30.20.240", "ct": 135, "color": "#7c3aed", "accent": "#a78bfa"}, "tech": {"name": "Tech Frontier", "ip": "10.30.20.241", "ct": 136, "color": "#2563eb", "accent": "#60a5fa"}, "science": {"name": "Science Decoded", "ip": "10.30.20.242", "ct": 137, "color": "#059669", "accent": "#34d399"}, "crypto": {"name": "Crypto Compass", "ip": "10.30.20.243", "ct": 138, "color": "#f59e0b", "accent": "#fbbf24"}, "linux": {"name": "Linux Lab", "ip": "10.30.20.244", "ct": 139, "color": "#f97316", "accent": "#fb923c"}, "gaming": {"name": "Game Layer", "ip": "10.30.20.246", "ct": 140, "color": "#ec4899", "accent": "#f472b6"}, "diy": {"name": "Maker Forge", "ip": "10.30.20.247", "ct": 141, "color": "#ef4444", "accent": "#f87171"}, "guides": {"name": "Practical Guides", "ip": "10.30.20.248", "ct": 142, "color": "#0891b2", "accent": "#22d3ee"}, } # Cached fleet state โ refreshed async fleet_cache = {"data": {}, "updated": None, "lock": threading.Lock()} def fetch_site_state(vertical, info): """Query a single site for health + stats.""" ip = info["ip"] state = { "vertical": vertical, "name": info["name"], "ip": ip, "ct": info["ct"], "color": info["color"], "accent": info["accent"], "alive": False, "articles": 0, "views": 0, "today_views": 0, "subscribers": 0, "latency_ms": 0, "recent_articles": [], "popular": [], "error": None, } import requests try: t0 = time.time() r = requests.get(f"http://{ip}:80/health", timeout=5) state["latency_ms"] = round((time.time() - t0) * 1000) if r.status_code == 200: h = r.json() state["alive"] = True state["articles"] = h.get("articles", 0) # Stats r2 = requests.get(f"http://{ip}:80/api/stats", timeout=5) if r2.status_code == 200: s = r2.json() state["views"] = s.get("total_views", 0) state["today_views"] = s.get("today_views", 0) popular = s.get("popular", []) state["popular"] = [{"title": a.get("title", "")[:80], "views": a.get("views", 0)} for a in popular[:3]] # Recent articles r3 = requests.get(f"http://{ip}:80/api/articles?limit=3", timeout=5) if r3.status_code == 200: arts = r3.json() state["recent_articles"] = [ {"title": a.get("title", "")[:80], "slug": a.get("slug", ""), "date": a.get("published_at", "")[:10], "words": a.get("word_count", 0)} for a in arts[:3] ] # Subscriber count try: r4 = requests.get(f"http://{ip}:5000/api/subscribers", timeout=3) if r4.status_code == 200: state["subscribers"] = r4.json().get("count", 0) except Exception: pass except Exception as e: state["error"] = str(e)[:80] return state def refresh_fleet(): """Background refresh of all 8 sites.""" import concurrent.futures with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor: futures = {executor.submit(fetch_site_state, v, i): v for v, i in SITES.items()} results = {} for future in concurrent.futures.as_completed(futures): vertical = futures[future] try: results[vertical] = future.result(timeout=10) except Exception: results[vertical] = {"vertical": vertical, "alive": False, "error": "timeout"} with fleet_cache["lock"]: fleet_cache["data"] = results fleet_cache["updated"] = datetime.now().isoformat() @app.route("/") def noc(): """Main NOC dashboard.""" # Trigger a refresh if stale (>30s) if fleet_cache["updated"] is None: refresh_fleet() elif (datetime.now() - datetime.fromisoformat(fleet_cache["updated"])).seconds > 30: threading.Thread(target=refresh_fleet, daemon=True).start() with fleet_cache["lock"]: data = dict(fleet_cache["data"]) updated = fleet_cache["updated"] # Compute aggregates alive = sum(1 for s in data.values() if s.get("alive")) total_articles = sum(s.get("articles", 0) for s in data.values()) total_views = sum(s.get("views", 0) for s in data.values()) total_today = sum(s.get("today_views", 0) for s in data.values()) total_subs = sum(s.get("subscribers", 0) for s in data.values()) sites_html = "" for vertical, s in data.items(): status_dot = "๐ข" if s.get("alive") else "๐ด" latency = f"{s.get('latency_ms', 0)}ms" lat_class = "fast" if s.get("latency_ms", 999) < 100 else ("medium" if s.get("latency_ms", 999) < 300 else "slow") # Build recent articles list recent_html = "" for art in s.get("recent_articles", []): recent_html += f'
' if not recent_html: recent_html = '