2026-08-17 21:30:44 +08:00
|
|
|
# -*- 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),
|
2026-08-18 07:14:13 +08:00
|
|
|
"sfx_volume": s.get("sfx_volume", 60),
|
2026-08-17 21:30:44 +08:00
|
|
|
"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))
|