2025-11-21 16:37:28 +00:00
|
|
|
<script lang="ts">
|
|
|
|
|
import MaintenanceBackupsList from '$lib/components/maintenance/MaintenanceBackupsList.svelte';
|
|
|
|
|
import { integrityCheck, type MaintenanceIntegrityResponseDto } from '@immich/sdk';
|
|
|
|
|
import { Button, Card, CardBody, Heading, HStack, Icon, Scrollable, Stack, Text } from '@immich/ui';
|
|
|
|
|
import { mdiAlert, mdiArrowLeft, mdiArrowRight, mdiCheck, mdiClose, mdiRefresh } from '@mdi/js';
|
|
|
|
|
import { onMount } from 'svelte';
|
2025-11-25 11:07:11 +00:00
|
|
|
import { t } from 'svelte-i18n';
|
2025-11-21 16:37:28 +00:00
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
end?: () => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let props: Props = $props();
|
|
|
|
|
let stage = $state(0);
|
|
|
|
|
|
|
|
|
|
let integrity: MaintenanceIntegrityResponseDto | undefined = $state();
|
|
|
|
|
|
|
|
|
|
async function reload() {
|
|
|
|
|
integrity = await integrityCheck();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMount(reload);
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
{#if stage === 0}
|
2025-11-25 11:07:11 +00:00
|
|
|
<Heading size="large" color="primary" tag="h1">{$t('maintenance_restore_library')}</Heading>
|
|
|
|
|
<Text>{$t('maintenance_restore_library_description')}</Text>
|
2025-11-21 16:37:28 +00:00
|
|
|
<Card>
|
|
|
|
|
<CardBody>
|
|
|
|
|
<Stack>
|
|
|
|
|
{#if integrity}
|
2025-11-24 14:53:52 +00:00
|
|
|
{#each integrity.storage as { folder, readable, writable } (folder)}
|
2025-11-21 16:37:28 +00:00
|
|
|
<HStack>
|
|
|
|
|
<Icon
|
|
|
|
|
icon={writable ? mdiCheck : mdiClose}
|
|
|
|
|
color={`rgb(var(--immich-ui-${writable ? 'success' : 'danger'}))`}
|
|
|
|
|
/>
|
|
|
|
|
<Text
|
2025-11-25 11:07:11 +00:00
|
|
|
>{folder} ({$t(
|
|
|
|
|
`maintenance_restore_library_folder_${writable ? 'pass' : readable ? 'write_fail' : 'read_fail'}`,
|
|
|
|
|
)})
|
|
|
|
|
</Text>
|
2025-11-21 16:37:28 +00:00
|
|
|
</HStack>
|
|
|
|
|
{/each}
|
2025-11-24 14:53:52 +00:00
|
|
|
{#each integrity.storage as { folder, files } (folder)}
|
2025-11-21 16:37:28 +00:00
|
|
|
{#if folder !== 'backups'}
|
|
|
|
|
<HStack class="items-start">
|
|
|
|
|
<Icon
|
|
|
|
|
class="mt-1"
|
|
|
|
|
icon={files ? mdiCheck : folder === 'profile' || folder === 'upload' ? mdiClose : mdiAlert}
|
|
|
|
|
color={`rgb(var(--immich-ui-${files ? 'success' : folder === 'profile' || folder === 'upload' ? 'danger' : 'warning'}))`}
|
|
|
|
|
/>
|
|
|
|
|
<Stack gap={0} class="items-start">
|
|
|
|
|
<Text>
|
|
|
|
|
{#if files}
|
2025-11-25 11:07:11 +00:00
|
|
|
{$t('maintenance_restore_library_folder_has_files', {
|
|
|
|
|
values: {
|
|
|
|
|
folder,
|
|
|
|
|
count: files,
|
|
|
|
|
},
|
|
|
|
|
})}
|
2025-11-21 16:37:28 +00:00
|
|
|
{:else}
|
2025-11-25 11:07:11 +00:00
|
|
|
{$t('maintenance_restore_library_folder_no_files', {
|
|
|
|
|
values: {
|
|
|
|
|
folder,
|
|
|
|
|
},
|
|
|
|
|
})}
|
2025-11-21 16:37:28 +00:00
|
|
|
{/if}
|
|
|
|
|
</Text>
|
|
|
|
|
{#if !files && (folder === 'profile' || folder === 'upload')}
|
2025-11-25 11:07:11 +00:00
|
|
|
<Text variant="italic">{$t('maintenance_restore_library_hint_missing_files')}</Text>
|
2025-11-21 16:37:28 +00:00
|
|
|
{/if}
|
|
|
|
|
{#if !files && (folder === 'encoded-video' || folder === 'thumbs')}
|
2025-11-25 11:07:11 +00:00
|
|
|
<Text variant="italic">{$t('maintenance_restore_library_hint_regenerate_later')}</Text>
|
2025-11-21 16:37:28 +00:00
|
|
|
{/if}
|
|
|
|
|
{#if !files && folder === 'library'}
|
2025-11-25 11:07:11 +00:00
|
|
|
<Text variant="italic">{$t('maintenance_restore_library_hint_storage_template_missing_files')}</Text
|
|
|
|
|
>
|
2025-11-21 16:37:28 +00:00
|
|
|
{/if}
|
|
|
|
|
</Stack>
|
|
|
|
|
</HStack>
|
|
|
|
|
{/if}
|
|
|
|
|
{/each}
|
|
|
|
|
|
2025-11-25 11:07:11 +00:00
|
|
|
<Button leadingIcon={mdiRefresh} variant="ghost" onclick={reload}>{$t('refresh')}</Button>
|
2025-11-21 16:37:28 +00:00
|
|
|
{:else}
|
|
|
|
|
<HStack>
|
|
|
|
|
<Icon icon={mdiRefresh} color="rgb(var(--immich-ui-primary))" />
|
2025-11-25 11:07:11 +00:00
|
|
|
<Text>{$t('maintenance_restore_library_loading')}</Text>
|
2025-11-21 16:37:28 +00:00
|
|
|
</HStack>
|
|
|
|
|
{/if}
|
|
|
|
|
</Stack>
|
|
|
|
|
</CardBody>
|
|
|
|
|
</Card>
|
2025-11-25 11:07:11 +00:00
|
|
|
<Text>{$t('maintenance_restore_library_confirm')}</Text>
|
2025-11-21 16:37:28 +00:00
|
|
|
<HStack>
|
2025-11-25 11:07:11 +00:00
|
|
|
<Button onclick={props.end} variant="ghost">{$t('cancel')}</Button>
|
|
|
|
|
<Button onclick={() => stage++} trailingIcon={mdiArrowRight}>{$t('next')}</Button>
|
2025-11-21 16:37:28 +00:00
|
|
|
</HStack>
|
|
|
|
|
{:else}
|
2025-11-25 11:07:11 +00:00
|
|
|
<Heading size="large" color="primary" tag="h1">{$t('maintenance_restore_from_backup')}</Heading>
|
2025-11-24 15:30:53 +00:00
|
|
|
<Scrollable class="max-h-80">
|
2025-11-21 16:37:28 +00:00
|
|
|
<MaintenanceBackupsList />
|
|
|
|
|
</Scrollable>
|
|
|
|
|
<HStack>
|
2025-11-25 11:07:11 +00:00
|
|
|
<Button onclick={props.end} variant="ghost">{$t('cancel')}</Button>
|
|
|
|
|
<Button onclick={() => stage--} variant="ghost" leadingIcon={mdiArrowLeft}>{$t('back')}</Button>
|
2025-11-21 16:37:28 +00:00
|
|
|
</HStack>
|
|
|
|
|
{/if}
|