From 27c694901d7edf2a8678be57214ef6210d55d8a1 Mon Sep 17 00:00:00 2001 From: Jonathan Sykes Date: Wed, 14 Oct 2026 03:09:12 +0800 Subject: [PATCH] Add upload library management and ops diagnostics --- .gitignore | 1 + frontend/admin.html | 104 ++++++++++++++++++++++++++++--- perf/README.md | 14 +++++ perf/admin-ops-bench.mjs | 29 +++++++++ playwright.admin-ops.config.js | 3 + server/db.js | 53 ++++++++++++++-- server/p2p-admin.js | 5 +- server/p2p-admin.test.js | 26 ++++++++ server/package.json | 2 +- server/request-monitor.js | 17 ++++-- server/request-monitor.test.js | 19 +++++- server/server.js | 9 ++- server/uploads.js | 108 ++++++++++++++++++++++++++++++--- server/uploads.test.js | 48 +++++++++++++-- tests/admin-ops.spec.js | 65 ++++++++++++++++++++ 15 files changed, 467 insertions(+), 36 deletions(-) create mode 100644 perf/admin-ops-bench.mjs create mode 100644 playwright.admin-ops.config.js create mode 100644 server/p2p-admin.test.js create mode 100644 tests/admin-ops.spec.js diff --git a/.gitignore b/.gitignore index 2f062fb..b03cabc 100755 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ plans/transport-shots/ # Search button visual review screenshots plans/search-button-shots/ plans/search-button-final-shots/ +plans/admin-ops-shots/ diff --git a/frontend/admin.html b/frontend/admin.html index 3a5f129..870f36d 100644 --- a/frontend/admin.html +++ b/frontend/admin.html @@ -372,6 +372,12 @@ .analytics-form .query { flex: 1 1 250px; } .analytics-form .field { width: 100%; min-width: 0; } .analytics-form button { min-height: 44px; } + #p-ops .btn, #p-devices .btn, #p-uploads .btn, #p-uploads .field, #p-devices .field { min-height: 44px; } + #p-uploads .tbl td { vertical-align: top; } + #p-uploads .tbl td .field { min-width: 130px; margin-bottom: 5px; } + #p-uploads .tbl label.btn { display: inline-flex; align-items: center; cursor: pointer; } + #p-uploads input[type="checkbox"], #p-devices input[type="checkbox"] { width: 20px; height: 20px; accent-color: var(--a1); } + #p-uploads input[type="file"]::file-selector-button { min-height: 36px; margin: 2px 8px 2px 0; padding: 0 10px; border: 1px solid var(--line-2); border-radius: 7px; background: var(--bg-3); color: var(--text); font: 650 12px var(--ui); } .bulk-selection { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; margin: 12px 0; } .bulk-selection label { display: inline-flex; align-items: center; gap: 9px; min-height: 44px; color: var(--text); } .bulk-selection input[type="checkbox"] { width: 20px; height: 20px; accent-color: var(--a1); } @@ -381,10 +387,17 @@ #bulkJobs .btn.sm { min-height: 44px; height: auto; } .analytics-table { overflow-x: auto; margin-top: 12px; } .analytics-table table { width: 100%; } + #opsCards { grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); } + #opsRoutes { min-width: 680px; } + #opsRoutes th:first-child, #opsRoutes td:first-child { position: sticky; left: 0; z-index: 1; background: var(--bg-1); } + #p-ops .scroll { overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: thin; } + #opsRoutes th:not(:first-child), #opsRoutes td:not(:first-child) { white-space: nowrap; } + .route-scroll-hint { display: none; } .collection-job { padding: 14px 0; border-bottom: 1px solid var(--line-2); overflow-wrap: anywhere; } .collection-job p { margin: 4px 0; color: var(--text-2); } #metadataDetail { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 400px; overflow: auto; font-size: 13px; } - @media (max-width: 480px) { .analytics-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .metric { padding: 12px; } .metric b { font-size: 22px; } } + @media (max-width: 760px) { .route-scroll-hint { display: block; margin: 2px 0 8px; font-size: 12px; } } + @media (max-width: 480px) { .analytics-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .metric { padding: 12px; } .metric b { font-size: 22px; } #opsCards { grid-template-columns: repeat(2,minmax(0,1fr)); } } .deep-grid { display: grid; grid-template-columns: repeat(auto-fit,minmax(min(100%,240px),1fr)); gap: 12px; } .deep-grid label { display: grid; gap: 5px; min-width: 0; color: var(--text); font-size: 13px; } .deep-search .muted { color: var(--text-2); } @@ -408,7 +421,7 @@ overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; scrollbar-width: thin; background: var(--bg-1); border-top: 1px solid var(--line-2); } - .nav .tab { flex: 0 0 68px; min-width: 68px; min-height: 44px; flex-direction: column; justify-content: center; gap: 3px; height: auto; padding: 4px 3px; font-size: 10.5px; border-radius: 12px; } + .nav .tab { flex: 0 0 68px; min-width: 68px; min-height: 44px; flex-direction: column; justify-content: center; gap: 3px; height: auto; padding: 4px 3px; font-size: 10px; line-height: 1.05; text-align: center; white-space: normal; border-radius: 12px; } .nav .tab .badge { position: absolute; top: 2px; left: 55%; } .steps { grid-template-columns: 1fr; } .ln-main { grid-template-columns: 82px minmax(0, 1fr) 44px; gap: 6px; padding: 7px; } @@ -744,7 +757,7 @@

Performance

Route latency, service queues, process health and storage headroom.

-

Route health

+

Route health

Swipe the table horizontally to compare every route metric.

Storage volumes

24-hour p95 history

@@ -752,7 +765,16 @@

P2P devices

Presence, stored holdings and device-level sharing controls.

-
+

Network health

Online devices · verified holdings · active intake · open intake, left to right.

+
+
+
+

Upload library

Edit metadata and visibility, replace files without changing IDs, and review storage by owner.

+
+

Storage by owner

+
+

+

Orphan files

Files in upload storage without a database row. Review before removing them from disk.