#!/usr/bin/env python3 """Simple HTTP server with save API for Mission Control.""" import http.server import json import os import shutil from datetime import datetime PORT = 8091 os.chdir(os.path.dirname(os.path.abspath(__file__))) class MissionControlHandler(http.server.SimpleHTTPRequestHandler): def end_headers(self): self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') self.send_header('Pragma', 'no-cache') self.send_header('Expires', '0') self.send_header('Access-Control-Allow-Origin', '*') self.send_header('Access-Control-Allow-Methods', 'GET, PUT, OPTIONS') self.send_header('Access-Control-Allow-Headers', 'Content-Type') super().end_headers() def do_OPTIONS(self): self.send_response(200) self.end_headers() def do_PUT(self): path = self.path.lstrip('/') if not path.startswith('data/') or not path.endswith('.json'): self.send_response(403) self.end_headers() self.wfile.write(b'{"error":"Only data/*.json writes allowed"}') return content_length = int(self.headers.get('Content-Length', 0)) body = self.rfile.read(content_length) try: data = json.loads(body) except json.JSONDecodeError as e: self.send_response(400) self.end_headers() self.wfile.write(json.dumps({"error": f"Invalid JSON: {e}"}).encode()) return filepath = os.path.join(os.getcwd(), path) os.makedirs(os.path.dirname(filepath), exist_ok=True) if os.path.exists(filepath): backup = filepath + f'.bak-{datetime.now().strftime("%Y%m%d-%H%M%S")}' shutil.copy2(filepath, backup) bak_dir = os.path.dirname(filepath) bak_base = os.path.basename(filepath) baks = sorted([f for f in os.listdir(bak_dir) if f.startswith(bak_base + '.bak-')]) for old in baks[:-10]: os.remove(os.path.join(bak_dir, old)) with open(filepath, 'w') as f: json.dump(data, f, indent=2) self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps({"ok": True, "saved": path}).encode()) def log_message(self, format, *args): pass if __name__ == '__main__': class ThreadedServer(http.server.ThreadingHTTPServer): allow_reuse_address = True server = ThreadedServer(('0.0.0.0', PORT), MissionControlHandler) print(f'Atomizer HQ Mission Control serving on port {PORT}') server.serve_forever()