"""대시보드 (화면 1, 읽기 전용) + 목록 컨트롤(검색·정렬·페이지네이션). GET / 전체 페이지 GET /repos 저장소 목록 전체 조각 (요약+검색창+표) — 새로고침용 GET /repos/table 표+페이저 조각 — 검색/정렬/페이지 이동 대상 저장소 수십 개 규모 가정: 전체를 가져와 enrich 후 메모리에서 검색·정렬·페이지네이션한다. (요약 카드는 검색/필터와 무관하게 항상 전체 인벤토리 기준.) """ from __future__ import annotations from math import ceil from typing import Any import httpx from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse from .. import task_store, views from ..deps import get_pulp_client, templates from ..pulp_client import PulpClient router = APIRouter() PAGE_SIZE = 10 def _load_repos( pulp: PulpClient, q: str = "", sort: str = "name", direction: str = "asc", page: int = 1, ) -> dict[str, Any]: syncing = task_store.active_count() base = {"q": q, "sort": sort, "dir": direction} try: raw = pulp.list_repos() except httpx.HTTPError as exc: return { **base, "repos": [], "summary": views.summarize([], syncing), "page": 1, "pages": 1, "total": 0, "error": str(exc), } repo_views: list[dict[str, Any]] = [] for repo in raw: version = None vhref = repo.get("latest_version_href") if vhref: try: version = pulp.get_version(vhref) except httpx.HTTPError: version = None # 버전 상세 실패는 치명적이지 않음 — 행은 표시 repo_views.append(views.build_repo_view(repo, version)) summary = views.summarize(repo_views, syncing) # 전체 기준 ql = q.strip().lower() filtered = [r for r in repo_views if ql in r["name"].lower()] if ql else repo_views filtered = views.sort_repos(filtered, sort, direction) total = len(filtered) pages = max(1, ceil(total / PAGE_SIZE)) page = min(max(page, 1), pages) window = filtered[(page - 1) * PAGE_SIZE : page * PAGE_SIZE] return { **base, "repos": window, "summary": summary, "page": page, "pages": pages, "total": total, "error": None, } @router.get("/", response_class=HTMLResponse) def dashboard( request: Request, q: str = "", sort: str = "name", dir: str = "asc", page: int = 1, pulp: PulpClient = Depends(get_pulp_client), ) -> HTMLResponse: return templates.TemplateResponse( request, "dashboard.html", _load_repos(pulp, q, sort, dir, page) ) @router.get("/repos", response_class=HTMLResponse) def repos_fragment( request: Request, q: str = "", sort: str = "name", dir: str = "asc", page: int = 1, pulp: PulpClient = Depends(get_pulp_client), ) -> HTMLResponse: return templates.TemplateResponse( request, "partials/repo_list.html", _load_repos(pulp, q, sort, dir, page) ) @router.get("/repos/table", response_class=HTMLResponse) def repos_table( request: Request, q: str = "", sort: str = "name", dir: str = "asc", page: int = 1, pulp: PulpClient = Depends(get_pulp_client), ) -> HTMLResponse: return templates.TemplateResponse( request, "partials/table_wrap.html", _load_repos(pulp, q, sort, dir, page) )