mirror of
https://github.com/immich-app/immich.git
synced 2025-12-26 09:14:58 +03:00
feat: manually trigger integrity jobs
feat: update summary after job runs
This commit is contained in:
@@ -345,6 +345,12 @@ export enum SourceType {
|
||||
Manual = 'manual',
|
||||
}
|
||||
|
||||
export enum IntegrityReportType {
|
||||
OrphanFile = 'orphan_file',
|
||||
MissingFile = 'missing_file',
|
||||
ChecksumFail = 'checksum_mismatch',
|
||||
}
|
||||
|
||||
export enum ManualJobName {
|
||||
PersonCleanup = 'person-cleanup',
|
||||
TagCleanup = 'tag-cleanup',
|
||||
@@ -352,6 +358,12 @@ export enum ManualJobName {
|
||||
MemoryCleanup = 'memory-cleanup',
|
||||
MemoryCreate = 'memory-create',
|
||||
BackupDatabase = 'backup-database',
|
||||
IntegrityMissingFiles = `integrity-missing-files`,
|
||||
IntegrityOrphanFiles = `integrity-orphan-files`,
|
||||
IntegrityChecksumFiles = `integrity-checksum-mismatch`,
|
||||
IntegrityMissingFilesRefresh = `integrity-missing-files-refresh`,
|
||||
IntegrityOrphanFilesRefresh = `integrity-orphan-files-refresh`,
|
||||
IntegrityChecksumFilesRefresh = `integrity-checksum-mismatch-refresh`,
|
||||
}
|
||||
|
||||
export enum AssetPathType {
|
||||
@@ -482,12 +494,6 @@ export enum CacheControl {
|
||||
None = 'none',
|
||||
}
|
||||
|
||||
export enum IntegrityReportType {
|
||||
OrphanFile = 'orphan_file',
|
||||
MissingFile = 'missing_file',
|
||||
ChecksumFail = 'checksum_mismatch',
|
||||
}
|
||||
|
||||
export enum ImmichEnvironment {
|
||||
Development = 'development',
|
||||
Testing = 'testing',
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
} from 'src/enum';
|
||||
import { ArgOf } from 'src/repositories/event.repository';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
import { IIntegrityOrphanedFilesJob, IIntegrityPathWithReportJob } from 'src/types';
|
||||
import { IIntegrityJob, IIntegrityOrphanedFilesJob, IIntegrityPathWithReportJob } from 'src/types';
|
||||
import { handlePromiseError } from 'src/utils/misc';
|
||||
|
||||
async function* chunk<T>(generator: AsyncIterableIterator<T>, n: number) {
|
||||
@@ -130,7 +130,7 @@ export class IntegrityService extends BaseService {
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.IntegrityOrphanedFilesQueueAll, queue: QueueName.BackgroundTask })
|
||||
async handleOrphanedFilesQueueAll(): Promise<JobStatus> {
|
||||
async handleOrphanedFilesQueueAll({ refreshOnly }: IIntegrityJob = {}): Promise<JobStatus> {
|
||||
this.logger.log(`Checking for out of date orphaned file reports...`);
|
||||
|
||||
const reports = this.assetJobRepository.streamIntegrityReports(IntegrityReportType.OrphanFile);
|
||||
@@ -148,6 +148,11 @@ export class IntegrityService extends BaseService {
|
||||
this.logger.log(`Queued report check of ${batchReports.length} report(s) (${total} so far)`);
|
||||
}
|
||||
|
||||
if (refreshOnly) {
|
||||
this.logger.log('Refresh complete.');
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
this.logger.log(`Scanning for orphaned files...`);
|
||||
|
||||
const assetPaths = this.storageRepository.walk({
|
||||
@@ -232,8 +237,8 @@ export class IntegrityService extends BaseService {
|
||||
const results = await Promise.all(
|
||||
paths.map(({ reportId, path }) =>
|
||||
stat(path)
|
||||
.then(() => reportId)
|
||||
.catch(() => void 0),
|
||||
.then(() => void 0)
|
||||
.catch(() => reportId),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -243,12 +248,18 @@ export class IntegrityService extends BaseService {
|
||||
await this.integrityReportRepository.deleteByIds(reportIds);
|
||||
}
|
||||
|
||||
this.logger.log(`Processed ${paths.length} and found ${reportIds.length} orphaned file(s).`);
|
||||
this.logger.log(`Processed ${paths.length} paths and found ${reportIds.length} report(s) out of date.`);
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.IntegrityMissingFilesQueueAll, queue: QueueName.BackgroundTask })
|
||||
async handleMissingFilesQueueAll(): Promise<JobStatus> {
|
||||
async handleMissingFilesQueueAll({ refreshOnly }: IIntegrityJob = {}): Promise<JobStatus> {
|
||||
if (refreshOnly) {
|
||||
// TODO
|
||||
this.logger.log('Refresh complete.');
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
this.logger.log(`Scanning for missing files...`);
|
||||
|
||||
const assetPaths = this.assetJobRepository.streamAssetPaths();
|
||||
@@ -304,7 +315,13 @@ export class IntegrityService extends BaseService {
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.IntegrityChecksumFiles, queue: QueueName.BackgroundTask })
|
||||
async handleChecksumFiles(): Promise<JobStatus> {
|
||||
async handleChecksumFiles({ refreshOnly }: IIntegrityJob = {}): Promise<JobStatus> {
|
||||
if (refreshOnly) {
|
||||
// TODO
|
||||
this.logger.log('Refresh complete.');
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
const timeLimit = 60 * 60 * 1000; // 1000;
|
||||
const percentageLimit = 1; // 0.25;
|
||||
|
||||
|
||||
@@ -34,6 +34,30 @@ const asJobItem = (dto: JobCreateDto): JobItem => {
|
||||
return { name: JobName.DatabaseBackup };
|
||||
}
|
||||
|
||||
case ManualJobName.IntegrityMissingFiles: {
|
||||
return { name: JobName.IntegrityMissingFilesQueueAll };
|
||||
}
|
||||
|
||||
case ManualJobName.IntegrityOrphanFiles: {
|
||||
return { name: JobName.IntegrityOrphanedFilesQueueAll };
|
||||
}
|
||||
|
||||
case ManualJobName.IntegrityChecksumFiles: {
|
||||
return { name: JobName.IntegrityChecksumFiles };
|
||||
}
|
||||
|
||||
case ManualJobName.IntegrityMissingFilesRefresh: {
|
||||
return { name: JobName.IntegrityMissingFilesQueueAll, data: { refreshOnly: true } };
|
||||
}
|
||||
|
||||
case ManualJobName.IntegrityOrphanFilesRefresh: {
|
||||
return { name: JobName.IntegrityOrphanedFilesQueueAll, data: { refreshOnly: true } };
|
||||
}
|
||||
|
||||
case ManualJobName.IntegrityChecksumFilesRefresh: {
|
||||
return { name: JobName.IntegrityChecksumFiles, data: { refreshOnly: true } };
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new BadRequestException('Invalid job name');
|
||||
}
|
||||
|
||||
@@ -282,6 +282,10 @@ export interface IWorkflowJob<T extends PluginTriggerType = PluginTriggerType> {
|
||||
event: WorkflowData[T];
|
||||
}
|
||||
|
||||
export interface IIntegrityJob {
|
||||
refreshOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface IIntegrityOrphanedFilesJob {
|
||||
type: 'asset' | 'asset_file';
|
||||
paths: string[];
|
||||
@@ -403,12 +407,12 @@ export type JobItem =
|
||||
| { name: JobName.WorkflowRun; data: IWorkflowJob }
|
||||
|
||||
// Integrity
|
||||
| { name: JobName.IntegrityOrphanedFilesQueueAll; data: IBaseJob }
|
||||
| { name: JobName.IntegrityOrphanedFilesQueueAll; data?: IIntegrityJob }
|
||||
| { name: JobName.IntegrityOrphanedFiles; data: IIntegrityOrphanedFilesJob }
|
||||
| { name: JobName.IntegrityOrphanedCheckReports; data: IIntegrityPathWithReportJob }
|
||||
| { name: JobName.IntegrityMissingFilesQueueAll; data: IBaseJob }
|
||||
| { name: JobName.IntegrityMissingFilesQueueAll; data?: IIntegrityJob }
|
||||
| { name: JobName.IntegrityMissingFiles; data: IIntegrityPathWithReportJob }
|
||||
| { name: JobName.IntegrityChecksumFiles; data: IBaseJob };
|
||||
| { name: JobName.IntegrityChecksumFiles; data?: IIntegrityJob };
|
||||
|
||||
export type VectorExtension = (typeof VECTOR_EXTENSIONS)[number];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user