mirror of
https://github.com/immich-app/immich.git
synced 2025-12-27 01:11:42 +03:00
feat: sub-pages for integrity reports
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { ByteUnit } from '$lib/utils/byte-units';
|
||||
import { Code, Icon, Text } from '@immich/ui';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
icon: string;
|
||||
icon?: string;
|
||||
title: string;
|
||||
value: number;
|
||||
unit?: ByteUnit | undefined;
|
||||
footer?: Snippet<[]>;
|
||||
}
|
||||
|
||||
let { icon, title, value, unit = undefined }: Props = $props();
|
||||
let { icon, title, value, unit = undefined, footer }: Props = $props();
|
||||
|
||||
const zeros = $derived(() => {
|
||||
const maxLength = 13;
|
||||
@@ -22,7 +24,9 @@
|
||||
|
||||
<div class="flex h-35 w-full flex-col justify-between rounded-3xl bg-subtle text-primary p-5">
|
||||
<div class="flex place-items-center gap-4">
|
||||
<Icon {icon} size="40" />
|
||||
{#if icon}
|
||||
<Icon {icon} size="40" />
|
||||
{/if}
|
||||
<Text size="large" fontWeight="bold" class="uppercase">{title}</Text>
|
||||
</div>
|
||||
|
||||
@@ -32,4 +36,6 @@
|
||||
<Code color="muted" class="absolute -top-5 end-1 font-light p-0">{unit}</Code>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{@render footer?.()}
|
||||
</div>
|
||||
|
||||
@@ -23,6 +23,7 @@ export enum AppRoute {
|
||||
ADMIN_LIBRARY_MANAGEMENT = '/admin/library-management',
|
||||
ADMIN_SETTINGS = '/admin/system-settings',
|
||||
ADMIN_MAINTENANCE_SETTINGS = '/admin/maintenance',
|
||||
ADMIN_MAINTENANCE_INTEGRITY_REPORT = '/admin/maintenance/integrity-report/',
|
||||
ADMIN_STATS = '/admin/server-status',
|
||||
ADMIN_JOBS = '/admin/jobs-status',
|
||||
ADMIN_REPAIR = '/admin/repair',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import BottomInfo from '$lib/components/shared-components/side-bar/bottom-info.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { NavbarItem } from '@immich/ui';
|
||||
import { mdiAccountMultipleOutline, mdiBookshelf, mdiCog, mdiServer, mdiSync } from '@mdi/js';
|
||||
import { mdiAccountMultipleOutline, mdiBookshelf, mdiCog, mdiServer, mdiSync, mdiWrench } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
</script>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<NavbarItem title={$t('users')} href={AppRoute.ADMIN_USERS} icon={mdiAccountMultipleOutline} />
|
||||
<NavbarItem title={$t('jobs')} href={AppRoute.ADMIN_JOBS} icon={mdiSync} />
|
||||
<NavbarItem title={$t('settings')} href={AppRoute.ADMIN_SETTINGS} icon={mdiCog} />
|
||||
<NavbarItem title={$t('admin.maintenance_settings')} href={AppRoute.ADMIN_MAINTENANCE_SETTINGS} icon={mdiCog} />
|
||||
<NavbarItem title={$t('admin.maintenance_settings')} href={AppRoute.ADMIN_MAINTENANCE_SETTINGS} icon={mdiWrench} />
|
||||
<NavbarItem title={$t('external_libraries')} href={AppRoute.ADMIN_LIBRARY_MANAGEMENT} icon={mdiBookshelf} />
|
||||
<NavbarItem title={$t('server_stats')} href={AppRoute.ADMIN_STATS} icon={mdiServer} />
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<script lang="ts">
|
||||
import AdminPageLayout from '$lib/components/layouts/AdminPageLayout.svelte';
|
||||
import SettingAccordionState from '$lib/components/shared-components/settings/setting-accordion-state.svelte';
|
||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||
import { QueryParameter } from '$lib/constants';
|
||||
import ServerStatisticsCard from '$lib/components/server-statistics/ServerStatisticsCard.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { MaintenanceAction, setMaintenanceMode } from '@immich/sdk';
|
||||
import { Button, HStack, IconButton, Text } from '@immich/ui';
|
||||
import { mdiDotsVertical, mdiProgressWrench, mdiRefresh } from '@mdi/js';
|
||||
import { Button, HStack, Text } from '@immich/ui';
|
||||
import { mdiProgressWrench } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
@@ -46,7 +45,26 @@
|
||||
|
||||
<section id="setting-content" class="flex place-content-center sm:mx-4">
|
||||
<section class="w-full pb-28 sm:w-5/6 md:w-[850px]">
|
||||
<SettingAccordionState queryParam={QueryParameter.IS_OPEN}>
|
||||
<p class="text-sm dark:text-immich-dark-fg uppercase">{$t('admin.maintenance_integrity_report')}</p>
|
||||
|
||||
<div class="mt-5 hidden justify-between lg:flex gap-4">
|
||||
{#each ['orphan_file', 'missing_file', 'checksum_mismatch'] as const as reportType (reportType)}
|
||||
<ServerStatisticsCard
|
||||
title={$t(`admin.maintenance_integrity_${reportType}`)}
|
||||
value={data.integrityReport[reportType]}
|
||||
>
|
||||
{#snippet footer()}
|
||||
<Button
|
||||
href={`${AppRoute.ADMIN_MAINTENANCE_INTEGRITY_REPORT + reportType}`}
|
||||
size="tiny"
|
||||
class="self-end mt-1">View Report</Button
|
||||
>
|
||||
{/snippet}
|
||||
</ServerStatisticsCard>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- <SettingAccordionState queryParam={QueryParameter.IS_OPEN}>
|
||||
<SettingAccordion
|
||||
title="Integrity Report"
|
||||
subtitle={`There are ${data.integrityReport.items.length} unresolved issues!`}
|
||||
@@ -86,7 +104,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</SettingAccordion>
|
||||
</SettingAccordionState>
|
||||
</SettingAccordionState> -->
|
||||
</section>
|
||||
</section>
|
||||
</AdminPageLayout>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { authenticate } from '$lib/utils/auth';
|
||||
import { getFormatter } from '$lib/utils/i18n';
|
||||
import { getIntegrityReport } from '@immich/sdk';
|
||||
import { getIntegrityReportSummary } from '@immich/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async ({ url }) => {
|
||||
await authenticate(url, { admin: true });
|
||||
const integrityReport = await getIntegrityReport();
|
||||
const integrityReport = await getIntegrityReportSummary();
|
||||
const $t = await getFormatter();
|
||||
|
||||
return {
|
||||
integrityReport,
|
||||
meta: {
|
||||
title: $t('admin.system_settings'),
|
||||
title: $t('admin.maintenance_settings'),
|
||||
},
|
||||
};
|
||||
}) satisfies PageLoad;
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<script lang="ts">
|
||||
import AdminPageLayout from '$lib/components/layouts/AdminPageLayout.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { IconButton } from '@immich/ui';
|
||||
import { mdiDotsVertical } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
interface Props {
|
||||
data: PageData;
|
||||
}
|
||||
|
||||
let { data }: Props = $props();
|
||||
|
||||
// async function switchToMaintenance() {
|
||||
// try {
|
||||
// await setMaintenanceMode({
|
||||
// setMaintenanceModeDto: {
|
||||
// action: MaintenanceAction.Start,
|
||||
// },
|
||||
// });
|
||||
// } catch (error) {
|
||||
// handleError(error, $t('admin.maintenance_start_error'));
|
||||
// }
|
||||
// }
|
||||
</script>
|
||||
|
||||
<AdminPageLayout
|
||||
breadcrumbs={[
|
||||
{ title: $t('admin.maintenance_settings'), href: AppRoute.ADMIN_MAINTENANCE_SETTINGS },
|
||||
{ title: $t('admin.maintenance_integrity_report') },
|
||||
{ title: data.meta.title },
|
||||
]}
|
||||
>
|
||||
<!-- {#snippet buttons()}
|
||||
<HStack gap={1}>
|
||||
<Button
|
||||
leadingIcon={mdiProgressWrench}
|
||||
size="small"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
onclick={switchToMaintenance}
|
||||
>
|
||||
<Text class="hidden md:block">{$t('admin.maintenance_start')}</Text>
|
||||
</Button>
|
||||
</HStack>
|
||||
{/snippet} -->
|
||||
|
||||
<section id="setting-content" class="flex place-content-center sm:mx-4">
|
||||
<section class="w-full pb-28 sm:w-5/6 md:w-[850px]">
|
||||
<table class="mt-5 w-full text-start">
|
||||
<thead
|
||||
class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray"
|
||||
>
|
||||
<tr class="flex w-full place-items-center">
|
||||
<th class="w-7/8 text-left px-2 text-sm font-medium">{$t('filename')}</th>
|
||||
<th class="w-1/8"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
class="block max-h-80 w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg"
|
||||
>
|
||||
{#each data.integrityReport.items as { id, path } (id)}
|
||||
<tr class="flex py-1 w-full place-items-center even:bg-subtle/20 odd:bg-subtle/80">
|
||||
<td class="w-7/8 text-ellipsis text-left px-2 text-sm select-all">{path}</td>
|
||||
<td class="w-1/8 text-ellipsis text-right flex justify-end px-2">
|
||||
<IconButton aria-label="Open" color="secondary" icon={mdiDotsVertical} variant="ghost" /></td
|
||||
>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</section>
|
||||
</AdminPageLayout>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { authenticate } from '$lib/utils/auth';
|
||||
import { getFormatter } from '$lib/utils/i18n';
|
||||
import { getIntegrityReport, IntegrityReportType } from '@immich/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async ({ params, url }) => {
|
||||
const type = params.type as IntegrityReportType;
|
||||
|
||||
await authenticate(url, { admin: true });
|
||||
const integrityReport = await getIntegrityReport({
|
||||
maintenanceGetIntegrityReportDto: {
|
||||
type,
|
||||
},
|
||||
});
|
||||
const $t = await getFormatter();
|
||||
|
||||
return {
|
||||
integrityReport,
|
||||
meta: {
|
||||
title: $t(`admin.maintenance_integrity_${type}`),
|
||||
},
|
||||
};
|
||||
}) satisfies PageLoad;
|
||||
Reference in New Issue
Block a user