import asyncio from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.staticfiles import StaticFiles import app.bets.confirmation # noqa: F401 (registers the "bet" confirmation handler) import app.rounds.confirmation # noqa: F401 (registers the "payout" confirmation handler) import app.withdrawals.confirmation # noqa: F401 (registers the "withdrawal" confirmation handler) from app.api.routes.admin import router as admin_router from app.api.routes.bets import router as bets_router from app.api.routes.users import router as users_router from app.api.routes.withdrawals import router as withdrawals_router from app.auth.routes import router as auth_router from app.config import settings from app.db.base import AsyncSessionLocal from app.electrum.client import ElectrumClient from app.electrum.listener import ElectrumListener from app.rounds.scheduler import RoundScheduler from app.tx.broadcast import RbfBumper from app.tx.confirmation import ConfirmationPoller from app.tx.locks import UserLocks def _make_electrum_client() -> ElectrumClient: return ElectrumClient(settings.electrum_host, settings.electrum_port, settings.electrum_use_ssl) @asynccontextmanager async def lifespan(app: FastAPI): listener = ElectrumListener(_make_electrum_client, AsyncSessionLocal) app.state.electrum_listener = listener app.state.user_locks = UserLocks() scheduler = RoundScheduler(AsyncSessionLocal, listener) poller = ConfirmationPoller(AsyncSessionLocal, lambda: listener.client) bumper = RbfBumper(AsyncSessionLocal, lambda: listener.client) tasks = [ asyncio.create_task(listener.run()), asyncio.create_task(scheduler.run()), asyncio.create_task(poller.run()), asyncio.create_task(bumper.run()), ] try: yield finally: for task in tasks: task.cancel() if listener.client is not None: await listener.client.close() app = FastAPI(title="PLM Lottery", lifespan=lifespan) app.include_router(auth_router) app.include_router(users_router) app.include_router(bets_router) app.include_router(withdrawals_router) app.include_router(admin_router) @app.get("/health") async def health() -> dict[str, str]: return {"status": "ok"} app.mount("/", StaticFiles(directory="app/static", html=True), name="static")