mirror of
https://github.com/immich-app/immich.git
synced 2025-12-19 09:13:14 +03:00
refactor: split integrity out of maintenance controller/service
This commit is contained in:
@@ -7,7 +7,7 @@ import { afterEach, beforeAll, describe, expect, it } from 'vitest';
|
|||||||
|
|
||||||
const assetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
const assetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
||||||
|
|
||||||
describe('/admin/maintenance', () => {
|
describe('/admin/integrity', () => {
|
||||||
let cookie: string | undefined;
|
let cookie: string | undefined;
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
let nonAdmin: LoginResponseDto;
|
let nonAdmin: LoginResponseDto;
|
||||||
@@ -18,7 +18,7 @@ describe('/admin/maintenance', () => {
|
|||||||
nonAdmin = await utils.userSetup(admin.accessToken, createUserDto.user1);
|
nonAdmin = await utils.userSetup(admin.accessToken, createUserDto.user1);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /integrity/summary (& jobs)', async () => {
|
describe('POST /summary (& jobs)', async () => {
|
||||||
let baseline: Record<IntegrityReportType, number>;
|
let baseline: Record<IntegrityReportType, number>;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@@ -53,7 +53,7 @@ describe('/admin/maintenance', () => {
|
|||||||
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/admin/maintenance/integrity/summary')
|
.get('/admin/integrity/summary')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ describe('/admin/maintenance', () => {
|
|||||||
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/admin/maintenance/integrity/summary')
|
.get('/admin/integrity/summary')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ describe('/admin/maintenance', () => {
|
|||||||
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/admin/maintenance/integrity/summary')
|
.get('/admin/integrity/summary')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ describe('/admin/maintenance', () => {
|
|||||||
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/admin/maintenance/integrity/summary')
|
.get('/admin/integrity/summary')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ describe('/admin/maintenance', () => {
|
|||||||
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/admin/maintenance/integrity/summary')
|
.get('/admin/integrity/summary')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ describe('/admin/maintenance', () => {
|
|||||||
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/admin/maintenance/integrity/summary')
|
.get('/admin/integrity/summary')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ describe('/admin/maintenance', () => {
|
|||||||
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
await utils.waitForQueueFinish(admin.accessToken, QueueName.IntegrityCheck);
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/admin/maintenance/integrity/summary')
|
.get('/admin/integrity/summary')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
|
|||||||
10
mobile/openapi/README.md
generated
10
mobile/openapi/README.md
generated
@@ -161,11 +161,11 @@ Class | Method | HTTP request | Description
|
|||||||
*LibrariesApi* | [**scanLibrary**](doc//LibrariesApi.md#scanlibrary) | **POST** /libraries/{id}/scan | Scan a library
|
*LibrariesApi* | [**scanLibrary**](doc//LibrariesApi.md#scanlibrary) | **POST** /libraries/{id}/scan | Scan a library
|
||||||
*LibrariesApi* | [**updateLibrary**](doc//LibrariesApi.md#updatelibrary) | **PUT** /libraries/{id} | Update a library
|
*LibrariesApi* | [**updateLibrary**](doc//LibrariesApi.md#updatelibrary) | **PUT** /libraries/{id} | Update a library
|
||||||
*LibrariesApi* | [**validate**](doc//LibrariesApi.md#validate) | **POST** /libraries/{id}/validate | Validate library settings
|
*LibrariesApi* | [**validate**](doc//LibrariesApi.md#validate) | **POST** /libraries/{id}/validate | Validate library settings
|
||||||
*MaintenanceAdminApi* | [**deleteIntegrityReport**](doc//MaintenanceAdminApi.md#deleteintegrityreport) | **DELETE** /admin/maintenance/integrity/report/{id} | Delete report entry and perform corresponding deletion action
|
*MaintenanceAdminApi* | [**deleteIntegrityReport**](doc//MaintenanceAdminApi.md#deleteintegrityreport) | **DELETE** /admin/integrity/report/{id} | Delete report entry and perform corresponding deletion action
|
||||||
*MaintenanceAdminApi* | [**getIntegrityReport**](doc//MaintenanceAdminApi.md#getintegrityreport) | **POST** /admin/maintenance/integrity/report | Get integrity report by type
|
*MaintenanceAdminApi* | [**getIntegrityReport**](doc//MaintenanceAdminApi.md#getintegrityreport) | **POST** /admin/integrity/report | Get integrity report by type
|
||||||
*MaintenanceAdminApi* | [**getIntegrityReportCsv**](doc//MaintenanceAdminApi.md#getintegrityreportcsv) | **GET** /admin/maintenance/integrity/report/{type}/csv | Export integrity report by type as CSV
|
*MaintenanceAdminApi* | [**getIntegrityReportCsv**](doc//MaintenanceAdminApi.md#getintegrityreportcsv) | **GET** /admin/integrity/report/{type}/csv | Export integrity report by type as CSV
|
||||||
*MaintenanceAdminApi* | [**getIntegrityReportFile**](doc//MaintenanceAdminApi.md#getintegrityreportfile) | **GET** /admin/maintenance/integrity/report/{id}/file | Download the orphan/broken file if one exists
|
*MaintenanceAdminApi* | [**getIntegrityReportFile**](doc//MaintenanceAdminApi.md#getintegrityreportfile) | **GET** /admin/integrity/report/{id}/file | Download the orphan/broken file if one exists
|
||||||
*MaintenanceAdminApi* | [**getIntegrityReportSummary**](doc//MaintenanceAdminApi.md#getintegrityreportsummary) | **GET** /admin/maintenance/integrity/summary | Get integrity report summary
|
*MaintenanceAdminApi* | [**getIntegrityReportSummary**](doc//MaintenanceAdminApi.md#getintegrityreportsummary) | **GET** /admin/integrity/summary | Get integrity report summary
|
||||||
*MaintenanceAdminApi* | [**maintenanceLogin**](doc//MaintenanceAdminApi.md#maintenancelogin) | **POST** /admin/maintenance/login | Log into maintenance mode
|
*MaintenanceAdminApi* | [**maintenanceLogin**](doc//MaintenanceAdminApi.md#maintenancelogin) | **POST** /admin/maintenance/login | Log into maintenance mode
|
||||||
*MaintenanceAdminApi* | [**setMaintenanceMode**](doc//MaintenanceAdminApi.md#setmaintenancemode) | **POST** /admin/maintenance | Set maintenance mode
|
*MaintenanceAdminApi* | [**setMaintenanceMode**](doc//MaintenanceAdminApi.md#setmaintenancemode) | **POST** /admin/maintenance | Set maintenance mode
|
||||||
*MapApi* | [**getMapMarkers**](doc//MapApi.md#getmapmarkers) | **GET** /map/markers | Retrieve map markers
|
*MapApi* | [**getMapMarkers**](doc//MapApi.md#getmapmarkers) | **GET** /map/markers | Retrieve map markers
|
||||||
|
|||||||
10
mobile/openapi/lib/api/maintenance_admin_api.dart
generated
10
mobile/openapi/lib/api/maintenance_admin_api.dart
generated
@@ -27,7 +27,7 @@ class MaintenanceAdminApi {
|
|||||||
/// * [String] id (required):
|
/// * [String] id (required):
|
||||||
Future<Response> deleteIntegrityReportWithHttpInfo(String id,) async {
|
Future<Response> deleteIntegrityReportWithHttpInfo(String id,) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final apiPath = r'/admin/maintenance/integrity/report/{id}'
|
final apiPath = r'/admin/integrity/report/{id}'
|
||||||
.replaceAll('{id}', id);
|
.replaceAll('{id}', id);
|
||||||
|
|
||||||
// ignore: prefer_final_locals
|
// ignore: prefer_final_locals
|
||||||
@@ -76,7 +76,7 @@ class MaintenanceAdminApi {
|
|||||||
/// * [MaintenanceGetIntegrityReportDto] maintenanceGetIntegrityReportDto (required):
|
/// * [MaintenanceGetIntegrityReportDto] maintenanceGetIntegrityReportDto (required):
|
||||||
Future<Response> getIntegrityReportWithHttpInfo(MaintenanceGetIntegrityReportDto maintenanceGetIntegrityReportDto,) async {
|
Future<Response> getIntegrityReportWithHttpInfo(MaintenanceGetIntegrityReportDto maintenanceGetIntegrityReportDto,) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final apiPath = r'/admin/maintenance/integrity/report';
|
final apiPath = r'/admin/integrity/report';
|
||||||
|
|
||||||
// ignore: prefer_final_locals
|
// ignore: prefer_final_locals
|
||||||
Object? postBody = maintenanceGetIntegrityReportDto;
|
Object? postBody = maintenanceGetIntegrityReportDto;
|
||||||
@@ -132,7 +132,7 @@ class MaintenanceAdminApi {
|
|||||||
/// * [IntegrityReportType] type (required):
|
/// * [IntegrityReportType] type (required):
|
||||||
Future<Response> getIntegrityReportCsvWithHttpInfo(IntegrityReportType type,) async {
|
Future<Response> getIntegrityReportCsvWithHttpInfo(IntegrityReportType type,) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final apiPath = r'/admin/maintenance/integrity/report/{type}/csv'
|
final apiPath = r'/admin/integrity/report/{type}/csv'
|
||||||
.replaceAll('{type}', type.toString());
|
.replaceAll('{type}', type.toString());
|
||||||
|
|
||||||
// ignore: prefer_final_locals
|
// ignore: prefer_final_locals
|
||||||
@@ -189,7 +189,7 @@ class MaintenanceAdminApi {
|
|||||||
/// * [String] id (required):
|
/// * [String] id (required):
|
||||||
Future<Response> getIntegrityReportFileWithHttpInfo(String id,) async {
|
Future<Response> getIntegrityReportFileWithHttpInfo(String id,) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final apiPath = r'/admin/maintenance/integrity/report/{id}/file'
|
final apiPath = r'/admin/integrity/report/{id}/file'
|
||||||
.replaceAll('{id}', id);
|
.replaceAll('{id}', id);
|
||||||
|
|
||||||
// ignore: prefer_final_locals
|
// ignore: prefer_final_locals
|
||||||
@@ -242,7 +242,7 @@ class MaintenanceAdminApi {
|
|||||||
/// Note: This method returns the HTTP [Response].
|
/// Note: This method returns the HTTP [Response].
|
||||||
Future<Response> getIntegrityReportSummaryWithHttpInfo() async {
|
Future<Response> getIntegrityReportSummaryWithHttpInfo() async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final apiPath = r'/admin/maintenance/integrity/summary';
|
final apiPath = r'/admin/integrity/summary';
|
||||||
|
|
||||||
// ignore: prefer_final_locals
|
// ignore: prefer_final_locals
|
||||||
Object? postBody;
|
Object? postBody;
|
||||||
|
|||||||
@@ -322,57 +322,7 @@
|
|||||||
"x-immich-state": "Stable"
|
"x-immich-state": "Stable"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/maintenance": {
|
"/admin/integrity/report": {
|
||||||
"post": {
|
|
||||||
"description": "Put Immich into or take it out of maintenance mode",
|
|
||||||
"operationId": "setMaintenanceMode",
|
|
||||||
"parameters": [],
|
|
||||||
"requestBody": {
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/SetMaintenanceModeDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"201": {
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"bearer": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cookie": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"api_key": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"summary": "Set maintenance mode",
|
|
||||||
"tags": [
|
|
||||||
"Maintenance (admin)"
|
|
||||||
],
|
|
||||||
"x-immich-admin-only": true,
|
|
||||||
"x-immich-history": [
|
|
||||||
{
|
|
||||||
"version": "v2.3.0",
|
|
||||||
"state": "Added"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "v2.3.0",
|
|
||||||
"state": "Alpha"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"x-immich-permission": "maintenance",
|
|
||||||
"x-immich-state": "Alpha"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/admin/maintenance/integrity/report": {
|
|
||||||
"post": {
|
"post": {
|
||||||
"description": "...",
|
"description": "...",
|
||||||
"operationId": "getIntegrityReport",
|
"operationId": "getIntegrityReport",
|
||||||
@@ -429,7 +379,7 @@
|
|||||||
"x-immich-state": "Alpha"
|
"x-immich-state": "Alpha"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/maintenance/integrity/report/{id}": {
|
"/admin/integrity/report/{id}": {
|
||||||
"delete": {
|
"delete": {
|
||||||
"description": "...",
|
"description": "...",
|
||||||
"operationId": "deleteIntegrityReport",
|
"operationId": "deleteIntegrityReport",
|
||||||
@@ -479,7 +429,7 @@
|
|||||||
"x-immich-state": "Alpha"
|
"x-immich-state": "Alpha"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/maintenance/integrity/report/{id}/file": {
|
"/admin/integrity/report/{id}/file": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "...",
|
"description": "...",
|
||||||
"operationId": "getIntegrityReportFile",
|
"operationId": "getIntegrityReportFile",
|
||||||
@@ -537,7 +487,7 @@
|
|||||||
"x-immich-state": "Alpha"
|
"x-immich-state": "Alpha"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/maintenance/integrity/report/{type}/csv": {
|
"/admin/integrity/report/{type}/csv": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "...",
|
"description": "...",
|
||||||
"operationId": "getIntegrityReportCsv",
|
"operationId": "getIntegrityReportCsv",
|
||||||
@@ -594,7 +544,7 @@
|
|||||||
"x-immich-state": "Alpha"
|
"x-immich-state": "Alpha"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/maintenance/integrity/summary": {
|
"/admin/integrity/summary": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "...",
|
"description": "...",
|
||||||
"operationId": "getIntegrityReportSummary",
|
"operationId": "getIntegrityReportSummary",
|
||||||
@@ -641,6 +591,56 @@
|
|||||||
"x-immich-state": "Alpha"
|
"x-immich-state": "Alpha"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/maintenance": {
|
||||||
|
"post": {
|
||||||
|
"description": "Put Immich into or take it out of maintenance mode",
|
||||||
|
"operationId": "setMaintenanceMode",
|
||||||
|
"parameters": [],
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/SetMaintenanceModeDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"bearer": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cookie": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"api_key": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"summary": "Set maintenance mode",
|
||||||
|
"tags": [
|
||||||
|
"Maintenance (admin)"
|
||||||
|
],
|
||||||
|
"x-immich-admin-only": true,
|
||||||
|
"x-immich-history": [
|
||||||
|
{
|
||||||
|
"version": "v2.3.0",
|
||||||
|
"state": "Added"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v2.3.0",
|
||||||
|
"state": "Alpha"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"x-immich-permission": "maintenance",
|
||||||
|
"x-immich-state": "Alpha"
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/maintenance/login": {
|
"/admin/maintenance/login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Login with maintenance token or cookie to receive current information and perform further actions.",
|
"description": "Login with maintenance token or cookie to receive current information and perform further actions.",
|
||||||
@@ -14581,6 +14581,10 @@
|
|||||||
"name": "Faces",
|
"name": "Faces",
|
||||||
"description": "A face is a detected human face within an asset, which can be associated with a person. Faces are normally detected via machine learning, but can also be created via manually."
|
"description": "A face is a detected human face within an asset, which can be associated with a person. Faces are normally detected via machine learning, but can also be created via manually."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Integrity (admin)",
|
||||||
|
"description": "Endpoints for viewing and managing integrity reports."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Jobs",
|
"name": "Jobs",
|
||||||
"description": "Queues and background jobs are used for processing tasks asynchronously. Queues can be paused and resumed as needed."
|
"description": "Queues and background jobs are used for processing tasks asynchronously. Queues can be paused and resumed as needed."
|
||||||
|
|||||||
@@ -40,9 +40,6 @@ export type ActivityStatisticsResponseDto = {
|
|||||||
comments: number;
|
comments: number;
|
||||||
likes: number;
|
likes: number;
|
||||||
};
|
};
|
||||||
export type SetMaintenanceModeDto = {
|
|
||||||
action: MaintenanceAction;
|
|
||||||
};
|
|
||||||
export type MaintenanceGetIntegrityReportDto = {
|
export type MaintenanceGetIntegrityReportDto = {
|
||||||
"type": IntegrityReportType;
|
"type": IntegrityReportType;
|
||||||
};
|
};
|
||||||
@@ -59,6 +56,9 @@ export type MaintenanceIntegrityReportSummaryResponseDto = {
|
|||||||
missing_file: number;
|
missing_file: number;
|
||||||
orphan_file: number;
|
orphan_file: number;
|
||||||
};
|
};
|
||||||
|
export type SetMaintenanceModeDto = {
|
||||||
|
action: MaintenanceAction;
|
||||||
|
};
|
||||||
export type MaintenanceLoginDto = {
|
export type MaintenanceLoginDto = {
|
||||||
token?: string;
|
token?: string;
|
||||||
};
|
};
|
||||||
@@ -1884,18 +1884,6 @@ export function unlinkAllOAuthAccountsAdmin(opts?: Oazapfts.RequestOpts) {
|
|||||||
method: "POST"
|
method: "POST"
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Set maintenance mode
|
|
||||||
*/
|
|
||||||
export function setMaintenanceMode({ setMaintenanceModeDto }: {
|
|
||||||
setMaintenanceModeDto: SetMaintenanceModeDto;
|
|
||||||
}, opts?: Oazapfts.RequestOpts) {
|
|
||||||
return oazapfts.ok(oazapfts.fetchText("/admin/maintenance", oazapfts.json({
|
|
||||||
...opts,
|
|
||||||
method: "POST",
|
|
||||||
body: setMaintenanceModeDto
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Get integrity report by type
|
* Get integrity report by type
|
||||||
*/
|
*/
|
||||||
@@ -1905,7 +1893,7 @@ export function getIntegrityReport({ maintenanceGetIntegrityReportDto }: {
|
|||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 201;
|
status: 201;
|
||||||
data: MaintenanceIntegrityReportResponseDto;
|
data: MaintenanceIntegrityReportResponseDto;
|
||||||
}>("/admin/maintenance/integrity/report", oazapfts.json({
|
}>("/admin/integrity/report", oazapfts.json({
|
||||||
...opts,
|
...opts,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: maintenanceGetIntegrityReportDto
|
body: maintenanceGetIntegrityReportDto
|
||||||
@@ -1917,7 +1905,7 @@ export function getIntegrityReport({ maintenanceGetIntegrityReportDto }: {
|
|||||||
export function deleteIntegrityReport({ id }: {
|
export function deleteIntegrityReport({ id }: {
|
||||||
id: string;
|
id: string;
|
||||||
}, opts?: Oazapfts.RequestOpts) {
|
}, opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchText(`/admin/maintenance/integrity/report/${encodeURIComponent(id)}`, {
|
return oazapfts.ok(oazapfts.fetchText(`/admin/integrity/report/${encodeURIComponent(id)}`, {
|
||||||
...opts,
|
...opts,
|
||||||
method: "DELETE"
|
method: "DELETE"
|
||||||
}));
|
}));
|
||||||
@@ -1931,7 +1919,7 @@ export function getIntegrityReportFile({ id }: {
|
|||||||
return oazapfts.ok(oazapfts.fetchBlob<{
|
return oazapfts.ok(oazapfts.fetchBlob<{
|
||||||
status: 200;
|
status: 200;
|
||||||
data: Blob;
|
data: Blob;
|
||||||
}>(`/admin/maintenance/integrity/report/${encodeURIComponent(id)}/file`, {
|
}>(`/admin/integrity/report/${encodeURIComponent(id)}/file`, {
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -1944,7 +1932,7 @@ export function getIntegrityReportCsv({ $type }: {
|
|||||||
return oazapfts.ok(oazapfts.fetchBlob<{
|
return oazapfts.ok(oazapfts.fetchBlob<{
|
||||||
status: 200;
|
status: 200;
|
||||||
data: Blob;
|
data: Blob;
|
||||||
}>(`/admin/maintenance/integrity/report/${encodeURIComponent($type)}/csv`, {
|
}>(`/admin/integrity/report/${encodeURIComponent($type)}/csv`, {
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -1955,10 +1943,22 @@ export function getIntegrityReportSummary(opts?: Oazapfts.RequestOpts) {
|
|||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 200;
|
status: 200;
|
||||||
data: MaintenanceIntegrityReportSummaryResponseDto;
|
data: MaintenanceIntegrityReportSummaryResponseDto;
|
||||||
}>("/admin/maintenance/integrity/summary", {
|
}>("/admin/integrity/summary", {
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Set maintenance mode
|
||||||
|
*/
|
||||||
|
export function setMaintenanceMode({ setMaintenanceModeDto }: {
|
||||||
|
setMaintenanceModeDto: SetMaintenanceModeDto;
|
||||||
|
}, opts?: Oazapfts.RequestOpts) {
|
||||||
|
return oazapfts.ok(oazapfts.fetchText("/admin/maintenance", oazapfts.json({
|
||||||
|
...opts,
|
||||||
|
method: "POST",
|
||||||
|
body: setMaintenanceModeDto
|
||||||
|
})));
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Log into maintenance mode
|
* Log into maintenance mode
|
||||||
*/
|
*/
|
||||||
@@ -5235,15 +5235,15 @@ export enum UserAvatarColor {
|
|||||||
Gray = "gray",
|
Gray = "gray",
|
||||||
Amber = "amber"
|
Amber = "amber"
|
||||||
}
|
}
|
||||||
export enum MaintenanceAction {
|
|
||||||
Start = "start",
|
|
||||||
End = "end"
|
|
||||||
}
|
|
||||||
export enum IntegrityReportType {
|
export enum IntegrityReportType {
|
||||||
OrphanFile = "orphan_file",
|
OrphanFile = "orphan_file",
|
||||||
MissingFile = "missing_file",
|
MissingFile = "missing_file",
|
||||||
ChecksumMismatch = "checksum_mismatch"
|
ChecksumMismatch = "checksum_mismatch"
|
||||||
}
|
}
|
||||||
|
export enum MaintenanceAction {
|
||||||
|
Start = "start",
|
||||||
|
End = "end"
|
||||||
|
}
|
||||||
export enum NotificationLevel {
|
export enum NotificationLevel {
|
||||||
Success = "success",
|
Success = "success",
|
||||||
Error = "error",
|
Error = "error",
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ export const endpointTags: Record<ApiTag, string> = {
|
|||||||
[ApiTag.Duplicates]: 'Endpoints for managing and identifying duplicate assets.',
|
[ApiTag.Duplicates]: 'Endpoints for managing and identifying duplicate assets.',
|
||||||
[ApiTag.Faces]:
|
[ApiTag.Faces]:
|
||||||
'A face is a detected human face within an asset, which can be associated with a person. Faces are normally detected via machine learning, but can also be created via manually.',
|
'A face is a detected human face within an asset, which can be associated with a person. Faces are normally detected via machine learning, but can also be created via manually.',
|
||||||
|
[ApiTag.Integrity]: 'Endpoints for viewing and managing integrity reports.',
|
||||||
[ApiTag.Jobs]:
|
[ApiTag.Jobs]:
|
||||||
'Queues and background jobs are used for processing tasks asynchronously. Queues can be paused and resumed as needed.',
|
'Queues and background jobs are used for processing tasks asynchronously. Queues can be paused and resumed as needed.',
|
||||||
[ApiTag.Libraries]:
|
[ApiTag.Libraries]:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { AuthController } from 'src/controllers/auth.controller';
|
|||||||
import { DownloadController } from 'src/controllers/download.controller';
|
import { DownloadController } from 'src/controllers/download.controller';
|
||||||
import { DuplicateController } from 'src/controllers/duplicate.controller';
|
import { DuplicateController } from 'src/controllers/duplicate.controller';
|
||||||
import { FaceController } from 'src/controllers/face.controller';
|
import { FaceController } from 'src/controllers/face.controller';
|
||||||
|
import { IntegrityController } from 'src/controllers/integrity.controller';
|
||||||
import { JobController } from 'src/controllers/job.controller';
|
import { JobController } from 'src/controllers/job.controller';
|
||||||
import { LibraryController } from 'src/controllers/library.controller';
|
import { LibraryController } from 'src/controllers/library.controller';
|
||||||
import { MaintenanceController } from 'src/controllers/maintenance.controller';
|
import { MaintenanceController } from 'src/controllers/maintenance.controller';
|
||||||
@@ -49,6 +50,7 @@ export const controllers = [
|
|||||||
DownloadController,
|
DownloadController,
|
||||||
DuplicateController,
|
DuplicateController,
|
||||||
FaceController,
|
FaceController,
|
||||||
|
IntegrityController,
|
||||||
JobController,
|
JobController,
|
||||||
LibraryController,
|
LibraryController,
|
||||||
MaintenanceController,
|
MaintenanceController,
|
||||||
|
|||||||
90
server/src/controllers/integrity.controller.ts
Normal file
90
server/src/controllers/integrity.controller.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import { Body, Controller, Delete, Get, Next, Param, Post, Res } from '@nestjs/common';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { NextFunction, Response } from 'express';
|
||||||
|
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||||
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
|
import {
|
||||||
|
MaintenanceGetIntegrityReportDto,
|
||||||
|
MaintenanceIntegrityReportResponseDto,
|
||||||
|
MaintenanceIntegrityReportSummaryResponseDto,
|
||||||
|
} from 'src/dtos/maintenance.dto';
|
||||||
|
import { ApiTag, Permission } from 'src/enum';
|
||||||
|
import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
|
||||||
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
import { IntegrityService } from 'src/services/integrity.service';
|
||||||
|
import { sendFile } from 'src/utils/file';
|
||||||
|
import { IntegrityReportTypeParamDto, UUIDParamDto } from 'src/validation';
|
||||||
|
|
||||||
|
@ApiTags(ApiTag.Maintenance)
|
||||||
|
@Controller('admin/integrity')
|
||||||
|
export class IntegrityController {
|
||||||
|
constructor(
|
||||||
|
private logger: LoggingRepository,
|
||||||
|
private service: IntegrityService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get('summary')
|
||||||
|
@Endpoint({
|
||||||
|
summary: 'Get integrity report summary',
|
||||||
|
description: '...',
|
||||||
|
history: new HistoryBuilder().added('v9.9.9').alpha('v9.9.9'),
|
||||||
|
})
|
||||||
|
@Authenticated({ permission: Permission.Maintenance, admin: true })
|
||||||
|
getIntegrityReportSummary(): Promise<MaintenanceIntegrityReportSummaryResponseDto> {
|
||||||
|
return this.service.getIntegrityReportSummary();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('report')
|
||||||
|
@Endpoint({
|
||||||
|
summary: 'Get integrity report by type',
|
||||||
|
description: '...',
|
||||||
|
history: new HistoryBuilder().added('v9.9.9').alpha('v9.9.9'),
|
||||||
|
})
|
||||||
|
@Authenticated({ permission: Permission.Maintenance, admin: true })
|
||||||
|
getIntegrityReport(@Body() dto: MaintenanceGetIntegrityReportDto): Promise<MaintenanceIntegrityReportResponseDto> {
|
||||||
|
return this.service.getIntegrityReport(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete('report/:id')
|
||||||
|
@Endpoint({
|
||||||
|
summary: 'Delete report entry and perform corresponding deletion action',
|
||||||
|
description: '...',
|
||||||
|
history: new HistoryBuilder().added('v9.9.9').alpha('v9.9.9'),
|
||||||
|
})
|
||||||
|
@Authenticated({ permission: Permission.Maintenance, admin: true })
|
||||||
|
async deleteIntegrityReport(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||||
|
await this.service.deleteIntegrityReport(auth, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('report/:type/csv')
|
||||||
|
@Endpoint({
|
||||||
|
summary: 'Export integrity report by type as CSV',
|
||||||
|
description: '...',
|
||||||
|
history: new HistoryBuilder().added('v9.9.9').alpha('v9.9.9'),
|
||||||
|
})
|
||||||
|
@FileResponse()
|
||||||
|
@Authenticated({ permission: Permission.Maintenance, admin: true })
|
||||||
|
getIntegrityReportCsv(@Param() { type }: IntegrityReportTypeParamDto, @Res() res: Response): void {
|
||||||
|
res.setHeader('Content-Type', 'text/csv');
|
||||||
|
res.setHeader('Cache-Control', 'private, no-cache, no-transform');
|
||||||
|
res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(`${Date.now()}-${type}.csv`)}"`);
|
||||||
|
|
||||||
|
this.service.getIntegrityReportCsv(type).pipe(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('report/:id/file')
|
||||||
|
@Endpoint({
|
||||||
|
summary: 'Download the orphan/broken file if one exists',
|
||||||
|
description: '...',
|
||||||
|
history: new HistoryBuilder().added('v9.9.9').alpha('v9.9.9'),
|
||||||
|
})
|
||||||
|
@FileResponse()
|
||||||
|
@Authenticated({ permission: Permission.Maintenance, admin: true })
|
||||||
|
async getIntegrityReportFile(
|
||||||
|
@Param() { id }: UUIDParamDto,
|
||||||
|
@Res() res: Response,
|
||||||
|
@Next() next: NextFunction,
|
||||||
|
): Promise<void> {
|
||||||
|
await sendFile(res, next, () => this.service.getIntegrityReportFile(id), this.logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,32 +1,19 @@
|
|||||||
import { BadRequestException, Body, Controller, Delete, Get, Next, Param, Post, Res } from '@nestjs/common';
|
import { BadRequestException, Body, Controller, Post, Res } from '@nestjs/common';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { NextFunction, Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import {
|
import { MaintenanceAuthDto, MaintenanceLoginDto, SetMaintenanceModeDto } from 'src/dtos/maintenance.dto';
|
||||||
MaintenanceAuthDto,
|
|
||||||
MaintenanceGetIntegrityReportDto,
|
|
||||||
MaintenanceIntegrityReportResponseDto,
|
|
||||||
MaintenanceIntegrityReportSummaryResponseDto,
|
|
||||||
MaintenanceLoginDto,
|
|
||||||
SetMaintenanceModeDto,
|
|
||||||
} from 'src/dtos/maintenance.dto';
|
|
||||||
import { ApiTag, ImmichCookie, MaintenanceAction, Permission } from 'src/enum';
|
import { ApiTag, ImmichCookie, MaintenanceAction, Permission } from 'src/enum';
|
||||||
import { Auth, Authenticated, FileResponse, GetLoginDetails } from 'src/middleware/auth.guard';
|
import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
|
||||||
import { LoginDetails } from 'src/services/auth.service';
|
import { LoginDetails } from 'src/services/auth.service';
|
||||||
import { MaintenanceService } from 'src/services/maintenance.service';
|
import { MaintenanceService } from 'src/services/maintenance.service';
|
||||||
import { sendFile } from 'src/utils/file';
|
|
||||||
import { respondWithCookie } from 'src/utils/response';
|
import { respondWithCookie } from 'src/utils/response';
|
||||||
import { IntegrityReportTypeParamDto, UUIDParamDto } from 'src/validation';
|
|
||||||
|
|
||||||
@ApiTags(ApiTag.Maintenance)
|
@ApiTags(ApiTag.Maintenance)
|
||||||
@Controller('admin/maintenance')
|
@Controller('admin/maintenance')
|
||||||
export class MaintenanceController {
|
export class MaintenanceController {
|
||||||
constructor(
|
constructor(private service: MaintenanceService) {}
|
||||||
private logger: LoggingRepository,
|
|
||||||
private service: MaintenanceService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@Post('login')
|
@Post('login')
|
||||||
@Endpoint({
|
@Endpoint({
|
||||||
@@ -59,69 +46,4 @@ export class MaintenanceController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('integrity/summary')
|
|
||||||
@Endpoint({
|
|
||||||
summary: 'Get integrity report summary',
|
|
||||||
description: '...',
|
|
||||||
history: new HistoryBuilder().added('v9.9.9').alpha('v9.9.9'),
|
|
||||||
})
|
|
||||||
@Authenticated({ permission: Permission.Maintenance, admin: true })
|
|
||||||
getIntegrityReportSummary(): Promise<MaintenanceIntegrityReportSummaryResponseDto> {
|
|
||||||
return this.service.getIntegrityReportSummary();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Post('integrity/report')
|
|
||||||
@Endpoint({
|
|
||||||
summary: 'Get integrity report by type',
|
|
||||||
description: '...',
|
|
||||||
history: new HistoryBuilder().added('v9.9.9').alpha('v9.9.9'),
|
|
||||||
})
|
|
||||||
@Authenticated({ permission: Permission.Maintenance, admin: true })
|
|
||||||
getIntegrityReport(@Body() dto: MaintenanceGetIntegrityReportDto): Promise<MaintenanceIntegrityReportResponseDto> {
|
|
||||||
return this.service.getIntegrityReport(dto);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Delete('integrity/report/:id')
|
|
||||||
@Endpoint({
|
|
||||||
summary: 'Delete report entry and perform corresponding deletion action',
|
|
||||||
description: '...',
|
|
||||||
history: new HistoryBuilder().added('v9.9.9').alpha('v9.9.9'),
|
|
||||||
})
|
|
||||||
@Authenticated({ permission: Permission.Maintenance, admin: true })
|
|
||||||
async deleteIntegrityReport(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
|
||||||
await this.service.deleteIntegrityReport(auth, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get('integrity/report/:type/csv')
|
|
||||||
@Endpoint({
|
|
||||||
summary: 'Export integrity report by type as CSV',
|
|
||||||
description: '...',
|
|
||||||
history: new HistoryBuilder().added('v9.9.9').alpha('v9.9.9'),
|
|
||||||
})
|
|
||||||
@FileResponse()
|
|
||||||
@Authenticated({ permission: Permission.Maintenance, admin: true })
|
|
||||||
getIntegrityReportCsv(@Param() { type }: IntegrityReportTypeParamDto, @Res() res: Response): void {
|
|
||||||
res.setHeader('Content-Type', 'text/csv');
|
|
||||||
res.setHeader('Cache-Control', 'private, no-cache, no-transform');
|
|
||||||
res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(`${Date.now()}-${type}.csv`)}"`);
|
|
||||||
|
|
||||||
this.service.getIntegrityReportCsv(type).pipe(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get('integrity/report/:id/file')
|
|
||||||
@Endpoint({
|
|
||||||
summary: 'Download the orphan/broken file if one exists',
|
|
||||||
description: '...',
|
|
||||||
history: new HistoryBuilder().added('v9.9.9').alpha('v9.9.9'),
|
|
||||||
})
|
|
||||||
@FileResponse()
|
|
||||||
@Authenticated({ permission: Permission.Maintenance, admin: true })
|
|
||||||
async getIntegrityReportFile(
|
|
||||||
@Param() { id }: UUIDParamDto,
|
|
||||||
@Res() res: Response,
|
|
||||||
@Next() next: NextFunction,
|
|
||||||
): Promise<void> {
|
|
||||||
await sendFile(res, next, () => this.service.getIntegrityReportFile(id), this.logger);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -863,6 +863,7 @@ export enum ApiTag {
|
|||||||
Download = 'Download',
|
Download = 'Download',
|
||||||
Duplicates = 'Duplicates',
|
Duplicates = 'Duplicates',
|
||||||
Faces = 'Faces',
|
Faces = 'Faces',
|
||||||
|
Integrity = 'Integrity (admin)',
|
||||||
Jobs = 'Jobs',
|
Jobs = 'Jobs',
|
||||||
Libraries = 'Libraries',
|
Libraries = 'Libraries',
|
||||||
Maintenance = 'Maintenance (admin)',
|
Maintenance = 'Maintenance (admin)',
|
||||||
|
|||||||
22
server/src/services/integrity.service.spec.ts
Normal file
22
server/src/services/integrity.service.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { IntegrityService } from 'src/services/integrity.service';
|
||||||
|
import { newTestService, ServiceMocks } from 'test/utils';
|
||||||
|
|
||||||
|
describe(IntegrityService.name, () => {
|
||||||
|
let sut: IntegrityService;
|
||||||
|
let mocks: ServiceMocks;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
({ sut, mocks } = newTestService(IntegrityService));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work', () => {
|
||||||
|
expect(sut).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe.skip('getIntegrityReportSummary'); // just calls repository
|
||||||
|
describe.skip('getIntegrityReport'); // just calls repository
|
||||||
|
describe.skip('getIntegrityReportCsv'); // just calls repository
|
||||||
|
|
||||||
|
describe.todo('getIntegrityReportFile');
|
||||||
|
describe.todo('deleteIntegrityReport');
|
||||||
|
});
|
||||||
@@ -2,13 +2,21 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { createHash } from 'node:crypto';
|
import { createHash } from 'node:crypto';
|
||||||
import { createReadStream } from 'node:fs';
|
import { createReadStream } from 'node:fs';
|
||||||
import { stat } from 'node:fs/promises';
|
import { stat } from 'node:fs/promises';
|
||||||
import { Writable } from 'node:stream';
|
import { basename } from 'node:path';
|
||||||
|
import { Readable, Writable } from 'node:stream';
|
||||||
import { pipeline } from 'node:stream/promises';
|
import { pipeline } from 'node:stream/promises';
|
||||||
import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants';
|
import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants';
|
||||||
import { StorageCore } from 'src/cores/storage.core';
|
import { StorageCore } from 'src/cores/storage.core';
|
||||||
import { OnEvent, OnJob } from 'src/decorators';
|
import { OnEvent, OnJob } from 'src/decorators';
|
||||||
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
|
import {
|
||||||
|
MaintenanceGetIntegrityReportDto,
|
||||||
|
MaintenanceIntegrityReportResponseDto,
|
||||||
|
MaintenanceIntegrityReportSummaryResponseDto,
|
||||||
|
} from 'src/dtos/maintenance.dto';
|
||||||
import {
|
import {
|
||||||
AssetStatus,
|
AssetStatus,
|
||||||
|
CacheControl,
|
||||||
DatabaseLock,
|
DatabaseLock,
|
||||||
ImmichWorker,
|
ImmichWorker,
|
||||||
IntegrityReportType,
|
IntegrityReportType,
|
||||||
@@ -28,6 +36,7 @@ import {
|
|||||||
IIntegrityPathWithChecksumJob,
|
IIntegrityPathWithChecksumJob,
|
||||||
IIntegrityPathWithReportJob,
|
IIntegrityPathWithReportJob,
|
||||||
} from 'src/types';
|
} from 'src/types';
|
||||||
|
import { ImmichFileResponse } from 'src/utils/file';
|
||||||
import { handlePromiseError } from 'src/utils/misc';
|
import { handlePromiseError } from 'src/utils/misc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,6 +154,54 @@ export class IntegrityService extends BaseService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getIntegrityReportSummary(): Promise<MaintenanceIntegrityReportSummaryResponseDto> {
|
||||||
|
return this.integrityRepository.getIntegrityReportSummary();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getIntegrityReport(dto: MaintenanceGetIntegrityReportDto): Promise<MaintenanceIntegrityReportResponseDto> {
|
||||||
|
return {
|
||||||
|
items: await this.integrityRepository.getIntegrityReports(dto.type),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getIntegrityReportCsv(type: IntegrityReportType): Readable {
|
||||||
|
return this.integrityRepository.streamIntegrityReportsCSV(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getIntegrityReportFile(id: string): Promise<ImmichFileResponse> {
|
||||||
|
const { path } = await this.integrityRepository.getById(id);
|
||||||
|
|
||||||
|
return new ImmichFileResponse({
|
||||||
|
path,
|
||||||
|
fileName: basename(path),
|
||||||
|
contentType: 'application/octet-stream',
|
||||||
|
cacheControl: CacheControl.PrivateWithoutCache,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteIntegrityReport(auth: AuthDto, id: string): Promise<void> {
|
||||||
|
const { path, assetId, fileAssetId } = await this.integrityRepository.getById(id);
|
||||||
|
|
||||||
|
if (assetId) {
|
||||||
|
await this.assetRepository.updateAll([assetId], {
|
||||||
|
deletedAt: new Date(),
|
||||||
|
status: AssetStatus.Trashed,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.eventRepository.emit('AssetTrashAll', {
|
||||||
|
assetIds: [assetId],
|
||||||
|
userId: auth.user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.integrityRepository.deleteById(id);
|
||||||
|
} else if (fileAssetId) {
|
||||||
|
await this.assetRepository.deleteFiles([{ id: fileAssetId }]);
|
||||||
|
} else {
|
||||||
|
await this.storageRepository.unlink(path);
|
||||||
|
await this.integrityRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OnJob({ name: JobName.IntegrityOrphanedFilesQueueAll, queue: QueueName.IntegrityCheck })
|
@OnJob({ name: JobName.IntegrityOrphanedFilesQueueAll, queue: QueueName.IntegrityCheck })
|
||||||
async handleOrphanedFilesQueueAll({ refreshOnly }: IIntegrityJob = {}): Promise<JobStatus> {
|
async handleOrphanedFilesQueueAll({ refreshOnly }: IIntegrityJob = {}): Promise<JobStatus> {
|
||||||
this.logger.log(`Checking for out of date orphaned file reports...`);
|
this.logger.log(`Checking for out of date orphaned file reports...`);
|
||||||
|
|||||||
@@ -106,7 +106,4 @@ describe(MaintenanceService.name, () => {
|
|||||||
expect(mocks.systemMetadata.get).toHaveBeenCalledTimes(1);
|
expect(mocks.systemMetadata.get).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.skip('getIntegrityReportSummary'); // just calls repository
|
|
||||||
describe.skip('getIntegrityReport'); // just calls repository
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,18 +1,9 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { basename } from 'node:path';
|
|
||||||
import { Readable } from 'node:stream';
|
|
||||||
import { OnEvent } from 'src/decorators';
|
import { OnEvent } from 'src/decorators';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { MaintenanceAuthDto } from 'src/dtos/maintenance.dto';
|
||||||
import {
|
import { SystemMetadataKey } from 'src/enum';
|
||||||
MaintenanceAuthDto,
|
|
||||||
MaintenanceGetIntegrityReportDto,
|
|
||||||
MaintenanceIntegrityReportResponseDto,
|
|
||||||
MaintenanceIntegrityReportSummaryResponseDto,
|
|
||||||
} from 'src/dtos/maintenance.dto';
|
|
||||||
import { AssetStatus, CacheControl, IntegrityReportType, SystemMetadataKey } from 'src/enum';
|
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { MaintenanceModeState } from 'src/types';
|
import { MaintenanceModeState } from 'src/types';
|
||||||
import { ImmichFileResponse } from 'src/utils/file';
|
|
||||||
import { createMaintenanceLoginUrl, generateMaintenanceSecret, signMaintenanceJwt } from 'src/utils/maintenance';
|
import { createMaintenanceLoginUrl, generateMaintenanceSecret, signMaintenanceJwt } from 'src/utils/maintenance';
|
||||||
import { getExternalDomain } from 'src/utils/misc';
|
import { getExternalDomain } from 'src/utils/misc';
|
||||||
|
|
||||||
@@ -59,52 +50,4 @@ export class MaintenanceService extends BaseService {
|
|||||||
|
|
||||||
return await createMaintenanceLoginUrl(baseUrl, auth, secret);
|
return await createMaintenanceLoginUrl(baseUrl, auth, secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
getIntegrityReportSummary(): Promise<MaintenanceIntegrityReportSummaryResponseDto> {
|
|
||||||
return this.integrityRepository.getIntegrityReportSummary();
|
|
||||||
}
|
|
||||||
|
|
||||||
async getIntegrityReport(dto: MaintenanceGetIntegrityReportDto): Promise<MaintenanceIntegrityReportResponseDto> {
|
|
||||||
return {
|
|
||||||
items: await this.integrityRepository.getIntegrityReports(dto.type),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getIntegrityReportCsv(type: IntegrityReportType): Readable {
|
|
||||||
return this.integrityRepository.streamIntegrityReportsCSV(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getIntegrityReportFile(id: string): Promise<ImmichFileResponse> {
|
|
||||||
const { path } = await this.integrityRepository.getById(id);
|
|
||||||
|
|
||||||
return new ImmichFileResponse({
|
|
||||||
path,
|
|
||||||
fileName: basename(path),
|
|
||||||
contentType: 'application/octet-stream',
|
|
||||||
cacheControl: CacheControl.PrivateWithoutCache,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteIntegrityReport(auth: AuthDto, id: string): Promise<void> {
|
|
||||||
const { path, assetId, fileAssetId } = await this.integrityRepository.getById(id);
|
|
||||||
|
|
||||||
if (assetId) {
|
|
||||||
await this.assetRepository.updateAll([assetId], {
|
|
||||||
deletedAt: new Date(),
|
|
||||||
status: AssetStatus.Trashed,
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.eventRepository.emit('AssetTrashAll', {
|
|
||||||
assetIds: [assetId],
|
|
||||||
userId: auth.user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.integrityRepository.deleteById(id);
|
|
||||||
} else if (fileAssetId) {
|
|
||||||
await this.assetRepository.deleteFiles([{ id: fileAssetId }]);
|
|
||||||
} else {
|
|
||||||
await this.storageRepository.unlink(path);
|
|
||||||
await this.integrityRepository.deleteById(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user