Merge claude/2
This commit is contained in:
commit
327fda8cd9
@ -21,12 +21,12 @@
|
||||
// Last N lines of combined stdout/stderr, multiplex-decoded.
|
||||
//
|
||||
// GET /api/system/storage
|
||||
// `docker system df` — total + reclaimable per category (images,
|
||||
// containers, build cache). Cached for STORAGE_TTL_MS because this is one
|
||||
// of the more expensive calls on a busy daemon. Named volumes are omitted:
|
||||
// LibrePortal apps keep data in bind mounts, so volume accounting is always
|
||||
// ~empty here — per-app on-disk usage is generated separately (see
|
||||
// webuiSystemAppStorage / /data/system/app_storage.json).
|
||||
// `docker system df` — total + reclaimable for the engine overhead worth
|
||||
// acting on (images, build cache). Cached for STORAGE_TTL_MS because this is
|
||||
// one of the more expensive calls on a busy daemon. Named volumes and
|
||||
// container writable layers are omitted: LibrePortal apps keep data in bind
|
||||
// mounts, so both read ~empty here — per-app on-disk usage is generated
|
||||
// separately (see webuiSystemAppStorage / /data/system/app_storage.json).
|
||||
//
|
||||
// Mounted at /api/system in routes.js (so paths are /api/system/containers
|
||||
// etc.). Uses the shared docker util (utils/docker.js) which talks to the
|
||||
@ -336,12 +336,15 @@ router.get('/storage', async (req, res) => {
|
||||
try {
|
||||
const df = await storageCache.get('df', () => dockerRequest('GET', '/system/df'));
|
||||
if (!df) return res.status(500).json({ error: 'no_data' });
|
||||
// Roll the verbose response up into headline numbers per category.
|
||||
// "Reclaimable" across this page reflects exactly what the Reclaim
|
||||
// button frees: dangling images + the whole build cache. Tagged-but-
|
||||
// unused images and stopped containers are deliberately NOT counted —
|
||||
// the safe prune leaves them alone — so the headline number matches the
|
||||
// button's effect instead of overstating it.
|
||||
// Roll the verbose response up into headline numbers per category. We
|
||||
// surface only the engine overhead worth acting on — images and build
|
||||
// cache — and skip container writable layers: for LibrePortal that's a
|
||||
// near-zero scratch number (app data lives in bind mounts, shown per-app
|
||||
// elsewhere) that just confuses the picture.
|
||||
// "Reclaimable" reflects exactly what the Reclaim button frees: dangling
|
||||
// images + the whole build cache. Tagged-but-unused images are
|
||||
// deliberately NOT counted — the safe prune leaves them alone — so the
|
||||
// headline matches the button's effect instead of overstating it.
|
||||
const isDangling = (im) => {
|
||||
const tags = im.RepoTags || [];
|
||||
return tags.length === 0 || tags.every(t => t.includes('<none>'));
|
||||
@ -356,14 +359,6 @@ router.get('/storage', async (req, res) => {
|
||||
},
|
||||
{ count: 0, size: 0, shared: 0, reclaimable: 0 }
|
||||
);
|
||||
const sumContainers = (df.Containers || []).reduce(
|
||||
(a, c) => {
|
||||
a.count++;
|
||||
a.size += c.SizeRw || 0;
|
||||
return a;
|
||||
},
|
||||
{ count: 0, size: 0, reclaimable: 0 }
|
||||
);
|
||||
const sumBuild = (df.BuildCache || []).reduce(
|
||||
(a, b) => {
|
||||
a.count++;
|
||||
@ -387,13 +382,12 @@ router.get('/storage', async (req, res) => {
|
||||
containers: im.Containers || 0,
|
||||
created: im.Created,
|
||||
}));
|
||||
const total = sumImages.size + sumContainers.size + sumBuild.size;
|
||||
const reclaimable = sumImages.reclaimable + sumContainers.reclaimable + sumBuild.reclaimable;
|
||||
const total = sumImages.size + sumBuild.size;
|
||||
const reclaimable = sumImages.reclaimable + sumBuild.reclaimable;
|
||||
res.set('Cache-Control', 'no-store');
|
||||
res.json({
|
||||
total, reclaimable,
|
||||
images: sumImages,
|
||||
containers: sumContainers,
|
||||
build_cache: sumBuild,
|
||||
top_images: topImages,
|
||||
updated: new Date().toISOString(),
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
// generator). The headline view for LibrePortal, whose data lives in bind
|
||||
// mounts, not named volumes.
|
||||
// - Docker engine `docker system df` — headline total + reclaimable, a
|
||||
// per-category donut (images / containers / build cache), per-category cards,
|
||||
// and the top images by size.
|
||||
// donut (images / build cache), per-category cards, and the top images by
|
||||
// size. The daemon's own overhead, separate from the per-app data.
|
||||
//
|
||||
// One backend call (GET /api/system/storage), cached server-side for 5s.
|
||||
// A "Reclaim space" button runs the safe prune (build cache + dangling
|
||||
@ -130,7 +130,7 @@ class SystemStoragePage {
|
||||
<a href="/admin/config/system" data-back>Admin · System</a>
|
||||
</div>
|
||||
<h1>Storage</h1>
|
||||
<p class="sys-storage-sub">On-disk space by app, plus Docker's images, containers, and build cache.</p>
|
||||
<p class="sys-storage-sub">On-disk space by app, plus Docker's own engine usage.</p>
|
||||
</div>
|
||||
<button type="button" class="sys-storage-reclaim" data-storage-reclaim title="Clear build cache and dangling images (images in use and app data are kept)">
|
||||
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>
|
||||
@ -146,7 +146,6 @@ class SystemStoragePage {
|
||||
static segmentsFrom(d) {
|
||||
return [
|
||||
{ key: 'images', label: 'Images', color: 'accent', data: (d && d.images) || {} },
|
||||
{ key: 'containers', label: 'Containers', color: 'status-info', data: (d && d.containers) || {} },
|
||||
{ key: 'build_cache', label: 'Build cache', color: 'status-warning', data: (d && d.build_cache) || {} },
|
||||
];
|
||||
}
|
||||
@ -231,7 +230,7 @@ class SystemStoragePage {
|
||||
</div>`;
|
||||
|
||||
const catCards = `
|
||||
<div class="sys-section-head"><h2>Categories</h2></div>
|
||||
<div class="sys-section-head"><h2>Docker engine</h2><span class="sys-chart-meta">images & build cache — the daemon's own usage, separate from your app data</span></div>
|
||||
<div class="sys-storage-cards">
|
||||
${segments.map(s => {
|
||||
const v = s.data || {};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user