vproxy / src /aistudio_api /api /app.py
jlshu's picture
Upload 85 files
95f8c4b verified
Raw History Blame Contribute Delete
4.62 kB
"""FastAPI application entrypoint."""
from __future__ import annotations
import argparse
import logging
from contextlib import asynccontextmanager
from fastapi import Depends, FastAPI
from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles
from aistudio_api.infrastructure.gateway.client import AIStudioClient
from .dependencies import require_api_key
from .routes_accounts import router as accounts_router
from .routes_gemini import router as gemini_router
from .routes_models import router as models_router
from .routes_system import protected_router as system_protected_router
from .routes_system import public_router as system_public_router
from .state import runtime_state
logging.basicConfig(
level=logging.INFO, format="%(asctime)s [%(name)s] %(message)s", datefmt="%H:%M:%S"
)
logger = logging.getLogger("aistudio.server")
@asynccontextmanager
async def lifespan(app: FastAPI):
import asyncio
from aistudio_api.application.account_rotator import RotationMode, init_rotator
from aistudio_api.application.account_service import AccountService
from aistudio_api.config import settings
from aistudio_api.infrastructure.account.account_store import AccountStore
client = AIStudioClient(
port=runtime_state.browser_port,
)
runtime_state.client = client
from aistudio_api.config import settings as app_settings
runtime_state.busy_lock = asyncio.Semaphore(app_settings.max_concurrency)
# 注入 snapshot 缓存引用,切号时需要清除
from aistudio_api.infrastructure.gateway.client import _snapshot_cache
runtime_state.snapshot_cache = _snapshot_cache
# 初始化账号管理服务
account_store = AccountStore()
account_service = AccountService(account_store)
runtime_state.account_service = account_service
# 初始化账号轮询器
rotation_mode = getattr(settings, "account_rotation_mode", "round_robin")
cooldown = getattr(settings, "account_cooldown_seconds", 60)
rotator = init_rotator(
account_store,
mode=RotationMode(rotation_mode),
cooldown_seconds=cooldown,
)
runtime_state.rotator = rotator
logger.info(
"Client initialized (port=%s, rotation=%s, accounts=%d)",
runtime_state.browser_port,
rotator.mode,
len(account_store.list_accounts()),
)
# 后台预热浏览器,避免首次请求延迟
warmup_task = None
async def _warmup():
try:
await client.warmup()
except Exception as e:
logger.warning("浏览器预热失败: %s", e)
warmup_task = asyncio.create_task(_warmup())
yield
logger.info("Shutting down")
if warmup_task and not warmup_task.done():
warmup_task.cancel()
if client:
try:
await client.close()
except Exception as e:
logger.debug("Error closing client: %s", e)
runtime_state.client = None
runtime_state.busy_lock = None
runtime_state.account_service = None
runtime_state.rotator = None
app = FastAPI(title="AI Studio API", lifespan=lifespan)
app.include_router(system_public_router)
app.include_router(system_protected_router, dependencies=[Depends(require_api_key)])
app.include_router(gemini_router, dependencies=[Depends(require_api_key)])
app.include_router(models_router, dependencies=[Depends(require_api_key)])
app.include_router(accounts_router, dependencies=[Depends(require_api_key)])
# 挂载静态文件
import os
static_dir = os.path.join(os.path.dirname(__file__), "..", "static")
if os.path.isdir(static_dir):
app.mount("/static", StaticFiles(directory=static_dir), name="static")
@app.get("/")
async def root():
return RedirectResponse(url="/static/index.html")
@app.get("/login")
async def login_page():
return RedirectResponse(url="/static/login.html")
@app.get("/auth/check")
async def auth_check():
"""检查认证状态,用于前端判断是否需要登录。"""
from aistudio_api.config import settings
return {"auth_enabled": settings.auth_enabled}
def main():
from aistudio_api.config import settings
parser = argparse.ArgumentParser(description="AI Studio API Server")
parser.add_argument("--port", type=int, default=settings.port)
parser.add_argument("--browser-port", type=int, default=settings.browser_port)
args = parser.parse_args()
runtime_state.browser_port = args.browser_port
import uvicorn
logger.info("Starting server on port %s", args.port)
uvicorn.run(app, host="0.0.0.0", port=args.port, log_level="info")