diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index 5a75d43..ecee41c 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -99,6 +99,31 @@ } } + // ---- Server-side import (admin only) ---- + interface ImportResult { + imported: number; + skipped: number; + errors: { filename: string; reason: string }[]; + } + let importPath = $state(''); + let importing = $state(false); + let importError = $state(''); + let importResult = $state(null); + + async function runImport() { + importing = true; + importError = ''; + importResult = null; + try { + const sub = importPath.trim(); + importResult = await api.post('/files/import', sub ? { path: sub } : {}); + } catch (e) { + importError = e instanceof ApiError ? e.message : 'Import failed'; + } finally { + importing = false; + } + } + // ---- Helpers ---- function formatDate(iso: string | null | undefined): string { if (!iso) return '—'; @@ -292,6 +317,57 @@ + + {#if $authStore.user?.isAdmin} +
+

Import from server

+

+ Ingest supported files sitting in the server's import folder. Successfully imported files + are removed from that folder, and a file's modified time is kept as its date when it has no + EXIF. Admin only. +

+ +
+ + +

Relative to the server's configured import folder.

+
+ + {#if importError} + + {/if} + {#if importResult} +

+ Imported {importResult.imported}, skipped {importResult.skipped}{importResult.errors + .length + ? `, ${importResult.errors.length} error${importResult.errors.length === 1 ? '' : 's'}` + : ''}. +

+ {#if importResult.errors.length} +
    + {#each importResult.errors as err} +
  • {err.filename} — {err.reason}
  • + {/each} +
+ {/if} + {/if} + +
+ +
+
+ {/if} +

@@ -535,6 +611,27 @@ line-height: 1.5; } + /* ---- Server import ---- */ + .import-errors { + list-style: none; + margin: 0; + padding: 8px 10px; + display: flex; + flex-direction: column; + gap: 4px; + border-radius: 7px; + background-color: color-mix(in srgb, var(--color-danger) 10%, transparent); + font-size: 0.8rem; + color: var(--color-text-muted); + max-height: 180px; + overflow-y: auto; + } + + .import-errors .err-file { + color: var(--color-text-primary); + font-weight: 600; + } + /* ---- Sessions ---- */ .sessions-list { list-style: none; diff --git a/openapi.yaml b/openapi.yaml index 5c3b89b..b80e90b 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -648,6 +648,12 @@ paths: post: tags: [Files] summary: Import files from a server directory + description: > + Admin only. Ingests supported files from the server's configured import + directory (optionally a subfolder of it). Subdirectories are skipped and + not recursed. A successfully imported file is removed from the import + folder. For files without an EXIF date, the source file's modified time + is used as content_datetime. requestBody: required: true content: