MusinguziBenard's picture
Upload app.py
354fc59 verified
Raw History Blame Contribute Delete
4.26 kB
# app.py
from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
import logging
import time
from api.health import router as health_router
from api.extract import router as extract_router
from api.enroll import router as enroll_router
from api.recognize import router as recognize_router
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = FastAPI(
title="SchoolMaster Face Recognition Service",
description="AI face recognition for school attendance",
version="1.0.0"
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.middleware("http")
async def log_requests(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
logger.info(f"{request.method} {request.url.path} - {response.status_code} - {process_time:.3f}s")
return response
@app.get("/")
async def root():
return {
"service": "SchoolMaster Face Recognition",
"status": "running",
"version": "1.0.0",
"endpoints": [
"GET /api/health",
"POST /api/extract",
"POST /api/enroll",
"POST /api/recognize"
]
}
app.include_router(health_router, prefix="/api", tags=["Health"])
app.include_router(extract_router, prefix="/api", tags=["Extraction"])
app.include_router(enroll_router, prefix="/api", tags=["Enrollment"])
app.include_router(recognize_router, prefix="/api", tags=["Recognition"])
@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc):
return JSONResponse(
status_code=exc.status_code,
content={"success": False, "message": exc.detail}
)
# ============================================================
# Minimal Gradio status page, mounted onto the SAME app/port.
# This is only here so the Space genuinely uses the Gradio SDK
# (keeping it eligible for free hosting) - your PHP backend
# talks to the /api/* routes above directly and never touches this UI.
#
# Wrapped defensively: if Gradio itself fails to import or mount for
# any reason (version mismatches inside HF's build environment have
# happened before), the actual API - the thing PHP depends on - must
# still start and serve requests. A broken status page should never
# be able to take down the real service.
# ============================================================
try:
import gradio as gr
import requests as _requests
def check_health():
try:
r = _requests.get(f"http://127.0.0.1:{7860}/api/health", timeout=5)
return r.json()
except Exception as e:
return {"error": str(e)}
with gr.Blocks(title="Face Recognition API") as demo:
gr.Markdown(
"# Face Recognition API\n\n"
"This is a status page only. Your school system's PHP backend talks "
"directly to the `/api/*` endpoints below - nothing here.\n\n"
"**Endpoints:** `/api/health` (GET), `/api/extract`, `/api/enroll`, "
"`/api/recognize` (POST, require `X-API-Key` header + multipart photos)"
)
btn = gr.Button("Check API Health", variant="primary")
out = gr.JSON(label="Result")
btn.click(fn=check_health, outputs=out)
app = gr.mount_gradio_app(app, demo, path="/ui")
logger.info("Gradio status page mounted at /ui")
except Exception as e:
logger.warning(f"Gradio status page failed to load, continuing without it: {e}")
if __name__ == "__main__":
import uvicorn
from config import Config
# Hugging Face Spaces health-checks port 7860 specifically - hardcoded
# here (not from Config.PORT/env) so a stray PORT variable in the
# Space's settings can never silently break this again.
SPACES_PORT = 7860
uvicorn.run(
app,
host=Config.HOST,
port=SPACES_PORT,
)