feat(backend): 更新核心模块和文件处理
- 更新配置模块 (config.py) - 更新数据库连接 (database.py) - 更新主应用入口 (main.py) - 更新数据模型 (models.py) - 更新基础 Schema (base.py) - 更新文件处理器 (docx, excel, pdf) - 更新 Dockerfile Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,23 +3,71 @@ YG-Dataset Backend Application
|
||||
FastAPI-based API server for dataset generation platform
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
import uuid
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from app.api.v1 import api_router
|
||||
from app.api.response import ApiResponse
|
||||
from app.core.config import settings
|
||||
from app.core.database import init_db
|
||||
from app.core.database import init_db, close_db
|
||||
from app.core.exceptions import AppException
|
||||
from app.core.logging import logger
|
||||
|
||||
|
||||
class RequestIDMiddleware(BaseHTTPMiddleware):
|
||||
"""Middleware to add request ID to each request"""
|
||||
|
||||
async def dispatch(self, request: Request, call_next):
|
||||
request_id = str(uuid.uuid4())
|
||||
request.state.request_id = request_id
|
||||
|
||||
# Add request ID to response headers
|
||||
response = await call_next(request)
|
||||
response.headers["X-Request-ID"] = request_id
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class TimingMiddleware(BaseHTTPMiddleware):
|
||||
"""Middleware to measure request processing time"""
|
||||
|
||||
async def dispatch(self, request: Request, call_next):
|
||||
start_time = time.time()
|
||||
|
||||
# Log request
|
||||
logger.info(f"→ {request.method} {request.url.path}")
|
||||
|
||||
response = await call_next(request)
|
||||
|
||||
process_time = time.time() - start_time
|
||||
response.headers["X-Process-Time"] = str(process_time)
|
||||
|
||||
# Log response
|
||||
logger.info(f"← {request.method} {request.url.path} | Status: {response.status_code} | Time: {process_time:.3f}s")
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Application lifespan events"""
|
||||
# Startup
|
||||
logger.info("Starting YG-Dataset application...")
|
||||
await init_db()
|
||||
logger.info("Database initialized successfully")
|
||||
yield
|
||||
# Shutdown
|
||||
pass
|
||||
logger.info("Shutting down YG-Dataset application...")
|
||||
await close_db()
|
||||
logger.info("Database connections closed")
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
@@ -29,15 +77,83 @@ app = FastAPI(
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
# CORS
|
||||
# Add custom middleware (order matters: last added = first executed)
|
||||
app.add_middleware(TimingMiddleware)
|
||||
app.add_middleware(RequestIDMiddleware)
|
||||
|
||||
# CORS - Configure properly for production
|
||||
# For development, you can use ["*"] but for production, specify exact origins
|
||||
ALLOWED_ORIGINS = settings.ALLOWED_ORIGINS.split(",") if settings.ALLOWED_ORIGINS else ["*"]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_origins=ALLOWED_ORIGINS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
# Exception handlers
|
||||
@app.exception_handler(AppException)
|
||||
async def app_exception_handler(request: Request, exc: AppException):
|
||||
"""Handle custom application exceptions"""
|
||||
logger.warning(f"App exception: {exc.message} | Code: {exc.code}")
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content=ApiResponse.fail(
|
||||
message=exc.message,
|
||||
error={"code": exc.code, "details": exc.details}
|
||||
).model_dump()
|
||||
)
|
||||
|
||||
|
||||
@app.exception_handler(RequestValidationError)
|
||||
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
||||
"""Handle validation exceptions"""
|
||||
errors = []
|
||||
for error in exc.errors():
|
||||
errors.append({
|
||||
"field": ".".join(str(loc) for loc in error["loc"]),
|
||||
"message": error["msg"],
|
||||
"type": error["type"]
|
||||
})
|
||||
logger.warning(f"Validation error: {errors}")
|
||||
return JSONResponse(
|
||||
status_code=422,
|
||||
content=ApiResponse.fail(
|
||||
message="Validation error",
|
||||
error={"code": "VALIDATION_ERROR", "details": {"errors": errors}}
|
||||
).model_dump()
|
||||
)
|
||||
|
||||
|
||||
@app.exception_handler(SQLAlchemyError)
|
||||
async def database_exception_handler(request: Request, exc: SQLAlchemyError):
|
||||
"""Handle database exceptions"""
|
||||
logger.error(f"Database error: {str(exc)}", exc_info=True)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content=ApiResponse.fail(
|
||||
message="Database operation failed",
|
||||
error={"code": "DATABASE_ERROR"}
|
||||
).model_dump()
|
||||
)
|
||||
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
async def general_exception_handler(request: Request, exc: Exception):
|
||||
"""Handle unhandled exceptions"""
|
||||
logger.error(f"Unhandled exception: {str(exc)}", exc_info=True)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content=ApiResponse.fail(
|
||||
message="Internal server error",
|
||||
error={"code": "INTERNAL_ERROR"}
|
||||
).model_dump()
|
||||
)
|
||||
|
||||
|
||||
# Include API routes
|
||||
app.include_router(api_router, prefix="/api/v1")
|
||||
|
||||
@@ -45,7 +161,10 @@ app.include_router(api_router, prefix="/api/v1")
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
"""Health check endpoint"""
|
||||
return {"status": "healthy", "version": "1.0.0"}
|
||||
return ApiResponse.ok(
|
||||
data={"status": "healthy", "version": "1.0.0"},
|
||||
message="Service is running"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user