Files
beige/backend/main.py
yangqianqian df1856d793 上线版: 产品表单统一+form嵌套修复+用户管理+部署+三套叙事
- 产品编辑入口统一走 ProductFormFull(卖点/风格/人群/品牌词全字段);
  修复开任务页 <form> 套 <form> 致"编辑产品"报错、改不了、跳回首个产品
- dashboard 入口卡片对齐实际路由: 系统管理(/config) 与 工作配置(/settings) 分开;
  settings ?tab=products 直达改用挂载后读 URL, 消除 hydration mismatch
- 新增用户管理(users API/admin service/改密页) + alembic 022/023/024
- 上线部署: Dockerfile / docker-compose.prod+https / nginx https / .env.example
- A8 三套正交叙事(痛点/场景/成分背书) + beige 调色去AI化 + 飞轮 text_import 高权重信号

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 18:08:13 +08:00

121 lines
4.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
main.py — FastAPI 应用入口
统一响应包络 / 全局异常处理 / 健康检查 / 路由注册
"""
import logging
import os
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from app.core.config import get_settings
from app.core.response import CloverHTTPException, ErrorCode
logger = logging.getLogger(__name__)
settings = get_settings()
def _cors_origins() -> list[str]:
origins = [
item.strip()
for item in (settings.CORS_ALLOW_ORIGINS or "").split(",")
if item.strip()
]
if settings.APP_ENV == "production":
return [origin for origin in origins if origin != "*"]
dev_origins = set(origins or ["http://localhost:3000"])
if "http://localhost:3000" in dev_origins:
dev_origins.add("http://127.0.0.1:3000")
if "http://127.0.0.1:3000" in dev_origins:
dev_origins.add("http://localhost:3000")
return sorted(dev_origins)
def create_app() -> FastAPI:
app = FastAPI(
title="Clover API",
version="0.1.0",
docs_url="/docs" if settings.APP_ENV != "production" else None,
redoc_url=None,
)
# ── CORS ──────────────────────────────────────────
app.add_middleware(
CORSMiddleware,
allow_origins=_cors_origins(),
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ── 全局异常处理(统一包络)──────────────────────
@app.exception_handler(CloverHTTPException)
async def clover_http_exception_handler(request: Request, exc: CloverHTTPException):
return JSONResponse(
status_code=exc.status_code,
content={"code": exc.biz_code, "message": exc.biz_message},
)
@app.exception_handler(Exception)
async def generic_exception_handler(request: Request, exc: Exception):
logger.error("Unhandled exception: %s %s%s", request.method, request.url, exc)
return JSONResponse(
status_code=500,
content={"code": ErrorCode.SERVER_ERROR, "message": "服务器内部错误"},
)
# ── 健康检查Docker 健康检查用)──────────────────
@app.get("/health")
async def health():
return {"status": "ok"}
# ── 路由注册(每个模块路由独立文件,这里汇总注册)─
_register_routers(app)
# ── 静态文件G4坑修复图片 /uploads 路由)──────
uploads_dir = settings.UPLOAD_ABS_ROOT
os.makedirs(uploads_dir, exist_ok=True)
app.mount("/uploads", StaticFiles(directory=uploads_dir), name="uploads")
return app
def _register_routers(app: FastAPI) -> None:
from app.api.v1.auth import router as auth_router
from app.api.v1.users import router as users_router
from app.api.v1.api_keys import router as api_keys_router
from app.api.v1.products import router as products_router
from app.api.v1.product_images import router as product_images_router
from app.api.v1.benchmarks import router as benchmarks_router
from app.api.v1.tasks import router as tasks_router
from app.api.v1.task_actions import router as task_actions_router
from app.api.v1.review import router as review_router
from app.api.v1.delivery import router as delivery_router
from app.api.v1.stream import router as stream_router
from app.api.v1.workspaces import router as workspaces_router
from app.api.v1.fission import router as fission_router
from app.api.v1.exports import router as exports_router
prefix = "/api/v1"
app.include_router(auth_router, prefix=prefix)
app.include_router(users_router, prefix=prefix)
app.include_router(api_keys_router, prefix=prefix)
# 导出 router 必须在 products_router 之前:否则 /products/export 被 /products/{product_id} 吞掉
app.include_router(exports_router, prefix=prefix)
app.include_router(products_router, prefix=prefix)
app.include_router(product_images_router, prefix=prefix)
app.include_router(benchmarks_router, prefix=prefix)
app.include_router(tasks_router, prefix=prefix)
app.include_router(task_actions_router, prefix=prefix)
app.include_router(review_router, prefix=prefix)
app.include_router(delivery_router, prefix=prefix)
app.include_router(stream_router, prefix=prefix)
app.include_router(workspaces_router, prefix=prefix)
app.include_router(fission_router, prefix=prefix)
app = create_app()