Spaces:
Running
Running
| import logging | |
| import os | |
| from contextlib import asynccontextmanager | |
| from fastapi import FastAPI | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.staticfiles import StaticFiles | |
| from fastapi_cache import FastAPICache | |
| from fastapi_cache.backends.inmemory import InMemoryBackend | |
| from fastapi_cache.backends.redis import RedisBackend | |
| from redis import asyncio as aioredis | |
| from app.api.routes import admin, auth, bookmarks, tests, series | |
| from app.core.config import settings | |
| from app.core.csrf import CSRFHeaderMiddleware | |
| from app.core.limiter import limiter | |
| from slowapi.errors import RateLimitExceeded | |
| from slowapi.middleware import SlowAPIMiddleware | |
| from slowapi import _rate_limit_exceeded_handler | |
| logger = logging.getLogger(__name__) | |
| async def lifespan(app: FastAPI): | |
| redis = None | |
| try: | |
| redis = aioredis.from_url( | |
| settings.REDIS_URL, encoding="utf8", decode_responses=True | |
| ) | |
| await redis.ping() | |
| FastAPICache.init(RedisBackend(redis), prefix="gateprep-cache") | |
| except Exception as exc: | |
| if redis is not None: | |
| await redis.close() | |
| redis = None | |
| if ( | |
| "localhost" not in settings.REDIS_URL | |
| and "127.0.0.1" not in settings.REDIS_URL | |
| ): | |
| raise RuntimeError( | |
| f"Redis cache unavailable at REDIS_URL={settings.REDIS_URL}" | |
| ) from exc | |
| logger.warning( | |
| "Redis cache unavailable at %s; using in-memory cache. " | |
| "Set REDIS_URL to a reachable Redis instance for shared production caching.", | |
| settings.REDIS_URL, | |
| ) | |
| FastAPICache.init(InMemoryBackend(), prefix="gateprep-cache") | |
| redis = None | |
| try: | |
| yield | |
| finally: | |
| if redis is not None: | |
| await redis.close() | |
| app = FastAPI(title="GATE Prep Platform", version="2.0.0", lifespan=lifespan) | |
| app.state.limiter = limiter | |
| app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) | |
| app.add_middleware(CSRFHeaderMiddleware) | |
| app.add_middleware(SlowAPIMiddleware) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=settings.cors_origins_list, | |
| allow_credentials=True, | |
| allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], | |
| allow_headers=["Content-Type", "Accept", "X-Requested-With"], | |
| ) | |
| app.include_router(auth.router) | |
| app.include_router(admin.router) | |
| app.include_router(tests.router) | |
| app.include_router(bookmarks.router) | |
| app.include_router(series.router) | |
| if os.path.exists(settings.UPLOAD_DIR): | |
| app.mount("/uploads", StaticFiles(directory=settings.UPLOAD_DIR), name="uploads") | |
| def root(): | |
| return {"status": "ok", "service": "GATE Prep API", "health": "/health"} | |
| def health(): | |
| return {"status": "ok", "version": "2.0.0"} | |