落地埋点 ingest/数据看板、首页宫格 CMS 与测评上下架;含账号引导、问答流式与免责声明去重,以及 review P1 审计同事务修复。 Co-authored-by: Cursor <cursoragent@cursor.com>
64 lines
2.2 KiB
Python
64 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Serve admin-h5 dist for /psy/admin (nginx strips prefix → this listens on :8003)."""
|
|
from __future__ import annotations
|
|
|
|
import mimetypes
|
|
import os
|
|
import sys
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
DIST = ROOT / "apps" / "admin-h5" / "dist"
|
|
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8003
|
|
|
|
|
|
class Handler(BaseHTTPRequestHandler):
|
|
def log_message(self, fmt: str, *args) -> None:
|
|
sys.stderr.write("%s - %s\n" % (self.address_string(), fmt % args))
|
|
|
|
def do_GET(self) -> None: # noqa: N802
|
|
path = self.path.split("?", 1)[0]
|
|
if path in ("", "/"):
|
|
path = "/index.html"
|
|
# security: no path escape
|
|
rel = path.lstrip("/")
|
|
candidate = (DIST / rel).resolve()
|
|
if not str(candidate).startswith(str(DIST.resolve())):
|
|
self.send_error(400)
|
|
return
|
|
if candidate.is_file():
|
|
self._file(candidate)
|
|
return
|
|
# hashed asset miss → 404 (not SPA), so MIME errors are obvious
|
|
if "/assets/" in path or path.endswith((".js", ".css", ".map", ".png", ".svg", ".ico", ".woff2")):
|
|
self.send_error(404)
|
|
return
|
|
index = DIST / "index.html"
|
|
if index.is_file():
|
|
self._file(index)
|
|
return
|
|
self.send_error(404)
|
|
|
|
def _file(self, file_path: Path) -> None:
|
|
data = file_path.read_bytes()
|
|
ctype, _ = mimetypes.guess_type(str(file_path))
|
|
self.send_response(200)
|
|
self.send_header("Content-Type", ctype or "application/octet-stream")
|
|
self.send_header("Content-Length", str(len(data)))
|
|
self.send_header("Cache-Control", "no-cache" if file_path.name == "index.html" else "public, max-age=86400")
|
|
self.end_headers()
|
|
self.wfile.write(data)
|
|
|
|
|
|
def main() -> None:
|
|
if not DIST.is_dir():
|
|
raise SystemExit(f"admin dist missing: {DIST} (run npm run build:admin)")
|
|
httpd = ThreadingHTTPServer(("127.0.0.1", PORT), Handler)
|
|
print(f"admin-h5 on http://127.0.0.1:{PORT}/ (nginx → /psy/admin/)", flush=True)
|
|
httpd.serve_forever()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|