refactor: orphan -> untracked

This commit is contained in:
izzy
2026-01-07 12:17:28 +00:00
parent d189722bbf
commit ed33f79e2a
27 changed files with 213 additions and 213 deletions

View File

@@ -100,7 +100,7 @@ describe('/admin/integrity', () => {
describe('POST /summary (& jobs)', async () => {
it.sequential('reports no issues', async () => {
await utils.createJob(admin.accessToken, {
name: ManualJobName.IntegrityOrphanFiles,
name: ManualJobName.IntegrityUntrackedFiles,
});
await utils.createJob(admin.accessToken, {
@@ -112,7 +112,7 @@ describe('/admin/integrity', () => {
});
await utils.createJob(admin.accessToken, {
name: ManualJobName.IntegrityOrphanFilesDeleteAll,
name: ManualJobName.IntegrityUntrackedFilesDeleteAll,
});
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
@@ -125,16 +125,16 @@ describe('/admin/integrity', () => {
expect(status).toBe(200);
expect(body).toEqual({
missing_file: 0,
orphan_file: 0,
untracked_file: 0,
checksum_mismatch: 0,
});
});
it.sequential('should detect an orphan file (job: check orphan files)', async () => {
await utils.putTextFile('orphan', `/data/upload/${admin.userId}/orphan1.png`);
it.sequential('should detect an untracked file (job: check untracked files)', async () => {
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked1.png`);
await utils.createJob(admin.accessToken, {
name: ManualJobName.IntegrityOrphanFiles,
name: ManualJobName.IntegrityUntrackedFiles,
});
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
@@ -147,18 +147,18 @@ describe('/admin/integrity', () => {
expect(status).toBe(200);
expect(body).toEqual(
expect.objectContaining({
orphan_file: 1,
untracked_file: 1,
}),
);
});
it.sequential('should detect outdated orphan file reports (job: refresh orphan files)', async () => {
it.sequential('should detect outdated untracked file reports (job: refresh untracked files)', async () => {
// these should not be detected:
await utils.putTextFile('orphan', `/data/upload/${admin.userId}/orphan2.png`);
await utils.putTextFile('orphan', `/data/upload/${admin.userId}/orphan3.png`);
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked2.png`);
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked3.png`);
await utils.createJob(admin.accessToken, {
name: ManualJobName.IntegrityOrphanFilesRefresh,
name: ManualJobName.IntegrityUntrackedFilesRefresh,
});
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
@@ -171,22 +171,22 @@ describe('/admin/integrity', () => {
expect(status).toBe(200);
expect(body).toEqual(
expect.objectContaining({
orphan_file: 0,
untracked_file: 0,
}),
);
});
it.sequential('should delete orphan files (job: delete all orphan file reports)', async () => {
await utils.putTextFile('orphan', `/data/upload/${admin.userId}/orphan1.png`);
it.sequential('should delete untracked files (job: delete all untracked file reports)', async () => {
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked1.png`);
await utils.createJob(admin.accessToken, {
name: ManualJobName.IntegrityOrphanFiles,
name: ManualJobName.IntegrityUntrackedFiles,
});
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
await utils.createJob(admin.accessToken, {
name: ManualJobName.IntegrityOrphanFilesDeleteAll,
name: ManualJobName.IntegrityUntrackedFilesDeleteAll,
});
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
@@ -199,7 +199,7 @@ describe('/admin/integrity', () => {
expect(status).toBe(200);
expect(body).toEqual(
expect.objectContaining({
orphan_file: 0,
untracked_file: 0,
}),
);
});
@@ -387,11 +387,11 @@ describe('/admin/integrity', () => {
});
describe('POST /report', async () => {
it.sequential('reports orphan files', async () => {
await utils.putTextFile('orphan', `/data/upload/${admin.userId}/orphan1.png`);
it.sequential('reports untracked files', async () => {
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked1.png`);
await utils.createJob(admin.accessToken, {
name: ManualJobName.IntegrityOrphanFiles,
name: ManualJobName.IntegrityUntrackedFiles,
});
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
@@ -399,7 +399,7 @@ describe('/admin/integrity', () => {
const { status, body } = await request(app)
.post('/admin/integrity/report')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ type: 'orphan_file' });
.send({ type: 'untracked_file' });
expect(status).toBe(200);
expect(body).toEqual({
@@ -407,8 +407,8 @@ describe('/admin/integrity', () => {
items: expect.arrayContaining([
{
id: expect.any(String),
type: 'orphan_file',
path: `/data/upload/${admin.userId}/orphan1.png`,
type: 'untracked_file',
path: `/data/upload/${admin.userId}/untracked1.png`,
assetId: null,
fileAssetId: null,
},
@@ -476,11 +476,11 @@ describe('/admin/integrity', () => {
});
describe('DELETE /report/:id', async () => {
it.sequential('delete orphan files', async () => {
await utils.putTextFile('orphan', `/data/upload/${admin.userId}/orphan1.png`);
it.sequential('delete untracked files', async () => {
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked1.png`);
await utils.createJob(admin.accessToken, {
name: ManualJobName.IntegrityOrphanFiles,
name: ManualJobName.IntegrityUntrackedFiles,
});
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
@@ -488,12 +488,12 @@ describe('/admin/integrity', () => {
const { status: listStatus, body: listBody } = await request(app)
.post('/admin/integrity/report')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ type: 'orphan_file' });
.send({ type: 'untracked_file' });
expect(listStatus).toBe(200);
const report = (listBody as IntegrityReportResponseDto).items.find(
(item) => item.path === `/data/upload/${admin.userId}/orphan1.png`,
(item) => item.path === `/data/upload/${admin.userId}/untracked1.png`,
)!;
const { status } = await request(app)
@@ -504,7 +504,7 @@ describe('/admin/integrity', () => {
expect(status).toBe(200);
await utils.createJob(admin.accessToken, {
name: ManualJobName.IntegrityOrphanFiles,
name: ManualJobName.IntegrityUntrackedFiles,
});
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
@@ -512,7 +512,7 @@ describe('/admin/integrity', () => {
const { status: listStatus2, body: listBody2 } = await request(app)
.post('/admin/integrity/report')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ type: 'orphan_file' });
.send({ type: 'untracked_file' });
expect(listStatus2).toBe(200);
expect(listBody2).not.toBe(
@@ -610,17 +610,17 @@ describe('/admin/integrity', () => {
});
describe('GET /report/:type/csv', () => {
it.sequential('exports orphan files as csv', async () => {
await utils.putTextFile('orphan', `/data/upload/${admin.userId}/orphan1.png`);
it.sequential('exports untracked files as csv', async () => {
await utils.putTextFile('untracked', `/data/upload/${admin.userId}/untracked1.png`);
await utils.createJob(admin.accessToken, {
name: ManualJobName.IntegrityOrphanFiles,
name: ManualJobName.IntegrityUntrackedFiles,
});
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
const { status, headers, text } = await request(app)
.get('/admin/integrity/report/orphan_file/csv')
.get('/admin/integrity/report/untracked_file/csv')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send();
@@ -628,17 +628,17 @@ describe('/admin/integrity', () => {
expect(headers['content-type']).toContain('text/csv');
expect(headers['content-disposition']).toContain('.csv');
expect(text).toContain('id,type,assetId,fileAssetId,path');
expect(text).toContain(`orphan_file`);
expect(text).toContain(`/data/upload/${admin.userId}/orphan1.png`);
expect(text).toContain(`untracked_file`);
expect(text).toContain(`/data/upload/${admin.userId}/untracked1.png`);
});
});
describe('GET /report/:id/file', () => {
it.sequential('downloads orphan file', async () => {
await utils.putTextFile('orphan-content', `/data/upload/${admin.userId}/orphan1.png`);
it.sequential('downloads untracked file', async () => {
await utils.putTextFile('untracked-content', `/data/upload/${admin.userId}/untracked1.png`);
await utils.createJob(admin.accessToken, {
name: ManualJobName.IntegrityOrphanFiles,
name: ManualJobName.IntegrityUntrackedFiles,
});
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
@@ -646,10 +646,10 @@ describe('/admin/integrity', () => {
const { body: listBody } = await request(app)
.post('/admin/integrity/report')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ type: 'orphan_file' });
.send({ type: 'untracked_file' });
const report = (listBody as IntegrityReportResponseDto).items.find(
(item) => item.path === `/data/upload/${admin.userId}/orphan1.png`,
(item) => item.path === `/data/upload/${admin.userId}/untracked1.png`,
)!;
const { status, headers, body } = await request(app)
@@ -660,7 +660,7 @@ describe('/admin/integrity', () => {
expect(status).toBe(200);
expect(headers['content-type']).toContain('application/octet-stream');
expect(body.toString()).toBe('orphan-content');
expect(body.toString()).toBe('untracked-content');
});
});
});

View File

@@ -195,9 +195,9 @@
"maintenance_integrity_missing_file": "Missing Files",
"maintenance_integrity_missing_file_job": "Check for missing files",
"maintenance_integrity_missing_file_refresh_job": "Refresh missing file reports",
"maintenance_integrity_orphan_file": "Orphan Files",
"maintenance_integrity_orphan_file_job": "Check for orphaned files",
"maintenance_integrity_orphan_file_refresh_job": "Refresh orphan file reports",
"maintenance_integrity_untracked_file": "Untracked Files",
"maintenance_integrity_untracked_file_job": "Check for untracked files",
"maintenance_integrity_untracked_file_refresh_job": "Refresh untracked file reports",
"maintenance_integrity_report": "Integrity Report",
"maintenance_settings": "Maintenance",
"maintenance_settings_description": "Put Immich into maintenance mode.",

View File

@@ -180,7 +180,7 @@ class MaintenanceAdminApi {
/// Download flagged file
///
/// Download the orphan/broken file if one exists
/// Download the untracked/broken file if one exists
///
/// Note: This method returns the HTTP [Response].
///
@@ -215,7 +215,7 @@ class MaintenanceAdminApi {
/// Download flagged file
///
/// Download the orphan/broken file if one exists
/// Download the untracked/broken file if one exists
///
/// Parameters:
///

View File

@@ -24,7 +24,7 @@ class IntegrityGetReportDto {
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
DateTime? cursor;
String? cursor;
/// Minimum value: 1
///
@@ -56,7 +56,7 @@ class IntegrityGetReportDto {
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
if (this.cursor != null) {
json[r'cursor'] = this.cursor!.toUtc().toIso8601String();
json[r'cursor'] = this.cursor;
} else {
// json[r'cursor'] = null;
}
@@ -78,7 +78,7 @@ class IntegrityGetReportDto {
final json = value.cast<String, dynamic>();
return IntegrityGetReportDto(
cursor: mapDateTime(json, r'cursor', r''),
cursor: mapValueOfType<String>(json, r'cursor'),
limit: num.parse('${json[r'limit']}'),
type: IntegrityReportType.fromJson(json[r'type'])!,
);

View File

@@ -25,7 +25,7 @@ class IntegrityReportResponseDto {
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
DateTime? nextCursor;
String? nextCursor;
@override
bool operator ==(Object other) => identical(this, other) || other is IntegrityReportResponseDto &&
@@ -45,7 +45,7 @@ class IntegrityReportResponseDto {
final json = <String, dynamic>{};
json[r'items'] = this.items;
if (this.nextCursor != null) {
json[r'nextCursor'] = this.nextCursor!.toUtc().toIso8601String();
json[r'nextCursor'] = this.nextCursor;
} else {
// json[r'nextCursor'] = null;
}
@@ -62,7 +62,7 @@ class IntegrityReportResponseDto {
return IntegrityReportResponseDto(
items: IntegrityReportDto.listFromJson(json[r'items']),
nextCursor: mapDateTime(json, r'nextCursor', r''),
nextCursor: mapValueOfType<String>(json, r'nextCursor'),
);
}
return null;

View File

@@ -15,36 +15,36 @@ class IntegrityReportSummaryResponseDto {
IntegrityReportSummaryResponseDto({
required this.checksumMismatch,
required this.missingFile,
required this.orphanFile,
required this.untrackedFile,
});
int checksumMismatch;
int missingFile;
int orphanFile;
int untrackedFile;
@override
bool operator ==(Object other) => identical(this, other) || other is IntegrityReportSummaryResponseDto &&
other.checksumMismatch == checksumMismatch &&
other.missingFile == missingFile &&
other.orphanFile == orphanFile;
other.untrackedFile == untrackedFile;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(checksumMismatch.hashCode) +
(missingFile.hashCode) +
(orphanFile.hashCode);
(untrackedFile.hashCode);
@override
String toString() => 'IntegrityReportSummaryResponseDto[checksumMismatch=$checksumMismatch, missingFile=$missingFile, orphanFile=$orphanFile]';
String toString() => 'IntegrityReportSummaryResponseDto[checksumMismatch=$checksumMismatch, missingFile=$missingFile, untrackedFile=$untrackedFile]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'checksum_mismatch'] = this.checksumMismatch;
json[r'missing_file'] = this.missingFile;
json[r'orphan_file'] = this.orphanFile;
json[r'untracked_file'] = this.untrackedFile;
return json;
}
@@ -59,7 +59,7 @@ class IntegrityReportSummaryResponseDto {
return IntegrityReportSummaryResponseDto(
checksumMismatch: mapValueOfType<int>(json, r'checksum_mismatch')!,
missingFile: mapValueOfType<int>(json, r'missing_file')!,
orphanFile: mapValueOfType<int>(json, r'orphan_file')!,
untrackedFile: mapValueOfType<int>(json, r'untracked_file')!,
);
}
return null;
@@ -109,7 +109,7 @@ class IntegrityReportSummaryResponseDto {
static const requiredKeys = <String>{
'checksum_mismatch',
'missing_file',
'orphan_file',
'untracked_file',
};
}

View File

@@ -23,13 +23,13 @@ class IntegrityReportType {
String toJson() => value;
static const orphanFile = IntegrityReportType._(r'orphan_file');
static const untrackedFile = IntegrityReportType._(r'untracked_file');
static const missingFile = IntegrityReportType._(r'missing_file');
static const checksumMismatch = IntegrityReportType._(r'checksum_mismatch');
/// List of all possible values in this [enum][IntegrityReportType].
static const values = <IntegrityReportType>[
orphanFile,
untrackedFile,
missingFile,
checksumMismatch,
];
@@ -70,7 +70,7 @@ class IntegrityReportTypeTypeTransformer {
IntegrityReportType? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'orphan_file': return IntegrityReportType.orphanFile;
case r'untracked_file': return IntegrityReportType.untrackedFile;
case r'missing_file': return IntegrityReportType.missingFile;
case r'checksum_mismatch': return IntegrityReportType.checksumMismatch;
default:

View File

@@ -78,9 +78,9 @@ class JobName {
static const ocrQueueAll = JobName._(r'OcrQueueAll');
static const ocr = JobName._(r'Ocr');
static const workflowRun = JobName._(r'WorkflowRun');
static const integrityOrphanedFilesQueueAll = JobName._(r'IntegrityOrphanedFilesQueueAll');
static const integrityOrphanedFiles = JobName._(r'IntegrityOrphanedFiles');
static const integrityOrphanedRefresh = JobName._(r'IntegrityOrphanedRefresh');
static const integrityUntrackedFilesQueueAll = JobName._(r'IntegrityUntrackedFilesQueueAll');
static const integrityUntrackedFiles = JobName._(r'IntegrityUntrackedFiles');
static const integrityUntrackedRefresh = JobName._(r'IntegrityUntrackedRefresh');
static const integrityMissingFilesQueueAll = JobName._(r'IntegrityMissingFilesQueueAll');
static const integrityMissingFiles = JobName._(r'IntegrityMissingFiles');
static const integrityMissingFilesRefresh = JobName._(r'IntegrityMissingFilesRefresh');
@@ -146,9 +146,9 @@ class JobName {
ocrQueueAll,
ocr,
workflowRun,
integrityOrphanedFilesQueueAll,
integrityOrphanedFiles,
integrityOrphanedRefresh,
integrityUntrackedFilesQueueAll,
integrityUntrackedFiles,
integrityUntrackedRefresh,
integrityMissingFilesQueueAll,
integrityMissingFiles,
integrityMissingFilesRefresh,
@@ -249,9 +249,9 @@ class JobNameTypeTransformer {
case r'OcrQueueAll': return JobName.ocrQueueAll;
case r'Ocr': return JobName.ocr;
case r'WorkflowRun': return JobName.workflowRun;
case r'IntegrityOrphanedFilesQueueAll': return JobName.integrityOrphanedFilesQueueAll;
case r'IntegrityOrphanedFiles': return JobName.integrityOrphanedFiles;
case r'IntegrityOrphanedRefresh': return JobName.integrityOrphanedRefresh;
case r'IntegrityUntrackedFilesQueueAll': return JobName.integrityUntrackedFilesQueueAll;
case r'IntegrityUntrackedFiles': return JobName.integrityUntrackedFiles;
case r'IntegrityUntrackedRefresh': return JobName.integrityUntrackedRefresh;
case r'IntegrityMissingFilesQueueAll': return JobName.integrityMissingFilesQueueAll;
case r'IntegrityMissingFiles': return JobName.integrityMissingFiles;
case r'IntegrityMissingFilesRefresh': return JobName.integrityMissingFilesRefresh;

View File

@@ -30,13 +30,13 @@ class ManualJobName {
static const memoryCreate = ManualJobName._(r'memory-create');
static const backupDatabase = ManualJobName._(r'backup-database');
static const integrityMissingFiles = ManualJobName._(r'integrity-missing-files');
static const integrityOrphanFiles = ManualJobName._(r'integrity-orphan-files');
static const integrityUntrackedFiles = ManualJobName._(r'integrity-untracked-files');
static const integrityChecksumMismatch = ManualJobName._(r'integrity-checksum-mismatch');
static const integrityMissingFilesRefresh = ManualJobName._(r'integrity-missing-files-refresh');
static const integrityOrphanFilesRefresh = ManualJobName._(r'integrity-orphan-files-refresh');
static const integrityUntrackedFilesRefresh = ManualJobName._(r'integrity-untracked-files-refresh');
static const integrityChecksumMismatchRefresh = ManualJobName._(r'integrity-checksum-mismatch-refresh');
static const integrityMissingFilesDeleteAll = ManualJobName._(r'integrity-missing-files-delete-all');
static const integrityOrphanFilesDeleteAll = ManualJobName._(r'integrity-orphan-files-delete-all');
static const integrityUntrackedFilesDeleteAll = ManualJobName._(r'integrity-untracked-files-delete-all');
static const integrityChecksumMismatchDeleteAll = ManualJobName._(r'integrity-checksum-mismatch-delete-all');
/// List of all possible values in this [enum][ManualJobName].
@@ -48,13 +48,13 @@ class ManualJobName {
memoryCreate,
backupDatabase,
integrityMissingFiles,
integrityOrphanFiles,
integrityUntrackedFiles,
integrityChecksumMismatch,
integrityMissingFilesRefresh,
integrityOrphanFilesRefresh,
integrityUntrackedFilesRefresh,
integrityChecksumMismatchRefresh,
integrityMissingFilesDeleteAll,
integrityOrphanFilesDeleteAll,
integrityUntrackedFilesDeleteAll,
integrityChecksumMismatchDeleteAll,
];
@@ -101,13 +101,13 @@ class ManualJobNameTypeTransformer {
case r'memory-create': return ManualJobName.memoryCreate;
case r'backup-database': return ManualJobName.backupDatabase;
case r'integrity-missing-files': return ManualJobName.integrityMissingFiles;
case r'integrity-orphan-files': return ManualJobName.integrityOrphanFiles;
case r'integrity-untracked-files': return ManualJobName.integrityUntrackedFiles;
case r'integrity-checksum-mismatch': return ManualJobName.integrityChecksumMismatch;
case r'integrity-missing-files-refresh': return ManualJobName.integrityMissingFilesRefresh;
case r'integrity-orphan-files-refresh': return ManualJobName.integrityOrphanFilesRefresh;
case r'integrity-untracked-files-refresh': return ManualJobName.integrityUntrackedFilesRefresh;
case r'integrity-checksum-mismatch-refresh': return ManualJobName.integrityChecksumMismatchRefresh;
case r'integrity-missing-files-delete-all': return ManualJobName.integrityMissingFilesDeleteAll;
case r'integrity-orphan-files-delete-all': return ManualJobName.integrityOrphanFilesDeleteAll;
case r'integrity-untracked-files-delete-all': return ManualJobName.integrityUntrackedFilesDeleteAll;
case r'integrity-checksum-mismatch-delete-all': return ManualJobName.integrityChecksumMismatchDeleteAll;
default:
if (!allowNull) {

View File

@@ -15,36 +15,36 @@ class SystemConfigIntegrityChecks {
SystemConfigIntegrityChecks({
required this.checksumFiles,
required this.missingFiles,
required this.orphanedFiles,
required this.untrackedFiles,
});
SystemConfigIntegrityChecksumJob checksumFiles;
SystemConfigIntegrityJob missingFiles;
SystemConfigIntegrityJob orphanedFiles;
SystemConfigIntegrityJob untrackedFiles;
@override
bool operator ==(Object other) => identical(this, other) || other is SystemConfigIntegrityChecks &&
other.checksumFiles == checksumFiles &&
other.missingFiles == missingFiles &&
other.orphanedFiles == orphanedFiles;
other.untrackedFiles == untrackedFiles;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(checksumFiles.hashCode) +
(missingFiles.hashCode) +
(orphanedFiles.hashCode);
(untrackedFiles.hashCode);
@override
String toString() => 'SystemConfigIntegrityChecks[checksumFiles=$checksumFiles, missingFiles=$missingFiles, orphanedFiles=$orphanedFiles]';
String toString() => 'SystemConfigIntegrityChecks[checksumFiles=$checksumFiles, missingFiles=$missingFiles, untrackedFiles=$untrackedFiles]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'checksumFiles'] = this.checksumFiles;
json[r'missingFiles'] = this.missingFiles;
json[r'orphanedFiles'] = this.orphanedFiles;
json[r'untrackedFiles'] = this.untrackedFiles;
return json;
}
@@ -59,7 +59,7 @@ class SystemConfigIntegrityChecks {
return SystemConfigIntegrityChecks(
checksumFiles: SystemConfigIntegrityChecksumJob.fromJson(json[r'checksumFiles'])!,
missingFiles: SystemConfigIntegrityJob.fromJson(json[r'missingFiles'])!,
orphanedFiles: SystemConfigIntegrityJob.fromJson(json[r'orphanedFiles'])!,
untrackedFiles: SystemConfigIntegrityJob.fromJson(json[r'untrackedFiles'])!,
);
}
return null;
@@ -109,7 +109,7 @@ class SystemConfigIntegrityChecks {
static const requiredKeys = <String>{
'checksumFiles',
'missingFiles',
'orphanedFiles',
'untrackedFiles',
};
}

View File

@@ -431,7 +431,7 @@
},
"/admin/integrity/report/{id}/file": {
"get": {
"description": "Download the orphan/broken file if one exists",
"description": "Download the untracked/broken file if one exists",
"operationId": "getIntegrityReportFile",
"parameters": [
{
@@ -16997,20 +16997,20 @@
"missing_file": {
"type": "integer"
},
"orphan_file": {
"untracked_file": {
"type": "integer"
}
},
"required": [
"checksum_mismatch",
"missing_file",
"orphan_file"
"untracked_file"
],
"type": "object"
},
"IntegrityReportType": {
"enum": [
"orphan_file",
"untracked_file",
"missing_file",
"checksum_mismatch"
],
@@ -17088,9 +17088,9 @@
"OcrQueueAll",
"Ocr",
"WorkflowRun",
"IntegrityOrphanedFilesQueueAll",
"IntegrityOrphanedFiles",
"IntegrityOrphanedRefresh",
"IntegrityUntrackedFilesQueueAll",
"IntegrityUntrackedFiles",
"IntegrityUntrackedRefresh",
"IntegrityMissingFilesQueueAll",
"IntegrityMissingFiles",
"IntegrityMissingFilesRefresh",
@@ -17368,13 +17368,13 @@
"memory-create",
"backup-database",
"integrity-missing-files",
"integrity-orphan-files",
"integrity-untracked-files",
"integrity-checksum-mismatch",
"integrity-missing-files-refresh",
"integrity-orphan-files-refresh",
"integrity-untracked-files-refresh",
"integrity-checksum-mismatch-refresh",
"integrity-missing-files-delete-all",
"integrity-orphan-files-delete-all",
"integrity-untracked-files-delete-all",
"integrity-checksum-mismatch-delete-all"
],
"type": "string"
@@ -22022,14 +22022,14 @@
"missingFiles": {
"$ref": "#/components/schemas/SystemConfigIntegrityJob"
},
"orphanedFiles": {
"untrackedFiles": {
"$ref": "#/components/schemas/SystemConfigIntegrityJob"
}
},
"required": [
"checksumFiles",
"missingFiles",
"orphanedFiles"
"untrackedFiles"
],
"type": "object"
},

View File

@@ -8,7 +8,7 @@ import * as Oazapfts from "@oazapfts/runtime";
import * as QS from "@oazapfts/runtime/query";
export const defaults: Oazapfts.Defaults<Oazapfts.CustomHeaders> = {
headers: {},
baseUrl: "/api",
baseUrl: "/api"
};
const oazapfts = Oazapfts.runtime(defaults);
export const servers = {
@@ -57,7 +57,7 @@ export type IntegrityReportResponseDto = {
export type IntegrityReportSummaryResponseDto = {
checksum_mismatch: number;
missing_file: number;
orphan_file: number;
untracked_file: number;
};
export type SetMaintenanceModeDto = {
action: MaintenanceAction;
@@ -1491,7 +1491,7 @@ export type SystemConfigIntegrityJob = {
export type SystemConfigIntegrityChecks = {
checksumFiles: SystemConfigIntegrityChecksumJob;
missingFiles: SystemConfigIntegrityJob;
orphanedFiles: SystemConfigIntegrityJob;
untrackedFiles: SystemConfigIntegrityJob;
};
export type JobSettingsDto = {
concurrency: number;
@@ -5257,7 +5257,7 @@ export enum UserAvatarColor {
Amber = "amber"
}
export enum IntegrityReportType {
OrphanFile = "orphan_file",
UntrackedFile = "untracked_file",
MissingFile = "missing_file",
ChecksumMismatch = "checksum_mismatch"
}
@@ -5503,13 +5503,13 @@ export enum ManualJobName {
MemoryCreate = "memory-create",
BackupDatabase = "backup-database",
IntegrityMissingFiles = "integrity-missing-files",
IntegrityOrphanFiles = "integrity-orphan-files",
IntegrityUntrackedFiles = "integrity-untracked-files",
IntegrityChecksumMismatch = "integrity-checksum-mismatch",
IntegrityMissingFilesRefresh = "integrity-missing-files-refresh",
IntegrityOrphanFilesRefresh = "integrity-orphan-files-refresh",
IntegrityUntrackedFilesRefresh = "integrity-untracked-files-refresh",
IntegrityChecksumMismatchRefresh = "integrity-checksum-mismatch-refresh",
IntegrityMissingFilesDeleteAll = "integrity-missing-files-delete-all",
IntegrityOrphanFilesDeleteAll = "integrity-orphan-files-delete-all",
IntegrityUntrackedFilesDeleteAll = "integrity-untracked-files-delete-all",
IntegrityChecksumMismatchDeleteAll = "integrity-checksum-mismatch-delete-all"
}
export enum QueueName {
@@ -5624,9 +5624,9 @@ export enum JobName {
OcrQueueAll = "OcrQueueAll",
Ocr = "Ocr",
WorkflowRun = "WorkflowRun",
IntegrityOrphanedFilesQueueAll = "IntegrityOrphanedFilesQueueAll",
IntegrityOrphanedFiles = "IntegrityOrphanedFiles",
IntegrityOrphanedRefresh = "IntegrityOrphanedRefresh",
IntegrityUntrackedFilesQueueAll = "IntegrityUntrackedFilesQueueAll",
IntegrityUntrackedFiles = "IntegrityUntrackedFiles",
IntegrityUntrackedRefresh = "IntegrityUntrackedRefresh",
IntegrityMissingFilesQueueAll = "IntegrityMissingFilesQueueAll",
IntegrityMissingFiles = "IntegrityMissingFiles",
IntegrityMissingFilesRefresh = "IntegrityMissingFilesRefresh",

View File

@@ -51,7 +51,7 @@ export interface SystemConfig {
enabled: boolean;
cronExpression: string;
};
orphanedFiles: {
untrackedFiles: {
enabled: boolean;
cronExpression: string;
};
@@ -243,7 +243,7 @@ export const defaults = Object.freeze<SystemConfig>({
enabled: true,
cronExpression: CronExpression.EVERY_DAY_AT_3AM,
},
orphanedFiles: {
untrackedFiles: {
enabled: true,
cronExpression: CronExpression.EVERY_DAY_AT_3AM,
},

View File

@@ -76,7 +76,7 @@ export class IntegrityController {
@Get('report/:id/file')
@Endpoint({
summary: 'Download flagged file',
description: 'Download the orphan/broken file if one exists',
description: 'Download the untracked/broken file if one exists',
history: new HistoryBuilder().added('v9.9.9').alpha('v9.9.9'),
})
@FileResponse()

View File

@@ -10,7 +10,7 @@ export class IntegrityReportSummaryResponseDto {
@ApiProperty({ type: 'integer' })
[IntegrityReportType.MissingFile]!: number;
@ApiProperty({ type: 'integer' })
[IntegrityReportType.OrphanFile]!: number;
[IntegrityReportType.UntrackedFile]!: number;
}
export class IntegrityGetReportDto {

View File

@@ -174,7 +174,7 @@ class SystemConfigIntegrityChecks {
@Type(() => SystemConfigIntegrityJob)
@ValidateNested()
@IsObject()
orphanedFiles!: SystemConfigIntegrityJob;
untrackedFiles!: SystemConfigIntegrityJob;
@Type(() => SystemConfigIntegrityChecksumJob)
@ValidateNested()

View File

@@ -347,7 +347,7 @@ export enum SourceType {
}
export enum IntegrityReportType {
OrphanFile = 'orphan_file',
UntrackedFile = 'untracked_file',
MissingFile = 'missing_file',
ChecksumFail = 'checksum_mismatch',
}
@@ -360,13 +360,13 @@ export enum ManualJobName {
MemoryCreate = 'memory-create',
BackupDatabase = 'backup-database',
IntegrityMissingFiles = `integrity-missing-files`,
IntegrityOrphanFiles = `integrity-orphan-files`,
IntegrityUntrackedFiles = `integrity-untracked-files`,
IntegrityChecksumFiles = `integrity-checksum-mismatch`,
IntegrityMissingFilesRefresh = `integrity-missing-files-refresh`,
IntegrityOrphanFilesRefresh = `integrity-orphan-files-refresh`,
IntegrityUntrackedFilesRefresh = `integrity-untracked-files-refresh`,
IntegrityChecksumFilesRefresh = `integrity-checksum-mismatch-refresh`,
IntegrityMissingFilesDeleteAll = `integrity-missing-files-delete-all`,
IntegrityOrphanFilesDeleteAll = `integrity-orphan-files-delete-all`,
IntegrityUntrackedFilesDeleteAll = `integrity-untracked-files-delete-all`,
IntegrityChecksumFilesDeleteAll = `integrity-checksum-mismatch-delete-all`,
}
@@ -662,9 +662,9 @@ export enum JobName {
WorkflowRun = 'WorkflowRun',
// Integrity
IntegrityOrphanedFilesQueueAll = 'IntegrityOrphanedFilesQueueAll',
IntegrityOrphanedFiles = 'IntegrityOrphanedFiles',
IntegrityOrphanedFilesRefresh = 'IntegrityOrphanedRefresh',
IntegrityUntrackedFilesQueueAll = 'IntegrityUntrackedFilesQueueAll',
IntegrityUntrackedFiles = 'IntegrityUntrackedFiles',
IntegrityUntrackedFilesRefresh = 'IntegrityUntrackedRefresh',
IntegrityMissingFilesQueueAll = 'IntegrityMissingFilesQueueAll',
IntegrityMissingFiles = 'IntegrityMissingFiles',
IntegrityMissingFilesRefresh = 'IntegrityMissingFilesRefresh',

View File

@@ -21,7 +21,7 @@ select
count(*) filter (
where
"type" = $3
) as "orphan_file"
) as "untracked_file"
from
"integrity_report"

View File

@@ -57,8 +57,8 @@ export class IntegrityRepository {
.select((eb) =>
eb.fn
.countAll<number>()
.filterWhere('type', '=', IntegrityReportType.OrphanFile)
.as(IntegrityReportType.OrphanFile),
.filterWhere('type', '=', IntegrityReportType.UntrackedFile)
.as(IntegrityReportType.UntrackedFile),
)
.executeTakeFirstOrThrow();
}
@@ -149,7 +149,7 @@ export class IntegrityRepository {
)
.leftJoin('integrity_report', (join) =>
join
.on('integrity_report.type', '=', IntegrityReportType.OrphanFile)
.on('integrity_report.type', '=', IntegrityReportType.UntrackedFile)
.on((eb) =>
eb.or([
eb('integrity_report.assetId', '=', eb.ref('allPaths.assetId')),

View File

@@ -145,7 +145,7 @@ describe(IntegrityService.name, () => {
expect(mocks.asset.deleteFiles).toHaveBeenCalledWith([{ id: 'fileAssetId' }]);
});
it('deletes orphaned file', async () => {
it('deletes untracked file', async () => {
mocks.integrityReport.getById.mockResolvedValue({
id: 'id',
createdAt: new Date(0),
@@ -169,7 +169,7 @@ describe(IntegrityService.name, () => {
});
});
describe('handleOrphanedFilesQueueAll', () => {
describe('handleUntrackedFilesQueueAll', () => {
beforeEach(() => {
mocks.integrityReport.streamIntegrityReportsWithAssetChecksum.mockReturnValue((function* () {})() as never);
});
@@ -189,11 +189,11 @@ describe(IntegrityService.name, () => {
})() as never,
);
await sut.handleOrphanedFilesQueueAll({ refreshOnly: false });
await sut.handleUntrackedFilesQueueAll({ refreshOnly: false });
expect(mocks.job.queue).toBeCalledTimes(4);
expect(mocks.job.queue).toBeCalledWith({
name: JobName.IntegrityOrphanedFiles,
name: JobName.IntegrityUntrackedFiles,
data: {
type: 'asset',
paths: expect.arrayContaining(['/path/to/file']),
@@ -201,7 +201,7 @@ describe(IntegrityService.name, () => {
});
expect(mocks.job.queue).toBeCalledWith({
name: JobName.IntegrityOrphanedFiles,
name: JobName.IntegrityUntrackedFiles,
data: {
type: 'asset_file',
paths: expect.arrayContaining(['/path/to/file3']),
@@ -216,11 +216,11 @@ describe(IntegrityService.name, () => {
})() as never,
);
await sut.handleOrphanedFilesQueueAll({ refreshOnly: false });
await sut.handleUntrackedFilesQueueAll({ refreshOnly: false });
expect(mocks.job.queue).toBeCalledTimes(1);
expect(mocks.job.queue).toBeCalledWith({
name: JobName.IntegrityOrphanedFilesRefresh,
name: JobName.IntegrityUntrackedFilesRefresh,
data: {
items: expect.arrayContaining(['mockReport']),
},
@@ -228,33 +228,33 @@ describe(IntegrityService.name, () => {
});
it('should succeed', async () => {
await expect(sut.handleOrphanedFilesQueueAll({ refreshOnly: false })).resolves.toBe(JobStatus.Success);
await expect(sut.handleUntrackedFilesQueueAll({ refreshOnly: false })).resolves.toBe(JobStatus.Success);
});
});
describe('handleOrphanedFiles', () => {
it('should detect orphaned asset files', async () => {
describe('handleUntrackedFiles', () => {
it('should detect untracked asset files', async () => {
mocks.integrityReport.getAssetPathsByPaths.mockResolvedValue([
{ originalPath: '/path/to/file1', encodedVideoPath: null },
]);
await sut.handleOrphanedFiles({
await sut.handleUntrackedFiles({
type: 'asset',
paths: ['/path/to/file1', '/path/to/orphan'],
paths: ['/path/to/file1', '/path/to/untracked'],
});
expect(mocks.integrityReport.getAssetPathsByPaths).toHaveBeenCalledWith(['/path/to/file1', '/path/to/orphan']);
expect(mocks.integrityReport.getAssetPathsByPaths).toHaveBeenCalledWith(['/path/to/file1', '/path/to/untracked']);
expect(mocks.integrityReport.create).toHaveBeenCalledWith([
{ type: IntegrityReportType.OrphanFile, path: '/path/to/orphan' },
{ type: IntegrityReportType.UntrackedFile, path: '/path/to/untracked' },
]);
});
it('should not create reports when no orphans found for assets', async () => {
it('should not create reports when no untracked files found for assets', async () => {
mocks.integrityReport.getAssetPathsByPaths.mockResolvedValue([
{ originalPath: '/path/to/file1', encodedVideoPath: '/path/to/encoded' },
]);
await sut.handleOrphanedFiles({
await sut.handleUntrackedFiles({
type: 'asset',
paths: ['/path/to/file1', '/path/to/encoded'],
});
@@ -262,28 +262,28 @@ describe(IntegrityService.name, () => {
expect(mocks.integrityReport.create).not.toHaveBeenCalled();
});
it('should detect orphaned asset_file files', async () => {
it('should detect untracked asset_file files', async () => {
mocks.integrityReport.getAssetFilePathsByPaths.mockResolvedValue([{ path: '/path/to/thumb1' }]);
await sut.handleOrphanedFiles({
await sut.handleUntrackedFiles({
type: 'asset_file',
paths: ['/path/to/thumb1', '/path/to/orphan_thumb'],
paths: ['/path/to/thumb1', '/path/to/untracked_thumb'],
});
expect(mocks.integrityReport.create).toHaveBeenCalledWith([
{ type: IntegrityReportType.OrphanFile, path: '/path/to/orphan_thumb' },
{ type: IntegrityReportType.UntrackedFile, path: '/path/to/untracked_thumb' },
]);
});
});
describe('handleOrphanedRefresh', () => {
describe('handleUntrackedRefresh', () => {
it('should delete reports for files that no longer exist', async () => {
mocks.storage.stat
.mockRejectedValueOnce(new Error('ENOENT'))
.mockResolvedValueOnce({} as never)
.mockRejectedValueOnce(new Error('ENOENT'));
await sut.handleOrphanedRefresh({
await sut.handleUntrackedRefresh({
items: [
{ reportId: 'report1', path: '/path/to/missing1' },
{ reportId: 'report2', path: '/path/to/existing' },
@@ -297,7 +297,7 @@ describe(IntegrityService.name, () => {
it('should not delete reports for files that still exist', async () => {
mocks.storage.stat.mockResolvedValue({} as never);
await sut.handleOrphanedRefresh({
await sut.handleUntrackedRefresh({
items: [{ reportId: 'report1', path: '/path/to/existing' }],
});
@@ -305,7 +305,7 @@ describe(IntegrityService.name, () => {
});
it('should succeed', async () => {
await expect(sut.handleOrphanedRefresh({ items: [] })).resolves.toBe(JobStatus.Success);
await expect(sut.handleUntrackedRefresh({ items: [] })).resolves.toBe(JobStatus.Success);
});
});
@@ -604,24 +604,24 @@ describe(IntegrityService.name, () => {
expect(mocks.job.queue).toHaveBeenCalledTimes(2);
});
it('should queue delete jobs for orphan file reports', async () => {
it('should queue delete jobs for untracked file reports', async () => {
mocks.integrityReport.streamIntegrityReportsByProperty.mockReturnValue(
(function* () {
yield { id: 'report1', path: '/path/to/orphan' };
yield { id: 'report1', path: '/path/to/untracked' };
})() as never,
);
await sut.handleDeleteAllIntegrityReports({ type: IntegrityReportType.OrphanFile });
await sut.handleDeleteAllIntegrityReports({ type: IntegrityReportType.UntrackedFile });
expect(mocks.integrityReport.streamIntegrityReportsByProperty).toHaveBeenCalledWith(
undefined,
IntegrityReportType.OrphanFile,
IntegrityReportType.UntrackedFile,
);
expect(mocks.job.queue).toHaveBeenCalledWith({
name: JobName.IntegrityDeleteReports,
data: {
reports: [{ id: 'report1', path: '/path/to/orphan' }],
reports: [{ id: 'report1', path: '/path/to/untracked' }],
},
});
});
@@ -648,7 +648,7 @@ describe(IntegrityService.name, () => {
{ id: 'report1', assetId: 'asset1', fileAssetId: null, path: '/path/to/file1' },
{ id: 'report2', assetId: 'asset2', fileAssetId: null, path: '/path/to/file2' },
{ id: 'report3', assetId: null, fileAssetId: 'fileAsset1', path: '/path/to/file3' },
{ id: 'report4', assetId: null, fileAssetId: null, path: '/path/to/orphan' },
{ id: 'report4', assetId: null, fileAssetId: null, path: '/path/to/untracked' },
],
});
@@ -666,7 +666,7 @@ describe(IntegrityService.name, () => {
expect(mocks.asset.deleteFiles).toHaveBeenCalledWith([{ id: 'fileAsset1' }]);
expect(mocks.storage.unlink).toHaveBeenCalledWith('/path/to/orphan');
expect(mocks.storage.unlink).toHaveBeenCalledWith('/path/to/untracked');
expect(mocks.integrityReport.deleteByIds).toHaveBeenCalledWith(['report4']);
});

View File

@@ -31,15 +31,15 @@ import {
IIntegrityDeleteReportTypeJob,
IIntegrityJob,
IIntegrityMissingFilesJob,
IIntegrityOrphanedFilesJob,
IIntegrityPathWithChecksumJob,
IIntegrityPathWithReportJob,
IIntegrityUntrackedFilesJob,
} from 'src/types';
import { ImmichFileResponse } from 'src/utils/file';
import { handlePromiseError } from 'src/utils/misc';
/**
* Orphan Files:
* Untracked Files:
* Files are detected in /data/encoded-video, /data/library, /data/upload
* Checked against the asset table
* Files are detected in /data/thumbs
@@ -69,20 +69,20 @@ export class IntegrityService extends BaseService {
@OnEvent({ name: 'ConfigInit', workers: [ImmichWorker.Microservices] })
async onConfigInit({
newConfig: {
integrityChecks: { orphanedFiles, missingFiles, checksumFiles },
integrityChecks: { untrackedFiles, missingFiles, checksumFiles },
},
}: ArgOf<'ConfigInit'>) {
this.integrityLock = await this.databaseRepository.tryLock(DatabaseLock.IntegrityCheck);
if (this.integrityLock) {
this.cronRepository.create({
name: 'integrityOrphanedFiles',
expression: orphanedFiles.cronExpression,
name: 'integrityUntrackedFiles',
expression: untrackedFiles.cronExpression,
onTick: () =>
handlePromiseError(
this.jobRepository.queue({ name: JobName.IntegrityOrphanedFilesQueueAll, data: {} }),
this.jobRepository.queue({ name: JobName.IntegrityUntrackedFilesQueueAll, data: {} }),
this.logger,
),
start: orphanedFiles.enabled,
start: untrackedFiles.enabled,
});
this.cronRepository.create({
@@ -109,7 +109,7 @@ export class IntegrityService extends BaseService {
@OnEvent({ name: 'ConfigUpdate', server: true })
onConfigUpdate({
newConfig: {
integrityChecks: { orphanedFiles, missingFiles, checksumFiles },
integrityChecks: { untrackedFiles, missingFiles, checksumFiles },
},
}: ArgOf<'ConfigUpdate'>) {
if (!this.integrityLock) {
@@ -117,9 +117,9 @@ export class IntegrityService extends BaseService {
}
this.cronRepository.update({
name: 'integrityOrphanedFiles',
expression: orphanedFiles.cronExpression,
start: orphanedFiles.enabled,
name: 'integrityUntrackedFiles',
expression: untrackedFiles.cronExpression,
start: untrackedFiles.enabled,
});
this.cronRepository.update({
@@ -194,16 +194,16 @@ export class IntegrityService extends BaseService {
}
}
@OnJob({ name: JobName.IntegrityOrphanedFilesQueueAll, queue: QueueName.IntegrityCheck })
async handleOrphanedFilesQueueAll({ refreshOnly }: IIntegrityJob = {}): Promise<JobStatus> {
this.logger.log(`Checking for out of date orphaned file reports...`);
@OnJob({ name: JobName.IntegrityUntrackedFilesQueueAll, queue: QueueName.IntegrityCheck })
async handleUntrackedFilesQueueAll({ refreshOnly }: IIntegrityJob = {}): Promise<JobStatus> {
this.logger.log(`Checking for out of date untracked file reports...`);
const reports = this.integrityRepository.streamIntegrityReportsWithAssetChecksum(IntegrityReportType.OrphanFile);
const reports = this.integrityRepository.streamIntegrityReportsWithAssetChecksum(IntegrityReportType.UntrackedFile);
let total = 0;
for await (const batchReports of chunk(reports, JOBS_LIBRARY_PAGINATION_SIZE)) {
await this.jobRepository.queue({
name: JobName.IntegrityOrphanedFilesRefresh,
name: JobName.IntegrityUntrackedFilesRefresh,
data: {
items: batchReports,
},
@@ -218,7 +218,7 @@ export class IntegrityService extends BaseService {
return JobStatus.Success;
}
this.logger.log(`Scanning for orphaned files...`);
this.logger.log(`Scanning for untracked files...`);
const assetPaths = this.storageRepository.walk({
pathsToCrawl: [StorageFolder.EncodedVideo, StorageFolder.Library, StorageFolder.Upload].map((folder) =>
@@ -247,7 +247,7 @@ export class IntegrityService extends BaseService {
total = 0;
for await (const [batchType, batchPaths] of paths()) {
await this.jobRepository.queue({
name: JobName.IntegrityOrphanedFiles,
name: JobName.IntegrityUntrackedFiles,
data: {
type: batchType,
paths: batchPaths,
@@ -257,48 +257,48 @@ export class IntegrityService extends BaseService {
const count = batchPaths.length;
total += count;
this.logger.log(`Queued orphan check of ${count} file(s) (${total} so far)`);
this.logger.log(`Queued untracked check of ${count} file(s) (${total} so far)`);
}
return JobStatus.Success;
}
@OnJob({ name: JobName.IntegrityOrphanedFiles, queue: QueueName.IntegrityCheck })
async handleOrphanedFiles({ type, paths }: IIntegrityOrphanedFilesJob): Promise<JobStatus> {
this.logger.log(`Processing batch of ${paths.length} files to check if they are orphaned.`);
@OnJob({ name: JobName.IntegrityUntrackedFiles, queue: QueueName.IntegrityCheck })
async handleUntrackedFiles({ type, paths }: IIntegrityUntrackedFilesJob): Promise<JobStatus> {
this.logger.log(`Processing batch of ${paths.length} files to check if they are untracked.`);
const orphanedFiles = new Set<string>(paths);
const untrackedFiles = new Set<string>(paths);
if (type === 'asset') {
const assets = await this.integrityRepository.getAssetPathsByPaths(paths);
for (const { originalPath, encodedVideoPath } of assets) {
orphanedFiles.delete(originalPath);
untrackedFiles.delete(originalPath);
if (encodedVideoPath) {
orphanedFiles.delete(encodedVideoPath);
untrackedFiles.delete(encodedVideoPath);
}
}
} else {
const assets = await this.integrityRepository.getAssetFilePathsByPaths(paths);
for (const { path } of assets) {
orphanedFiles.delete(path);
untrackedFiles.delete(path);
}
}
if (orphanedFiles.size > 0) {
if (untrackedFiles.size > 0) {
await this.integrityRepository.create(
[...orphanedFiles].map((path) => ({
type: IntegrityReportType.OrphanFile,
[...untrackedFiles].map((path) => ({
type: IntegrityReportType.UntrackedFile,
path,
})),
);
}
this.logger.log(`Processed ${paths.length} and found ${orphanedFiles.size} orphaned file(s).`);
this.logger.log(`Processed ${paths.length} and found ${untrackedFiles.size} untracked file(s).`);
return JobStatus.Success;
}
@OnJob({ name: JobName.IntegrityOrphanedFilesRefresh, queue: QueueName.IntegrityCheck })
async handleOrphanedRefresh({ items }: IIntegrityPathWithReportJob): Promise<JobStatus> {
@OnJob({ name: JobName.IntegrityUntrackedFilesRefresh, queue: QueueName.IntegrityCheck })
async handleUntrackedRefresh({ items }: IIntegrityPathWithReportJob): Promise<JobStatus> {
this.logger.log(`Processing batch of ${items.length} reports to check if they are out of date.`);
const results = await Promise.all(
@@ -619,7 +619,7 @@ export class IntegrityService extends BaseService {
properties = ['assetId', 'fileAssetId'] as const;
break;
}
case IntegrityReportType.OrphanFile: {
case IntegrityReportType.UntrackedFile: {
properties = [void 0] as const;
break;
}

View File

@@ -38,8 +38,8 @@ const asJobItem = (dto: JobCreateDto): JobItem => {
return { name: JobName.IntegrityMissingFilesQueueAll };
}
case ManualJobName.IntegrityOrphanFiles: {
return { name: JobName.IntegrityOrphanedFilesQueueAll };
case ManualJobName.IntegrityUntrackedFiles: {
return { name: JobName.IntegrityUntrackedFilesQueueAll };
}
case ManualJobName.IntegrityChecksumFiles: {
@@ -50,8 +50,8 @@ const asJobItem = (dto: JobCreateDto): JobItem => {
return { name: JobName.IntegrityMissingFilesQueueAll, data: { refreshOnly: true } };
}
case ManualJobName.IntegrityOrphanFilesRefresh: {
return { name: JobName.IntegrityOrphanedFilesQueueAll, data: { refreshOnly: true } };
case ManualJobName.IntegrityUntrackedFilesRefresh: {
return { name: JobName.IntegrityUntrackedFilesQueueAll, data: { refreshOnly: true } };
}
case ManualJobName.IntegrityChecksumFilesRefresh: {
@@ -62,8 +62,8 @@ const asJobItem = (dto: JobCreateDto): JobItem => {
return { name: JobName.IntegrityDeleteReportType, data: { type: IntegrityReportType.MissingFile } };
}
case ManualJobName.IntegrityOrphanFilesDeleteAll: {
return { name: JobName.IntegrityDeleteReportType, data: { type: IntegrityReportType.OrphanFile } };
case ManualJobName.IntegrityUntrackedFilesDeleteAll: {
return { name: JobName.IntegrityDeleteReportType, data: { type: IntegrityReportType.UntrackedFile } };
}
case ManualJobName.IntegrityChecksumFilesDeleteAll: {

View File

@@ -74,7 +74,7 @@ const updatedConfig = Object.freeze<SystemConfig>({
tonemap: ToneMapping.Hable,
},
integrityChecks: {
orphanedFiles: {
untrackedFiles: {
enabled: true,
cronExpression: '0 03 * * *',
},

View File

@@ -294,7 +294,7 @@ export interface IIntegrityDeleteReportsJob {
}[];
}
export interface IIntegrityOrphanedFilesJob {
export interface IIntegrityUntrackedFilesJob {
type: 'asset' | 'asset_file';
paths: string[];
}
@@ -428,9 +428,9 @@ export type JobItem =
| { name: JobName.WorkflowRun; data: IWorkflowJob }
// Integrity
| { name: JobName.IntegrityOrphanedFilesQueueAll; data?: IIntegrityJob }
| { name: JobName.IntegrityOrphanedFiles; data: IIntegrityOrphanedFilesJob }
| { name: JobName.IntegrityOrphanedFilesRefresh; data: IIntegrityPathWithReportJob }
| { name: JobName.IntegrityUntrackedFilesQueueAll; data?: IIntegrityJob }
| { name: JobName.IntegrityUntrackedFiles; data: IIntegrityUntrackedFilesJob }
| { name: JobName.IntegrityUntrackedFilesRefresh; data: IIntegrityPathWithReportJob }
| { name: JobName.IntegrityMissingFilesQueueAll; data?: IIntegrityJob }
| { name: JobName.IntegrityMissingFiles; data: IIntegrityPathWithReportJob }
| { name: JobName.IntegrityMissingFilesRefresh; data: IIntegrityPathWithReportJob }

View File

@@ -21,8 +21,8 @@
value: ManualJobName.IntegrityMissingFiles,
},
{
title: $t('admin.maintenance_integrity_orphan_file_job'),
value: ManualJobName.IntegrityOrphanFiles,
title: $t('admin.maintenance_integrity_untracked_file_job'),
value: ManualJobName.IntegrityUntrackedFiles,
},
{
title: $t('admin.maintenance_integrity_checksum_mismatch_job'),
@@ -33,8 +33,8 @@
value: ManualJobName.IntegrityMissingFilesRefresh,
},
{
title: $t('admin.maintenance_integrity_orphan_file_refresh_job'),
value: ManualJobName.IntegrityOrphanFilesRefresh,
title: $t('admin.maintenance_integrity_untracked_file_refresh_job'),
value: ManualJobName.IntegrityUntrackedFilesRefresh,
},
{
title: $t('admin.maintenance_integrity_checksum_mismatch_refresh_job'),

View File

@@ -30,7 +30,7 @@
let integrityReport: IntegrityReportSummaryResponseDto = $state(data.integrityReport);
const TYPES: IntegrityReportType[] = [
IntegrityReportType.OrphanFile,
IntegrityReportType.UntrackedFile,
IntegrityReportType.MissingFile,
IntegrityReportType.ChecksumMismatch,
];
@@ -53,8 +53,8 @@
async function runJob(reportType: IntegrityReportType, refreshOnly?: boolean) {
let name: ManualJobName;
switch (reportType) {
case IntegrityReportType.OrphanFile: {
name = refreshOnly ? ManualJobName.IntegrityOrphanFilesRefresh : ManualJobName.IntegrityOrphanFiles;
case IntegrityReportType.UntrackedFile: {
name = refreshOnly ? ManualJobName.IntegrityUntrackedFilesRefresh : ManualJobName.IntegrityUntrackedFiles;
break;
}
case IntegrityReportType.MissingFile: {

View File

@@ -57,8 +57,8 @@
if (confirm) {
let name: ManualJobName;
switch (data.type) {
case IntegrityReportType.OrphanFile: {
name = ManualJobName.IntegrityOrphanFilesDeleteAll;
case IntegrityReportType.UntrackedFile: {
name = ManualJobName.IntegrityUntrackedFilesDeleteAll;
break;
}
case IntegrityReportType.MissingFile: {
@@ -108,7 +108,7 @@
const handleOpen = async (event: Event, props: Partial<ContextMenuBaseProps>, reportId: string) => {
const items: MenuItems = [];
if (data.type === IntegrityReportType.OrphanFile || data.type === IntegrityReportType.ChecksumMismatch) {
if (data.type === IntegrityReportType.UntrackedFile || data.type === IntegrityReportType.ChecksumMismatch) {
items.push({
title: $t('download'),
icon: mdiDownload,