Files
server-core/app/park/admin.py
T
Pine 064b06ecf0 feat(park): 全量迁入园区重模块 llm/rag/asr/s2s/vision + 重依赖
迁入 app/park:llm(对话)、rag(双路向量知识库)、tools/ai_tools(智能体工具)、
asr(语音识别)、s2s_bridge(实时语音桥)、vision_yolo/vision_llm(人脸/多模态)、
knowledge/*.md、vendor/s2s-cloud(s2s 云化栈);routers 补 /api/ai|kb|asr|vision|
s2s|tools 端点。智能体提示词/企业名录改读 park_config 主数据源。pyproject 加重依赖
(dashscope/numpy/openai/torch/transformers/ultralytics/websockets/soundfile/scipy/
nltk/jinja2)。TestClient 冒烟:ai/chat(无 Key 走本地规则)、kb、s2s、tools、display 均 200。
2026-08-24 17:28:53 +08:00

93 lines
2.9 KiB
Python

# -*- coding: utf-8 -*-
"""管理后台页面(FastAPI + Jinja2 服务端渲染)
GET /admin → 登录页 或 管理台(按 Cookie 鉴权)
POST /admin/login → 表单登录,成功写入签名 Cookie
GET /admin/logout → 清除 Cookie
媒体库 / 播放列表 / 设置均由 Jinja 服务端渲染,动态操作用少量 JS 调 REST
"""
import hashlib
import hmac
import logging
from fastapi import Request
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from pathlib import Path
from .config import settings
from .storage import storage
from .routers import _list_media, _playlist_files
log = logging.getLogger("dpm.admin")
TEMPLATES_DIR = Path(__file__).resolve().parent.parent / "templates"
templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
# ---------------- 签名 Cookie ----------------
def _admin_token():
return hmac.new(settings.ADMIN_SECRET.encode(), b"dpm-admin", hashlib.sha256).hexdigest()
def _verify_admin_cookie(cookie):
return bool(cookie) and hmac.compare_digest(cookie, _admin_token())
def _set_admin_cookie(resp):
resp.set_cookie("dpm_admin", _admin_token(), max_age=7 * 86400, httponly=True, samesite="lax")
return resp
def _clear_admin_cookie(resp):
resp.delete_cookie("dpm_admin")
return resp
# ---------------- 路由 ----------------
async def admin_page(request: Request):
if not _verify_admin_cookie(request.cookies.get("dpm_admin")):
return templates.TemplateResponse(
request=request,
name="admin.html",
context={
"logged_in": False,
"error": request.query_params.get("error") == "1",
},
)
files = _list_media()["files"]
playlist = _playlist_files()
s = storage.get_settings()
return templates.TemplateResponse(
request=request,
name="admin.html",
context={
"logged_in": True,
"error": False,
"files": files,
"playlist": playlist["files"],
"play_mode": s.get("play_mode", "sequential"),
"image_duration": s.get("image_duration", 5),
"volume": s.get("volume", 80),
"sfx_volume": s.get("sfx_volume", 60),
"fullscreen": s.get("fullscreen", True),
"autostart": s.get("autostart", False),
"from_screen": request.query_params.get("from") == "screen",
},
)
async def admin_login(request: Request):
form = await request.form()
s = storage.get_settings()
if form.get("username") == s.get("username") and form.get("password") == s.get("password"):
return _set_admin_cookie(RedirectResponse("/admin", status_code=303))
return RedirectResponse("/admin?error=1", status_code=303)
async def admin_logout():
return _clear_admin_cookie(RedirectResponse("/admin", status_code=303))