064b06ecf0
迁入 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。
93 lines
2.9 KiB
Python
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))
|