Atlas - server.py
Home / usr / net / PulseWatch Lines: 1 | Size: 3881 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1import json 2import os 3import time 4import threading 5import uuid 6from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler 7from urllib.parse import urlparse, parse_qs 8 9DATA_DIR = "/var/lib/pulsewatch" 10os.makedirs(DATA_DIR, exist_ok=True) 11 12lock = threading.Lock() 13 14def load_json(path): 15 if not os.path.exists(path): 16 return {} 17 with open(path, "r") as f: 18 return json.load(f) 19 20 21def store_stats(host, payload): 22 path = os.path.join(DATA_DIR, f"{host}.json") 23 ts = str(int(time.time())) 24 25 with lock: 26 data = {} 27 if os.path.exists(path): 28 with open(path, "r") as f: 29 data = json.load(f) 30 31 data[ts] = payload 32 33 with open(path, "w") as f: 34 json.dump(data, f) 35 36def store_ping(host, payload): 37 path = os.path.join(DATA_DIR, f"{host}_pings.json") 38 ts = str(int(time.time())) 39 40 with lock: 41 data = {} 42 if os.path.exists(path): 43 with open(path, "r") as f: 44 data = json.load(f) 45 46 data[ts] = payload 47 48 with open(path, "w") as f: 49 json.dump(data, f) 50 51 52class Handler(BaseHTTPRequestHandler): 53 def do_POST(self): 54 if self.path != "/ingest": 55 self.send_error(404) 56 return 57 58 length = int(self.headers.get("Content-Length", 0)) 59 body = self.rfile.read(length) 60 61 try: 62 payload = json.loads(body) 63 except json.JSONDecodeError: 64 self.send_error(400) 65 return 66 67 host = payload.get("hostname") or self.client_address[0] 68 69 if payload.get("event") in ("ping_received", "ping_rtt"): 70 store_ping(host, payload) 71 else: 72 store_stats(host, payload) 73 74 self.send_response(200) 75 self.end_headers() 76 self.wfile.write(b"OK") 77 78 def do_GET(self): 79 parsed = urlparse(self.path) 80 81 # --- ping endpoint (unchanged) --- 82 if parsed.path == "/ping": 83 qs = parse_qs(parsed.query) 84 ts = qs.get("ts", [None])[0] 85 ping_id = qs.get("id", [None])[0] 86 87 if ts is None or ping_id is None: 88 self.send_error(400) 89 return 90 91 self.send_response(200) 92 self.send_header("Content-Type", "application/json") 93 self.end_headers() 94 self.wfile.write(json.dumps({ 95 "ts": ts, 96 "ping_id": ping_id 97 }).encode()) 98 return 99 100 # --- list hosts --- 101 if parsed.path == "/data/hosts": 102 hosts = [] 103 for f in os.listdir(DATA_DIR): 104 if f.endswith(".json") and not f.endswith("_pings.json"): 105 hosts.append(f.replace(".json", "")) 106 107 self.send_response(200) 108 self.send_header("Content-Type", "application/json") 109 self.end_headers() 110 self.wfile.write(json.dumps(hosts).encode()) 111 return 112 113 # --- per-host data --- 114 if parsed.path.startswith("/data/host/"): 115 parts = parsed.path.split("/") 116 if len(parts) < 4: 117 self.send_error(404) 118 return 119 120 host = parts[3] 121 is_ping = len(parts) == 5 and parts[4] == "pings" 122 123 fname = ( 124 f"{host}_pings.json" 125 if is_ping 126 else f"{host}.json" 127 ) 128 129 path = os.path.join(DATA_DIR, fname) 130 data = load_json(path) 131 132 self.send_response(200) 133 self.send_header("Content-Type", "application/json") 134 self.end_headers() 135 self.wfile.write(json.dumps(data).encode()) 136 return 137 138 self.send_error(404) 139 140 141 def log_message(self, *_): 142 pass 143 144if __name__ == "__main__": 145 server = ThreadingHTTPServer(("0.0.0.0", 9000), Handler) 146 server.serve_forever() 147[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.