Files
app_03_pregnancy/backend/app/main.py
Karlcarlo ded7ea1228 feat: 新增可配置的路径前缀以支持子路径部署
添加前后端环境变量配置示例,新增后端路径前缀配置项,更新后端路由与静态文件挂载逻辑,调整前端代理配置与 API 调用代码,适配子路径部署场景。
2026-05-21 20:56:22 +08:00

123 lines
3.6 KiB
Python

import logging
import os
import time
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, JSONResponse
from app.config import settings
from app.database import init_db
from app.routers import (
auth,
user,
report,
analysis,
chat,
health_records,
calendar,
doctor,
share,
)
# 日志配置
logging.basicConfig(
level=logging.DEBUG if settings.debug else logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger("孕育无忧")
@asynccontextmanager
async def lifespan(app: FastAPI):
logger.info("服务启动中...")
await init_db()
os.makedirs(settings.upload_dir, exist_ok=True)
logger.info("服务启动完成 http://%s:%s", settings.host, settings.port)
yield
logger.info("服务关闭")
app = FastAPI(
title="孕育无忧 API",
description="孕期健康检查报告解读后端服务",
version="1.0.0",
lifespan=lifespan,
)
api_prefix = settings.api_prefix.rstrip("/") or ""
if api_prefix and not api_prefix.startswith("/"):
api_prefix = f"/{api_prefix}"
uploads_prefix = settings.uploads_prefix.rstrip("/") or ""
if uploads_prefix and not uploads_prefix.startswith("/"):
uploads_prefix = f"/{uploads_prefix}"
# CORS 配置
cors_origins = (
["*"]
if settings.cors_origins == "*"
else [o.strip() for o in settings.cors_origins.split(",") if o.strip()]
)
app.add_middleware(
CORSMiddleware,
allow_origins=cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 请求日志中间件
@app.middleware("http")
async def log_requests(request: Request, call_next):
start = time.time()
response = await call_next(request)
duration = round((time.time() - start) * 1000)
logger.info(
"%s %s → %s (%dms)",
request.method,
request.url.path,
response.status_code,
duration,
)
return response
# 全局异常处理
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
logger.error("未处理异常 %s %s: %s", request.method, request.url.path, exc, exc_info=True)
return JSONResponse(
status_code=500,
content={"detail": "服务器内部错误,请稍后重试"},
)
app.mount(uploads_prefix or "/uploads", StaticFiles(directory=settings.upload_dir), name="uploads")
app.include_router(auth.router, prefix=f"{api_prefix}/auth", tags=["认证"])
app.include_router(user.router, prefix=f"{api_prefix}/user", tags=["用户"])
app.include_router(report.router, prefix=f"{api_prefix}/report", tags=["报告"])
app.include_router(analysis.router, prefix=f"{api_prefix}/analysis", tags=["AI解读"])
app.include_router(chat.router, prefix=f"{api_prefix}/chat", tags=["AI对话"])
app.include_router(health_records.router, prefix=f"{api_prefix}/health-records", tags=["健康档案"])
app.include_router(calendar.router, prefix=f"{api_prefix}/calendar", tags=["产检日历"])
app.include_router(doctor.router, prefix=f"{api_prefix}/doctor", tags=["专家咨询"])
app.include_router(share.router, prefix=f"{api_prefix}/share", tags=["分享"])
@app.get(f"{api_prefix}/health")
async def health_check():
return {"status": "ok", "service": "孕育无忧 API"}
@app.get("/")
async def web_demo():
"""Web 体验页面"""
return FileResponse(
os.path.join(os.path.dirname(__file__), "static", "index.html")
)