from io import BytesIO from fastapi import Depends, FastAPI, File, Form, HTTPException, Request, UploadFile from fastapi.responses import HTMLResponse, StreamingResponse from app.config import settings app = FastAPI(title="face-lock", version="0.3.0") def require_auth(request: Request) -> None: if not settings.auth_enabled: return header_name = settings.auth_header_name.lower() provided = request.headers.get(header_name) if not provided and request.headers.get("authorization", "").lower().startswith("bearer "): provided = request.headers.get("authorization", "")[7:].strip() if provided != settings.auth_token: raise HTTPException(status_code=401, detail="unauthorized") @app.get("/health") def health(): return { "ok": True, "env": settings.env, "auth_enabled": settings.auth_enabled, "auth_header": settings.auth_header_name if settings.auth_enabled else None, } @app.get("/", response_class=HTMLResponse) def index(): return HTMLResponse( """ face-lock

face-lock

Square the subject, crop it, and keep the raw blobs out of sight.

Docs

      
Result
Crop
Annotated source
""".replace("__AUTH_HEADER_NAME__", settings.auth_header_name) ) @app.post("/api/focus") async def focus( request: Request, file: UploadFile = File(...), buffer_ratio: float = Form(0.15), detector: str = Form("subject"), _auth: None = Depends(require_auth), ): from app.vision import process_image try: payload = await file.read() result = process_image(payload, file.filename or "upload", buffer_ratio=buffer_ratio, detector=detector) return { "filename": result["filename"], "detector": result["detector"], "method": result["method"], "buffer_ratio": result["buffer_ratio"], "detected_bbox": result["detected_bbox"], "square_bbox": result["square_bbox"], "source_size": result["source_size"], "crop_data_url": result["crop_data_url"], "annotated_data_url": result["annotated_data_url"], } except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc @app.post("/api/focus/image") async def focus_image( request: Request, file: UploadFile = File(...), buffer_ratio: float = Form(0.15), detector: str = Form("subject"), _auth: None = Depends(require_auth), ): from app.vision import process_image try: payload = await file.read() result = process_image(payload, file.filename or "upload", buffer_ratio=buffer_ratio, detector=detector) return StreamingResponse(BytesIO(result["crop_bytes"]), media_type="image/jpeg") except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc