fishapi / app /main.py
kamau1's picture
Redesign for a clean, professional look
32aa8db verified
"""
FastAPI Marine Species Identification API
Main application entry point for the marine species identification API.
"""
import asyncio
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
import uvicorn
from app.core.config import settings
from app.core.logging import setup_logging, get_logger
from app.api.v1.api import api_router
from app.services.model_service import model_service
# Setup logging
setup_logging()
logger = get_logger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
Application lifespan manager.
Handles startup and shutdown events.
"""
# Startup
logger.info("Starting Marine Species Identification API...")
try:
# Ensure model is available and loaded
await model_service.ensure_model_available()
logger.info("Model loaded successfully")
except Exception as e:
logger.error(f"Failed to load model during startup: {str(e)}")
# Don't fail startup - let health checks handle this
logger.info("API startup completed")
yield
# Shutdown
logger.info("Shutting down Marine Species Identification API...")
# Create FastAPI application
app = FastAPI(
title=settings.PROJECT_NAME,
version=settings.VERSION,
description=settings.DESCRIPTION,
openapi_url=f"{settings.API_V1_STR}/openapi.json",
docs_url="/docs",
redoc_url="/redoc",
lifespan=lifespan
)
# Mount static files and templates (for dashboard)
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Configure appropriately for production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Global exception handler
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
"""Global exception handler for unhandled errors."""
logger.error(f"Unhandled exception: {str(exc)}", exc_info=True)
return JSONResponse(
status_code=500,
content={
"error": "Internal Server Error",
"message": "An unexpected error occurred",
"details": str(exc) if settings.DEBUG else None
}
)
# Include API router
app.include_router(api_router, prefix=settings.API_V1_STR)
# Dashboard endpoint (new web interface)
@app.get("/", response_class=HTMLResponse, tags=["Dashboard"])
async def dashboard(request: Request):
"""
Main dashboard for species identification.
Provides a web interface for testing the API.
"""
return templates.TemplateResponse("dashboard.html", {
"request": request,
"active_tab": "dashboard"
})
# API root endpoint (moved to /api for clarity)
@app.get("/api", tags=["Root"])
async def api_root():
"""
API root endpoint providing basic API information.
"""
return {
"message": "Seamo Species API",
"version": settings.VERSION,
"docs": "/docs",
"health": f"{settings.API_V1_STR}/health",
"api_info": f"{settings.API_V1_STR}/info",
"dashboard": "/"
}
# Health check endpoint at root level (for load balancers)
@app.get("/health", tags=["Health"])
async def root_health():
"""Simple health check at root level."""
return {"status": "ok"}
if __name__ == "__main__":
# Run the application
uvicorn.run(
"app.main:app",
host=settings.HOST,
port=settings.PORT,
reload=False, # Set to True for development
log_level="info"
)