from __future__ import annotations import mimetypes import os from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.responses import FileResponse from pydantic import BaseModel, field_validator from sqlalchemy.ext.asyncio import AsyncSession from ..database import get_db from ..models import AuthUserORM from ..services.result_delivery_service import result_delivery_service from .dependencies import _add_operation_audit_log, _get_current_user router = APIRouter() class ResultDeliveryCreateRequest(BaseModel): channel: str product_ids: Optional[List[int]] = None compat_result_ids: Optional[List[int]] = None package_mode: str = "directory" include_checksums: Optional[bool] = None @field_validator("channel", mode="before") @classmethod def _validate_channel(cls, value): text = str(value or "").strip().lower() if not text: raise ValueError("channel is required") return text @field_validator("package_mode", mode="before") @classmethod def _validate_package_mode(cls, value): text = str(value or "directory").strip().lower() if text not in {"directory", "zip"}: raise ValueError("package_mode must be directory or zip") return text @router.get("/result-deliveries/channels") async def get_result_delivery_channels( current_user: AuthUserORM = Depends(_get_current_user), ): _ = current_user return {"items": result_delivery_service.channels()} @router.post("/result-deliveries", status_code=202) async def create_result_delivery( request: ResultDeliveryCreateRequest, http_request: Request, db: AsyncSession = Depends(get_db), current_user: AuthUserORM = Depends(_get_current_user), ): try: delivery = await result_delivery_service.create_delivery( db, user=current_user, channel=request.channel, product_ids=request.product_ids, compat_result_ids=request.compat_result_ids, package_mode=request.package_mode, include_checksums=request.include_checksums, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc await _add_operation_audit_log( db, request=http_request, action="result_delivery_created", resource="result-deliveries", detail={ "delivery_id": delivery.delivery_id, "channel": delivery.channel, "package_mode": delivery.package_mode, "item_count": delivery.item_count, "task_id": delivery.task_id, "job_id": delivery.job_id, }, user=current_user, ) await db.commit() return result_delivery_service.serialize_delivery(delivery) @router.get("/result-deliveries") async def list_result_deliveries( mine: bool = True, include_all: bool = False, limit: int = 50, offset: int = 0, db: AsyncSession = Depends(get_db), current_user: AuthUserORM = Depends(_get_current_user), ): allow_all = bool(include_all) and str(current_user.role or "").lower() == "admin" if mine: allow_all = False return await result_delivery_service.list_deliveries( db, user=current_user, include_all=allow_all, include_items=True, item_limit=5, limit=limit, offset=offset, ) @router.get("/result-deliveries/{delivery_id}") async def get_result_delivery( delivery_id: str, db: AsyncSession = Depends(get_db), current_user: AuthUserORM = Depends(_get_current_user), ): delivery = await result_delivery_service.get_delivery( db, delivery_id=delivery_id, user=current_user, include_items=True, ) if delivery is None: raise HTTPException(status_code=404, detail="delivery not found") return result_delivery_service.serialize_delivery(delivery, include_items=True) @router.get("/result-deliveries/{delivery_id}/manifest") async def download_result_delivery_manifest( delivery_id: str, db: AsyncSession = Depends(get_db), current_user: AuthUserORM = Depends(_get_current_user), ): try: path = await result_delivery_service.resolve_manifest_path( db, delivery_id=delivery_id, user=current_user, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return FileResponse(path, filename=os.path.basename(path), media_type="application/json") @router.get("/result-deliveries/{delivery_id}/archive/download") async def download_result_delivery_archive( delivery_id: str, db: AsyncSession = Depends(get_db), current_user: AuthUserORM = Depends(_get_current_user), ): try: path = await result_delivery_service.resolve_archive_path( db, delivery_id=delivery_id, user=current_user, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return FileResponse(path, filename=os.path.basename(path), media_type="application/zip") @router.get("/result-deliveries/{delivery_id}/files/{item_id}/download") async def download_result_delivery_item( delivery_id: str, item_id: int, db: AsyncSession = Depends(get_db), current_user: AuthUserORM = Depends(_get_current_user), ): try: path = await result_delivery_service.resolve_item_path( db, delivery_id=delivery_id, item_id=item_id, user=current_user, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc media_type = mimetypes.guess_type(path)[0] or "application/octet-stream" return FileResponse(path, filename=os.path.basename(path), media_type=media_type)