from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from sqlalchemy import text from api import ai, auth, categories, collection, fx, image, ozon, products, publish, shops from config import get_settings from db import get_engine # web/ 是 v1 工具台,留在仓库根,故上跳一级 WEB_DIR = Path(__file__).resolve().parents[1] / "web" # 本地存储(开发兜底)媒体目录 MEDIA_DIR = Path(__file__).resolve().parents[1] / "data" / "media" app = FastAPI(title="Ozon Seller Kit", version="0.2.0") settings = get_settings() if settings.cors_origin_list: app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origin_list, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 业务路由 app.include_router(auth.router) app.include_router(collection.router) app.include_router(products.router) app.include_router(shops.router) app.include_router(categories.router) app.include_router(publish.router) app.include_router(fx.router) app.include_router(ai.router) app.include_router(image.router) app.include_router(ozon.router) @app.on_event("startup") async def on_startup() -> None: # 开发便利:确保表存在(生产以 Alembic 迁移为准,create_all 幂等不删表) from db import Base import models # noqa: F401 MEDIA_DIR.mkdir(parents=True, exist_ok=True) async with get_engine().begin() as conn: await conn.run_sync(Base.metadata.create_all) @app.get("/api/health") async def health() -> dict: db_ok = True try: async with get_engine().connect() as conn: await conn.execute(text("SELECT 1")) except Exception: # noqa: BLE001 db_ok = False return {"status": "ok" if db_ok else "degraded", "db": db_ok} # 本地媒体(开发兜底存储) MEDIA_DIR.mkdir(parents=True, exist_ok=True) app.mount("/media", StaticFiles(directory=str(MEDIA_DIR)), name="media") if WEB_DIR.is_dir(): app.mount("/", StaticFiles(directory=str(WEB_DIR), html=True), name="web")