import asyncio import time from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi import Request from contextlib import asynccontextmanager from .core.config import settings from .api.v1 import api_router from .core.database import SessionLocal, get_db from .core.init_db import init_default_data from .services.attendance_service import close_stale_attendance_sessions from sqlalchemy.orm import Session async def close_stale_attendance_loop(): """Periodically close forgotten RFID check-ins after midnight.""" while True: await asyncio.sleep(3600) db = SessionLocal() try: close_stale_attendance_sessions(db) except Exception as exc: print(f"Failed to close stale attendance sessions: {exc}") finally: db.close() @asynccontextmanager async def lifespan(app: FastAPI): """Handle startup and shutdown events""" # Startup db: Session = next(get_db()) try: init_default_data(db) close_stale_attendance_sessions(db) finally: db.close() attendance_task = asyncio.create_task(close_stale_attendance_loop()) yield # Shutdown (if needed) attendance_task.cancel() try: await attendance_task except asyncio.CancelledError: pass app = FastAPI( title=settings.APP_NAME, version=settings.APP_VERSION, openapi_url=f"{settings.API_V1_PREFIX}/openapi.json", lifespan=lifespan ) # Set up CORS app.add_middleware( CORSMiddleware, allow_origins=settings.BACKEND_CORS_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.middleware("http") async def add_request_timing_headers(request: Request, call_next): started_at = time.perf_counter() response = await call_next(request) elapsed_ms = (time.perf_counter() - started_at) * 1000 response.headers["X-Process-Time-Ms"] = f"{elapsed_ms:.1f}" response.headers["Server-Timing"] = f"app;dur={elapsed_ms:.1f}" return response # Include API router app.include_router(api_router, prefix=settings.API_V1_PREFIX) @app.get("/") async def root(): return { "message": f"Welcome to {settings.APP_NAME}", "version": settings.APP_VERSION, "docs": "/docs" } @app.get("/health") async def health_check(): return {"status": "healthy"}