"""Image listing + update-check endpoints.""" from __future__ import annotations from fastapi import APIRouter, Depends from auth import get_current_user, require_admin from docker_client import DockerError, get_client, safe_call from models.user import User from services import update_service router = APIRouter(prefix="/api/images", tags=["images"]) COMPOSE_PROJECT_LABEL = "com.docker.compose.project" @router.get("") def list_images(_user: User = Depends(get_current_user)) -> list[dict]: try: client = get_client() images = safe_call(client.images.list) containers = safe_call(client.containers.list, all=True) except DockerError: return [] # Map image ref -> stacks using it. usage: dict[str, set[str]] = {} for c in containers: ref = c.attrs.get("Config", {}).get("Image") stack = c.labels.get(COMPOSE_PROJECT_LABEL) if ref: usage.setdefault(ref, set()) if stack: usage[ref].add(stack) cache = update_service.get_cache() result = [] for img in images: tags = img.tags or [] if not tags: continue for tag in tags: upd = cache.get(tag) result.append( { "id": img.short_id, "tag": tag, "size": img.attrs.get("Size", 0), "created": img.attrs.get("Created"), "stacks": sorted(usage.get(tag, set())), "update": upd, } ) result.sort(key=lambda r: r["tag"]) return result @router.get("/updates") def updates(_user: User = Depends(get_current_user)) -> dict: return update_service.get_cache() @router.post("/check") async def check(_user: User = Depends(require_admin)) -> dict: return await update_service.check_all()