Files
sstent d1cfd0fd8e feat: implement Fitbit OAuth, Garmin MFA, and optimize segment discovery
- Add Fitbit authentication flow (save credentials, OAuth callback handling)
- Implement Garmin MFA support with successful session/cookie handling
- Optimize segment discovery with new sampling and activity query services
- Refactor database session management in discovery API for better testability
- Enhance activity data parsing for charts and analysis
- Update tests to use testcontainers and proper dependency injection
- Clean up repository by ignoring and removing tracked transient files (.pyc, .db)
2026-01-16 15:35:26 -08:00

119 lines
3.6 KiB
Python

from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from contextlib import asynccontextmanager
from src.utils.logging_config import setup_logging
from alembic.config import Config
from alembic import command
import os
import logging
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
setup_logging()
logger = logging.getLogger(__name__)
logger.info("--- Application Starting Up ---")
logger.debug("--- TEST DEBUG LOG AT STARTUP ---")
alembic_cfg = Config("alembic.ini")
database_url = os.getenv("DATABASE_URL")
if database_url and not os.getenv("TESTING"):
alembic_cfg.set_main_option("sqlalchemy.url", database_url)
try:
# command.upgrade(alembic_cfg, "head")
logger.info("Database migrations skipped (manual override).")
except Exception as e:
logger.error(f"Error running database migrations: {e}")
else:
logger.warning("DATABASE_URL not set, skipping migrations.")
# Start Scheduler
if not os.getenv("TESTING"):
try:
from src.services.scheduler import scheduler
scheduler.start()
logger.info("Scheduler started.")
except Exception as e:
logger.error(f"Failed to start scheduler: {e}")
else:
logger.info("TESTING mode detected: Scheduler disabled.")
yield
logger.info("--- Application Shutting Down ---")
if not os.getenv("TESTING"):
try:
from src.services.scheduler import scheduler
scheduler.stop()
except:
pass
app = FastAPI(lifespan=lifespan)
# Add middleware for request logging
@app.middleware("http")
async def log_requests(request: Request, call_next):
logger = logging.getLogger("src.middleware")
logger.info(f"Incoming Request: {request.method} {request.url.path}")
try:
response = await call_next(request)
logger.info(f"Request Completed: {response.status_code}")
return response
except Exception as e:
logger.error(f"Request Failed: {e}")
raise
from pathlib import Path
# Resolve absolute path to static directory
BASE_DIR = Path(__file__).resolve().parent
STATIC_DIR = BASE_DIR.parent / "static"
if not STATIC_DIR.exists():
# Fallback or create?
# For now, just logging warning or ensuring it works in dev
logging.warning(f"Static directory not found at {STATIC_DIR}")
# Create it to prevent crash?
STATIC_DIR.mkdir(parents=True, exist_ok=True)
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
templates = Jinja2Templates(directory="templates")
from src.api import status, sync, auth, logs, metrics, activities, scheduling, config_routes
app.include_router(status.router, prefix="/api")
app.include_router(sync.router, prefix="/api")
app.include_router(auth.router, prefix="/api")
app.include_router(config_routes.router, prefix="/api")
app.include_router(logs.router, prefix="/api")
app.include_router(metrics.router, prefix="/api")
app.include_router(activities.router, prefix="/api")
app.include_router(activities.router, prefix="/api")
app.include_router(scheduling.router, prefix="/api")
from src.api import segments
app.include_router(segments.router, prefix="/api")
from src.api import bike_setups
app.include_router(bike_setups.router)
from src.api import discovery
app.include_router(discovery.router, prefix="/api/discovery")
from src.api import analysis
app.include_router(analysis.router, prefix="/api")
from src.routers import web
app.include_router(web.router)
# Trigger reload