refactor: align backend layout and supabase infra

Consolidate backend modules/tests under the backend package while syncing Supabase compose/env config and related plans.
This commit is contained in:
qzl
2026-02-05 15:13:06 +08:00
parent 3cfcb11240
commit ad06fe7de4
111 changed files with 5540 additions and 1362 deletions
+133
View File
@@ -0,0 +1,133 @@
from __future__ import annotations
from fastapi import FastAPI, HTTPException, Request
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
from core.config.settings import config
from core.http.models import HealthResponse
from core.http.response import build_problem_details
from core.logging import configure_logging, get_logger
from v1.router import router as mobile_router
configure_logging(config)
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=config.cors.allow_origins,
allow_credentials=config.cors.allow_credentials,
allow_methods=config.cors.allow_methods,
allow_headers=config.cors.allow_headers,
)
app.include_router(mobile_router)
logger = get_logger("api.app")
@app.get("/health", response_model=HealthResponse)
async def health() -> HealthResponse:
return HealthResponse(status="ok")
def _build_http_error_response(
request: Request,
exc: Exception,
status_code: int,
detail: object,
) -> JSONResponse:
instance = request.url.path
detail_text = detail if isinstance(detail, str) else "Request failed"
logger.warning(
"HTTP error",
status_code=status_code,
detail=detail_text,
detail_extra=detail,
path=request.url.path,
method=request.method,
)
problem = build_problem_details(
status_code=status_code,
detail=detail_text,
instance=instance,
)
return JSONResponse(
status_code=status_code,
content=problem.model_dump(),
media_type="application/problem+json",
)
@app.exception_handler(HTTPException)
async def http_exception_handler(
request: Request,
exc: HTTPException,
) -> JSONResponse:
return _build_http_error_response(
request=request,
exc=exc,
status_code=exc.status_code,
detail=exc.detail,
)
@app.exception_handler(StarletteHTTPException)
async def starlette_http_exception_handler(
request: Request,
exc: StarletteHTTPException,
) -> JSONResponse:
return _build_http_error_response(
request=request,
exc=exc,
status_code=exc.status_code,
detail=exc.detail,
)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(
request: Request,
exc: RequestValidationError,
) -> JSONResponse:
instance = request.url.path
logger.warning(
"Request validation error",
path=request.url.path,
method=request.method,
errors=exc.errors(),
)
problem = build_problem_details(
status_code=422,
detail="Invalid request",
instance=instance,
)
return JSONResponse(
status_code=422,
content=problem.model_dump(),
media_type="application/problem+json",
)
@app.exception_handler(Exception)
async def unhandled_exception_handler(
request: Request,
exc: Exception,
) -> JSONResponse:
instance = request.url.path
logger.exception(
"Unhandled error",
path=request.url.path,
method=request.method,
)
problem = build_problem_details(
status_code=500,
detail="Internal Server Error",
instance=instance,
)
return JSONResponse(
status_code=500,
content=problem.model_dump(),
media_type="application/problem+json",
)