"""Archive location and preflight API (US06-01). Registering a location writes a marker onto the medium; preflight is a command rather than a read, because it probes the destination, hashes the scope, and issues the token a later archive plan must present (US06-02). Neither endpoint moves or removes a single library file. """ from __future__ import annotations from fastapi import APIRouter, Request from fastapi.responses import JSONResponse from pydantic import BaseModel from photo_pipeline.services.archives import ArchiveError, ArchiveService router = APIRouter(tags=["archives"]) # Which failures are the caller's request (422) and which are a missing thing (404). NOT_FOUND_CODES = {"unknown_location"} class RegisterLocationRequest(BaseModel): name: str root: str class PreflightRequest(BaseModel): location_id: str # ``None`` means every album; an explicit list scopes the check. albums: list[str] | None = None def _service(request: Request) -> ArchiveService: return ArchiveService(request.app.state.session_factory, config=request.app.state.config) def _error(error: ArchiveError) -> JSONResponse: status = 404 if error.code in NOT_FOUND_CODES else 422 return JSONResponse( status_code=status, content={"error": {"code": error.code, "message": str(error)}} ) @router.post("/archive-locations", status_code=201) def register_location(body: RegisterLocationRequest, request: Request): try: return _service(request).register(body.name, body.root) except ArchiveError as error: return _error(error) @router.get("/archive-locations") def list_locations(request: Request) -> dict: return {"locations": _service(request).locations()} @router.post("/archive-preflight") def preflight(body: PreflightRequest, request: Request): try: return _service(request).preflight(body.location_id, body.albums) except ArchiveError as error: return _error(error)