"""FastAPI backend for garment photo cleaning.""" import os from pathlib import Path from fastapi import FastAPI, File, Form, HTTPException, UploadFile, status from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, JSONResponse from fastapi.staticfiles import StaticFiles from cleaner import ( DEFAULT_MODEL, CleanerError, build_prompt, clean_garment, get_image_info, resolve_api_key, ) DEFAULT_PROMPT = build_prompt("beige") STATIC_DIR = Path(__file__).resolve().parent / "static" STATIC_DIR.mkdir(parents=True, exist_ok=True) app = FastAPI(title="unwrap-clothes") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/api/health") def health_check(): return {"status": "ok"} @app.get("/") def read_root(): index_file = STATIC_DIR / "index.html" if index_file.is_file(): return FileResponse(index_file) return JSONResponse({"status": "ok", "message": "unwrap-clothes backend is running"}) @app.post("/api/clean") async def clean_endpoint( file: UploadFile | None = File(default=None), image: UploadFile | None = File(default=None), prompt: str = Form(default=DEFAULT_PROMPT), model: str = Form(default=DEFAULT_MODEL), api_key: str | None = Form(default=None), restore_res: bool = Form(default=True), restore_resolution: bool | None = Form(default=None), ): upload_file = file or image if upload_file is None: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Image file is required (field 'file' or 'image').", ) effective_restore = restore_resolution if restore_resolution is not None else restore_res effective_api_key = resolve_api_key(api_key) if not effective_api_key: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="API key is required. Provide it in the form or set OPENROUTER_API_KEY.", ) try: file_bytes = await upload_file.read() finally: await upload_file.close() if not file_bytes: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Uploaded file is empty.", ) orig_dims, _ = get_image_info(file_bytes) if orig_dims is None: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid image file. Could not decode image.", ) try: clean_result = clean_garment( image_data=file_bytes, prompt=prompt, api_key=effective_api_key, model=model, restore_res=effective_restore, ) except CleanerError as exc: raise HTTPException( status_code=status.HTTP_502_BAD_GATEWAY, detail=str(exc), ) return clean_result.to_payload() if STATIC_DIR.is_dir(): app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") if __name__ == "__main__": import uvicorn uvicorn.run("app:app", host="127.0.0.1", port=8000, reload=True)