Automated Action 53d9909fb6 Create gamified kids learning API with FastAPI and SQLite
- Set up project structure with FastAPI and SQLite
- Implement user authentication with JWT
- Create models for learning content (subjects, lessons, quizzes)
- Add progress tracking and gamification features
- Implement comprehensive API documentation
- Add error handling and validation
- Set up proper logging and health check endpoint
2025-06-17 18:25:16 +00:00

188 lines
5.1 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
from datetime import datetime, timezone
from typing import Any, Dict
import os
import traceback
import uvicorn
from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
from app.api.v1.api import api_router
from app.core.config import settings
from app.core.exceptions import BaseAPIException
app = FastAPI(
title=settings.PROJECT_NAME,
description=settings.PROJECT_DESCRIPTION,
version=settings.VERSION,
openapi_url="/openapi.json",
docs_url="/docs",
redoc_url="/redoc",
)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Add request logging middleware if not in production
if os.getenv("ENVIRONMENT", "development") != "production":
from app.core.middleware import RequestLoggingMiddleware
app.add_middleware(RequestLoggingMiddleware)
# Exception handlers
@app.exception_handler(BaseAPIException)
async def base_api_exception_handler(request: Request, exc: BaseAPIException) -> JSONResponse:
"""
Handle custom API exceptions.
"""
return JSONResponse(
status_code=exc.status_code,
content={
"error": {
"code": exc.status_code,
"message": exc.detail,
"type": exc.__class__.__name__,
}
},
headers=exc.headers,
)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
"""
Handle request validation errors.
"""
errors = exc.errors()
error_details = []
for error in errors:
loc = ".".join(str(x) for x in error["loc"])
error_details.append(
{
"field": loc,
"message": error["msg"],
"type": error["type"],
}
)
return JSONResponse(
status_code=400,
content={
"error": {
"code": 400,
"message": "Validation Error",
"type": "ValidationError",
"details": error_details,
}
},
)
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException) -> JSONResponse:
"""
Handle standard HTTP exceptions.
"""
return JSONResponse(
status_code=exc.status_code,
content={
"error": {
"code": exc.status_code,
"message": exc.detail,
"type": "HTTPException",
}
},
headers=exc.headers,
)
@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception) -> JSONResponse:
"""
Handle all other exceptions.
"""
# In production, you might want to log the error instead of returning the traceback
error_detail = str(exc)
if os.getenv("ENVIRONMENT", "development") == "development":
error_detail = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
return JSONResponse(
status_code=500,
content={
"error": {
"code": 500,
"message": "Internal Server Error",
"type": exc.__class__.__name__,
"detail": error_detail,
}
},
)
# Include API router
app.include_router(api_router, prefix=settings.API_V1_STR)
@app.get("/")
async def root() -> Dict[str, Any]:
"""
Root endpoint that returns basic service information.
"""
return {
"title": settings.PROJECT_NAME,
"description": settings.PROJECT_DESCRIPTION,
"version": settings.VERSION,
"docs_url": "/docs",
"redoc_url": "/redoc",
"openapi_url": "/openapi.json",
"health_check": "/health",
"api_base_url": settings.API_V1_STR,
}
@app.get("/health", tags=["health"])
async def health_check() -> Dict[str, Any]:
"""
Health check endpoint to verify the service is running correctly.
"""
try:
# Check database connection
from app.db.session import SessionLocal
db = SessionLocal()
db.execute("SELECT 1")
db.close()
db_status = "connected"
except Exception as e:
# Using a general exception handler with specific error handling
db_status = f"error: {e!s}"
return {
"status": "healthy",
"timestamp": datetime.now(tz=timezone.utc).isoformat(),
"version": settings.VERSION,
"database": db_status,
"environment": os.getenv("ENVIRONMENT", "development"),
}
if __name__ == "__main__":
# Using localhost for development, configurable for production
host = os.getenv("HOST", "127.0.0.1")
port = int(os.getenv("PORT", "8000"))
uvicorn.run("main:app", host=host, port=port, reload=True)