from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from config import settings from database import engine, Base from auth_utils import hash_password import models.admin # noqa: F401 import models.site # noqa: F401 import models.menu_snapshot # noqa: F401 import models.online_order # noqa: F401 import models.manager_account # noqa: F401 from routers import auth, sites, heartbeat def _seed_default_admin(): from sqlalchemy.orm import Session from models.admin import Admin with Session(engine) as db: if not db.query(Admin).filter(Admin.username == settings.ADMIN_USERNAME).first(): db.add(Admin( username=settings.ADMIN_USERNAME, password_hash=hash_password(settings.ADMIN_PASSWORD), role="sysadmin", )) db.commit() def _run_migrations(): """Apply additive schema changes that create_all won't handle.""" from sqlalchemy import text migrations = [ # Per-site order counter for public_ref generation (e.g. "ORD-0042") "ALTER TABLE sites ADD COLUMN order_counter INTEGER NOT NULL DEFAULT 0", ] for sql in migrations: try: with engine.connect() as conn: conn.execute(text(sql)) conn.commit() except Exception: pass @asynccontextmanager async def lifespan(app: FastAPI): Base.metadata.create_all(bind=engine) _run_migrations() _seed_default_admin() yield app = FastAPI(title="POS Cloud Backend", lifespan=lifespan) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) app.include_router(auth.router, prefix="/api/auth", tags=["auth"]) app.include_router(sites.router, prefix="/api/sites", tags=["sites"]) app.include_router(heartbeat.router, prefix="/api/heartbeat", tags=["heartbeat"]) @app.get("/health") def health(): return {"status": "ok"}