108 lines
3.9 KiB
Python
108 lines
3.9 KiB
Python
"""
|
|
FastAPI application entry point.
|
|
|
|
Run with:
|
|
uvicorn app.main:app --reload --port 8000
|
|
|
|
(see backend/README.md / the project documentation for full setup steps)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import threading
|
|
from pathlib import Path
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.responses import FileResponse
|
|
|
|
from app.infrastructure.settings import API_CORS_ORIGINS
|
|
from app.api.routers import health, brands, search, chat, catalog, system
|
|
from app.api.routers import stores, discounts, analytics as store_analytics, trending, recommendations, store_admin
|
|
from app.api.routers import nutrition, nutrition_admin, upload
|
|
from app.api.routers import auth, user_products, admin_train
|
|
from app.services.store_db import ensure_store_intelligence_schema
|
|
from app.services.nutrition_db import ensure_nutrition_schema
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
app = FastAPI(
|
|
title="Brand Product Search Engine - RAG API",
|
|
description=(
|
|
"Local, CPU-only RAG API over an Indian FMCG product catalog stored in pgvector. "
|
|
"Includes automated multi-store intelligence, ML discount engines, and nutrition intelligence."
|
|
),
|
|
version="3.2.0",
|
|
)
|
|
|
|
|
|
@app.on_event("startup")
|
|
def _on_startup() -> None:
|
|
# Asynchronously ensure schemas and background auto-init so server starts instantly (<1s)
|
|
def _async_init():
|
|
try:
|
|
ensure_store_intelligence_schema()
|
|
ensure_nutrition_schema()
|
|
from app.api.routers.system import _run_background_auto_seed
|
|
_run_background_auto_seed()
|
|
except Exception as e:
|
|
logger.warning("Startup background init warning: %s", e)
|
|
|
|
threading.Thread(target=_async_init, daemon=True).start()
|
|
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=API_CORS_ORIGINS,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
app.include_router(health.router, prefix="/api")
|
|
app.include_router(auth.router, prefix="/api")
|
|
app.include_router(user_products.router, prefix="/api")
|
|
app.include_router(admin_train.router, prefix="/api")
|
|
app.include_router(system.router, prefix="/api")
|
|
app.include_router(brands.router, prefix="/api")
|
|
app.include_router(search.router, prefix="/api")
|
|
app.include_router(chat.router, prefix="/api")
|
|
app.include_router(catalog.router, prefix="/api")
|
|
app.include_router(stores.router, prefix="/api")
|
|
app.include_router(discounts.router, prefix="/api")
|
|
app.include_router(store_analytics.router, prefix="/api")
|
|
app.include_router(trending.router, prefix="/api")
|
|
app.include_router(recommendations.router, prefix="/api")
|
|
app.include_router(store_admin.router, prefix="/api")
|
|
app.include_router(nutrition.router, prefix="/api")
|
|
app.include_router(nutrition_admin.router, prefix="/api")
|
|
app.include_router(upload.router, prefix="/api")
|
|
|
|
# Serve built frontend static files if dist exists (single-port unified deployment)
|
|
FRONTEND_DIST = Path(__file__).resolve().parents[2] / "frontend" / "dist"
|
|
if FRONTEND_DIST.exists() and (FRONTEND_DIST / "assets").exists():
|
|
app.mount("/assets", StaticFiles(directory=str(FRONTEND_DIST / "assets")), name="assets")
|
|
|
|
@app.get("/{full_path:path}")
|
|
def serve_frontend(full_path: str):
|
|
if full_path.startswith("api") or full_path.startswith("docs") or full_path.startswith("openapi.json"):
|
|
return None
|
|
file_path = FRONTEND_DIST / full_path
|
|
if file_path.exists() and file_path.is_file():
|
|
return FileResponse(file_path)
|
|
return FileResponse(FRONTEND_DIST / "index.html")
|
|
else:
|
|
@app.get("/")
|
|
def root() -> dict:
|
|
return {
|
|
"service": "Brand Product Search Engine - RAG API",
|
|
"docs": "/docs",
|
|
"health": "/api/health",
|
|
"system_status": "/api/system/status",
|
|
}
|