| """POST /assets — extract image/video/audio/svg-icon/font assets from a URL.""" |
|
|
| import logging |
| import uuid |
|
|
| from fastapi import APIRouter, HTTPException, status |
|
|
| from app.schemas import AssetExtractRequest, AssetExtractResponse, ErrorResponse |
| from app.services.asset_service import AssetExtractError, extract_assets |
| from app.services.fetcher import FetchError |
|
|
| router = APIRouter() |
|
|
|
|
| def _log_request(req: AssetExtractRequest, phase: str, **extra) -> None: |
| logger = logging.getLogger(__name__) |
| logger.info("request phase=%s id=%s url=%r groups=%r enrich=%s include_data_uris=%s extra=%r", |
| phase, req._request_id or "?", req.url, req.groups, req.enrich, |
| req.include_data_uris, extra) |
|
|
|
|
| def _log_response(req: AssetExtractRequest, status_code: int, phase: str, **extra) -> None: |
| logger = logging.getLogger(__name__) |
| logger.info("response phase=%s status=%s id=%s url=%r extra=%r", |
| phase, status_code, req._request_id or "?", req.url, extra) |
|
|
|
|
| @router.post("/assets", response_model=AssetExtractResponse) |
| async def extract_assets_endpoint(req: AssetExtractRequest) -> AssetExtractResponse: |
| """Extract assets from a URL, grouped by category.""" |
| _log_request(req, "start") |
| req_id = req._request_id or str(uuid.uuid4()) |
| try: |
| result = await extract_assets( |
| url=req.url, |
| include_data_uris=req.include_data_uris, |
| enrich=req.enrich, |
| groups=req.groups, |
| ) |
| _log_response(req, 200, "ok", total=result.total) |
| return result |
| except AssetExtractError as exc: |
| _log_response(req, 502, "asset_extract_error", detail=str(exc)) |
| stages = None |
| if hasattr(exc, '__cause__') and isinstance(exc.__cause__, FetchError): |
| stages = exc.__cause__.stages |
| raise HTTPException( |
| status_code=status.HTTP_502_BAD_GATEWAY, |
| detail=ErrorResponse( |
| error="asset extraction failed", |
| code=502, |
| detail=str(exc), |
| request_id=req_id, |
| stages=stages, |
| ).model_dump(), |
| ) from exc |
| except Exception as exc: |
| _log_response(req, 500, "unexpected_error", detail=str(exc)) |
| raise HTTPException( |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
| detail=ErrorResponse( |
| error="unexpected server error", |
| code=500, |
| detail=str(exc), |
| request_id=req_id, |
| ).model_dump(), |
| ) from exc |
|
|
|
|
| @router.get("/health") |
| async def health_check() -> dict[str, str]: |
| return {"status": "ok", "endpoint": "/assets"} |
|
|