avinash-rai's picture
Final GUVI hardening and HF-ready submission
76908f5
# app/main.py - FastAPI Application Entry Point
from contextlib import asynccontextmanager
from datetime import datetime
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, HTMLResponse, Response, FileResponse
from fastapi.staticfiles import StaticFiles
import time
import os
import sys
# Force UTF-8 for Windows consoles and file system
os.environ["PYTHONUTF8"] = "1"
# if sys.platform == "win32":
# import codecs
# sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
# sys.stderr = codecs.getwriter("utf-8")(sys.stderr.detach())
from app.config import settings
from app.agents.orchestrator import orchestrator
from app.api.routes import api_router, enforcement_router, guvi_router
from app.utils.logger import setup_logging
setup_logging()
BANNER = """
========================================================================
SENTINEL - Scam Honeypot System
========================================================================
"""
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Initialize and cleanup application resources."""
print(BANNER)
print("Starting API...")
from app.database.db import init_db, close_db
await init_db()
await orchestrator.initialize()
yield
print("Shutting down...")
await orchestrator.shutdown()
await close_db()
app = FastAPI(
title="Scam Honeypot API",
description="AI-powered honeypot for scam detection and intelligence extraction",
version=settings.VERSION,
docs_url="/docs",
redoc_url="/redoc",
lifespan=lifespan
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# from app.middleware.rate_limiter import RateLimitMiddleware
# app.add_middleware(RateLimitMiddleware)
# Consolidated Web Folder Mapping
WEB_DIR = "web"
app.mount("/static", StaticFiles(directory=WEB_DIR), name="static")
app.mount("/docs", StaticFiles(directory="docs"), name="docs")
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(round(process_time * 1000, 2))
return response
@app.get("/favicon.ico", include_in_schema=False)
async def favicon():
return Response(content="Honeypot", media_type="image/x-icon")
@app.get("/", response_class=FileResponse, tags=["Info"])
async def root(request: Request):
"""Landing page serving from consolidated folder."""
return FileResponse(f"{WEB_DIR}/index.html")
# API Routers
app.include_router(api_router)
app.include_router(enforcement_router)
app.include_router(guvi_router)
print("============================================================")
print(f"DEBUG: STARTING APP FROM: {__file__}")
print("============================================================")
from app.decoys.fake_endpoints import router as decoy_router
app.include_router(decoy_router)
from app.api.feeds import router as feeds_router
app.include_router(feeds_router, prefix="/api/v1/intelligence")
# Mount web directory to root as a fallback for relative assets (style.css, app.js)
app.mount("/", StaticFiles(directory=WEB_DIR), name="web_root")
from fastapi.exceptions import RequestValidationError
from app.utils.logger import AgentLogger
api_logger = AgentLogger("api")
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
# DEBUG: Log validation errors for GUVI endpoint
body = None
try:
body = await request.body()
body_str = body.decode('utf-8')[:500] if body else "EMPTY"
except:
body_str = "UNREADABLE"
api_logger.error(f"[VALIDATION ERROR] Path: {request.url.path}")
api_logger.error(f"[VALIDATION ERROR] Body Preview: {body_str}")
api_logger.error(f"[VALIDATION ERROR] Details: {str(exc.errors())}")
return JSONResponse(status_code=422, content={"status": "error", "message": "Validation Error", "detail": exc.errors()})
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
import traceback
traceback.print_exc() # Log to console/file
return JSONResponse(status_code=500, content={"status": "error", "message": str(exc)})
if __name__ == "__main__":
import uvicorn
# Hugging Face Spaces defaults to 7860
port = int(os.getenv("PORT", 7860))
# Disable reload in production for better performance and stability
uvicorn.run("app.main:app", host="0.0.0.0", port=port, reload=False)