"""FastAPI application assembly with CORS, routers, and APScheduler.""" import logging from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from apscheduler.schedulers.asyncio import AsyncIOScheduler from app.config import get_settings from app.database import engine, init_db, async_session from app.cache import cache from app.routers import equipment, health, admin, rental_requests, contact from app.services.sync_service import SyncService from app.services.email_service import EmailService from app.mcp_server import get_mcp_app, mcp_session_manager logger = logging.getLogger(__name__) settings = get_settings() scheduler = AsyncIOScheduler() async def run_equipment_sync() -> None: """Scheduled job: run equipment sync every 6 hours.""" logger.info("Starting scheduled equipment sync") async with async_session() as db: sync_service = SyncService(db) result = await sync_service.run_sync() logger.info("Scheduled sync complete: %s", result) async def run_email_retry() -> None: """Scheduled job: retry failed emails every 15 minutes.""" logger.info("Starting scheduled email retry") async with async_session() as db: sent = await EmailService.retry_failed_emails(db) logger.info("Email retry complete: %d emails sent", sent) @asynccontextmanager async def lifespan(app: FastAPI): """Application lifespan: init DB, start scheduler, connect cache.""" await init_db() await cache.connect() _mcp_cm = mcp_session_manager.run() await _mcp_cm.__aenter__() scheduler.add_job(run_equipment_sync, trigger="cron", hour="*/6", id="equipment_sync", replace_existing=True) scheduler.add_job(run_email_retry, trigger="cron", minute="*/15", id="email_retry", replace_existing=True) scheduler.start() logger.info("APScheduler started") yield scheduler.shutdown(wait=False) await _mcp_cm.__aexit__(None, None, None) await cache.disconnect() await engine.dispose() app = FastAPI(title="HMS Licht & Ton API", version="1.0.0", lifespan=lifespan) origins = [o.strip() for o in settings.cors_origins.split(",") if o.strip()] app.add_middleware(CORSMiddleware, allow_origins=origins, allow_methods=["GET", "POST"], allow_headers=["*"], allow_credentials=True) app.include_router(equipment.router) app.include_router(health.router) app.include_router(admin.router) app.include_router(rental_requests.router) app.include_router(contact.router) app.mount("/mcp", get_mcp_app())