From 69b2e36a389c8c8f19984deec3d571b05fdacd3f Mon Sep 17 00:00:00 2001 From: izzy Date: Tue, 13 Jan 2026 13:18:58 +0000 Subject: [PATCH] refactor: use new web service architecture (1/2) --- .../integrity/IntegrityReportTableItem.svelte | 41 ++++ web/src/lib/managers/event-manager.svelte.ts | 3 + web/src/lib/services/integrity.service.ts | 151 +++++++++++++++ web/src/lib/services/maintenance.service.ts | 31 ++++ web/src/routes/admin/maintenance/+page.svelte | 30 +-- .../integrity-report/[type]/+page.svelte | 175 ++++-------------- 6 files changed, 263 insertions(+), 168 deletions(-) create mode 100644 web/src/lib/components/maintenance/integrity/IntegrityReportTableItem.svelte create mode 100644 web/src/lib/services/integrity.service.ts create mode 100644 web/src/lib/services/maintenance.service.ts diff --git a/web/src/lib/components/maintenance/integrity/IntegrityReportTableItem.svelte b/web/src/lib/components/maintenance/integrity/IntegrityReportTableItem.svelte new file mode 100644 index 0000000000..cb3674ab3a --- /dev/null +++ b/web/src/lib/components/maintenance/integrity/IntegrityReportTableItem.svelte @@ -0,0 +1,41 @@ + + + + + + {path} + + + + diff --git a/web/src/lib/managers/event-manager.svelte.ts b/web/src/lib/managers/event-manager.svelte.ts index 7124fc4524..216d3c2971 100644 --- a/web/src/lib/managers/event-manager.svelte.ts +++ b/web/src/lib/managers/event-manager.svelte.ts @@ -5,6 +5,7 @@ import type { AlbumResponseDto, ApiKeyResponseDto, AssetResponseDto, + IntegrityReportType, LibraryResponseDto, LoginResponseDto, PersonResponseDto, @@ -64,6 +65,8 @@ export type Events = { SystemConfigUpdate: [SystemConfigDto]; + IntegrityReportDelete: [{ type?: IntegrityReportType; id?: string; isDeleting: boolean; isDeleted: boolean }]; + LibraryCreate: [LibraryResponseDto]; LibraryUpdate: [LibraryResponseDto]; LibraryDelete: [{ id: string }]; diff --git a/web/src/lib/services/integrity.service.ts b/web/src/lib/services/integrity.service.ts new file mode 100644 index 0000000000..e741c23a33 --- /dev/null +++ b/web/src/lib/services/integrity.service.ts @@ -0,0 +1,151 @@ +import { eventManager } from '$lib/managers/event-manager.svelte'; +import { handleError } from '$lib/utils/handle-error'; +import { getFormatter } from '$lib/utils/i18n'; +import { createJob, deleteIntegrityReport, getBaseUrl, IntegrityReportType, ManualJobName } from '@immich/sdk'; +import { modalManager, toastManager, type ActionItem } from '@immich/ui'; +import { mdiDownload, mdiTrashCanOutline } from '@mdi/js'; +import type { MessageFormatter } from 'svelte-i18n'; + +export const getIntegrityReportActions = ($t: MessageFormatter, reportType: IntegrityReportType) => { + const Download: ActionItem = { + type: $t('command'), + title: $t('admin.download_csv'), + icon: mdiDownload, + onAction() { + handleDownloadIntegrityReportCsv(reportType); + }, + }; + + const Delete: ActionItem = { + type: $t('command'), + title: $t('trash_page_delete_all'), + icon: mdiTrashCanOutline, + color: 'danger', + onAction() { + void handleRemoveAllIntegrityReportItems(reportType); + }, + }; + + return { Download, Delete }; +}; + +export const getIntegrityReportItemActions = ( + $t: MessageFormatter, + reportId: string, + reportType: IntegrityReportType, +) => { + const Download = + reportType === IntegrityReportType.UntrackedFile || reportType === IntegrityReportType.ChecksumMismatch + ? { + title: $t('download'), + icon: mdiDownload, + onAction() { + void handleDownloadIntegrityReportFile(reportId); + }, + } + : undefined; + + const Delete = { + title: $t('delete'), + icon: mdiTrashCanOutline, + color: 'danger', + onAction() { + void handleRemoveIntegrityReportItem(reportId); + }, + }; + + return { Download, Delete }; +}; + +export const handleDownloadIntegrityReportFile = (reportId: string) => { + location.href = `${getBaseUrl()}/admin/integrity/report/${reportId}/file`; +}; + +export const handleDownloadIntegrityReportCsv = (reportType: IntegrityReportType) => { + location.href = `${getBaseUrl()}/admin/integrity/report/${reportType}/csv`; +}; + +export const handleRemoveAllIntegrityReportItems = async (reportType: IntegrityReportType) => { + const $t = await getFormatter(); + const confirm = await modalManager.showDialog({ + confirmText: $t('delete'), + }); + + if (confirm) { + let name: ManualJobName; + switch (reportType) { + case IntegrityReportType.UntrackedFile: { + name = ManualJobName.IntegrityUntrackedFilesDeleteAll; + break; + } + case IntegrityReportType.MissingFile: { + name = ManualJobName.IntegrityMissingFilesDeleteAll; + break; + } + case IntegrityReportType.ChecksumMismatch: { + name = ManualJobName.IntegrityChecksumMismatchDeleteAll; + break; + } + } + + try { + eventManager.emit('IntegrityReportDelete', { + type: reportType, + isDeleting: true, + isDeleted: false, + }); + + await createJob({ jobCreateDto: { name } }); + toastManager.success($t('admin.job_created')); + + eventManager.emit('IntegrityReportDelete', { + type: reportType, + isDeleting: false, + isDeleted: true, + }); + } catch (error) { + handleError(error, $t('failed_to_delete_file')); + + eventManager.emit('IntegrityReportDelete', { + type: reportType, + isDeleting: false, + isDeleted: false, + }); + } + } +}; + +export const handleRemoveIntegrityReportItem = async (reportId: string) => { + const $t = await getFormatter(); + const confirm = await modalManager.showDialog({ + confirmText: $t('delete'), + }); + + if (confirm) { + try { + eventManager.emit('IntegrityReportDelete', { + id: reportId, + isDeleting: true, + isDeleted: false, + }); + + await deleteIntegrityReport({ + id: reportId, + }); + + eventManager.emit('IntegrityReportDelete', { + id: reportId, + isDeleting: false, + isDeleted: true, + }); + } catch (error) { + handleError(error, $t('failed_to_delete_file')); + + eventManager.emit('IntegrityReportDelete', { + id: reportId, + isDeleting: false, + isDeleted: false, + }); + } + } +}; diff --git a/web/src/lib/services/maintenance.service.ts b/web/src/lib/services/maintenance.service.ts new file mode 100644 index 0000000000..848d40ec35 --- /dev/null +++ b/web/src/lib/services/maintenance.service.ts @@ -0,0 +1,31 @@ +import { handleError } from '$lib/utils/handle-error'; +import { getFormatter } from '$lib/utils/i18n'; +import { MaintenanceAction, setMaintenanceMode, type SetMaintenanceModeDto } from '@immich/sdk'; +import type { ActionItem } from '@immich/ui'; +import { mdiProgressWrench } from '@mdi/js'; +import type { MessageFormatter } from 'svelte-i18n'; + +export const getMaintenanceAdminActions = ($t: MessageFormatter) => { + const StartMaintenance: ActionItem = { + title: $t('admin.maintenance_start'), + onAction: () => + handleSetMaintenanceMode({ + action: MaintenanceAction.Start, + }), + icon: mdiProgressWrench, + }; + + return { StartMaintenance }; +}; + +export const handleSetMaintenanceMode = async (dto: SetMaintenanceModeDto) => { + const $t = await getFormatter(); + + try { + await setMaintenanceMode({ + setMaintenanceModeDto: dto, + }); + } catch (error) { + handleError(error, $t('admin.maintenance_start_error')); + } +}; diff --git a/web/src/routes/admin/maintenance/+page.svelte b/web/src/routes/admin/maintenance/+page.svelte index 402734c7d6..3425812cbd 100644 --- a/web/src/routes/admin/maintenance/+page.svelte +++ b/web/src/routes/admin/maintenance/+page.svelte @@ -2,6 +2,7 @@ import AdminPageLayout from '$lib/components/layouts/AdminPageLayout.svelte'; import ServerStatisticsCard from '$lib/components/server-statistics/ServerStatisticsCard.svelte'; import { AppRoute } from '$lib/constants'; + import { getMaintenanceAdminActions } from '$lib/services/maintenance.service'; import { asyncTimeout } from '$lib/utils'; import { handleError } from '$lib/utils/handle-error'; import { @@ -9,14 +10,11 @@ getIntegrityReportSummary, getQueuesLegacy, IntegrityReportType, - MaintenanceAction, ManualJobName, - setMaintenanceMode, type IntegrityReportSummaryResponseDto, type QueuesResponseLegacyDto, } from '@immich/sdk'; import { Button, HStack, toastManager } from '@immich/ui'; - import { mdiProgressWrench } from '@mdi/js'; import { onDestroy, onMount } from 'svelte'; import { t } from 'svelte-i18n'; import type { PageData } from './$types'; @@ -25,7 +23,8 @@ data: PageData; } - let { data }: Props = $props(); + const { data }: Props = $props(); + const { StartMaintenance } = $derived(getMaintenanceAdminActions($t)); let integrityReport: IntegrityReportSummaryResponseDto = $state(data.integrityReport); @@ -35,18 +34,6 @@ IntegrityReportType.ChecksumMismatch, ]; - async function switchToMaintenance() { - try { - await setMaintenanceMode({ - setMaintenanceModeDto: { - action: MaintenanceAction.Start, - }, - }); - } catch (error) { - handleError(error, $t('admin.maintenance_start_error')); - } - } - let jobs: QueuesResponseLegacyDto | undefined = $state(); let expectingUpdate: boolean = $state(false); @@ -106,16 +93,7 @@ }); - +
diff --git a/web/src/routes/admin/maintenance/integrity-report/[type]/+page.svelte b/web/src/routes/admin/maintenance/integrity-report/[type]/+page.svelte index 8873ef12f9..b8ef9efa7e 100644 --- a/web/src/routes/admin/maintenance/integrity-report/[type]/+page.svelte +++ b/web/src/routes/admin/maintenance/integrity-report/[type]/+page.svelte @@ -1,36 +1,14 @@ + + { - location.href = `${getBaseUrl()}/admin/maintenance/integrity/report/${data.type}/csv`; - }, - }, - { - title: $t('trash_page_delete_all'), - onAction: removeAll, - icon: mdiTrashCanOutline, - }, - ]} + actions={[Download, Delete]} >
-
+
{$t('filename')} @@ -197,19 +100,7 @@ {#each integrityReport.items as { id, path } (id)} - - {path} - handleOpen(event, { position: 'top-right' }, id)} - aria-label={$t('open')} - disabled={deleting.has(id) || deleting.has('all')} - /> - + {/each}