from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from contextlib import asynccontextmanager from sqlalchemy import text from app.core.database import engine, AsyncSessionLocal, Base from app.core.config import settings from app.api import auth, activities, routes, health, records, upload @asynccontextmanager async def lifespan(app: FastAPI): # Create tables async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) # Try to enable TimescaleDB hypertable for data points try: await conn.execute(text( "SELECT create_hypertable('activity_data_points', 'timestamp', " "if_not_exists => TRUE, migrate_data => TRUE)" )) except Exception: pass # Already exists or TimescaleDB not available # Seed admin user async with AsyncSessionLocal() as db: from sqlalchemy import select from app.models.user import User from app.core.security import hash_password result = await db.execute( select(User).where(User.username == settings.admin_username) ) if not result.scalar_one_or_none(): admin = User( username=settings.admin_username, hashed_password=hash_password(settings.admin_password), is_admin=True, ) db.add(admin) await db.commit() yield app = FastAPI( title="FitTracker", version="1.0.0", lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=["*"] if settings.environment == "development" else [], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(auth.router, prefix="/api/auth", tags=["auth"]) app.include_router(activities.router, prefix="/api/activities", tags=["activities"]) app.include_router(routes.router, prefix="/api/routes", tags=["routes"]) app.include_router(health.router, prefix="/api/health-metrics", tags=["health"]) app.include_router(records.router, prefix="/api/records", tags=["records"]) app.include_router(upload.router, prefix="/api/upload", tags=["upload"]) @app.get("/health") async def healthcheck(): return {"status": "ok"}