""" 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'
{art["date"]}{art["title"][:65]}{art["words"]}w
' if not recent_html: recent_html = '
No articles yet
' sites_html += f"""
{s.get('articles', 0)}
Articles
{s.get('views', 0)}
Views
{s.get('today_views', 0)}
Today
{s.get('subscribers', 0)}
Subs
{latency}
Latency
{(s.get('error') or 'โœ…')[:20]}
Status
{recent_html}
""" html = NOC_TEMPLATE html = html.replace('__ALIVE__', str(alive)) html = html.replace('__TOTAL__', str(len(data))) html = html.replace('__TOTAL_ARTICLES__', str(total_articles)) html = html.replace('__TOTAL_VIEWS__', str(total_views)) html = html.replace('__TOTAL_TODAY__', str(total_today)) html = html.replace('__TOTAL_SUBS__', str(total_subs)) html = html.replace('__UPDATED__', str(updated[:19] if updated else "never")) html = html.replace('__SITES_HTML__', sites_html) return html @app.route("/api/fleet") def api_fleet(): """JSON fleet state.""" refresh_fleet() with fleet_cache["lock"]: return jsonify({ "updated": fleet_cache["updated"], "sites": fleet_cache["data"], }) @app.route("/api/refresh", methods=["POST"]) def api_refresh(): refresh_fleet() return jsonify({"status": "refreshed", "updated": fleet_cache["updated"]}) @app.route("/health") def health(): return jsonify({"status": "ok"}) # โ”€โ”€โ”€ Template โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ NOC_TEMPLATE = """ NOC โ€” Publisher Fleet Monitor
๐ŸŸข __ALIVE__/__TOTAL__ online ๐Ÿ• __UPDATED__ ๐Ÿ”„ auto-refresh 60s
__TOTAL_ARTICLES__
Total Articles
__TOTAL_VIEWS__
Total Views
__TOTAL_TODAY__
Today Views
__TOTAL_SUBS__
Subscribers
__ALIVE__/8
Sites Live
6AM
Next Publish
__SITES_HTML__
""" if __name__ == "__main__": import argparse ap = argparse.ArgumentParser() ap.add_argument("--port", type=int, default=5090) ap.add_argument("--host", type=str, default="127.0.0.1") args = ap.parse_args() # Initial refresh refresh_fleet() print(f"๐Ÿ–ฅ NOC Dashboard โ†’ http://{args.host}:{args.port}") app.run(host=args.host, port=args.port, debug=False)