diff options
| author | dax <me@dax.ist> | 2026-08-05 20:14:45 +0100 |
|---|---|---|
| committer | dax <me@dax.ist> | 2026-08-05 20:14:45 +0100 |
| commit | 994cdbec9dd6ae6b965b56f4f6816a1dfef8bcc9 (patch) | |
| tree | 0d7b0d1c5bec8d34b5ebe731af34ce3e8b1706ca /public/admin.js | |
| parent | d2a8b2abc908311fc249454086d0d0eacfe861e1 (diff) | |
staging: local drop-folder (watch/) auto-import with startup recovery + failed batches; admin local-files panel; upload 413 pre-checkHEADmain
Diffstat (limited to 'public/admin.js')
| -rw-r--r-- | public/admin.js | 53 |
1 files changed, 52 insertions, 1 deletions
diff --git a/public/admin.js b/public/admin.js index 90dd6b0..5291620 100644 --- a/public/admin.js +++ b/public/admin.js @@ -99,6 +99,13 @@ function fmtUptime(s) { return h ? `${h} h ${m} m` : `${m} m` } +function fmtSize(n) { + if (n >= 1e9) return (n / 1e9).toFixed(1) + " GB" + if (n >= 1e6) return (n / 1e6).toFixed(1) + " MB" + if (n >= 1e3) return Math.round(n / 1e3) + " KB" + return n + " B" +} + let debounceTimer = 0 function debounce(fn, ms) { clearTimeout(debounceTimer) @@ -294,6 +301,7 @@ async function enterDash() { try { await loadStatus() await loadRecent() + await loadStaging().catch(() => {}) } catch (err) { if (views.dash.hidden) return; } } @@ -316,6 +324,34 @@ async function loadRecent() { $("#im-recent").hidden = data.photos.length === 0 } +async function loadStaging() { + const d = await api("/api/admin/staging") + $("#st-path").textContent = d.watch + const filesEl = $("#st-files") + filesEl.replaceChildren() + for (const f of d.files) { + const row = document.createElement("div") + row.className = "st-file" + const n = document.createElement("span") + n.className = "n" + n.textContent = f.name + const s = document.createElement("span") + s.className = "s" + s.textContent = fmtSize(f.size) + " · " + fmtAge(Date.now() - f.mtime) + row.append(n, s) + filesEl.appendChild(row) + } + $("#st-none").hidden = d.files.length > 0 + const logEl = $("#st-log") + logEl.replaceChildren() + for (const r of d.imports) { + const row = document.createElement("div") + row.className = "st-log-item " + (r.ok ? "ok" : "bad") + row.textContent = `${new Date(r.at).toLocaleString()} — ${r.ok ? `imported ${r.count} ${r.count === 1 ? "photo" : "photos"}` : "failed: " + (r.error || "unknown error")}` + logEl.appendChild(row) + } +} + function setImProgress(pct, text) { $("#im-progress").hidden = false $("#im-status").textContent = text @@ -360,6 +396,7 @@ async function importFiles(list) { toast(`imported ${data.imported} ${data.imported === 1 ? "photo" : "photos"}`) await loadStatus() await loadRecent() + await loadStaging().catch(() => {}) } catch (err) { $("#im-err").textContent = err.message } finally { @@ -378,10 +415,24 @@ async function init() { $("#tab-status").hidden = tab !== "status" $("#tab-import").hidden = tab !== "import" $("#tab-graph").hidden = tab !== "graph" - if (tab === "import") loadRecent().catch((e2) => toast(e2.message)) + if (tab === "import") { + loadRecent().catch((e2) => toast(e2.message)) + loadStaging().catch((e2) => toast(e2.message)) + } }) }) + $("#st-scan").addEventListener("click", async () => { + const btn = $("#st-scan") + btn.disabled = true + try { + await api("/api/admin/staging", { method: "POST" }) + await loadStaging() + toast("scan complete") + } catch (err) { toast(err.message) } + btn.disabled = false + }) + $("#setup-form").addEventListener("submit", async (e) => { e.preventDefault() const err = $("#setup-err") |
