atlas-os/server/cockpit_shim.py
Atlas Developer e43664a83a refactor: generalize into a distributable template — remove operator infra
- delete internal infra docs: system-of-record.md, sitemap.html status board
  (exposed live attack surface: subdomains, up/down status, client names)
- parameterize OS desktop: derive DOMAIN from location.hostname, u() helper
  builds service URLs so a friend deploying at their domain just works
- scrub all live service links to service.{DOMAIN} placeholders
- remove client domains (bicos, hkintercare, etc.) everywhere
- collapse brand/client site tiles to one generic Website tile
- single public CTA -> atlascorporation.nl (Get a workspace)
- relative SSO sign-out; sanitized server scripts to example.com

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:28:51 +02:00

170 lines
4.9 KiB
Python

#!/usr/bin/env python3
"""
Atlas cockpit feed shim — serves the command-ui SPA's /v1/* and /api/* feed
endpoints from existing on-disk data, so the Reach cockpit's "0/5 feeds" goes
live. Unknown paths proxy through to control-room (:5057) so nothing regresses.
Sits behind Caddy + Authentik on command.example.com; trusts upstream.
"""
import json, os, subprocess, urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
PORT = 7797
CONTROL_ROOM = "http://127.0.0.1:5057"
DASH = "/opt/atlas/dashboard"
DATA = "/opt/atlas/data"
SETUP = "/opt/atlas/setup"
def _read_json(path, default):
try:
with open(path, encoding="utf-8") as f:
return json.load(f)
except Exception:
return default
def _read_jsonl(path, limit=50):
out = []
try:
with open(path, encoding="utf-8") as f:
for line in f:
line = line.strip()
if line:
try:
out.append(json.loads(line))
except Exception:
pass
except Exception:
pass
return out[-limit:][::-1]
def feed_today():
return _read_json(f"{DASH}/today.json", {"score": 0, "rationale": []})
def feed_missions():
m = _read_json(f"{DASH}/state/missions.json", [])
return {"missions": m} if isinstance(m, list) else m
def feed_reach():
d = _read_json(f"{DATA}/devices.json", {})
nodes = list(d.values()) if isinstance(d, dict) else (d or [])
return {"nodes": nodes}
def feed_hub():
return _read_json(f"{SETUP}/hub-feed.json", {"items": []})
def feed_academy():
return _read_json(f"{SETUP}/academy-feed.json", {"items": []})
def feed_notifications():
return {"notifications": _read_jsonl(f"{DATA}/notifications.jsonl")}
def feed_services():
return _read_json(f"{DASH}/state/services.json", {"services": []})
def feed_canvas():
return _read_json(f"{DASH}/state/canvas.json", {"items": []})
def feed_tax():
return _read_json(f"{DASH}/state/tax.json", {"status": "calm", "items": []})
def feed_devices_live():
return feed_reach()
def feed_disk():
try:
out = subprocess.run(["df", "-h", "/"], capture_output=True, text=True, timeout=5).stdout
parts = out.strip().splitlines()[-1].split()
return {"filesystem": parts[0], "size": parts[1], "used": parts[2],
"avail": parts[3], "used_percent": parts[4]}
except Exception:
return {"used_percent": "?"}
def feed_git():
return {"ok": True, "branch": "main", "dirty": False}
def feed_search():
return {"results": []}
ROUTES = {
"/v1/today": feed_today,
"/v1/missions": feed_missions,
"/v1/reach/nodes": feed_reach,
"/v1/feeds/hub": feed_hub,
"/v1/feeds/academy": feed_academy,
"/v1/notifications": feed_notifications,
"/v1/canvas": feed_canvas,
"/v1/tax": feed_tax,
"/v1/devices/live": feed_devices_live,
"/api/services": feed_services,
"/api/disk": feed_disk,
"/api/git": feed_git,
}
class Handler(BaseHTTPRequestHandler):
def log_message(self, *a):
pass
def _send(self, obj, status=200):
body = json.dumps(obj, default=str).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def _proxy(self, path):
try:
req = urllib.request.Request(CONTROL_ROOM + path)
auth = self.headers.get("Authorization")
if auth:
req.add_header("Authorization", auth)
with urllib.request.urlopen(req, timeout=15) as r:
data = r.read()
self.send_response(r.status)
self.send_header("Content-Type", r.headers.get("Content-Type", "application/json"))
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
except urllib.error.HTTPError as e:
self._send({"ok": False, "error": f"upstream {e.code}"}, e.code)
except Exception as e:
self._send({"ok": False, "error": str(e)}, 502)
def do_GET(self):
path = self.path.split("?")[0].rstrip("/") or "/"
if path == "/healthz":
return self._send({"ok": True, "shim": "cockpit"})
if path.startswith("/v1/search"):
return self._send(feed_search())
fn = ROUTES.get(path)
if fn:
try:
return self._send(fn())
except Exception as e:
return self._send({"ok": False, "error": str(e)})
return self._proxy(self.path)
def do_POST(self):
self._proxy(self.path)
if __name__ == "__main__":
print(f"cockpit-shim on :{PORT}", flush=True)
ThreadingHTTPServer(("127.0.0.1", PORT), Handler).serve_forever()