import logging import os import time from contextlib import asynccontextmanager from fastapi import FastAPI from database import engine, Base logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) from api import kds as kds_api from migrations.add_kds_tables import run_migration as run_kds_tables from migrations.add_kds_course_flow import run_migration as run_kds_course_flow from migrations.add_kds_order_tracking import run_migration as run_kds_order_tracking from migrations.add_kds_bookings_refresh import run_migration as run_kds_bookings_refresh from services.signalr_listener import start_signalr_listener, stop_signalr_listener logger = logging.getLogger(__name__) async def _run(name, coro): try: await coro() logger.info(f"{name} migration completed") except Exception as e: logger.warning(f"{name} migration warning (may be expected): {e}") @asynccontextmanager async def lifespan(app: FastAPI): # Create any SQLAlchemy-mapped KDS tables (idempotent) async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) # KDS migrations — extend kitchen_db schema with KDS columns/tables. # Requires kitchen to be deployed first (kitchen_settings must exist). await _run("KDS tables", run_kds_tables) await _run("KDS course flow", run_kds_course_flow) await _run("KDS order tracking", run_kds_order_tracking) await _run("KDS bookings refresh", run_kds_bookings_refresh) await start_signalr_listener() yield await stop_signalr_listener() await engine.dispose() app = FastAPI( title="KDS", description="Kitchen Display System — SambaPOS SignalR ticket feed, course flow", version="1.0.0", lifespan=lifespan, ) STARTED_AT = str(int(time.time() * 1000)) app.include_router(kds_api.router, prefix="/api/kds", tags=["KDS"]) @app.get("/health") async def health_check(): return {"status": "healthy", "version": os.environ.get("BUILD_VERSION", STARTED_AT)}