| |
|
|
| 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 |
|
|
| |
| os.environ["PYTHONUTF8"] = "1" |
| |
| |
| |
| |
|
|
| 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=["*"], |
| ) |
|
|
| |
| |
|
|
| |
| 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") |
|
|
| |
| 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") |
|
|
| |
| 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): |
| |
| 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() |
| return JSONResponse(status_code=500, content={"status": "error", "message": str(exc)}) |
|
|
| if __name__ == "__main__": |
| import uvicorn |
| |
| port = int(os.getenv("PORT", 7860)) |
| |
| uvicorn.run("app.main:app", host="0.0.0.0", port=port, reload=False) |
|
|