feat: settings panel + multi-folder scanning

Settings panel (⚙ in header):
- Watch Folders: add/remove paths, toggle enabled; worker polls every 30s
  and calls watcher.add/unwatch without restart
- Ingestion: thumb/preview size and quality, applied per-file at ingest time
- Duplicate Detection: configurable Hamming distance threshold
- Danger Zone: clear all dismissed duplicate pairs

Multi-folder scanning:
- watch_folders table replaces single PHOTO_DIR env var
- PHOTO_DIR env var seeds the first entry on startup
- settings table with defaults for all tunable values
- Worker reads settings from DB on each drain cycle so changes are live

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 4.6 committed 2026-08-18 07:58:44 -04:00
1 parent 8c5c36857f
commit d18c855571
8 files changed
+494 -73

No files matched your search

+31
View File
@@ -68,6 +68,37 @@ export function removeFromCollection(photoId, collectionId) {
return api(`/api/photos/${photoId}/collections/${collectionId}`, { method: 'DELETE' });
}
// Settings
export function fetchSettings() {
return api('/api/settings');
}
export function patchSettings(updates) {
return api('/api/settings', { method: 'PATCH', ...json(updates) });
}
// Watch folders
export function fetchWatchFolders() {
return api('/api/watch-folders');
}
export function addWatchFolder(path) {
return api('/api/watch-folders', { method: 'POST', ...json({ path }) });
}
export function removeWatchFolder(id) {
return api(`/api/watch-folders/${id}`, { method: 'DELETE' });
}
export function toggleWatchFolder(id, enabled) {
return api(`/api/watch-folders/${id}`, { method: 'PATCH', ...json({ enabled }) });
}
// Admin
export function clearDismissals() {
return api('/api/admin/clear-dismissals', { method: 'POST' });
}
// Duplicates
export function fetchDuplicates() {
return api('/api/duplicates');