from fastapi import APIRouter, UploadFile, File, Form, HTTPException from fastapi.responses import JSONResponse, FileResponse from typing import Optional import tempfile import shutil import os from core.registry.loader import get_tasks from core.execution.executor import execute_task # ===================================================== # ROUTER BUILDER # ===================================================== def build_api_router() -> APIRouter: """ Dynamically builds API routes from TASK REGISTRY. Generated endpoints: POST /execute/{task} GET /tasks GET /health """ router = APIRouter(tags=["API"]) # ================================================= # EXECUTE TASK # ================================================= @router.post("/execute/{task_name}") async def execute( task_name: str, file: Optional[UploadFile] = File(None), url_input: Optional[str] = Form(None), ): """ Universal execution endpoint. Accepts: - file upload - url_input """ tasks = get_tasks() if task_name not in tasks: raise HTTPException( status_code=404, detail=f"Task '{task_name}' not found", ) temp_path = None try: # ----------------------------------------- # SAVE UPLOADED FILE # ----------------------------------------- if file: suffix = os.path.splitext(file.filename)[1] with tempfile.NamedTemporaryFile( delete=False, suffix=suffix, ) as tmp: shutil.copyfileobj(file.file, tmp) temp_path = tmp.name # ----------------------------------------- # BUILD INPUT PAYLOAD # ----------------------------------------- payload = { "file_path": temp_path, "url_input": url_input, } # ----------------------------------------- # EXECUTE TASK # ----------------------------------------- result = await execute_task(task_name, payload) # ----------------------------------------- # FILE RESPONSE # ----------------------------------------- if isinstance(result, dict) and result.get("file"): output_file = result["file"] if os.path.exists(output_file): return FileResponse( output_file, filename=os.path.basename(output_file), ) # ----------------------------------------- # JSON RESPONSE # ----------------------------------------- return JSONResponse(result) except Exception as e: raise HTTPException( status_code=500, detail=str(e), ) finally: # ----------------------------------------- # CLEANUP TEMP FILE # ----------------------------------------- if temp_path and os.path.exists(temp_path): os.unlink(temp_path) # ================================================= # TASK LIST # ================================================= @router.get("/tasks") async def list_tasks(): """ Returns registry tasks. Used by UI and Docs builder. """ tasks = get_tasks() return { name: { "category": getattr(t, "category", "general"), "description": getattr(t, "description", ""), } for name, t in tasks.items() } # ================================================= # HEALTH CHECK # ================================================= @router.get("/health") async def health(): return { "status": "ok", "tasks_loaded": len(get_tasks()), } return router