from contextlib import asynccontextmanager from fastapi import FastAPI, Body, Depends, HTTPException from fastapi.responses import JSONResponse from sqlalchemy.ext.asyncio import AsyncSession from typing import Annotated from .database.connection import engine from .database.base import SQLModel # Ensures all models in base.py are visible from .routes.activities_router import router as activity_router from .routes.auth_router import router as auth_router from .routes.goal_router import router as goal_router from .routes.daylog_router import router as day_logs_router from .routes.transaction_router import router as transaction_router from .routes.analytics_router import router as analytics_router from .database.connection import get_session from .database.models import User from .utils.auth import get_current_user from .utils.services import CommandError, command_error_payload, dispatch_command from fastapi.middleware.cors import CORSMiddleware import logging logger = logging.getLogger(__name__) # 1. Lifespan event for startup and shutdown actions @asynccontextmanager async def lifespan(app: FastAPI): logger.info("Starting up the application...") # Startup actions: Create database tables if they do not exist async with engine.begin() as conn: await conn.run_sync(SQLModel.metadata.create_all) yield # Application runs here # Shutdown actions (e.g., closing connection pools if necessary) await engine.dispose() app = FastAPI(lifespan=lifespan) app.add_middleware( CORSMiddleware, allow_origins=["*"], # Accept requests from anywhere allow_credentials=False, # Must be False when using "*" allow_methods=["*"], allow_headers=["*"], ) # Register your routes into the core application app.include_router(auth_router) app.include_router(activity_router) app.include_router(goal_router) app.include_router(day_logs_router) app.include_router(transaction_router) app.include_router(analytics_router) @app.get("/") def read_root(): return {"message": "DailyMetric backend is running"} @app.post("/chat") async def chat_endpoint( current_user: Annotated[User, Depends(get_current_user)], command: str = Body(..., embed=True), db: AsyncSession = Depends(get_session), ): try: return await dispatch_command(command, current_user, db) except CommandError as exc: return JSONResponse(status_code=exc.status_code, content=command_error_payload(exc)) @app.get("/health") def health_check(): return {"status": "healthy"}