"""Public-camera endpoints for the live ops wall. ``GET /cameras`` -- any signed-in user (auth applied at include time in main.py): cameras inside a bbox from every provider an admin has enabled, each with the provider's required attribution, disclaimer and catalog freshness. ``/admin/camera-providers`` -- platform admin only: list every catalogued provider with its licence status and health, and toggle it. Gated licences need ``acknowledge_licence: true``; that acknowledgement is stored with who and when and written to audit_log (see app/internal/cameras/registry.py). """ from fastapi import APIRouter, Depends, HTTPException, Query from fastapi.responses import JSONResponse from pydantic import BaseModel from app.internal.auth import describe_actor, require_admin_token from app.internal.cameras import registry router = APIRouter(prefix="/cameras", tags=["cameras"]) admin_router = APIRouter(prefix="/admin/camera-providers", tags=["admin"]) @router.get("") async def get_cameras( south: float = Query(..., ge=-90, le=90), west: float = Query(..., ge=-180, le=180), north: float = Query(..., ge=-90, le=90), east: float = Query(..., ge=-180, le=180), ): if south > north or west > east: raise HTTPException(400, "bbox must satisfy south<=north and west<=east") return await registry.cameras_in_bbox((south, west, north, east)) class ProviderToggle(BaseModel): enabled: bool acknowledge_licence: bool = False @admin_router.get("") async def list_camera_providers(_=Depends(require_admin_token)): return {"providers": await registry.list_providers()} @admin_router.put("/{provider_id}") async def set_camera_provider(provider_id: str, body: ProviderToggle, principal: dict = Depends(require_admin_token)): try: return await registry.set_enabled(provider_id, body.enabled, describe_actor(principal), acknowledge_licence=body.acknowledge_licence) except registry.ProviderError as e: return JSONResponse(status_code=e.code, content={"detail": e.message, **e.extra})