feat: integrity checks admin settings (#29406)

This commit is contained in:
Daniel Dietzler
2026-06-30 21:58:51 +02:00
committed by GitHub
parent 6a7a34d294
commit 02506424a7
3 changed files with 197 additions and 0 deletions

View File

@@ -108,6 +108,21 @@
"image_thumbnail_quality_description": "Thumbnail quality from 1-100. Higher is better, but produces larger files and can reduce app responsiveness.",
"image_thumbnail_title": "Thumbnail Settings",
"import_config_from_json_description": "Import system config by uploading a JSON config file",
"integrity_checks_checksum_files": "Checksum files",
"integrity_checks_checksum_files_description": "Configure the checksum check",
"integrity_checks_checksum_files_enable_description": "Enable the checksum check",
"integrity_checks_checksum_files_percentage_limit": "Percentage limit",
"integrity_checks_checksum_files_percentage_limit_description": "Configure the maximum percentage between 0.01 and 1 for how much the checksum check should run each interval.",
"integrity_checks_checksum_files_time_limit": "Time limit",
"integrity_checks_checksum_files_time_limit_description": "Configure the maximum duration for which the checksum check should run each interval. (ms)",
"integrity_checks_missing_files": "Missing files",
"integrity_checks_missing_files_description": "Configure the frequency and enable or disable the missing files check",
"integrity_checks_missing_files_enable_description": "Enable the missing files check",
"integrity_checks_settings": "Integrity checks",
"integrity_checks_settings_description": "Manage integrity checks intervals",
"integrity_checks_untracked_files": "Untracked files",
"integrity_checks_untracked_files_description": "Configure the frequency and enable or disable the untracked files check",
"integrity_checks_untracked_files_enable_description": "Enable the untracked files check",
"job_concurrency": "{job} concurrency",
"job_created": "Job created",
"job_not_concurrency_safe": "This job is not concurrency-safe.",

View File

@@ -31,6 +31,7 @@
mdiBookshelf,
mdiClockOutline,
mdiDatabaseOutline,
mdiFileCheckOutline,
mdiFileDocumentOutline,
mdiFolderOutline,
mdiImageOutline,
@@ -47,6 +48,7 @@
import type { Component } from 'svelte';
import { t } from 'svelte-i18n';
import type { PageData } from './$types';
import IntegrityChecksSettings from './IntegrityChecksSettings.svelte';
type Props = {
data: PageData;
@@ -82,6 +84,13 @@
key: 'image',
icon: mdiImageOutline,
},
{
component: IntegrityChecksSettings,
title: $t('admin.integrity_checks_settings'),
subtitle: $t('admin.integrity_checks_settings_description'),
key: 'integrity-checks',
icon: mdiFileCheckOutline,
},
{
component: JobSettings,
title: $t('admin.job_settings'),

View File

@@ -0,0 +1,173 @@
<script lang="ts">
import SettingAccordion from '$lib/components/shared-components/settings/SettingAccordion.svelte';
import SettingInputField from '$lib/components/shared-components/settings/SettingInputField.svelte';
import SettingSwitch from '$lib/components/shared-components/settings/SettingSwitch.svelte';
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
import { SettingInputFieldType } from '$lib/constants';
import FormatMessage from '$lib/elements/FormatMessage.svelte';
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
import { Link } from '@immich/ui';
import { t } from 'svelte-i18n';
import { fade } from 'svelte/transition';
const disabled = $derived(featureFlagsManager.value.configFile);
const config = $derived(systemConfigManager.value);
let configToEdit = $state(systemConfigManager.cloneValue());
</script>
<div>
<div in:fade={{ duration: 500 }}>
<form autocomplete="off" onsubmit={(event) => event.preventDefault()}>
<div class="ms-4 mt-4 flex flex-col gap-4">
<SettingAccordion
key="integrity-checks-missing-files"
title={$t('admin.integrity_checks_missing_files')}
subtitle={$t('admin.integrity_checks_missing_files_description')}
>
<div class="ms-4 mt-4 flex flex-col gap-4">
<SettingSwitch
title={$t('admin.integrity_checks_missing_files_enable_description')}
{disabled}
bind:checked={configToEdit.integrityChecks.missingFiles.enabled}
/>
<SettingInputField
inputType={SettingInputFieldType.TEXT}
label={$t('admin.cron_expression')}
bind:value={configToEdit.integrityChecks.missingFiles.cronExpression}
required={true}
{disabled}
isEdited={!(
configToEdit.integrityChecks.missingFiles.cronExpression ===
config.integrityChecks.missingFiles.cronExpression
)}
>
{#snippet descriptionSnippet()}
<p class="text-sm dark:text-immich-dark-fg">
<FormatMessage key="admin.cron_expression_description">
{#snippet children({ message })}
<Link
href="https://crontab.guru/#{configToEdit.backup.database.cronExpression.replaceAll(' ', '_')}"
>
{message}
<br />
</Link>
{/snippet}
</FormatMessage>
</p>
{/snippet}
</SettingInputField>
</div>
</SettingAccordion>
<SettingAccordion
key="integrity-checks-untracked-files"
title={$t('admin.integrity_checks_untracked_files')}
subtitle={$t('admin.integrity_checks_untracked_files_description')}
>
<div class="ms-4 mt-4 flex flex-col gap-4">
<SettingSwitch
title={$t('admin.integrity_checks_untracked_files_enable_description')}
{disabled}
bind:checked={configToEdit.integrityChecks.untrackedFiles.enabled}
/>
<SettingInputField
inputType={SettingInputFieldType.TEXT}
label={$t('admin.cron_expression')}
bind:value={configToEdit.integrityChecks.untrackedFiles.cronExpression}
required={true}
{disabled}
isEdited={!(
configToEdit.integrityChecks.untrackedFiles.cronExpression ===
config.integrityChecks.untrackedFiles.cronExpression
)}
>
{#snippet descriptionSnippet()}
<p class="text-sm dark:text-immich-dark-fg">
<FormatMessage key="admin.cron_expression_description">
{#snippet children({ message })}
<Link
href="https://crontab.guru/#{configToEdit.backup.database.cronExpression.replaceAll(' ', '_')}"
>
{message}
<br />
</Link>
{/snippet}
</FormatMessage>
</p>
{/snippet}
</SettingInputField>
</div>
</SettingAccordion>
<SettingAccordion
key="integrity-checks-checksum-files"
title={$t('admin.integrity_checks_checksum_files')}
subtitle={$t('admin.integrity_checks_checksum_files_description')}
>
<div class="ms-4 mt-4 flex flex-col gap-4">
<SettingSwitch
title={$t('admin.integrity_checks_checksum_files_enable_description')}
{disabled}
bind:checked={configToEdit.integrityChecks.checksumFiles.enabled}
/>
<SettingInputField
inputType={SettingInputFieldType.TEXT}
label={$t('admin.cron_expression')}
bind:value={configToEdit.integrityChecks.checksumFiles.cronExpression}
required={true}
{disabled}
isEdited={!(
configToEdit.integrityChecks.checksumFiles.cronExpression ===
config.integrityChecks.checksumFiles.cronExpression
)}
>
{#snippet descriptionSnippet()}
<p class="text-sm dark:text-immich-dark-fg">
<FormatMessage key="admin.cron_expression_description">
{#snippet children({ message })}
<Link
href="https://crontab.guru/#{configToEdit.backup.database.cronExpression.replaceAll(' ', '_')}"
>
{message}
<br />
</Link>
{/snippet}
</FormatMessage>
</p>
{/snippet}
</SettingInputField>
<SettingInputField
inputType={SettingInputFieldType.NUMBER}
label={$t('admin.integrity_checks_checksum_files_time_limit')}
description={$t('admin.integrity_checks_checksum_files_time_limit_description')}
bind:value={configToEdit.integrityChecks.checksumFiles.timeLimit}
{disabled}
isEdited={configToEdit.integrityChecks.checksumFiles.timeLimit !==
config.integrityChecks.checksumFiles.timeLimit}
/>
<SettingInputField
inputType={SettingInputFieldType.NUMBER}
label={$t('admin.integrity_checks_checksum_files_percentage_limit')}
description={$t('admin.integrity_checks_checksum_files_percentage_limit_description')}
bind:value={configToEdit.integrityChecks.checksumFiles.percentageLimit}
step="0.01"
min={0.01}
max={1}
{disabled}
isEdited={configToEdit.integrityChecks.checksumFiles.percentageLimit !==
config.integrityChecks.checksumFiles.percentageLimit}
/>
</div>
</SettingAccordion>
<SettingButtonsRow bind:configToEdit keys={['integrityChecks']} {disabled} />
</div>
</form>
</div>
</div>