"""نقطه ورود برنامه FastAPI برای Core Platform Service.""" from __future__ import annotations from contextlib import asynccontextmanager from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from app import __version__ from app.api.v1 import api_router, health from app.core.cache import cache from app.core.config import settings from app.core.logging import configure_logging, get_logger from app.middlewares.tenant import TenantResolutionMiddleware from shared.exceptions import AppError from shared.responses import ErrorDetail, ErrorResponse configure_logging() logger = get_logger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): logger.info("service_starting", extra={"service": settings.service_name}) yield await cache.close() logger.info("service_stopped") def create_app() -> FastAPI: app = FastAPI( title=f"{settings.platform_name} - Core Platform Service", version=__version__, description="سرویس هسته پلتفرم SaaS چندمستأجری (فاز ۱).", lifespan=lifespan, ) # CORS - در production باید origins محدود شود. # allow_origin_regex برای ساب‌دامین tenantها (.torbatyar.ir) app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origin_list, allow_origin_regex=r"https?://([a-z0-9-]+\.)*torbatyar\.ir", allow_credentials=False, allow_methods=["*"], allow_headers=["*"], ) # Middleware تشخیص tenant. app.add_middleware( TenantResolutionMiddleware, base_domain=settings.platform_base_domain or None ) # ثبت exception handlerها برای تبدیل خطاهای دامنه به پاسخ استاندارد. _register_exception_handlers(app) # routerها app.include_router(health.router) app.include_router(api_router, prefix=settings.api_v1_prefix) return app def _register_exception_handlers(app: FastAPI) -> None: @app.exception_handler(AppError) async def app_error_handler(request: Request, exc: AppError) -> JSONResponse: response = ErrorResponse( error=ErrorDetail( code=exc.error_code, message=exc.message, details=exc.details ) ) return JSONResponse(status_code=exc.status_code, content=response.model_dump()) @app.exception_handler(Exception) async def unhandled_handler(request: Request, exc: Exception) -> JSONResponse: logger.error("unhandled_exception", extra={"error": str(exc)}, exc_info=exc) response = ErrorResponse( error=ErrorDetail(code="internal_error", message="خطای داخلی سرور") ) return JSONResponse(status_code=500, content=response.model_dump()) app = create_app()