Files
Atomizer/hq/mission-control/serve.py

74 lines
2.6 KiB
Python

#!/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()