from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware from app.core import config from app.api import materials, videos, publish, login_helper, auth, admin, ref_audios, ai, tools from loguru import logger import os settings = config.settings app = FastAPI(title="ViGent TalkingHead Agent") from fastapi import Request from starlette.middleware.base import BaseHTTPMiddleware import time import traceback class LoggingMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): start_time = time.time() logger.info(f"START Request: {request.method} {request.url}") logger.info(f"HEADERS: {dict(request.headers)}") try: response = await call_next(request) process_time = time.time() - start_time logger.info(f"END Request: {request.method} {request.url} - Status: {response.status_code} - Duration: {process_time:.2f}s") return response except Exception as e: process_time = time.time() - start_time logger.error(f"EXCEPTION during request {request.method} {request.url}: {str(e)}\n{traceback.format_exc()}") raise e app.add_middleware(LoggingMiddleware) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Create dirs settings.UPLOAD_DIR.mkdir(parents=True, exist_ok=True) settings.OUTPUT_DIR.mkdir(parents=True, exist_ok=True) (settings.UPLOAD_DIR / "materials").mkdir(exist_ok=True) app.mount("/outputs", StaticFiles(directory=str(settings.OUTPUT_DIR)), name="outputs") app.mount("/uploads", StaticFiles(directory=str(settings.UPLOAD_DIR)), name="uploads") # 注册路由 app.include_router(materials.router, prefix="/api/materials", tags=["Materials"]) app.include_router(videos.router, prefix="/api/videos", tags=["Videos"]) app.include_router(publish.router, prefix="/api/publish", tags=["Publish"]) app.include_router(login_helper.router, prefix="/api", tags=["LoginHelper"]) app.include_router(auth.router) # /api/auth app.include_router(admin.router) # /api/admin app.include_router(ref_audios.router, prefix="/api/ref-audios", tags=["RefAudios"]) app.include_router(ai.router) # /api/ai app.include_router(tools.router, prefix="/api/tools", tags=["Tools"]) @app.on_event("startup") async def init_admin(): """ 服务启动时初始化管理员账号 """ admin_phone = settings.ADMIN_PHONE admin_password = settings.ADMIN_PASSWORD if not admin_phone or not admin_password: logger.warning("未配置 ADMIN_PHONE 和 ADMIN_PASSWORD,跳过管理员初始化") return try: from app.core.supabase import get_supabase from app.core.security import get_password_hash supabase = get_supabase() # 检查是否已存在 existing = supabase.table("users").select("id").eq("phone", admin_phone).execute() if existing.data: logger.info(f"管理员账号已存在: {admin_phone}") return # 创建管理员 supabase.table("users").insert({ "phone": admin_phone, "password_hash": get_password_hash(admin_password), "username": "Admin", "role": "admin", "is_active": True, "expires_at": None # 永不过期 }).execute() logger.success(f"管理员账号已创建: {admin_phone}") except Exception as e: logger.error(f"初始化管理员失败: {e}") @app.get("/health") def health(): return {"status": "ok"}