File size: 3,475 Bytes
228add1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
"""
FastAPI main application.
Cattle Breed Classifier API.
"""

import sys
from pathlib import Path
from contextlib import asynccontextmanager

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

# Ensure project root is in path
PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))

from backend.app.core.config import get_settings
from backend.app.core.logging import logger
from backend.app.api.routes import health, predict, metadata
from backend.app.services.inference import inference_service
from backend.app.services.breed_info import breed_info_service


@asynccontextmanager
async def lifespan(app: FastAPI):
    """Startup and shutdown events."""
    # Startup
    logger.info("Starting Cattle Breed Classifier API...")
    try:
        inference_service.load()
        breed_info_service.load()
        logger.info("All services loaded successfully")
    except Exception as e:
        logger.error(f"Startup error: {e}")
        logger.info("API started with limited functionality")

    yield

    # Shutdown
    logger.info("Shutting down...")


def create_app() -> FastAPI:
    """Create and configure FastAPI application."""
    settings = get_settings()

    app = FastAPI(
        title=settings.app_name,
        version=settings.app_version,
        description=(
            "API for classifying Indian cattle breeds using deep learning. "
            "Supports image upload, URL, and base64 input. "
            "Returns breed prediction with confidence and metadata."
        ),
        lifespan=lifespan,
        docs_url="/docs",
        redoc_url="/redoc",
    )

    # CORS
    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.cors_origins,
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )

    # Include routers
    app.include_router(health.router)
    app.include_router(predict.router)
    app.include_router(metadata.router)

    # Serve Static frontend if it exists
    static_dir = PROJECT_ROOT / "frontend" / "dist"
    if static_dir.exists() and static_dir.is_dir():
        from fastapi.staticfiles import StaticFiles
        from fastapi.responses import FileResponse
        
        # Mount assets
        assets_dir = static_dir / "assets"
        if assets_dir.exists():
            app.mount("/assets", StaticFiles(directory=str(assets_dir)), name="assets")

        @app.get("/", tags=["Root"])
        async def root():
            return FileResponse(str(static_dir / "index.html"))
            
        # Catch-all for react router
        @app.get("/{full_path:path}", tags=["Root"])
        async def serve_spa(full_path: str):
            # Exclude api requests
            if full_path.startswith("api/") or full_path.startswith("docs") or full_path.startswith("redoc"):
                return {"detail": "Not Found"}
            return FileResponse(str(static_dir / "index.html"))
    else:
        @app.get("/", tags=["Root"])
        async def root():
            return {
                "message": "Cattle Breed Classifier API",
                "version": settings.app_version,
                "docs": "/docs",
            }

    return app


app = create_app()


if __name__ == "__main__":
    import uvicorn
    settings = get_settings()
    uvicorn.run(
        "backend.app.main:app",
        host=settings.host,
        port=settings.port,
        reload=settings.debug,
    )