Spaces:
Running
Running
File size: 5,412 Bytes
6c9b8f1 b1c84b5 6c9b8f1 954286a 6c9b8f1 b1c84b5 6c9b8f1 b1c84b5 6c9b8f1 b1c84b5 32ac1d9 6c9b8f1 32ac1d9 b1c84b5 32ac1d9 6c9b8f1 | 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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | """
PhilVerify β FastAPI Application Entry Point
Run: uvicorn main:app --reload --port 8000
Docs: http://localhost:8000/docs
"""
import logging
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from config import get_settings
from api.routes.verify import router as verify_router
from api.routes.history import router as history_router
from api.routes.trends import router as trends_router
from api.routes.preview import router as preview_router
# ββ Logging βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
logging.basicConfig(
level=getattr(logging, get_settings().log_level.upper(), logging.INFO),
format="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
)
logger = logging.getLogger("philverify")
# ββ Lifespan (startup / shutdown) βββββββββββββββββββββββββββββββββββββββββββββ
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Warm up NLP models on startup so first request isn't slow."""
logger.info("π PhilVerify starting up...")
try:
# Lazy-import to avoid crashing if heavy deps not yet installed
from nlp.language_detector import LanguageDetector
from nlp.preprocessor import TextPreprocessor
from ml.tfidf_classifier import TFIDFClassifier
app.state.preprocessor = TextPreprocessor()
app.state.language_detector = LanguageDetector()
classifier = TFIDFClassifier()
classifier.train() # Trains on seed dataset if model not persisted
app.state.classifier = classifier
logger.info("β
NLP models ready")
except ImportError as e:
logger.warning("β οΈ Some NLP modules not installed yet: %s β stubs will be used", e)
yield # ββ App is running ββ
logger.info("π PhilVerify shutting down")
# ββ App βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
settings = get_settings()
app = FastAPI(
title="PhilVerify API",
description=(
"Multimodal fake news detection for Philippine social media. "
"Supports text, URL, image (OCR), and video (Whisper ASR) inputs."
),
version="0.1.0",
docs_url="/docs",
redoc_url="/redoc",
lifespan=lifespan,
)
# ββ CORS ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
app.add_middleware(
CORSMiddleware,
allow_origins=settings.allowed_origins_list,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ββ Global Error Handler ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
logger.exception("Unhandled error on %s %s: %s", request.method, request.url.path, exc)
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={"error": "Internal server error", "detail": str(exc)},
)
# ββ Routers (all under /api so Firebase Hosting rewrite β Cloud Run match) ββββ
# Frontend calls /api/verify/..., Firebase Hosting forwards full path to Cloud Run.
# In dev, Vite proxy forwards /api/... directly without stripping β so same prefix.
from fastapi import APIRouter as _APIRouter
_api = _APIRouter(prefix="/api")
_api.include_router(verify_router)
_api.include_router(history_router)
_api.include_router(trends_router)
_api.include_router(preview_router)
app.include_router(_api)
# ββ Health ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@app.get("/", tags=["Health"])
async def root():
return {
"service": "PhilVerify",
"version": "0.1.0",
"status": "operational",
"docs": "/docs",
}
# Cloud Run health check (no /api prefix so load balancer can reach it)
# HEAD support for UptimeRobot and other uptime monitors
@app.get("/health", tags=["Health"])
@app.head("/health", tags=["Health"])
@app.get("/api/health", tags=["Health"])
@app.head("/api/health", tags=["Health"])
async def health():
return {"status": "ok", "env": settings.app_env}
# ββ Dev runner ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host="0.0.0.0",
port=int(os.getenv("PORT", 8000)),
reload=settings.debug,
log_level=settings.log_level.lower(),
)
|