| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182 |
- from __future__ import annotations
- from contextlib import asynccontextmanager
- from typing import AsyncGenerator
- from fastapi import FastAPI
- from fastapi.middleware.cors import CORSMiddleware
- from app.config import settings
- from app.db import close_pool, init_pool, get_pool
- from app.middleware.logging import LoggingMiddleware
- from app.services.ws_hub import hub as ws_hub # noqa: F401
- from app.services.scheduler import start_scheduler, stop_scheduler
- @asynccontextmanager
- async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
- await init_pool()
- pool = get_pool()
- # 建 users 表并初始化 admin
- await pool.execute("""
- CREATE TABLE IF NOT EXISTS crawl.users (
- id BIGSERIAL PRIMARY KEY,
- username VARCHAR(100) NOT NULL UNIQUE,
- password_hash TEXT NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
- )
- """)
- from app.routers.auth import ensure_admin_user
- await ensure_admin_user()
- # 清理上次进程意外退出遗留的僵死任务
- cleaned = await pool.execute(
- """
- UPDATE scrape_jobs SET status = 'failed', error = '服务重启,任务中断', updated_at = NOW()
- WHERE status IN ('pending', 'running')
- """
- )
- import logging
- logging.getLogger(__name__).info(f"[startup] 清理僵死任务: {cleaned}")
- await start_scheduler()
- yield
- await stop_scheduler()
- await close_pool()
- app = FastAPI(title="Sentinel Lens", lifespan=lifespan)
- app.add_middleware(
- CORSMiddleware,
- allow_origins=settings.allowed_origins,
- allow_credentials=True,
- allow_methods=["*"],
- allow_headers=["*"],
- )
- app.add_middleware(LoggingMiddleware)
- # Router registration
- from app.routers import stats # noqa: E402
- from app.routers import logs # noqa: E402
- from app.routers import scrape # noqa: E402
- from app.routers import public # noqa: E402
- from app.routers import ws # noqa: E402
- from app.routers import models # noqa: E402
- from app.routers import schedule # noqa: E402
- from app.routers import discounts # noqa: E402
- from app.routers import auth # noqa: E402
- from app.routers import scrape_stats # noqa: E402
- app.include_router(stats.router, prefix="/api")
- app.include_router(logs.router, prefix="/api")
- app.include_router(scrape.router, prefix="/api")
- app.include_router(public.router, prefix="/api/public")
- app.include_router(models.router, prefix="/api")
- app.include_router(schedule.router, prefix="/api")
- app.include_router(discounts.router, prefix="/api")
- app.include_router(auth.router, prefix="/api")
- app.include_router(scrape_stats.router, prefix="/api")
- app.include_router(ws.router)
- @app.get("/")
- async def health_check() -> dict:
- return {"status": "ok", "service": "sentinel-lens"}
|