feat: tag cleanup job (#12654)

This commit is contained in:
Jason Rasmussen
2024-09-16 16:49:12 -04:00
committed by GitHub
parent 4a1ff6abce
commit b74b20824a
23 changed files with 476 additions and 10 deletions

View File

@@ -220,7 +220,7 @@
role="listbox"
id={listboxId}
transition:fly={{ duration: 250 }}
class="absolute text-left text-sm w-full max-h-64 overflow-y-auto bg-white dark:bg-gray-800 border-t-0 border-gray-300 dark:border-gray-900 rounded-b-xl z-10"
class="absolute text-left text-sm w-full max-h-64 overflow-y-auto bg-white dark:bg-gray-800 border-t-0 border-gray-300 dark:border-gray-900 rounded-b-xl z-[10000]"
class:border={isOpen}
tabindex="-1"
>

View File

@@ -41,6 +41,7 @@
"confirm_email_below": "To confirm, type \"{email}\" below",
"confirm_reprocess_all_faces": "Are you sure you want to reprocess all faces? This will also clear named people.",
"confirm_user_password_reset": "Are you sure you want to reset {user}'s password?",
"create_job": "Create job",
"disable_login": "Disable login",
"duplicate_detection_job_description": "Run machine learning on assets to detect similar images. Relies on Smart Search",
"exclusion_pattern_description": "Exclusion patterns lets you ignore files and folders when scanning your library. This is useful if you have folders that contain files you don't want to import, such as RAW files.",
@@ -68,6 +69,7 @@
"image_thumbnail_resolution": "Thumbnail resolution",
"image_thumbnail_resolution_description": "Used when viewing groups of photos (main timeline, album view, etc.). Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness.",
"job_concurrency": "{job} concurrency",
"job_created": "Job created",
"job_not_concurrency_safe": "This job is not concurrency-safe.",
"job_settings": "Job Settings",
"job_settings_description": "Manage job concurrency",
@@ -196,6 +198,7 @@
"password_settings": "Password Login",
"password_settings_description": "Manage password login settings",
"paths_validated_successfully": "All paths validated successfully",
"person_cleanup_job": "Person cleanup",
"quota_size_gib": "Quota Size (GiB)",
"refreshing_all_libraries": "Refreshing all libraries",
"registration": "Admin Registration",
@@ -209,6 +212,7 @@
"reset_settings_to_recent_saved": "Reset settings to the recent saved settings",
"scanning_library_for_changed_files": "Scanning library for changed files",
"scanning_library_for_new_files": "Scanning library for new files",
"search_jobs": "Search jobs...",
"send_welcome_email": "Send welcome email",
"server_external_domain_settings": "External domain",
"server_external_domain_settings_description": "Domain for public shared links, including http(s)://",
@@ -236,6 +240,7 @@
"storage_template_settings_description": "Manage the folder structure and file name of the upload asset",
"storage_template_user_label": "<code>{label}</code> is the user's Storage Label",
"system_settings": "System Settings",
"tag_cleanup_job": "Tag cleanup",
"theme_custom_css_settings": "Custom CSS",
"theme_custom_css_settings_description": "Cascading Style Sheets allow the design of Immich to be customized.",
"theme_settings": "Theme Settings",
@@ -309,6 +314,7 @@
"trash_settings_description": "Manage trash settings",
"untracked_files": "Untracked Files",
"untracked_files_description": "These files are not tracked by the application. They can be the results of failed moves, interrupted uploads, or left behind due to a bug",
"user_cleanup_job": "User cleanup",
"user_delete_delay": "<b>{user}</b>'s account and assets will be scheduled for permanent deletion in {delay, plural, one {# day} other {# days}}.",
"user_delete_delay_settings": "Delete delay",
"user_delete_delay_settings_description": "Number of days after removal to permanently delete a user's account and assets. The user deletion job runs at midnight to check for users that are ready for deletion. Changes to this setting will be evaluated at the next execution.",

View File

@@ -3,10 +3,17 @@
import LinkButton from '$lib/components/elements/buttons/link-button.svelte';
import Icon from '$lib/components/elements/icon.svelte';
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
import ConfirmDialog from '$lib/components/shared-components/dialog/confirm-dialog.svelte';
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import { AppRoute } from '$lib/constants';
import { asyncTimeout } from '$lib/utils';
import { getAllJobsStatus, type AllJobStatusResponseDto } from '@immich/sdk';
import { mdiCog } from '@mdi/js';
import { handleError } from '$lib/utils/handle-error';
import { createJob, getAllJobsStatus, ManualJobName, type AllJobStatusResponseDto } from '@immich/sdk';
import { mdiCog, mdiPlus } from '@mdi/js';
import { onDestroy, onMount } from 'svelte';
import { t } from 'svelte-i18n';
import type { PageData } from './$types';
@@ -16,6 +23,8 @@
let jobs: AllJobStatusResponseDto;
let running = true;
let isOpen = false;
let selectedJob: ComboBoxOption | undefined = undefined;
onMount(async () => {
while (running) {
@@ -27,10 +36,38 @@
onDestroy(() => {
running = false;
});
const options = [
{ title: $t('admin.person_cleanup_job'), value: ManualJobName.PersonCleanup },
{ title: $t('admin.tag_cleanup_job'), value: ManualJobName.TagCleanup },
{ title: $t('admin.user_cleanup_job'), value: ManualJobName.UserCleanup },
].map(({ value, title }) => ({ id: value, label: title, value }));
const handleCancel = () => (isOpen = false);
const handleCreate = async () => {
if (!selectedJob) {
return;
}
try {
await createJob({ jobCreateDto: { name: selectedJob.value as ManualJobName } });
notificationController.show({ message: $t('admin.job_created'), type: NotificationType.Info });
handleCancel();
} catch (error) {
handleError(error, $t('errors.unable_to_submit_job'));
}
};
</script>
<UserPageLayout title={data.meta.title} admin>
<div class="flex justify-end" slot="buttons">
<LinkButton on:click={() => (isOpen = true)}>
<div class="flex place-items-center gap-2 text-sm">
<Icon path={mdiPlus} size="18" />
{$t('admin.create_job')}
</div>
</LinkButton>
<LinkButton href="{AppRoute.ADMIN_SETTINGS}?isOpen=job">
<div class="flex place-items-center gap-2 text-sm">
<Icon path={mdiCog} size="18" />
@@ -46,3 +83,24 @@
</section>
</section>
</UserPageLayout>
{#if isOpen}
<ConfirmDialog
confirmColor="primary"
title={$t('admin.create_job')}
disabled={!selectedJob}
onConfirm={handleCreate}
onCancel={handleCancel}
>
<form on:submit|preventDefault={handleCreate} autocomplete="off" id="create-tag-form" slot="prompt" class="w-full">
<div class="flex flex-col gap-1 text-left">
<Combobox
bind:selectedOption={selectedJob}
label={$t('jobs')}
{options}
placeholder={$t('admin.search_jobs')}
/>
</div>
</form>
</ConfirmDialog>
{/if}