summaryrefslogtreecommitdiff
path: root/playground/web
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-06-16 07:32:18 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-06-16 07:32:18 +0000
commit1b1e6d6165531b4489f665bb0fd46e2d996de19f (patch)
treebabda355a71fd4094a1180b373b02d5ca2c8e0a4 /playground/web
parentc86abd29845fa6feb61b1fddb8e8bb41312d55a8 (diff)
feat: add playground/web/server.py (missed in c86abd2)
Stdlib Python status page on port 9090 referenced in the service gateway commit but not included. Serves system info at / and a JSON health check at /health. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'playground/web')
-rw-r--r--playground/web/server.py94
1 files changed, 94 insertions, 0 deletions
diff --git a/playground/web/server.py b/playground/web/server.py
new file mode 100644
index 0000000..56ae429
--- /dev/null
+++ b/playground/web/server.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+"""
+Playground demo service — stdlib HTTP status page on port 9090.
+Registered behind doot's auth layer at /playground/.
+Start: python3 playground/web/server.py
+"""
+
+import http.server
+import json
+import os
+import platform
+import socket
+import time
+from datetime import datetime, timezone
+
+START_TIME = time.monotonic()
+PORT = int(os.environ.get("PLAYGROUND_PORT", "9090"))
+
+STATUS_HTML = """\
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>Playground — System Status</title>
+<style>
+ body {{ font-family: monospace; background: #0f111a; color: #cdd6f4; margin: 0; padding: 2rem; }}
+ h1 {{ color: #89b4fa; margin-bottom: 0.25rem; }}
+ .subtitle {{ color: #6c7086; font-size: 0.85rem; margin-bottom: 2rem; }}
+ table {{ border-collapse: collapse; width: 100%; max-width: 640px; }}
+ td {{ padding: 0.4rem 0.8rem; border-bottom: 1px solid #313244; }}
+ td:first-child {{ color: #a6e3a1; width: 200px; }}
+ .badge {{ display: inline-block; background: #1e6e3a; color: #a6e3a1;
+ padding: 0.1rem 0.5rem; border-radius: 3px; font-size: 0.8rem; }}
+</style>
+</head>
+<body>
+<h1>Playground</h1>
+<p class="subtitle">doot service gateway demo &mdash; authenticated via doot session</p>
+<table>
+ <tr><td>status</td><td><span class="badge">ok</span></td></tr>
+ <tr><td>hostname</td><td>{hostname}</td></tr>
+ <tr><td>python</td><td>{python}</td></tr>
+ <tr><td>platform</td><td>{platform}</td></tr>
+ <tr><td>uptime</td><td>{uptime}</td></tr>
+ <tr><td>server time</td><td>{now}</td></tr>
+ <tr><td>pid</td><td>{pid}</td></tr>
+ <tr><td>port</td><td>{port}</td></tr>
+</table>
+</body>
+</html>
+"""
+
+
+def _uptime() -> str:
+ elapsed = int(time.monotonic() - START_TIME)
+ h, rem = divmod(elapsed, 3600)
+ m, s = divmod(rem, 60)
+ return f"{h}h {m}m {s}s"
+
+
+class Handler(http.server.BaseHTTPRequestHandler):
+ def log_message(self, fmt, *args): # quieter logs
+ print(f"{self.address_string()} {fmt % args}")
+
+ def do_GET(self):
+ if self.path in ("/health", "/health/"):
+ body = json.dumps({"status": "ok", "uptime": _uptime()}).encode()
+ self._respond(200, "application/json", body)
+ return
+
+ html = STATUS_HTML.format(
+ hostname=socket.gethostname(),
+ python=platform.python_version(),
+ platform=platform.system() + " " + platform.release(),
+ uptime=_uptime(),
+ now=datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC"),
+ pid=os.getpid(),
+ port=PORT,
+ ).encode()
+ self._respond(200, "text/html; charset=utf-8", html)
+
+ def _respond(self, code, content_type, body):
+ self.send_response(code)
+ self.send_header("Content-Type", content_type)
+ self.send_header("Content-Length", str(len(body)))
+ self.end_headers()
+ self.wfile.write(body)
+
+
+if __name__ == "__main__":
+ server = http.server.HTTPServer(("127.0.0.1", PORT), Handler)
+ print(f"playground listening on http://127.0.0.1:{PORT} (pid {os.getpid()})")
+ server.serve_forever()