from fastapi import APIRouter, Cookie, HTTPException, Request from fastapi.responses import HTMLResponse from app.db import auth_db from app.db.scheduler_db import ( enqueue_manual_trigger, get_job_config, get_scheduler_overview, list_manual_triggers, set_job_enabled, set_job_interval, ) from app.web.shared import ( SchedulerIntervalRequest, SchedulerToggleRequest, SchedulerTriggerRequest, login_redirect, require_admin, ) def build_router(templates): router = APIRouter() @router.get("/admin.html", response_class=HTMLResponse) async def admin_page(request: Request, altcoin_session: str = Cookie(default="")): if not auth_db.get_user_by_session_token(altcoin_session): return login_redirect() try: require_admin(altcoin_session) except HTTPException as e: return HTMLResponse(content=f"

需要管理员权限

{e.detail}

返回看板", status_code=e.status_code) return templates.TemplateResponse(request=request, name="admin.html", context={"show_nav": True}) @router.get("/api/admin/check") async def api_admin_check(altcoin_session: str = Cookie(default="")): try: user = require_admin(altcoin_session) return {"is_admin": True, "email": user.get("email", "")} except HTTPException: return {"is_admin": False} @router.get("/api/admin/stats") async def api_admin_stats(altcoin_session: str = Cookie(default="")): require_admin(altcoin_session) return auth_db.get_admin_stats() @router.get("/api/admin/users") async def api_admin_users(search: str = "", offset: int = 0, limit: int = 50, tab: str = "all", altcoin_session: str = Cookie(default="")): require_admin(altcoin_session) return auth_db.get_admin_users(search=search, offset=offset, limit=limit, tab=tab) @router.get("/api/admin/orders") async def api_admin_orders(search: str = "", offset: int = 0, limit: int = 50, status: str = "all", altcoin_session: str = Cookie(default="")): require_admin(altcoin_session) return auth_db.get_admin_orders(search=search, offset=offset, limit=limit, status=status) @router.get("/api/scheduler/jobs") async def api_scheduler_jobs(altcoin_session: str = Cookie(default="")): require_admin(altcoin_session) return get_scheduler_overview() @router.post("/api/scheduler/jobs/{job_name}/toggle") async def api_scheduler_toggle(job_name: str, payload: SchedulerToggleRequest, altcoin_session: str = Cookie(default="")): require_admin(altcoin_session) if not set_job_enabled(job_name, payload.enabled): raise HTTPException(status_code=404, detail="任务不存在") return {"ok": True, "job_name": job_name, "enabled": payload.enabled} @router.post("/api/scheduler/jobs/{job_name}/interval") async def api_scheduler_interval(job_name: str, payload: SchedulerIntervalRequest, altcoin_session: str = Cookie(default="")): require_admin(altcoin_session) if payload.every_seconds < 30: raise HTTPException(status_code=400, detail="周期不能低于 30 秒") if not set_job_interval(job_name, payload.every_seconds): raise HTTPException(status_code=404, detail="任务不存在") return {"ok": True, "job_name": job_name, "every_seconds": payload.every_seconds} @router.post("/api/scheduler/jobs/{job_name}/trigger") async def api_scheduler_trigger(job_name: str, payload: SchedulerTriggerRequest, altcoin_session: str = Cookie(default="")): user = require_admin(altcoin_session) job = get_job_config(job_name) if not job: raise HTTPException(status_code=404, detail="任务不存在") if not job.get("enabled") and not payload.force: raise HTTPException(status_code=409, detail="任务已关闭,需要确认后 force=true 才能单次运行") trigger_id = enqueue_manual_trigger(job_name, force=payload.force, requested_by=user.get("email", "")) return {"ok": True, "job_name": job_name, "trigger_id": trigger_id, "force": payload.force} @router.get("/api/scheduler/triggers") async def api_scheduler_triggers(limit: int = 30, altcoin_session: str = Cookie(default="")): require_admin(altcoin_session) return {"items": list_manual_triggers(limit=limit)} return router