Replaces the read-only "Largest images" top-10 table with a Tasks-style list of
ALL Docker images, with select-one / select-multiple / clear-all removal that
mirrors the Tasks page UX (row checkboxes, master select-all, a button that
morphs Clear All ↔ Delete Selected (N), an eo confirm modal).
Deletion routes through the task system, NOT a new web API: a new
`libreportal system image rm [--force] <ids>` CLI subcommand (validates each
ref, loops runFileOp docker image rm, reports a tally) is invoked via the
system_image_rm task action — same pattern as Reclaim. The web backend change
is read-only (uncap the existing /storage image list). In-use images are
skipped by default with an opt-in "force-remove" toggle (warned). The page
stays put, toasts, and refreshes on the task's completion event.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Like named volumes, a container's writable layer is a near-zero scratch
number for LibrePortal (app data lives in bind mounts, shown per-app), so
sitting it next to per-app storage just confused things. Remove the
"Containers" slice/card and its backend summation, and reframe the Docker
breakdown as "Docker engine" overhead (images + build cache) — clearly
separate from your app data.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
LibrePortal apps keep data in bind mounts, so Docker named-volume
accounting is always ~empty and just reads as a confusing "0 B". Now that
per-app on-disk usage covers the real "what's filling my disk" question,
remove volumes end to end: the donut slice, category card, "Largest
volumes" table and the System-page count, plus the backend's volume
summation and top_volumes payload. Reclaim copy no longer references
volumes (it reassures about app data instead).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Three fixes from testing the storage page:
- Placement: the "Reclaim space" button moves into the page header,
top-right (matching the metric page), instead of sitting in the body.
- It now actually reclaims: build cache needs -a to drop (docker reports
0 B "reclaimable" without it, but it's pure cache — safe to clear), so
the CLI uses `docker builder prune -af`. Previously the safe scope
freed ~nothing on a box whose reclaimable was mostly cache.
- Honest "Reclaimable" number: /api/system/storage was counting the
whole build cache AND unused tagged images, overstating what the safe
prune frees (e.g. 340 MB shown, ~96 MB per docker, button cleared 0).
Reclaimable now = dangling images + build cache only; stopped
containers and volumes are never counted (the safe prune never touches
them). Headline now matches the button's effect.
Also simplify the CLI output (drop the jargony scope notice and the
reclaimed-total greps) and re-enable the now-persistent header button
after the post-reclaim refreshes.
Signed-off-by: librelad <librelad@digitalangels.vip>
Promotes the admin → System area from a single index page with a transient
overlay into a real router with four addressable sub-pages, plus a docker-
api-backed read surface to drive them.
URLs:
/admin/config/system index (gauges + trends + per-app table)
/admin/config/system/metric/<key> single-metric deep-dive
/admin/config/system/app/<name> per-container app deep-dive
/admin/config/system/storage docker disk-usage breakdown
The path resolves to category=`system` in adminCategoryFromPath, so the
existing SPA dispatch still drops you into AdminSystem; AdminSystem then
reads the rest of the path and mounts the right sub-renderer into
config-section. Each sub-page owns its own DOM + lifecycle and is disposed
when the orchestrator re-mounts on the next navigation. Browser back, page
reload, and shareable URLs all work — no modal, no overlay state, no
fragile open/close lifecycle. Esc on the metric page navigates back to the
index.
Backend (containers/libreportal/backend):
- utils/docker.js — shared client for the bind-mounted Docker socket
(extracted from service-routes.js' inline copy). dockerRequest,
dockerStream, and a multiplex-log decoder for /containers/:id/logs.
- routes/docker-info-routes.js mounted at /api/system, contributes:
GET /containers full list, plus grouped-by-app shape
GET /containers/:id inspect projection (limits, mounts,
networks, ports, health, restart count)
GET /containers/:id/stats one-shot CPU% / memory / network /
blkio / pids (derived from precpu/cpu
deltas, like `docker stats`)
GET /containers/:id/logs last N lines, multiplex-decoded
GET /storage `docker system df` rolled up per
category, plus top-10 images +
top-10 volumes by size
Frontend (containers/libreportal/frontend/js/components/admin):
- admin-system.js — refactored into orchestrator + index view. _parsePath
drives dispatch; sub-views are window.SystemMetricPage /
SystemAppPage / SystemStoragePage classes mounted into config-section.
The per-app table is now keyboard-focusable rows that navigate to the
per-container page; the Docker strip grows a "Storage" tile that
navigates to the storage page.
- system-metric-page.js (renamed from system-detail.js, rewritten as an
in-flow page renderer). Same chart visuals as the old overlay — grid,
axis, area gradient, peak/min/now markers, hover crosshair + tooltip
scrubbing, per-metric accent theming — but rendered into the page
instead of a fixed-position panel. Range picker reflects to ?range=
so refresh preserves the selection. 1 Hz SSE feed splices into the
chart tail in real time.
- system-app-page.js — for each container in the app stack: status,
image, image-id, uptime; live stats card (cpu / mem with limit-pct /
rx / tx / blkio r-w / pids, polled every 2s with warn+danger colour
cues at 80% and 95% of memory limit); limits panel (memory, cpu,
pids, restart policy, restart count, started-ago); healthcheck
status + last 3 probes; networks table (name, IP, gateway, MAC);
published ports; mounts table with type badges; collapsible log tail
with refresh.
- system-storage-page.js — donut chart (cumulative-arc, hand-rolled
SVG) splits total in-use disk by images / volumes / containers /
build cache; per-category cards with size + reclaimable; top-10
images and top-10 volumes tables with "unused" / "orphan" badges.
CSS (containers/libreportal/frontend/css/admin.css):
Overlay-specific rules (.sys-detail wrapper, backdrop, panel, close
button, body lock) removed. Inner chart rules (stats grid, svg, grid,
axes, peak/min/now, crosshair, tooltip, foot) retained and reused by
the metric page. New blocks for .sys-metric-page, .sys-app-page (with
stat warn/danger colour states, health pills, mount-type badges, log
pre styling), .sys-storage-page (donut + legend + headline + per-
category cards + orphan/unused badges), .sys-app-row (clickable
rows with arrow + accent hover), .sys-stat-link (clickable Docker
strip tile).
Signed-off-by: librelad <librelad@digitalangels.vip>