Files
sasa-membership/backend/app/main.py
T
nathanb d024bf7fa3 stuff changed:
- ui has been made 'kinda better' (after making it worse for a while lol
- ESP rfid readers are now supported [ill upload the code for them in another repo later]
- admin system has been secured a bit better and seems to be working well
2026-05-08 20:46:58 +01:00

93 lines
2.3 KiB
Python

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"}