from fastapi import FastAPI, Request, status from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from app.core.config import settings from app.api.router import api_router from app.core.database import Base, engine import logging # Set up logging logging.basicConfig(level=logging.INFO) # Initialize database tables try: Base.metadata.create_all(bind=engine) logging.info("Database tables initialized successfully.") # Proactively ensure chest_cm, waist_cm, hips_cm columns exist in body_profiles from sqlalchemy import text with engine.begin() as conn: for col in ["chest_cm", "waist_cm", "hips_cm"]: try: conn.execute(text(f"ALTER TABLE body_profiles ADD COLUMN IF NOT EXISTS {col} REAL;")) except Exception as e: # Fallback for SQLite or older databases where IF NOT EXISTS might fail try: conn.execute(text(f"ALTER TABLE body_profiles ADD COLUMN {col} REAL;")) except Exception: pass # Ensure chat_messages has conversation_id (added after the table first shipped) try: conn.execute(text("ALTER TABLE chat_messages ADD COLUMN IF NOT EXISTS conversation_id UUID;")) except Exception: pass # Ensure chat_messages has tokens_used (added after the table first shipped) try: conn.execute(text("ALTER TABLE chat_messages ADD COLUMN IF NOT EXISTS tokens_used INTEGER NOT NULL DEFAULT 0;")) except Exception: pass # Ensure users has plan/premium_renews_at (added after the table first shipped) try: conn.execute(text("ALTER TABLE users ADD COLUMN IF NOT EXISTS plan VARCHAR(20) NOT NULL DEFAULT 'free';")) except Exception: pass try: conn.execute(text("ALTER TABLE users ADD COLUMN IF NOT EXISTS premium_renews_at TIMESTAMPTZ;")) except Exception: pass except Exception as e: logging.error(f"Error initializing database: {e}") app = FastAPI( title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json" ) # CORS Middleware configurations (Security Guidelines) app.add_middleware( CORSMiddleware, allow_origins=settings.BACKEND_CORS_ORIGINS, allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], allow_headers=["*"], ) from fastapi.staticfiles import StaticFiles import os # Register routers app.include_router(api_router, prefix=settings.API_V1_STR) # Mount local storage folder as static files if mode is local if settings.STORAGE_MODE == "local": os.makedirs(settings.UPLOAD_DIR, exist_ok=True) app.mount("/static", StaticFiles(directory=settings.UPLOAD_DIR), name="static") # Global Exception Handler to avoid leaking internals (Security Guidelines) @app.exception_handler(Exception) def global_exception_handler(request: Request, exc: Exception): logging.error(f"Internal error occurred on {request.url.path}: {exc}", exc_info=True) return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"detail": "Có lỗi hệ thống xảy ra. Vui lòng thử lại sau."} ) @app.get("/health", tags=["healthcheck"]) def health_check(): return {"status": "healthy"}