feat(ops): ECR-007 行为分析与 ECR-008 内容运营后台
落地埋点 ingest/数据看板、首页宫格 CMS 与测评上下架;含账号引导、问答流式与免责声明去重,以及 review P1 审计同事务修复。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user