from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from sqlalchemy import func, select from app.config import settings from app.database import async_session, engine from app.models import Base, Show, StationConfig, HistoryEntry, TeamMember, CommunityHighlight from app.user_models import User from app.api import events, tiers, auth, station_config, history, team, community, shows, storage async def _ensure_station_config(session): """Ensure the singleton station config row exists (idempotent).""" result = await session.execute( select(StationConfig).where(StationConfig.key == "default") ) if result.scalar_one_or_none() is None: print(" → Station config missing — seeding defaults...") from seed import seed as run_seed # Only seed the station config, not the full dataset from seed import STATION_CONFIG, _upsert_station_config await _upsert_station_config(session, STATION_CONFIG) await session.commit() print(" ✓ Station config seeded") @asynccontextmanager async def lifespan(app: FastAPI): # Create tables on startup async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) # Auto-seed if database is empty async with async_session() as session: result = await session.execute(select(func.count(Show.id))) count = result.scalar() if count == 0: print(" → Database is empty — running seed...") from seed import seed as run_seed await run_seed() return # Full seed already included station config # Ensure station config exists (runs even if DB already had data) async with async_session() as session: await _ensure_station_config(session) # Bootstrap admin user if configured if settings.ADMIN_USERNAME and settings.ADMIN_PASSWORD: async with async_session() as session: result = await session.execute( select(User).where( User.email == f"{settings.ADMIN_USERNAME}@local", User.auth_provider == "local", ) ) if result.scalar_one_or_none() is None: admin_user = User( email=f"{settings.ADMIN_USERNAME}@local", display_name=settings.ADMIN_USERNAME, auth_provider="local", is_admin=True, ) session.add(admin_user) await session.commit() print(f" → Bootstrap admin created: {settings.ADMIN_USERNAME}") yield app = FastAPI( title="KMountain Flower Radio API", description="Backend API for the KMountain Flower Radio Station website.", version="1.0.0", lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=settings.CORS_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Register routers app.include_router(auth.router, prefix="/api/auth", tags=["auth"]) app.include_router(events.router, prefix="/api/events", tags=["events"]) app.include_router(tiers.router, prefix="/api/tiers", tags=["tiers"]) app.include_router(station_config.router, prefix="/api/station-config", tags=["station-config"]) app.include_router(history.router, prefix="/api/history", tags=["history"]) app.include_router(team.router, prefix="/api/team", tags=["team"]) app.include_router(community.router, prefix="/api/community", tags=["community"]) app.include_router(shows.router, prefix="/api/shows", tags=["shows"]) app.include_router(storage.router, prefix="/api/storage", tags=["storage"]) # Dev-only admin router (disabled in production) if settings.ENV != "production": from app.api import admin app.include_router(admin.router, prefix="/api/admin", tags=["admin"])