from contextlib import asynccontextmanager from fastapi import FastAPI from starlette.middleware.sessions import SessionMiddleware from app.config import Settings, get_settings from app.db.session import create_db_engine, create_session_factory, initialize_schema from app.fit.rewriter import convert_fit_device from app.garmin.uploader import GarminUploader from app.mywhoosh.client import MyWhooshClient from app.security.credentials import CredentialCipher from app.sync.manager import SyncManager from app.sync.scheduler import SyncScheduler from app.web.routes import router as web_router def create_app(settings: Settings | None = None) -> FastAPI: resolved = settings or get_settings() resolved.data_dir.mkdir(parents=True, exist_ok=True) resolved.tokens_dir.mkdir(parents=True, exist_ok=True) resolved.activities_dir.mkdir(parents=True, exist_ok=True) @asynccontextmanager async def lifespan(app: FastAPI): cipher = CredentialCipher(resolved.credential_encryption_key) def mywhoosh_factory(token_store): return MyWhooshClient(token_store) def garmin_factory(email, password, tokenstore): return GarminUploader(email=email, password=password, tokenstore=tokenstore) sync_manager = SyncManager( session_factory=app.state.session_factory, credential_cipher=cipher, settings=resolved, mywhoosh_factory=mywhoosh_factory, garmin_factory=garmin_factory, fit_converter=convert_fit_device, ) app.state.sync_manager = sync_manager scheduler = SyncScheduler(sync_manager, interval_seconds=resolved.sync_interval_minutes * 60) app.state.scheduler = scheduler await scheduler.start() yield await scheduler.stop() app = FastAPI(title="MyWhoosh Garmin Sync", lifespan=lifespan) app.state.settings = resolved engine = create_db_engine(resolved.database_url) initialize_schema(engine) app.state.db_engine = engine app.state.session_factory = create_session_factory(engine) app.add_middleware( SessionMiddleware, secret_key=resolved.secret_key, same_site="lax", https_only=resolved.session_https_only, ) app.include_router(web_router) @app.get("/healthz") def healthz() -> dict[str, str]: return {"status": "ok"} return app