from contextlib import asynccontextmanager import os from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from .routers import admin, annotations, auth, backup, pdfs, ws from .database import SessionLocal from .models import User, UserRole, UserStatus from .security import hash_password def _seed_admin() -> None: """Create the default admin account if it does not exist yet.""" username = os.getenv("ADMIN_USERNAME", "admin") password = os.getenv("ADMIN_PASSWORD", "Admin@12345") email = os.getenv("ADMIN_EMAIL", "admin@lms.local") db = SessionLocal() try: exists = db.query(User).filter(User.username == username).first() if not exists: db.add(User( username = username, email = email, password_hash = hash_password(password), role = UserRole.admin, status = UserStatus.approved, )) db.commit() print(f"[seed] Admin account '{username}' created.") else: print(f"[seed] Admin account '{username}' already exists — skipped.") finally: db.close() @asynccontextmanager async def lifespan(app: FastAPI): # Tables are managed by Alembic migrations. # Run: alembic upgrade head (done by docker-compose entrypoint) _seed_admin() yield app = FastAPI(title="LMS API", lifespan=lifespan) app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:3000", "http://localhost:3001"], allow_credentials=True, # required for cookies allow_methods=["*"], allow_headers=["*"], ) app.include_router(auth.router, prefix="/api") app.include_router(admin.router, prefix="/api") app.include_router(backup.router, prefix="/api") app.include_router(pdfs.router, prefix="/api") app.include_router(annotations.router, prefix="/api") app.include_router(ws.router) # WebSocket — no /api prefix (ws:// path)