Files
mywhoosh2garmin/app/main.py
Bastian Wagner 85b0d861b4 feat: restyle admin UI and add per-user sync run history
Adds a self-hosted stylesheet (no CDN dependencies) with a card-based
dashboard and color-coded status badges, and shows the last 10 sync
runs per user on the detail page.
2026-08-15 20:19:31 +02:00

81 lines
2.7 KiB
Python

from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
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.operations import router as operations_router
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.include_router(operations_router)
app.mount(
"/static",
StaticFiles(directory=str(Path(__file__).resolve().parent / "web" / "static")),
name="static",
)
@app.get("/healthz")
def healthz() -> dict[str, str]:
return {"status": "ok"}
return app