- Unify API: lib/api.ts uses /api/v1, inbox uses /api/inbox (rewrites) - Remove localhost refs: openapi, inbox page - Add rewrites: /api/inbox|tmc -> inbox-server, /api/v1 -> FastAPI - Add stub routes: knowledge/insights, recommendations, search, log-error - Transfer from PAPA: prompts (inspection, tmc), scripts, supabase, data/tmc-requests - Fix inbox-server: ORDER BY created_at, package.json - Remove redundant app/api/inbox/files route (rewrites handle it) - knowledge/ in gitignore (large PDFs) Co-authored-by: Cursor <cursoragent@cursor.com>
74 lines
2.9 KiB
Python
74 lines
2.9 KiB
Python
"""
|
|
Централизованная обработка исключений для API.
|
|
"""
|
|
import logging
|
|
from fastapi import Request, status
|
|
from fastapi.responses import JSONResponse
|
|
from fastapi.exceptions import RequestValidationError
|
|
from sqlalchemy.exc import SQLAlchemyError, IntegrityError
|
|
from pydantic import ValidationError
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def pydantic_validation_error_handler(request: Request, exc: ValidationError):
|
|
"""Ошибки валидации Pydantic при сериализации ответа (например, datetime из SQLite)."""
|
|
errs = exc.errors()
|
|
logger.warning("Pydantic ValidationError on %s: %s", request.url.path, errs)
|
|
return JSONResponse(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
content={
|
|
"detail": "Ошибка формата данных. Подробности в логе бэкенда.",
|
|
"errors": errs,
|
|
},
|
|
)
|
|
|
|
|
|
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
|
"""Обработчик ошибок валидации Pydantic."""
|
|
errors = exc.errors()
|
|
logger.warning(f"Validation error on {request.url.path}: {errors}")
|
|
return JSONResponse(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
content={
|
|
"detail": "Ошибка валидации данных",
|
|
"errors": errors,
|
|
},
|
|
)
|
|
|
|
|
|
async def integrity_error_handler(request: Request, exc: IntegrityError):
|
|
"""Обработчик ошибок целостности БД (дубликаты, внешние ключи и т.д.)."""
|
|
logger.error(f"Database integrity error on {request.url.path}: {str(exc)}")
|
|
return JSONResponse(
|
|
status_code=status.HTTP_409_CONFLICT,
|
|
content={
|
|
"detail": "Нарушение целостности данных. Возможно, запись уже существует.",
|
|
},
|
|
)
|
|
|
|
|
|
async def sqlalchemy_error_handler(request: Request, exc: SQLAlchemyError):
|
|
"""Обработчик общих ошибок SQLAlchemy."""
|
|
logger.error(f"Database error on {request.url.path}: {str(exc)}", exc_info=True)
|
|
return JSONResponse(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
content={
|
|
"detail": "Ошибка базы данных. Обратитесь к администратору.",
|
|
},
|
|
)
|
|
|
|
|
|
async def general_exception_handler(request: Request, exc: Exception):
|
|
"""Обработчик всех остальных исключений."""
|
|
logger.error(
|
|
f"Unhandled exception on {request.url.path}: {str(exc)}",
|
|
exc_info=True,
|
|
)
|
|
return JSONResponse(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
content={
|
|
"detail": "Внутренняя ошибка сервера. Обратитесь к администратору.",
|
|
},
|
|
)
|