valheim-map/api_server.py

95 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Nerdhalla API server — serves POI data and server status."""
import json
import sqlite3
import subprocess
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
DB_PATH = "/tmp/valheim_poi.db"
NERDCADE_SSH = "aelith@100.86.206.39"
NERDCADE_PORT = 46129
class APIHandler(BaseHTTPRequestHandler):
def do_GET(self):
parsed = urlparse(self.path)
path = parsed.path
params = parse_qs(parsed.query)
if path == "/api/status":
self._handle_status()
elif path == "/api/pois":
self._handle_pois(params)
else:
self.send_error(404, "Not found")
def _handle_status(self):
try:
result = subprocess.run(
["ssh", "-p", str(NERDCADE_PORT), "-o", "ConnectTimeout=5",
"-o", "StrictHostKeyChecking=no", NERDCADE_SSH,
"systemctl --user is-active amp* 2>/dev/null || echo 'inactive'"],
capture_output=True, text=True, timeout=10
)
online = "active" in result.stdout.lower()
data = {"online": online, "players": None}
except Exception:
data = {"online": False, "players": None}
self._json_response(data)
def _handle_pois(self, params):
try:
limit = min(int(params.get("limit", [500])[0]), 11309)
offset = int(params.get("offset", [0])[0])
biome = params.get("biome", [None])[0]
search = params.get("search", [None])[0]
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
cur = conn.cursor()
where_clauses = []
bindings = []
if biome:
where_clauses.append("category = ?")
bindings.append(biome)
if search:
where_clauses.append("prefab_name LIKE ?")
bindings.append(f"%{search}%")
where = ""
if where_clauses:
where = "WHERE " + " AND ".join(where_clauses)
cur.execute(f"SELECT pos_x, pos_y, pos_z, prefab_name, category FROM locations {where} ORDER BY prefab_name LIMIT ? OFFSET ?", bindings + [limit, offset])
rows = [{
"x": r["pos_x"],
"y": r["pos_z"], # Valheim uses Z as north-south
"prefab": r["prefab_name"],
"category": r["category"]
} for r in cur.fetchall()]
conn.close()
self._json_response(rows)
except Exception as e:
self._json_response({"error": str(e)}, status=500)
def _json_response(self, data, status=200):
body = json.dumps(data).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, format, *args):
sys.stderr.write("[API] %s - %s\n" % (self.client_address[0], format % args))
if __name__ == "__main__":
port = 8081
server = HTTPServer(("127.0.0.1", port), APIHandler)
print(f"[Nerdhalla API] Listening on 127.0.0.1:{port}")
server.serve_forever()