feat(libraries): scope a library scan to one registered folder

POST /api/libraries/:id/scan accepts an optional {"folder_path": "..."}:
the scan then covers exactly that one registered folder's subtree instead
of the whole library. The path is validated against the library's
registered folders — anything else is a 400, never trusted. An omitted
body keeps today's full-library behavior, and coalescing (409 with the
active job) is unchanged.

The LibraryPanel folder rows get a per-folder Scan button: pure htmx
attributes posting to the same scan endpoint, with hxValsFolderScan
emitting plain eval-free JSON for hx-vals alongside the existing
hxValsFolderDelete precedent. Progress surfaces through the existing
active-scan polling; no new client code.

Safety: the scanner's archive-reconciliation guard (previous commit)
skips partially-covered libraries, so a one-folder scan cannot mark the
library's other folders' items missing.
This commit is contained in:
John O'Keefe committed 2026-10-27 13:22:41 -04:00
1 parent 6f37be59fc
commit 0f1d0abbb0
6 files changed
+286 -197

No files matched your search

+12
View File
@@ -267,6 +267,18 @@ templ LibraryPanel(user User, libraryID string, library LibraryData, folders []F
for _, folder := range folders {
<div class="flex items-center justify-between gap-2 p-2 rounded-lg" style="background-color: var(--bg-primary);">
<code class="text-xs flex-1 truncate" style="color: var(--text-primary)">{ folder.FolderPath }</code>
<button
class="icon-btn h-7 w-7 shrink-0"
hx-post={ "/api/libraries/" + libraryID + "/scan" }
hx-vals={ hxValsFolderScan(folder.FolderPath) }
hx-target="#scan-indicator"
hx-swap="innerHTML"
hx-confirm="Scan this folder?"
title="Scan this folder"
aria-label="Scan this folder"
>
@Icon("refresh", "h-3.5 w-3.5")
</button>
<button
class="icon-btn h-7 w-7 shrink-0"
hx-delete={ "/admin/library/" + libraryID + "/folders" }