Files
hms-licht-ton/backend/app/main.py
T
Agent Zero e66bcc7058 fix: seed admin user on startup and run sync as background task
- Seed/update admin user from ADMIN_USERNAME/ADMIN_PASSWORD settings on
  startup (admin_users table was empty, login was impossible)
- POST /api/admin/sync now returns immediately and runs the equipment
  sync via BackgroundTasks (full sync took minutes and hit nginx 504)
- run_sync accepts optional sync_log_id to reuse a pre-created log entry
- Adapt admin router test to background sync behavior
2026-09-24 21:41:35 +02:00

95 lines
3.3 KiB
Python

"""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.models.admin_user import AdminUser
from app.auth import get_password_hash
from sqlalchemy import select
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)
async def seed_admin_user() -> None:
"""Ensure the admin user from settings exists (create or update password)."""
async with async_session() as db:
result = await db.execute(
select(AdminUser).where(AdminUser.username == settings.admin_username)
)
user = result.scalar_one_or_none()
if not user:
db.add(AdminUser(
username=settings.admin_username,
password_hash=get_password_hash(settings.admin_password),
))
logger.info("Seeded admin user %s", settings.admin_username)
else:
user.password_hash = get_password_hash(settings.admin_password)
await db.commit()
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan: init DB, seed admin, start scheduler, connect cache."""
await init_db()
await seed_admin_user()
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())