mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	feat(web,server): offline/untracked files admin tool (#4447)
* feat: admin repair orphans tool * chore: open api * fix: include upload folder * fix: bugs * feat: empty placeholder * fix: checks * feat: move buttons to top of page * feat: styling and clipboard * styling * better clicking hitbox * fix: show title on hover * feat: download report * restrict file access to immich related files * Add description --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com> Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
This commit is contained in:
		
							
								
								
									
										378
									
								
								cli/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										378
									
								
								cli/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							| @@ -1604,6 +1604,109 @@ export interface ExifResponseDto { | ||||
|      */ | ||||
|     'timeZone'?: string | null; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface FileChecksumDto | ||||
|  */ | ||||
| export interface FileChecksumDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {Array<string>} | ||||
|      * @memberof FileChecksumDto | ||||
|      */ | ||||
|     'filenames': Array<string>; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface FileChecksumResponseDto | ||||
|  */ | ||||
| export interface FileChecksumResponseDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof FileChecksumResponseDto | ||||
|      */ | ||||
|     'checksum': string; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof FileChecksumResponseDto | ||||
|      */ | ||||
|     'filename': string; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface FileReportDto | ||||
|  */ | ||||
| export interface FileReportDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {Array<string>} | ||||
|      * @memberof FileReportDto | ||||
|      */ | ||||
|     'extras': Array<string>; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {Array<FileReportItemDto>} | ||||
|      * @memberof FileReportDto | ||||
|      */ | ||||
|     'orphans': Array<FileReportItemDto>; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface FileReportFixDto | ||||
|  */ | ||||
| export interface FileReportFixDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {Array<FileReportItemDto>} | ||||
|      * @memberof FileReportFixDto | ||||
|      */ | ||||
|     'items': Array<FileReportItemDto>; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface FileReportItemDto | ||||
|  */ | ||||
| export interface FileReportItemDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof FileReportItemDto | ||||
|      */ | ||||
|     'checksum'?: string; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof FileReportItemDto | ||||
|      */ | ||||
|     'entityId': string; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {PathEntityType} | ||||
|      * @memberof FileReportItemDto | ||||
|      */ | ||||
|     'entityType': PathEntityType; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {PathType} | ||||
|      * @memberof FileReportItemDto | ||||
|      */ | ||||
|     'pathType': PathType; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof FileReportItemDto | ||||
|      */ | ||||
|     'pathValue': string; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
| @@ -2186,6 +2289,40 @@ export interface OAuthConfigResponseDto { | ||||
|      */ | ||||
|     'url'?: string; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @enum {string} | ||||
|  */ | ||||
| 
 | ||||
| export const PathEntityType = { | ||||
|     Asset: 'asset', | ||||
|     Person: 'person', | ||||
|     User: 'user' | ||||
| } as const; | ||||
| 
 | ||||
| export type PathEntityType = typeof PathEntityType[keyof typeof PathEntityType]; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @enum {string} | ||||
|  */ | ||||
| 
 | ||||
| export const PathType = { | ||||
|     Original: 'original', | ||||
|     JpegThumbnail: 'jpeg_thumbnail', | ||||
|     WebpThumbnail: 'webp_thumbnail', | ||||
|     EncodedVideo: 'encoded_video', | ||||
|     Sidecar: 'sidecar', | ||||
|     Face: 'face', | ||||
|     Profile: 'profile' | ||||
| } as const; | ||||
| 
 | ||||
| export type PathType = typeof PathType[keyof typeof PathType]; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
| @@ -8821,6 +8958,50 @@ export class AssetApi extends BaseAPI { | ||||
|  */ | ||||
| export const AuditApiAxiosParamCreator = function (configuration?: Configuration) { | ||||
|     return { | ||||
|         /** | ||||
|          *  | ||||
|          * @param {FileReportFixDto} fileReportFixDto  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         fixAuditFiles: async (fileReportFixDto: FileReportFixDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             // verify required parameter 'fileReportFixDto' is not null or undefined
 | ||||
|             assertParamExists('fixAuditFiles', 'fileReportFixDto', fileReportFixDto) | ||||
|             const localVarPath = `/audit/file-report/fix`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
|             if (configuration) { | ||||
|                 baseOptions = configuration.baseOptions; | ||||
|             } | ||||
| 
 | ||||
|             const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; | ||||
|             const localVarHeaderParameter = {} as any; | ||||
|             const localVarQueryParameter = {} as any; | ||||
| 
 | ||||
|             // authentication cookie required
 | ||||
| 
 | ||||
|             // authentication api_key required
 | ||||
|             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||
| 
 | ||||
|             // authentication bearer required
 | ||||
|             // http bearer authentication required
 | ||||
|             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             localVarHeaderParameter['Content-Type'] = 'application/json'; | ||||
| 
 | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
|             localVarRequestOptions.data = serializeDataIfNeeded(fileReportFixDto, localVarRequestOptions, configuration) | ||||
| 
 | ||||
|             return { | ||||
|                 url: toPathString(localVarUrlObj), | ||||
|                 options: localVarRequestOptions, | ||||
|             }; | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {EntityType} entityType  | ||||
| @@ -8875,6 +9056,88 @@ export const AuditApiAxiosParamCreator = function (configuration?: Configuration | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
| 
 | ||||
|             return { | ||||
|                 url: toPathString(localVarUrlObj), | ||||
|                 options: localVarRequestOptions, | ||||
|             }; | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAuditFiles: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/audit/file-report`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
|             if (configuration) { | ||||
|                 baseOptions = configuration.baseOptions; | ||||
|             } | ||||
| 
 | ||||
|             const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; | ||||
|             const localVarHeaderParameter = {} as any; | ||||
|             const localVarQueryParameter = {} as any; | ||||
| 
 | ||||
|             // authentication cookie required
 | ||||
| 
 | ||||
|             // authentication api_key required
 | ||||
|             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||
| 
 | ||||
|             // authentication bearer required
 | ||||
|             // http bearer authentication required
 | ||||
|             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
| 
 | ||||
|             return { | ||||
|                 url: toPathString(localVarUrlObj), | ||||
|                 options: localVarRequestOptions, | ||||
|             }; | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {FileChecksumDto} fileChecksumDto  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getFileChecksums: async (fileChecksumDto: FileChecksumDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             // verify required parameter 'fileChecksumDto' is not null or undefined
 | ||||
|             assertParamExists('getFileChecksums', 'fileChecksumDto', fileChecksumDto) | ||||
|             const localVarPath = `/audit/file-report/checksum`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
|             if (configuration) { | ||||
|                 baseOptions = configuration.baseOptions; | ||||
|             } | ||||
| 
 | ||||
|             const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; | ||||
|             const localVarHeaderParameter = {} as any; | ||||
|             const localVarQueryParameter = {} as any; | ||||
| 
 | ||||
|             // authentication cookie required
 | ||||
| 
 | ||||
|             // authentication api_key required
 | ||||
|             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||
| 
 | ||||
|             // authentication bearer required
 | ||||
|             // http bearer authentication required
 | ||||
|             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             localVarHeaderParameter['Content-Type'] = 'application/json'; | ||||
| 
 | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
|             localVarRequestOptions.data = serializeDataIfNeeded(fileChecksumDto, localVarRequestOptions, configuration) | ||||
| 
 | ||||
|             return { | ||||
|                 url: toPathString(localVarUrlObj), | ||||
|                 options: localVarRequestOptions, | ||||
| @@ -8890,6 +9153,16 @@ export const AuditApiAxiosParamCreator = function (configuration?: Configuration | ||||
| export const AuditApiFp = function(configuration?: Configuration) { | ||||
|     const localVarAxiosParamCreator = AuditApiAxiosParamCreator(configuration) | ||||
|     return { | ||||
|         /** | ||||
|          *  | ||||
|          * @param {FileReportFixDto} fileReportFixDto  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async fixAuditFiles(fileReportFixDto: FileReportFixDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.fixAuditFiles(fileReportFixDto, options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {EntityType} entityType  | ||||
| @@ -8902,6 +9175,25 @@ export const AuditApiFp = function(configuration?: Configuration) { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAuditDeletes(entityType, after, userId, options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async getAuditFiles(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<FileReportDto>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAuditFiles(options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {FileChecksumDto} fileChecksumDto  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async getFileChecksums(fileChecksumDto: FileChecksumDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<FileChecksumResponseDto>>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getFileChecksums(fileChecksumDto, options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| @@ -8912,6 +9204,15 @@ export const AuditApiFp = function(configuration?: Configuration) { | ||||
| export const AuditApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { | ||||
|     const localVarFp = AuditApiFp(configuration) | ||||
|     return { | ||||
|         /** | ||||
|          *  | ||||
|          * @param {AuditApiFixAuditFilesRequest} requestParameters Request parameters. | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         fixAuditFiles(requestParameters: AuditApiFixAuditFilesRequest, options?: AxiosRequestConfig): AxiosPromise<void> { | ||||
|             return localVarFp.fixAuditFiles(requestParameters.fileReportFixDto, options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {AuditApiGetAuditDeletesRequest} requestParameters Request parameters. | ||||
| @@ -8921,9 +9222,40 @@ export const AuditApiFactory = function (configuration?: Configuration, basePath | ||||
|         getAuditDeletes(requestParameters: AuditApiGetAuditDeletesRequest, options?: AxiosRequestConfig): AxiosPromise<AuditDeletesResponseDto> { | ||||
|             return localVarFp.getAuditDeletes(requestParameters.entityType, requestParameters.after, requestParameters.userId, options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAuditFiles(options?: AxiosRequestConfig): AxiosPromise<FileReportDto> { | ||||
|             return localVarFp.getAuditFiles(options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {AuditApiGetFileChecksumsRequest} requestParameters Request parameters. | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getFileChecksums(requestParameters: AuditApiGetFileChecksumsRequest, options?: AxiosRequestConfig): AxiosPromise<Array<FileChecksumResponseDto>> { | ||||
|             return localVarFp.getFileChecksums(requestParameters.fileChecksumDto, options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Request parameters for fixAuditFiles operation in AuditApi. | ||||
|  * @export | ||||
|  * @interface AuditApiFixAuditFilesRequest | ||||
|  */ | ||||
| export interface AuditApiFixAuditFilesRequest { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {FileReportFixDto} | ||||
|      * @memberof AuditApiFixAuditFiles | ||||
|      */ | ||||
|     readonly fileReportFixDto: FileReportFixDto | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Request parameters for getAuditDeletes operation in AuditApi. | ||||
|  * @export | ||||
| @@ -8952,6 +9284,20 @@ export interface AuditApiGetAuditDeletesRequest { | ||||
|     readonly userId?: string | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Request parameters for getFileChecksums operation in AuditApi. | ||||
|  * @export | ||||
|  * @interface AuditApiGetFileChecksumsRequest | ||||
|  */ | ||||
| export interface AuditApiGetFileChecksumsRequest { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {FileChecksumDto} | ||||
|      * @memberof AuditApiGetFileChecksums | ||||
|      */ | ||||
|     readonly fileChecksumDto: FileChecksumDto | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * AuditApi - object-oriented interface | ||||
|  * @export | ||||
| @@ -8959,6 +9305,17 @@ export interface AuditApiGetAuditDeletesRequest { | ||||
|  * @extends {BaseAPI} | ||||
|  */ | ||||
| export class AuditApi extends BaseAPI { | ||||
|     /** | ||||
|      *  | ||||
|      * @param {AuditApiFixAuditFilesRequest} requestParameters Request parameters. | ||||
|      * @param {*} [options] Override http request option. | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AuditApi | ||||
|      */ | ||||
|     public fixAuditFiles(requestParameters: AuditApiFixAuditFilesRequest, options?: AxiosRequestConfig) { | ||||
|         return AuditApiFp(this.configuration).fixAuditFiles(requestParameters.fileReportFixDto, options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @param {AuditApiGetAuditDeletesRequest} requestParameters Request parameters. | ||||
| @@ -8969,6 +9326,27 @@ export class AuditApi extends BaseAPI { | ||||
|     public getAuditDeletes(requestParameters: AuditApiGetAuditDeletesRequest, options?: AxiosRequestConfig) { | ||||
|         return AuditApiFp(this.configuration).getAuditDeletes(requestParameters.entityType, requestParameters.after, requestParameters.userId, options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @param {*} [options] Override http request option. | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AuditApi | ||||
|      */ | ||||
|     public getAuditFiles(options?: AxiosRequestConfig) { | ||||
|         return AuditApiFp(this.configuration).getAuditFiles(options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @param {AuditApiGetFileChecksumsRequest} requestParameters Request parameters. | ||||
|      * @param {*} [options] Override http request option. | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AuditApi | ||||
|      */ | ||||
|     public getFileChecksums(requestParameters: AuditApiGetFileChecksumsRequest, options?: AxiosRequestConfig) { | ||||
|         return AuditApiFp(this.configuration).getFileChecksums(requestParameters.fileChecksumDto, options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|   | ||||
							
								
								
									
										21
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							| @@ -59,6 +59,11 @@ doc/DownloadInfoDto.md | ||||
| doc/DownloadResponseDto.md | ||||
| doc/EntityType.md | ||||
| doc/ExifResponseDto.md | ||||
| doc/FileChecksumDto.md | ||||
| doc/FileChecksumResponseDto.md | ||||
| doc/FileReportDto.md | ||||
| doc/FileReportFixDto.md | ||||
| doc/FileReportItemDto.md | ||||
| doc/ImportAssetDto.md | ||||
| doc/JobApi.md | ||||
| doc/JobCommand.md | ||||
| @@ -84,6 +89,8 @@ doc/OAuthCallbackDto.md | ||||
| doc/OAuthConfigDto.md | ||||
| doc/OAuthConfigResponseDto.md | ||||
| doc/PartnerApi.md | ||||
| doc/PathEntityType.md | ||||
| doc/PathType.md | ||||
| doc/PeopleResponseDto.md | ||||
| doc/PeopleUpdateDto.md | ||||
| doc/PeopleUpdateItem.md | ||||
| @@ -227,6 +234,11 @@ lib/model/download_info_dto.dart | ||||
| lib/model/download_response_dto.dart | ||||
| lib/model/entity_type.dart | ||||
| lib/model/exif_response_dto.dart | ||||
| lib/model/file_checksum_dto.dart | ||||
| lib/model/file_checksum_response_dto.dart | ||||
| lib/model/file_report_dto.dart | ||||
| lib/model/file_report_fix_dto.dart | ||||
| lib/model/file_report_item_dto.dart | ||||
| lib/model/import_asset_dto.dart | ||||
| lib/model/job_command.dart | ||||
| lib/model/job_command_dto.dart | ||||
| @@ -248,6 +260,8 @@ lib/model/o_auth_authorize_response_dto.dart | ||||
| lib/model/o_auth_callback_dto.dart | ||||
| lib/model/o_auth_config_dto.dart | ||||
| lib/model/o_auth_config_response_dto.dart | ||||
| lib/model/path_entity_type.dart | ||||
| lib/model/path_type.dart | ||||
| lib/model/people_response_dto.dart | ||||
| lib/model/people_update_dto.dart | ||||
| lib/model/people_update_item.dart | ||||
| @@ -364,6 +378,11 @@ test/download_info_dto_test.dart | ||||
| test/download_response_dto_test.dart | ||||
| test/entity_type_test.dart | ||||
| test/exif_response_dto_test.dart | ||||
| test/file_checksum_dto_test.dart | ||||
| test/file_checksum_response_dto_test.dart | ||||
| test/file_report_dto_test.dart | ||||
| test/file_report_fix_dto_test.dart | ||||
| test/file_report_item_dto_test.dart | ||||
| test/import_asset_dto_test.dart | ||||
| test/job_api_test.dart | ||||
| test/job_command_dto_test.dart | ||||
| @@ -389,6 +408,8 @@ test/o_auth_callback_dto_test.dart | ||||
| test/o_auth_config_dto_test.dart | ||||
| test/o_auth_config_response_dto_test.dart | ||||
| test/partner_api_test.dart | ||||
| test/path_entity_type_test.dart | ||||
| test/path_type_test.dart | ||||
| test/people_response_dto_test.dart | ||||
| test/people_update_dto_test.dart | ||||
| test/people_update_item_test.dart | ||||
|   | ||||
							
								
								
									
										10
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							| @@ -117,7 +117,10 @@ Class | Method | HTTP request | Description | ||||
| *AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{id} |  | ||||
| *AssetApi* | [**updateAssets**](doc//AssetApi.md#updateassets) | **PUT** /asset |  | ||||
| *AssetApi* | [**uploadFile**](doc//AssetApi.md#uploadfile) | **POST** /asset/upload |  | ||||
| *AuditApi* | [**fixAuditFiles**](doc//AuditApi.md#fixauditfiles) | **POST** /audit/file-report/fix |  | ||||
| *AuditApi* | [**getAuditDeletes**](doc//AuditApi.md#getauditdeletes) | **GET** /audit/deletes |  | ||||
| *AuditApi* | [**getAuditFiles**](doc//AuditApi.md#getauditfiles) | **GET** /audit/file-report |  | ||||
| *AuditApi* | [**getFileChecksums**](doc//AuditApi.md#getfilechecksums) | **POST** /audit/file-report/checksum |  | ||||
| *AuthenticationApi* | [**adminSignUp**](doc//AuthenticationApi.md#adminsignup) | **POST** /auth/admin-sign-up |  | ||||
| *AuthenticationApi* | [**changePassword**](doc//AuthenticationApi.md#changepassword) | **POST** /auth/change-password |  | ||||
| *AuthenticationApi* | [**getAuthDevices**](doc//AuthenticationApi.md#getauthdevices) | **GET** /auth/devices |  | ||||
| @@ -247,6 +250,11 @@ Class | Method | HTTP request | Description | ||||
|  - [DownloadResponseDto](doc//DownloadResponseDto.md) | ||||
|  - [EntityType](doc//EntityType.md) | ||||
|  - [ExifResponseDto](doc//ExifResponseDto.md) | ||||
|  - [FileChecksumDto](doc//FileChecksumDto.md) | ||||
|  - [FileChecksumResponseDto](doc//FileChecksumResponseDto.md) | ||||
|  - [FileReportDto](doc//FileReportDto.md) | ||||
|  - [FileReportFixDto](doc//FileReportFixDto.md) | ||||
|  - [FileReportItemDto](doc//FileReportItemDto.md) | ||||
|  - [ImportAssetDto](doc//ImportAssetDto.md) | ||||
|  - [JobCommand](doc//JobCommand.md) | ||||
|  - [JobCommandDto](doc//JobCommandDto.md) | ||||
| @@ -268,6 +276,8 @@ Class | Method | HTTP request | Description | ||||
|  - [OAuthCallbackDto](doc//OAuthCallbackDto.md) | ||||
|  - [OAuthConfigDto](doc//OAuthConfigDto.md) | ||||
|  - [OAuthConfigResponseDto](doc//OAuthConfigResponseDto.md) | ||||
|  - [PathEntityType](doc//PathEntityType.md) | ||||
|  - [PathType](doc//PathType.md) | ||||
|  - [PeopleResponseDto](doc//PeopleResponseDto.md) | ||||
|  - [PeopleUpdateDto](doc//PeopleUpdateDto.md) | ||||
|  - [PeopleUpdateItem](doc//PeopleUpdateItem.md) | ||||
|   | ||||
							
								
								
									
										163
									
								
								mobile/openapi/doc/AuditApi.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										163
									
								
								mobile/openapi/doc/AuditApi.md
									
									
									
										generated
									
									
									
								
							| @@ -9,9 +9,66 @@ All URIs are relative to */api* | ||||
| 
 | ||||
| Method | HTTP request | Description | ||||
| ------------- | ------------- | ------------- | ||||
| [**fixAuditFiles**](AuditApi.md#fixauditfiles) | **POST** /audit/file-report/fix |  | ||||
| [**getAuditDeletes**](AuditApi.md#getauditdeletes) | **GET** /audit/deletes |  | ||||
| [**getAuditFiles**](AuditApi.md#getauditfiles) | **GET** /audit/file-report |  | ||||
| [**getFileChecksums**](AuditApi.md#getfilechecksums) | **POST** /audit/file-report/checksum |  | ||||
| 
 | ||||
| 
 | ||||
| # **fixAuditFiles** | ||||
| > fixAuditFiles(fileReportFixDto) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### Example | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| // TODO Configure API key authorization: cookie | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure API key authorization: api_key | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure HTTP Bearer authorization: bearer | ||||
| // Case 1. Use String Token | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); | ||||
| // Case 2. Use Function which generate token. | ||||
| // String yourTokenGeneratorFunction() { ... } | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||
| 
 | ||||
| final api_instance = AuditApi(); | ||||
| final fileReportFixDto = FileReportFixDto(); // FileReportFixDto |  | ||||
| 
 | ||||
| try { | ||||
|     api_instance.fixAuditFiles(fileReportFixDto); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AuditApi->fixAuditFiles: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| 
 | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **fileReportFixDto** | [**FileReportFixDto**](FileReportFixDto.md)|  |  | ||||
| 
 | ||||
| ### Return type | ||||
| 
 | ||||
| void (empty response body) | ||||
| 
 | ||||
| ### Authorization | ||||
| 
 | ||||
| [cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) | ||||
| 
 | ||||
| ### HTTP request headers | ||||
| 
 | ||||
|  - **Content-Type**: application/json | ||||
|  - **Accept**: Not defined | ||||
| 
 | ||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||
| 
 | ||||
| # **getAuditDeletes** | ||||
| > AuditDeletesResponseDto getAuditDeletes(entityType, after, userId) | ||||
| 
 | ||||
| @@ -71,3 +128,109 @@ Name | Type | Description  | Notes | ||||
| 
 | ||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||
| 
 | ||||
| # **getAuditFiles** | ||||
| > FileReportDto getAuditFiles() | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### Example | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| // TODO Configure API key authorization: cookie | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure API key authorization: api_key | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure HTTP Bearer authorization: bearer | ||||
| // Case 1. Use String Token | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); | ||||
| // Case 2. Use Function which generate token. | ||||
| // String yourTokenGeneratorFunction() { ... } | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||
| 
 | ||||
| final api_instance = AuditApi(); | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.getAuditFiles(); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AuditApi->getAuditFiles: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| This endpoint does not need any parameter. | ||||
| 
 | ||||
| ### Return type | ||||
| 
 | ||||
| [**FileReportDto**](FileReportDto.md) | ||||
| 
 | ||||
| ### Authorization | ||||
| 
 | ||||
| [cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) | ||||
| 
 | ||||
| ### HTTP request headers | ||||
| 
 | ||||
|  - **Content-Type**: Not defined | ||||
|  - **Accept**: application/json | ||||
| 
 | ||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||
| 
 | ||||
| # **getFileChecksums** | ||||
| > List<FileChecksumResponseDto> getFileChecksums(fileChecksumDto) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### Example | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| // TODO Configure API key authorization: cookie | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure API key authorization: api_key | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure HTTP Bearer authorization: bearer | ||||
| // Case 1. Use String Token | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); | ||||
| // Case 2. Use Function which generate token. | ||||
| // String yourTokenGeneratorFunction() { ... } | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||
| 
 | ||||
| final api_instance = AuditApi(); | ||||
| final fileChecksumDto = FileChecksumDto(); // FileChecksumDto |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.getFileChecksums(fileChecksumDto); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AuditApi->getFileChecksums: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| 
 | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **fileChecksumDto** | [**FileChecksumDto**](FileChecksumDto.md)|  |  | ||||
| 
 | ||||
| ### Return type | ||||
| 
 | ||||
| [**List<FileChecksumResponseDto>**](FileChecksumResponseDto.md) | ||||
| 
 | ||||
| ### Authorization | ||||
| 
 | ||||
| [cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) | ||||
| 
 | ||||
| ### HTTP request headers | ||||
| 
 | ||||
|  - **Content-Type**: application/json | ||||
|  - **Accept**: application/json | ||||
| 
 | ||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||
| 
 | ||||
|   | ||||
							
								
								
									
										15
									
								
								mobile/openapi/doc/FileChecksumDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								mobile/openapi/doc/FileChecksumDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| # openapi.model.FileChecksumDto | ||||
| 
 | ||||
| ## Load the model package | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| ``` | ||||
| 
 | ||||
| ## Properties | ||||
| Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| **filenames** | **List<String>** |  | [default to const []] | ||||
| 
 | ||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										16
									
								
								mobile/openapi/doc/FileChecksumResponseDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								mobile/openapi/doc/FileChecksumResponseDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| # openapi.model.FileChecksumResponseDto | ||||
| 
 | ||||
| ## Load the model package | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| ``` | ||||
| 
 | ||||
| ## Properties | ||||
| Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| **checksum** | **String** |  |  | ||||
| **filename** | **String** |  |  | ||||
| 
 | ||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										16
									
								
								mobile/openapi/doc/FileReportDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								mobile/openapi/doc/FileReportDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| # openapi.model.FileReportDto | ||||
| 
 | ||||
| ## Load the model package | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| ``` | ||||
| 
 | ||||
| ## Properties | ||||
| Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| **extras** | **List<String>** |  | [default to const []] | ||||
| **orphans** | [**List<FileReportItemDto>**](FileReportItemDto.md) |  | [default to const []] | ||||
| 
 | ||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										15
									
								
								mobile/openapi/doc/FileReportFixDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								mobile/openapi/doc/FileReportFixDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| # openapi.model.FileReportFixDto | ||||
| 
 | ||||
| ## Load the model package | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| ``` | ||||
| 
 | ||||
| ## Properties | ||||
| Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| **items** | [**List<FileReportItemDto>**](FileReportItemDto.md) |  | [default to const []] | ||||
| 
 | ||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										19
									
								
								mobile/openapi/doc/FileReportItemDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								mobile/openapi/doc/FileReportItemDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| # openapi.model.FileReportItemDto | ||||
| 
 | ||||
| ## Load the model package | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| ``` | ||||
| 
 | ||||
| ## Properties | ||||
| Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| **checksum** | **String** |  | [optional]  | ||||
| **entityId** | **String** |  |  | ||||
| **entityType** | [**PathEntityType**](PathEntityType.md) |  |  | ||||
| **pathType** | [**PathType**](PathType.md) |  |  | ||||
| **pathValue** | **String** |  |  | ||||
| 
 | ||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										14
									
								
								mobile/openapi/doc/PathEntityType.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								mobile/openapi/doc/PathEntityType.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| # openapi.model.PathEntityType | ||||
| 
 | ||||
| ## Load the model package | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| ``` | ||||
| 
 | ||||
| ## Properties | ||||
| Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| 
 | ||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										14
									
								
								mobile/openapi/doc/PathType.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								mobile/openapi/doc/PathType.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| # openapi.model.PathType | ||||
| 
 | ||||
| ## Load the model package | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| ``` | ||||
| 
 | ||||
| ## Properties | ||||
| Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| 
 | ||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										7
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							| @@ -96,6 +96,11 @@ part 'model/download_info_dto.dart'; | ||||
| part 'model/download_response_dto.dart'; | ||||
| part 'model/entity_type.dart'; | ||||
| part 'model/exif_response_dto.dart'; | ||||
| part 'model/file_checksum_dto.dart'; | ||||
| part 'model/file_checksum_response_dto.dart'; | ||||
| part 'model/file_report_dto.dart'; | ||||
| part 'model/file_report_fix_dto.dart'; | ||||
| part 'model/file_report_item_dto.dart'; | ||||
| part 'model/import_asset_dto.dart'; | ||||
| part 'model/job_command.dart'; | ||||
| part 'model/job_command_dto.dart'; | ||||
| @@ -117,6 +122,8 @@ part 'model/o_auth_authorize_response_dto.dart'; | ||||
| part 'model/o_auth_callback_dto.dart'; | ||||
| part 'model/o_auth_config_dto.dart'; | ||||
| part 'model/o_auth_config_response_dto.dart'; | ||||
| part 'model/path_entity_type.dart'; | ||||
| part 'model/path_type.dart'; | ||||
| part 'model/people_response_dto.dart'; | ||||
| part 'model/people_update_dto.dart'; | ||||
| part 'model/people_update_item.dart'; | ||||
|   | ||||
							
								
								
									
										130
									
								
								mobile/openapi/lib/api/audit_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										130
									
								
								mobile/openapi/lib/api/audit_api.dart
									
									
									
										generated
									
									
									
								
							| @@ -16,6 +16,45 @@ class AuditApi { | ||||
| 
 | ||||
|   final ApiClient apiClient; | ||||
| 
 | ||||
|   /// Performs an HTTP 'POST /audit/file-report/fix' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [FileReportFixDto] fileReportFixDto (required): | ||||
|   Future<Response> fixAuditFilesWithHttpInfo(FileReportFixDto fileReportFixDto,) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/audit/file-report/fix'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody = fileReportFixDto; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     const contentTypes = <String>['application/json']; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'POST', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [FileReportFixDto] fileReportFixDto (required): | ||||
|   Future<void> fixAuditFiles(FileReportFixDto fileReportFixDto,) async { | ||||
|     final response = await fixAuditFilesWithHttpInfo(fileReportFixDto,); | ||||
|     if (response.statusCode >= HttpStatus.badRequest) { | ||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'GET /audit/deletes' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
| @@ -76,4 +115,95 @@ class AuditApi { | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'GET /audit/file-report' operation and returns the [Response]. | ||||
|   Future<Response> getAuditFilesWithHttpInfo() async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/audit/file-report'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     const contentTypes = <String>[]; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'GET', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Future<FileReportDto?> getAuditFiles() async { | ||||
|     final response = await getAuditFilesWithHttpInfo(); | ||||
|     if (response.statusCode >= HttpStatus.badRequest) { | ||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||
|     } | ||||
|     // When a remote server returns no body with a status of 204, we shall not decode it. | ||||
|     // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" | ||||
|     // FormatException when trying to decode an empty string. | ||||
|     if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { | ||||
|       return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'FileReportDto',) as FileReportDto; | ||||
|      | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'POST /audit/file-report/checksum' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [FileChecksumDto] fileChecksumDto (required): | ||||
|   Future<Response> getFileChecksumsWithHttpInfo(FileChecksumDto fileChecksumDto,) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/audit/file-report/checksum'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody = fileChecksumDto; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     const contentTypes = <String>['application/json']; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'POST', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [FileChecksumDto] fileChecksumDto (required): | ||||
|   Future<List<FileChecksumResponseDto>?> getFileChecksums(FileChecksumDto fileChecksumDto,) async { | ||||
|     final response = await getFileChecksumsWithHttpInfo(fileChecksumDto,); | ||||
|     if (response.statusCode >= HttpStatus.badRequest) { | ||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||
|     } | ||||
|     // When a remote server returns no body with a status of 204, we shall not decode it. | ||||
|     // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" | ||||
|     // FormatException when trying to decode an empty string. | ||||
|     if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { | ||||
|       final responseBody = await _decodeBodyBytes(response); | ||||
|       return (await apiClient.deserializeAsync(responseBody, 'List<FileChecksumResponseDto>') as List) | ||||
|         .cast<FileChecksumResponseDto>() | ||||
|         .toList(); | ||||
| 
 | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										14
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							| @@ -283,6 +283,16 @@ class ApiClient { | ||||
|           return EntityTypeTypeTransformer().decode(value); | ||||
|         case 'ExifResponseDto': | ||||
|           return ExifResponseDto.fromJson(value); | ||||
|         case 'FileChecksumDto': | ||||
|           return FileChecksumDto.fromJson(value); | ||||
|         case 'FileChecksumResponseDto': | ||||
|           return FileChecksumResponseDto.fromJson(value); | ||||
|         case 'FileReportDto': | ||||
|           return FileReportDto.fromJson(value); | ||||
|         case 'FileReportFixDto': | ||||
|           return FileReportFixDto.fromJson(value); | ||||
|         case 'FileReportItemDto': | ||||
|           return FileReportItemDto.fromJson(value); | ||||
|         case 'ImportAssetDto': | ||||
|           return ImportAssetDto.fromJson(value); | ||||
|         case 'JobCommand': | ||||
| @@ -325,6 +335,10 @@ class ApiClient { | ||||
|           return OAuthConfigDto.fromJson(value); | ||||
|         case 'OAuthConfigResponseDto': | ||||
|           return OAuthConfigResponseDto.fromJson(value); | ||||
|         case 'PathEntityType': | ||||
|           return PathEntityTypeTypeTransformer().decode(value); | ||||
|         case 'PathType': | ||||
|           return PathTypeTypeTransformer().decode(value); | ||||
|         case 'PeopleResponseDto': | ||||
|           return PeopleResponseDto.fromJson(value); | ||||
|         case 'PeopleUpdateDto': | ||||
|   | ||||
							
								
								
									
										6
									
								
								mobile/openapi/lib/api_helper.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								mobile/openapi/lib/api_helper.dart
									
									
									
										generated
									
									
									
								
							| @@ -91,6 +91,12 @@ String parameterToString(dynamic value) { | ||||
|   if (value is ModelType) { | ||||
|     return ModelTypeTypeTransformer().encode(value).toString(); | ||||
|   } | ||||
|   if (value is PathEntityType) { | ||||
|     return PathEntityTypeTypeTransformer().encode(value).toString(); | ||||
|   } | ||||
|   if (value is PathType) { | ||||
|     return PathTypeTypeTransformer().encode(value).toString(); | ||||
|   } | ||||
|   if (value is SharedLinkType) { | ||||
|     return SharedLinkTypeTypeTransformer().encode(value).toString(); | ||||
|   } | ||||
|   | ||||
							
								
								
									
										100
									
								
								mobile/openapi/lib/model/file_checksum_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								mobile/openapi/lib/model/file_checksum_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| part of openapi.api; | ||||
| 
 | ||||
| class FileChecksumDto { | ||||
|   /// Returns a new [FileChecksumDto] instance. | ||||
|   FileChecksumDto({ | ||||
|     this.filenames = const [], | ||||
|   }); | ||||
| 
 | ||||
|   List<String> filenames; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => identical(this, other) || other is FileChecksumDto && | ||||
|      other.filenames == filenames; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (filenames.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'FileChecksumDto[filenames=$filenames]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|       json[r'filenames'] = this.filenames; | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
|   /// Returns a new [FileChecksumDto] instance and imports its values from | ||||
|   /// [value] if it's a [Map], null otherwise. | ||||
|   // ignore: prefer_constructors_over_static_methods | ||||
|   static FileChecksumDto? fromJson(dynamic value) { | ||||
|     if (value is Map) { | ||||
|       final json = value.cast<String, dynamic>(); | ||||
| 
 | ||||
|       return FileChecksumDto( | ||||
|         filenames: json[r'filenames'] is List | ||||
|             ? (json[r'filenames'] as List).cast<String>() | ||||
|             : const [], | ||||
|       ); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<FileChecksumDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <FileChecksumDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = FileChecksumDto.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| 
 | ||||
|   static Map<String, FileChecksumDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, FileChecksumDto>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = FileChecksumDto.fromJson(entry.value); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of FileChecksumDto-objects as value to a dart map | ||||
|   static Map<String, List<FileChecksumDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<FileChecksumDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       // ignore: parameter_assignments | ||||
|       json = json.cast<String, dynamic>(); | ||||
|       for (final entry in json.entries) { | ||||
|         map[entry.key] = FileChecksumDto.listFromJson(entry.value, growable: growable,); | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   /// The list of required keys that must be present in a JSON. | ||||
|   static const requiredKeys = <String>{ | ||||
|     'filenames', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										106
									
								
								mobile/openapi/lib/model/file_checksum_response_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								mobile/openapi/lib/model/file_checksum_response_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| part of openapi.api; | ||||
| 
 | ||||
| class FileChecksumResponseDto { | ||||
|   /// Returns a new [FileChecksumResponseDto] instance. | ||||
|   FileChecksumResponseDto({ | ||||
|     required this.checksum, | ||||
|     required this.filename, | ||||
|   }); | ||||
| 
 | ||||
|   String checksum; | ||||
| 
 | ||||
|   String filename; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => identical(this, other) || other is FileChecksumResponseDto && | ||||
|      other.checksum == checksum && | ||||
|      other.filename == filename; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (checksum.hashCode) + | ||||
|     (filename.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'FileChecksumResponseDto[checksum=$checksum, filename=$filename]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|       json[r'checksum'] = this.checksum; | ||||
|       json[r'filename'] = this.filename; | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
|   /// Returns a new [FileChecksumResponseDto] instance and imports its values from | ||||
|   /// [value] if it's a [Map], null otherwise. | ||||
|   // ignore: prefer_constructors_over_static_methods | ||||
|   static FileChecksumResponseDto? fromJson(dynamic value) { | ||||
|     if (value is Map) { | ||||
|       final json = value.cast<String, dynamic>(); | ||||
| 
 | ||||
|       return FileChecksumResponseDto( | ||||
|         checksum: mapValueOfType<String>(json, r'checksum')!, | ||||
|         filename: mapValueOfType<String>(json, r'filename')!, | ||||
|       ); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<FileChecksumResponseDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <FileChecksumResponseDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = FileChecksumResponseDto.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| 
 | ||||
|   static Map<String, FileChecksumResponseDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, FileChecksumResponseDto>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = FileChecksumResponseDto.fromJson(entry.value); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of FileChecksumResponseDto-objects as value to a dart map | ||||
|   static Map<String, List<FileChecksumResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<FileChecksumResponseDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       // ignore: parameter_assignments | ||||
|       json = json.cast<String, dynamic>(); | ||||
|       for (final entry in json.entries) { | ||||
|         map[entry.key] = FileChecksumResponseDto.listFromJson(entry.value, growable: growable,); | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   /// The list of required keys that must be present in a JSON. | ||||
|   static const requiredKeys = <String>{ | ||||
|     'checksum', | ||||
|     'filename', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										108
									
								
								mobile/openapi/lib/model/file_report_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								mobile/openapi/lib/model/file_report_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| part of openapi.api; | ||||
| 
 | ||||
| class FileReportDto { | ||||
|   /// Returns a new [FileReportDto] instance. | ||||
|   FileReportDto({ | ||||
|     this.extras = const [], | ||||
|     this.orphans = const [], | ||||
|   }); | ||||
| 
 | ||||
|   List<String> extras; | ||||
| 
 | ||||
|   List<FileReportItemDto> orphans; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => identical(this, other) || other is FileReportDto && | ||||
|      other.extras == extras && | ||||
|      other.orphans == orphans; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (extras.hashCode) + | ||||
|     (orphans.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'FileReportDto[extras=$extras, orphans=$orphans]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|       json[r'extras'] = this.extras; | ||||
|       json[r'orphans'] = this.orphans; | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
|   /// Returns a new [FileReportDto] instance and imports its values from | ||||
|   /// [value] if it's a [Map], null otherwise. | ||||
|   // ignore: prefer_constructors_over_static_methods | ||||
|   static FileReportDto? fromJson(dynamic value) { | ||||
|     if (value is Map) { | ||||
|       final json = value.cast<String, dynamic>(); | ||||
| 
 | ||||
|       return FileReportDto( | ||||
|         extras: json[r'extras'] is List | ||||
|             ? (json[r'extras'] as List).cast<String>() | ||||
|             : const [], | ||||
|         orphans: FileReportItemDto.listFromJson(json[r'orphans']), | ||||
|       ); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<FileReportDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <FileReportDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = FileReportDto.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| 
 | ||||
|   static Map<String, FileReportDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, FileReportDto>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = FileReportDto.fromJson(entry.value); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of FileReportDto-objects as value to a dart map | ||||
|   static Map<String, List<FileReportDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<FileReportDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       // ignore: parameter_assignments | ||||
|       json = json.cast<String, dynamic>(); | ||||
|       for (final entry in json.entries) { | ||||
|         map[entry.key] = FileReportDto.listFromJson(entry.value, growable: growable,); | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   /// The list of required keys that must be present in a JSON. | ||||
|   static const requiredKeys = <String>{ | ||||
|     'extras', | ||||
|     'orphans', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										98
									
								
								mobile/openapi/lib/model/file_report_fix_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								mobile/openapi/lib/model/file_report_fix_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| part of openapi.api; | ||||
| 
 | ||||
| class FileReportFixDto { | ||||
|   /// Returns a new [FileReportFixDto] instance. | ||||
|   FileReportFixDto({ | ||||
|     this.items = const [], | ||||
|   }); | ||||
| 
 | ||||
|   List<FileReportItemDto> items; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => identical(this, other) || other is FileReportFixDto && | ||||
|      other.items == items; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (items.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'FileReportFixDto[items=$items]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|       json[r'items'] = this.items; | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
|   /// Returns a new [FileReportFixDto] instance and imports its values from | ||||
|   /// [value] if it's a [Map], null otherwise. | ||||
|   // ignore: prefer_constructors_over_static_methods | ||||
|   static FileReportFixDto? fromJson(dynamic value) { | ||||
|     if (value is Map) { | ||||
|       final json = value.cast<String, dynamic>(); | ||||
| 
 | ||||
|       return FileReportFixDto( | ||||
|         items: FileReportItemDto.listFromJson(json[r'items']), | ||||
|       ); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<FileReportFixDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <FileReportFixDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = FileReportFixDto.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| 
 | ||||
|   static Map<String, FileReportFixDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, FileReportFixDto>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = FileReportFixDto.fromJson(entry.value); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of FileReportFixDto-objects as value to a dart map | ||||
|   static Map<String, List<FileReportFixDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<FileReportFixDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       // ignore: parameter_assignments | ||||
|       json = json.cast<String, dynamic>(); | ||||
|       for (final entry in json.entries) { | ||||
|         map[entry.key] = FileReportFixDto.listFromJson(entry.value, growable: growable,); | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   /// The list of required keys that must be present in a JSON. | ||||
|   static const requiredKeys = <String>{ | ||||
|     'items', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										139
									
								
								mobile/openapi/lib/model/file_report_item_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								mobile/openapi/lib/model/file_report_item_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| part of openapi.api; | ||||
| 
 | ||||
| class FileReportItemDto { | ||||
|   /// Returns a new [FileReportItemDto] instance. | ||||
|   FileReportItemDto({ | ||||
|     this.checksum, | ||||
|     required this.entityId, | ||||
|     required this.entityType, | ||||
|     required this.pathType, | ||||
|     required this.pathValue, | ||||
|   }); | ||||
| 
 | ||||
|   /// | ||||
|   /// Please note: This property should have been non-nullable! Since the specification file | ||||
|   /// does not include a default value (using the "default:" property), however, the generated | ||||
|   /// source code must fall back to having a nullable type. | ||||
|   /// Consider adding a "default:" property in the specification file to hide this note. | ||||
|   /// | ||||
|   String? checksum; | ||||
| 
 | ||||
|   String entityId; | ||||
| 
 | ||||
|   PathEntityType entityType; | ||||
| 
 | ||||
|   PathType pathType; | ||||
| 
 | ||||
|   String pathValue; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => identical(this, other) || other is FileReportItemDto && | ||||
|      other.checksum == checksum && | ||||
|      other.entityId == entityId && | ||||
|      other.entityType == entityType && | ||||
|      other.pathType == pathType && | ||||
|      other.pathValue == pathValue; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (checksum == null ? 0 : checksum!.hashCode) + | ||||
|     (entityId.hashCode) + | ||||
|     (entityType.hashCode) + | ||||
|     (pathType.hashCode) + | ||||
|     (pathValue.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'FileReportItemDto[checksum=$checksum, entityId=$entityId, entityType=$entityType, pathType=$pathType, pathValue=$pathValue]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|     if (this.checksum != null) { | ||||
|       json[r'checksum'] = this.checksum; | ||||
|     } else { | ||||
|     //  json[r'checksum'] = null; | ||||
|     } | ||||
|       json[r'entityId'] = this.entityId; | ||||
|       json[r'entityType'] = this.entityType; | ||||
|       json[r'pathType'] = this.pathType; | ||||
|       json[r'pathValue'] = this.pathValue; | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
|   /// Returns a new [FileReportItemDto] instance and imports its values from | ||||
|   /// [value] if it's a [Map], null otherwise. | ||||
|   // ignore: prefer_constructors_over_static_methods | ||||
|   static FileReportItemDto? fromJson(dynamic value) { | ||||
|     if (value is Map) { | ||||
|       final json = value.cast<String, dynamic>(); | ||||
| 
 | ||||
|       return FileReportItemDto( | ||||
|         checksum: mapValueOfType<String>(json, r'checksum'), | ||||
|         entityId: mapValueOfType<String>(json, r'entityId')!, | ||||
|         entityType: PathEntityType.fromJson(json[r'entityType'])!, | ||||
|         pathType: PathType.fromJson(json[r'pathType'])!, | ||||
|         pathValue: mapValueOfType<String>(json, r'pathValue')!, | ||||
|       ); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<FileReportItemDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <FileReportItemDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = FileReportItemDto.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| 
 | ||||
|   static Map<String, FileReportItemDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, FileReportItemDto>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = FileReportItemDto.fromJson(entry.value); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of FileReportItemDto-objects as value to a dart map | ||||
|   static Map<String, List<FileReportItemDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<FileReportItemDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       // ignore: parameter_assignments | ||||
|       json = json.cast<String, dynamic>(); | ||||
|       for (final entry in json.entries) { | ||||
|         map[entry.key] = FileReportItemDto.listFromJson(entry.value, growable: growable,); | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   /// The list of required keys that must be present in a JSON. | ||||
|   static const requiredKeys = <String>{ | ||||
|     'entityId', | ||||
|     'entityType', | ||||
|     'pathType', | ||||
|     'pathValue', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										88
									
								
								mobile/openapi/lib/model/path_entity_type.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								mobile/openapi/lib/model/path_entity_type.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| part of openapi.api; | ||||
| 
 | ||||
| 
 | ||||
| class PathEntityType { | ||||
|   /// Instantiate a new enum with the provided [value]. | ||||
|   const PathEntityType._(this.value); | ||||
| 
 | ||||
|   /// The underlying value of this enum member. | ||||
|   final String value; | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => value; | ||||
| 
 | ||||
|   String toJson() => value; | ||||
| 
 | ||||
|   static const asset = PathEntityType._(r'asset'); | ||||
|   static const person = PathEntityType._(r'person'); | ||||
|   static const user = PathEntityType._(r'user'); | ||||
| 
 | ||||
|   /// List of all possible values in this [enum][PathEntityType]. | ||||
|   static const values = <PathEntityType>[ | ||||
|     asset, | ||||
|     person, | ||||
|     user, | ||||
|   ]; | ||||
| 
 | ||||
|   static PathEntityType? fromJson(dynamic value) => PathEntityTypeTypeTransformer().decode(value); | ||||
| 
 | ||||
|   static List<PathEntityType>? listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <PathEntityType>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = PathEntityType.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /// Transformation class that can [encode] an instance of [PathEntityType] to String, | ||||
| /// and [decode] dynamic data back to [PathEntityType]. | ||||
| class PathEntityTypeTypeTransformer { | ||||
|   factory PathEntityTypeTypeTransformer() => _instance ??= const PathEntityTypeTypeTransformer._(); | ||||
| 
 | ||||
|   const PathEntityTypeTypeTransformer._(); | ||||
| 
 | ||||
|   String encode(PathEntityType data) => data.value; | ||||
| 
 | ||||
|   /// Decodes a [dynamic value][data] to a PathEntityType. | ||||
|   /// | ||||
|   /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, | ||||
|   /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] | ||||
|   /// cannot be decoded successfully, then an [UnimplementedError] is thrown. | ||||
|   /// | ||||
|   /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, | ||||
|   /// and users are still using an old app with the old code. | ||||
|   PathEntityType? decode(dynamic data, {bool allowNull = true}) { | ||||
|     if (data != null) { | ||||
|       switch (data) { | ||||
|         case r'asset': return PathEntityType.asset; | ||||
|         case r'person': return PathEntityType.person; | ||||
|         case r'user': return PathEntityType.user; | ||||
|         default: | ||||
|           if (!allowNull) { | ||||
|             throw ArgumentError('Unknown enum value to decode: $data'); | ||||
|           } | ||||
|       } | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Singleton [PathEntityTypeTypeTransformer] instance. | ||||
|   static PathEntityTypeTypeTransformer? _instance; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										100
									
								
								mobile/openapi/lib/model/path_type.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								mobile/openapi/lib/model/path_type.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| part of openapi.api; | ||||
| 
 | ||||
| 
 | ||||
| class PathType { | ||||
|   /// Instantiate a new enum with the provided [value]. | ||||
|   const PathType._(this.value); | ||||
| 
 | ||||
|   /// The underlying value of this enum member. | ||||
|   final String value; | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => value; | ||||
| 
 | ||||
|   String toJson() => value; | ||||
| 
 | ||||
|   static const original = PathType._(r'original'); | ||||
|   static const jpegThumbnail = PathType._(r'jpeg_thumbnail'); | ||||
|   static const webpThumbnail = PathType._(r'webp_thumbnail'); | ||||
|   static const encodedVideo = PathType._(r'encoded_video'); | ||||
|   static const sidecar = PathType._(r'sidecar'); | ||||
|   static const face = PathType._(r'face'); | ||||
|   static const profile = PathType._(r'profile'); | ||||
| 
 | ||||
|   /// List of all possible values in this [enum][PathType]. | ||||
|   static const values = <PathType>[ | ||||
|     original, | ||||
|     jpegThumbnail, | ||||
|     webpThumbnail, | ||||
|     encodedVideo, | ||||
|     sidecar, | ||||
|     face, | ||||
|     profile, | ||||
|   ]; | ||||
| 
 | ||||
|   static PathType? fromJson(dynamic value) => PathTypeTypeTransformer().decode(value); | ||||
| 
 | ||||
|   static List<PathType>? listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <PathType>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = PathType.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /// Transformation class that can [encode] an instance of [PathType] to String, | ||||
| /// and [decode] dynamic data back to [PathType]. | ||||
| class PathTypeTypeTransformer { | ||||
|   factory PathTypeTypeTransformer() => _instance ??= const PathTypeTypeTransformer._(); | ||||
| 
 | ||||
|   const PathTypeTypeTransformer._(); | ||||
| 
 | ||||
|   String encode(PathType data) => data.value; | ||||
| 
 | ||||
|   /// Decodes a [dynamic value][data] to a PathType. | ||||
|   /// | ||||
|   /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, | ||||
|   /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] | ||||
|   /// cannot be decoded successfully, then an [UnimplementedError] is thrown. | ||||
|   /// | ||||
|   /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, | ||||
|   /// and users are still using an old app with the old code. | ||||
|   PathType? decode(dynamic data, {bool allowNull = true}) { | ||||
|     if (data != null) { | ||||
|       switch (data) { | ||||
|         case r'original': return PathType.original; | ||||
|         case r'jpeg_thumbnail': return PathType.jpegThumbnail; | ||||
|         case r'webp_thumbnail': return PathType.webpThumbnail; | ||||
|         case r'encoded_video': return PathType.encodedVideo; | ||||
|         case r'sidecar': return PathType.sidecar; | ||||
|         case r'face': return PathType.face; | ||||
|         case r'profile': return PathType.profile; | ||||
|         default: | ||||
|           if (!allowNull) { | ||||
|             throw ArgumentError('Unknown enum value to decode: $data'); | ||||
|           } | ||||
|       } | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Singleton [PathTypeTypeTransformer] instance. | ||||
|   static PathTypeTypeTransformer? _instance; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										15
									
								
								mobile/openapi/test/audit_api_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										15
									
								
								mobile/openapi/test/audit_api_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -17,10 +17,25 @@ void main() { | ||||
|   // final instance = AuditApi(); | ||||
| 
 | ||||
|   group('tests for AuditApi', () { | ||||
|     //Future fixAuditFiles(FileReportFixDto fileReportFixDto) async | ||||
|     test('test fixAuditFiles', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<AuditDeletesResponseDto> getAuditDeletes(EntityType entityType, DateTime after, { String userId }) async | ||||
|     test('test getAuditDeletes', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<FileReportDto> getAuditFiles() async | ||||
|     test('test getAuditFiles', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<List<FileChecksumResponseDto>> getFileChecksums(FileChecksumDto fileChecksumDto) async | ||||
|     test('test getFileChecksums', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|   }); | ||||
| } | ||||
|   | ||||
							
								
								
									
										27
									
								
								mobile/openapi/test/file_checksum_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								mobile/openapi/test/file_checksum_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| import 'package:openapi/api.dart'; | ||||
| import 'package:test/test.dart'; | ||||
| 
 | ||||
| // tests for FileChecksumDto | ||||
| void main() { | ||||
|   // final instance = FileChecksumDto(); | ||||
| 
 | ||||
|   group('test FileChecksumDto', () { | ||||
|     // List<String> filenames (default value: const []) | ||||
|     test('to test the property `filenames`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
| 
 | ||||
|   }); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										32
									
								
								mobile/openapi/test/file_checksum_response_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								mobile/openapi/test/file_checksum_response_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| import 'package:openapi/api.dart'; | ||||
| import 'package:test/test.dart'; | ||||
| 
 | ||||
| // tests for FileChecksumResponseDto | ||||
| void main() { | ||||
|   // final instance = FileChecksumResponseDto(); | ||||
| 
 | ||||
|   group('test FileChecksumResponseDto', () { | ||||
|     // String checksum | ||||
|     test('to test the property `checksum`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // String filename | ||||
|     test('to test the property `filename`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
| 
 | ||||
|   }); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										32
									
								
								mobile/openapi/test/file_report_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								mobile/openapi/test/file_report_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| import 'package:openapi/api.dart'; | ||||
| import 'package:test/test.dart'; | ||||
| 
 | ||||
| // tests for FileReportDto | ||||
| void main() { | ||||
|   // final instance = FileReportDto(); | ||||
| 
 | ||||
|   group('test FileReportDto', () { | ||||
|     // List<String> extras (default value: const []) | ||||
|     test('to test the property `extras`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // List<FileReportItemDto> orphans (default value: const []) | ||||
|     test('to test the property `orphans`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
| 
 | ||||
|   }); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										27
									
								
								mobile/openapi/test/file_report_fix_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								mobile/openapi/test/file_report_fix_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| import 'package:openapi/api.dart'; | ||||
| import 'package:test/test.dart'; | ||||
| 
 | ||||
| // tests for FileReportFixDto | ||||
| void main() { | ||||
|   // final instance = FileReportFixDto(); | ||||
| 
 | ||||
|   group('test FileReportFixDto', () { | ||||
|     // List<FileReportItemDto> items (default value: const []) | ||||
|     test('to test the property `items`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
| 
 | ||||
|   }); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										47
									
								
								mobile/openapi/test/file_report_item_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								mobile/openapi/test/file_report_item_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| import 'package:openapi/api.dart'; | ||||
| import 'package:test/test.dart'; | ||||
| 
 | ||||
| // tests for FileReportItemDto | ||||
| void main() { | ||||
|   // final instance = FileReportItemDto(); | ||||
| 
 | ||||
|   group('test FileReportItemDto', () { | ||||
|     // String checksum | ||||
|     test('to test the property `checksum`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // String entityId | ||||
|     test('to test the property `entityId`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // PathEntityType entityType | ||||
|     test('to test the property `entityType`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // PathType pathType | ||||
|     test('to test the property `pathType`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // String pathValue | ||||
|     test('to test the property `pathValue`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
| 
 | ||||
|   }); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										21
									
								
								mobile/openapi/test/path_entity_type_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								mobile/openapi/test/path_entity_type_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| import 'package:openapi/api.dart'; | ||||
| import 'package:test/test.dart'; | ||||
| 
 | ||||
| // tests for PathEntityType | ||||
| void main() { | ||||
| 
 | ||||
|   group('test PathEntityType', () { | ||||
| 
 | ||||
|   }); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										21
									
								
								mobile/openapi/test/path_type_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								mobile/openapi/test/path_type_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| import 'package:openapi/api.dart'; | ||||
| import 'package:test/test.dart'; | ||||
| 
 | ||||
| // tests for PathType | ||||
| void main() { | ||||
| 
 | ||||
|   group('test PathType', () { | ||||
| 
 | ||||
|   }); | ||||
| 
 | ||||
| } | ||||
| @@ -2286,6 +2286,118 @@ | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/audit/file-report": { | ||||
|       "get": { | ||||
|         "operationId": "getAuditFiles", | ||||
|         "parameters": [], | ||||
|         "responses": { | ||||
|           "200": { | ||||
|             "content": { | ||||
|               "application/json": { | ||||
|                 "schema": { | ||||
|                   "$ref": "#/components/schemas/FileReportDto" | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             "description": "" | ||||
|           } | ||||
|         }, | ||||
|         "security": [ | ||||
|           { | ||||
|             "bearer": [] | ||||
|           }, | ||||
|           { | ||||
|             "cookie": [] | ||||
|           }, | ||||
|           { | ||||
|             "api_key": [] | ||||
|           } | ||||
|         ], | ||||
|         "tags": [ | ||||
|           "Audit" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/audit/file-report/checksum": { | ||||
|       "post": { | ||||
|         "operationId": "getFileChecksums", | ||||
|         "parameters": [], | ||||
|         "requestBody": { | ||||
|           "content": { | ||||
|             "application/json": { | ||||
|               "schema": { | ||||
|                 "$ref": "#/components/schemas/FileChecksumDto" | ||||
|               } | ||||
|             } | ||||
|           }, | ||||
|           "required": true | ||||
|         }, | ||||
|         "responses": { | ||||
|           "201": { | ||||
|             "content": { | ||||
|               "application/json": { | ||||
|                 "schema": { | ||||
|                   "items": { | ||||
|                     "$ref": "#/components/schemas/FileChecksumResponseDto" | ||||
|                   }, | ||||
|                   "type": "array" | ||||
|                 } | ||||
|               } | ||||
|             }, | ||||
|             "description": "" | ||||
|           } | ||||
|         }, | ||||
|         "security": [ | ||||
|           { | ||||
|             "bearer": [] | ||||
|           }, | ||||
|           { | ||||
|             "cookie": [] | ||||
|           }, | ||||
|           { | ||||
|             "api_key": [] | ||||
|           } | ||||
|         ], | ||||
|         "tags": [ | ||||
|           "Audit" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/audit/file-report/fix": { | ||||
|       "post": { | ||||
|         "operationId": "fixAuditFiles", | ||||
|         "parameters": [], | ||||
|         "requestBody": { | ||||
|           "content": { | ||||
|             "application/json": { | ||||
|               "schema": { | ||||
|                 "$ref": "#/components/schemas/FileReportFixDto" | ||||
|               } | ||||
|             } | ||||
|           }, | ||||
|           "required": true | ||||
|         }, | ||||
|         "responses": { | ||||
|           "201": { | ||||
|             "description": "" | ||||
|           } | ||||
|         }, | ||||
|         "security": [ | ||||
|           { | ||||
|             "bearer": [] | ||||
|           }, | ||||
|           { | ||||
|             "cookie": [] | ||||
|           }, | ||||
|           { | ||||
|             "api_key": [] | ||||
|           } | ||||
|         ], | ||||
|         "tags": [ | ||||
|           "Audit" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/auth/admin-sign-up": { | ||||
|       "post": { | ||||
|         "operationId": "adminSignUp", | ||||
| @@ -6580,6 +6692,97 @@ | ||||
|         }, | ||||
|         "type": "object" | ||||
|       }, | ||||
|       "FileChecksumDto": { | ||||
|         "properties": { | ||||
|           "filenames": { | ||||
|             "items": { | ||||
|               "type": "string" | ||||
|             }, | ||||
|             "type": "array" | ||||
|           } | ||||
|         }, | ||||
|         "required": [ | ||||
|           "filenames" | ||||
|         ], | ||||
|         "type": "object" | ||||
|       }, | ||||
|       "FileChecksumResponseDto": { | ||||
|         "properties": { | ||||
|           "checksum": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "filename": { | ||||
|             "type": "string" | ||||
|           } | ||||
|         }, | ||||
|         "required": [ | ||||
|           "filename", | ||||
|           "checksum" | ||||
|         ], | ||||
|         "type": "object" | ||||
|       }, | ||||
|       "FileReportDto": { | ||||
|         "properties": { | ||||
|           "extras": { | ||||
|             "items": { | ||||
|               "type": "string" | ||||
|             }, | ||||
|             "type": "array" | ||||
|           }, | ||||
|           "orphans": { | ||||
|             "items": { | ||||
|               "$ref": "#/components/schemas/FileReportItemDto" | ||||
|             }, | ||||
|             "type": "array" | ||||
|           } | ||||
|         }, | ||||
|         "required": [ | ||||
|           "orphans", | ||||
|           "extras" | ||||
|         ], | ||||
|         "type": "object" | ||||
|       }, | ||||
|       "FileReportFixDto": { | ||||
|         "properties": { | ||||
|           "items": { | ||||
|             "items": { | ||||
|               "$ref": "#/components/schemas/FileReportItemDto" | ||||
|             }, | ||||
|             "type": "array" | ||||
|           } | ||||
|         }, | ||||
|         "required": [ | ||||
|           "items" | ||||
|         ], | ||||
|         "type": "object" | ||||
|       }, | ||||
|       "FileReportItemDto": { | ||||
|         "properties": { | ||||
|           "checksum": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "entityId": { | ||||
|             "format": "uuid", | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "entityType": { | ||||
|             "$ref": "#/components/schemas/PathEntityType" | ||||
|           }, | ||||
|           "pathType": { | ||||
|             "$ref": "#/components/schemas/PathType" | ||||
|           }, | ||||
|           "pathValue": { | ||||
|             "type": "string" | ||||
|           } | ||||
|         }, | ||||
|         "required": [ | ||||
|           "entityId", | ||||
|           "entityType", | ||||
|           "pathType", | ||||
|           "pathValue" | ||||
|         ], | ||||
|         "type": "object" | ||||
|       }, | ||||
|       "ImportAssetDto": { | ||||
|         "properties": { | ||||
|           "assetPath": { | ||||
| @@ -7027,6 +7230,26 @@ | ||||
|         ], | ||||
|         "type": "object" | ||||
|       }, | ||||
|       "PathEntityType": { | ||||
|         "enum": [ | ||||
|           "asset", | ||||
|           "person", | ||||
|           "user" | ||||
|         ], | ||||
|         "type": "string" | ||||
|       }, | ||||
|       "PathType": { | ||||
|         "enum": [ | ||||
|           "original", | ||||
|           "jpeg_thumbnail", | ||||
|           "webp_thumbnail", | ||||
|           "encoded_video", | ||||
|           "sidecar", | ||||
|           "face", | ||||
|           "profile" | ||||
|         ], | ||||
|         "type": "string" | ||||
|       }, | ||||
|       "PeopleResponseDto": { | ||||
|         "properties": { | ||||
|           "people": { | ||||
|   | ||||
| @@ -1,8 +1,10 @@ | ||||
| import { EntityType } from '@app/infra/entities'; | ||||
| import { AssetPathType, EntityType, PathType, PersonPathType, UserPathType } from '@app/infra/entities'; | ||||
| import { ApiProperty } from '@nestjs/swagger'; | ||||
| import { Type } from 'class-transformer'; | ||||
| import { IsDate, IsEnum, IsUUID } from 'class-validator'; | ||||
| import { Optional } from '../domain.util'; | ||||
| import { IsArray, IsDate, IsEnum, IsString, IsUUID, ValidateNested } from 'class-validator'; | ||||
| import { Optional, ValidateUUID } from '../domain.util'; | ||||
|  | ||||
| const PathEnum = Object.values({ ...AssetPathType, ...PersonPathType, ...UserPathType }); | ||||
|  | ||||
| export class AuditDeletesDto { | ||||
|   @IsDate() | ||||
| @@ -19,7 +21,54 @@ export class AuditDeletesDto { | ||||
|   userId?: string; | ||||
| } | ||||
|  | ||||
| export enum PathEntityType { | ||||
|   ASSET = 'asset', | ||||
|   PERSON = 'person', | ||||
|   USER = 'user', | ||||
| } | ||||
|  | ||||
| export class AuditDeletesResponseDto { | ||||
|   needsFullSync!: boolean; | ||||
|   ids!: string[]; | ||||
| } | ||||
|  | ||||
| export class FileReportDto { | ||||
|   orphans!: FileReportItemDto[]; | ||||
|   extras!: string[]; | ||||
| } | ||||
|  | ||||
| export class FileChecksumDto { | ||||
|   @IsString({ each: true }) | ||||
|   filenames!: string[]; | ||||
| } | ||||
|  | ||||
| export class FileChecksumResponseDto { | ||||
|   filename!: string; | ||||
|   checksum!: string; | ||||
| } | ||||
|  | ||||
| export class FileReportFixDto { | ||||
|   @IsArray() | ||||
|   @ValidateNested({ each: true }) | ||||
|   @Type(() => FileReportItemDto) | ||||
|   items!: FileReportItemDto[]; | ||||
| } | ||||
|  | ||||
| // used both as request and response dto | ||||
| export class FileReportItemDto { | ||||
|   @ValidateUUID() | ||||
|   entityId!: string; | ||||
|  | ||||
|   @ApiProperty({ enumName: 'PathEntityType', enum: PathEntityType }) | ||||
|   @IsEnum(PathEntityType) | ||||
|   entityType!: PathEntityType; | ||||
|  | ||||
|   @ApiProperty({ enumName: 'PathType', enum: PathEnum }) | ||||
|   @IsEnum(PathEnum) | ||||
|   pathType!: PathType; | ||||
|  | ||||
|   @IsString() | ||||
|   pathValue!: string; | ||||
|  | ||||
|   checksum?: string; | ||||
| } | ||||
|   | ||||
| @@ -1,17 +1,45 @@ | ||||
| import { DatabaseAction, EntityType } from '@app/infra/entities'; | ||||
| import { IAccessRepositoryMock, auditStub, authStub, newAccessRepositoryMock, newAuditRepositoryMock } from '@test'; | ||||
| import { IAuditRepository } from '../repositories'; | ||||
| import { | ||||
|   IAccessRepositoryMock, | ||||
|   auditStub, | ||||
|   authStub, | ||||
|   newAccessRepositoryMock, | ||||
|   newAssetRepositoryMock, | ||||
|   newAuditRepositoryMock, | ||||
|   newCryptoRepositoryMock, | ||||
|   newPersonRepositoryMock, | ||||
|   newStorageRepositoryMock, | ||||
|   newUserRepositoryMock, | ||||
| } from '@test'; | ||||
| import { | ||||
|   IAssetRepository, | ||||
|   IAuditRepository, | ||||
|   ICryptoRepository, | ||||
|   IPersonRepository, | ||||
|   IStorageRepository, | ||||
|   IUserRepository, | ||||
| } from '../repositories'; | ||||
| import { AuditService } from './audit.service'; | ||||
| 
 | ||||
| describe(AuditService.name, () => { | ||||
|   let sut: AuditService; | ||||
|   let accessMock: IAccessRepositoryMock; | ||||
|   let assetMock: jest.Mocked<IAssetRepository>; | ||||
|   let auditMock: jest.Mocked<IAuditRepository>; | ||||
|   let cryptoMock: jest.Mocked<ICryptoRepository>; | ||||
|   let personMock: jest.Mocked<IPersonRepository>; | ||||
|   let storageMock: jest.Mocked<IStorageRepository>; | ||||
|   let userMock: jest.Mocked<IUserRepository>; | ||||
| 
 | ||||
|   beforeEach(async () => { | ||||
|     accessMock = newAccessRepositoryMock(); | ||||
|     assetMock = newAssetRepositoryMock(); | ||||
|     cryptoMock = newCryptoRepositoryMock(); | ||||
|     auditMock = newAuditRepositoryMock(); | ||||
|     sut = new AuditService(accessMock, auditMock); | ||||
|     personMock = newPersonRepositoryMock(); | ||||
|     storageMock = newStorageRepositoryMock(); | ||||
|     userMock = newUserRepositoryMock(); | ||||
|     sut = new AuditService(accessMock, assetMock, cryptoMock, personMock, auditMock, storageMock, userMock); | ||||
|   }); | ||||
| 
 | ||||
|   it('should work', () => { | ||||
| @@ -1,19 +1,44 @@ | ||||
| import { DatabaseAction } from '@app/infra/entities'; | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import { AssetPathType, DatabaseAction, PersonPathType, UserPathType } from '@app/infra/entities'; | ||||
| import { BadRequestException, Inject, Injectable, Logger } from '@nestjs/common'; | ||||
| import { DateTime } from 'luxon'; | ||||
| import { resolve } from 'node:path'; | ||||
| import { AccessCore, Permission } from '../access'; | ||||
| import { AuthUserDto } from '../auth'; | ||||
| import { AUDIT_LOG_MAX_DURATION } from '../domain.constant'; | ||||
| import { IAccessRepository, IAuditRepository } from '../repositories'; | ||||
| import { AuditDeletesDto, AuditDeletesResponseDto } from './audit.dto'; | ||||
| import { usePagination } from '../domain.util'; | ||||
| import { JOBS_ASSET_PAGINATION_SIZE } from '../job'; | ||||
| import { | ||||
|   IAccessRepository, | ||||
|   IAssetRepository, | ||||
|   IAuditRepository, | ||||
|   ICryptoRepository, | ||||
|   IPersonRepository, | ||||
|   IStorageRepository, | ||||
|   IUserRepository, | ||||
| } from '../repositories'; | ||||
| import { StorageCore, StorageFolder } from '../storage'; | ||||
| import { | ||||
|   AuditDeletesDto, | ||||
|   AuditDeletesResponseDto, | ||||
|   FileChecksumDto, | ||||
|   FileChecksumResponseDto, | ||||
|   FileReportItemDto, | ||||
|   PathEntityType, | ||||
| } from './audit.dto'; | ||||
|  | ||||
| @Injectable() | ||||
| export class AuditService { | ||||
|   private access: AccessCore; | ||||
|   private logger = new Logger(AuditService.name); | ||||
|  | ||||
|   constructor( | ||||
|     @Inject(IAccessRepository) accessRepository: IAccessRepository, | ||||
|     @Inject(IAssetRepository) private assetRepository: IAssetRepository, | ||||
|     @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, | ||||
|     @Inject(IPersonRepository) private personRepository: IPersonRepository, | ||||
|     @Inject(IAuditRepository) private repository: IAuditRepository, | ||||
|     @Inject(IStorageRepository) private storageRepository: IStorageRepository, | ||||
|     @Inject(IUserRepository) private userRepository: IUserRepository, | ||||
|   ) { | ||||
|     this.access = new AccessCore(accessRepository); | ||||
|   } | ||||
| @@ -40,4 +65,160 @@ export class AuditService { | ||||
|       ids: audits.map(({ entityId }) => entityId), | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   async getChecksums(dto: FileChecksumDto) { | ||||
|     const results: FileChecksumResponseDto[] = []; | ||||
|     for (const filename of dto.filenames) { | ||||
|       if (!StorageCore.isImmichPath(filename)) { | ||||
|         throw new BadRequestException( | ||||
|           `Could not get the checksum of ${filename} because the file isn't accessible by Immich`, | ||||
|         ); | ||||
|       } | ||||
|  | ||||
|       const checksum = await this.cryptoRepository.hashFile(filename); | ||||
|       results.push({ filename, checksum: checksum.toString('base64') }); | ||||
|     } | ||||
|     return results; | ||||
|   } | ||||
|  | ||||
|   async fixItems(items: FileReportItemDto[]) { | ||||
|     for (const { entityId: id, pathType, pathValue } of items) { | ||||
|       if (!StorageCore.isImmichPath(pathValue)) { | ||||
|         throw new BadRequestException( | ||||
|           `Could not fix item ${id} with path ${pathValue} because the file isn't accessible by Immich`, | ||||
|         ); | ||||
|       } | ||||
|  | ||||
|       switch (pathType) { | ||||
|         case AssetPathType.ENCODED_VIDEO: | ||||
|           await this.assetRepository.save({ id, encodedVideoPath: pathValue }); | ||||
|           break; | ||||
|  | ||||
|         case AssetPathType.JPEG_THUMBNAIL: | ||||
|           await this.assetRepository.save({ id, resizePath: pathValue }); | ||||
|           break; | ||||
|  | ||||
|         case AssetPathType.WEBP_THUMBNAIL: | ||||
|           await this.assetRepository.save({ id, webpPath: pathValue }); | ||||
|           break; | ||||
|  | ||||
|         case AssetPathType.ORIGINAL: | ||||
|           await this.assetRepository.save({ id, originalPath: pathValue }); | ||||
|           break; | ||||
|  | ||||
|         case AssetPathType.SIDECAR: | ||||
|           await this.assetRepository.save({ id, sidecarPath: pathValue }); | ||||
|           break; | ||||
|  | ||||
|         case PersonPathType.FACE: | ||||
|           await this.personRepository.update({ id, thumbnailPath: pathValue }); | ||||
|           break; | ||||
|  | ||||
|         case UserPathType.PROFILE: | ||||
|           await this.userRepository.update(id, { profileImagePath: pathValue }); | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   async getFileReport() { | ||||
|     const fullPath = (filename: string) => resolve(filename); | ||||
|     const hasFile = (items: Set<string>, filename: string) => items.has(filename) || items.has(fullPath(filename)); | ||||
|     const crawl = async (folder: StorageFolder) => | ||||
|       new Set(await this.storageRepository.crawl({ pathsToCrawl: [StorageCore.getBaseFolder(folder)] })); | ||||
|  | ||||
|     const uploadFiles = await crawl(StorageFolder.UPLOAD); | ||||
|     const libraryFiles = await crawl(StorageFolder.LIBRARY); | ||||
|     const thumbFiles = await crawl(StorageFolder.THUMBNAILS); | ||||
|     const videoFiles = await crawl(StorageFolder.ENCODED_VIDEO); | ||||
|     const profileFiles = await crawl(StorageFolder.PROFILE); | ||||
|     const allFiles = new Set<string>(); | ||||
|     for (const list of [libraryFiles, thumbFiles, videoFiles, profileFiles, uploadFiles]) { | ||||
|       for (const item of list) { | ||||
|         allFiles.add(item); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     const track = (filename: string | null) => { | ||||
|       if (!filename) { | ||||
|         return; | ||||
|       } | ||||
|       allFiles.delete(filename); | ||||
|       allFiles.delete(fullPath(filename)); | ||||
|     }; | ||||
|  | ||||
|     this.logger.log( | ||||
|       `Found ${libraryFiles.size} original files, ${thumbFiles.size} thumbnails, ${videoFiles.size} encoded videos, ${profileFiles.size} profile files`, | ||||
|     ); | ||||
|     const pagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (options) => | ||||
|       this.assetRepository.getAll(options, { withDeleted: true }), | ||||
|     ); | ||||
|  | ||||
|     let assetCount = 0; | ||||
|  | ||||
|     const orphans: FileReportItemDto[] = []; | ||||
|     for await (const assets of pagination) { | ||||
|       assetCount += assets.length; | ||||
|       for (const { id, originalPath, resizePath, encodedVideoPath, webpPath, isExternal, checksum } of assets) { | ||||
|         for (const file of [originalPath, resizePath, encodedVideoPath, webpPath]) { | ||||
|           track(file); | ||||
|         } | ||||
|  | ||||
|         const entity = { entityId: id, entityType: PathEntityType.ASSET, checksum: checksum.toString('base64') }; | ||||
|         if ( | ||||
|           originalPath && | ||||
|           !hasFile(libraryFiles, originalPath) && | ||||
|           !hasFile(uploadFiles, originalPath) && | ||||
|           // Android motion assets | ||||
|           !hasFile(videoFiles, originalPath) && | ||||
|           // ignore external library assets | ||||
|           !isExternal | ||||
|         ) { | ||||
|           orphans.push({ ...entity, pathType: AssetPathType.ORIGINAL, pathValue: originalPath }); | ||||
|         } | ||||
|         if (resizePath && !hasFile(thumbFiles, resizePath)) { | ||||
|           orphans.push({ ...entity, pathType: AssetPathType.JPEG_THUMBNAIL, pathValue: resizePath }); | ||||
|         } | ||||
|         if (webpPath && !hasFile(thumbFiles, webpPath)) { | ||||
|           orphans.push({ ...entity, pathType: AssetPathType.WEBP_THUMBNAIL, pathValue: webpPath }); | ||||
|         } | ||||
|         if (encodedVideoPath && !hasFile(videoFiles, encodedVideoPath)) { | ||||
|           orphans.push({ ...entity, pathType: AssetPathType.WEBP_THUMBNAIL, pathValue: encodedVideoPath }); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     const users = await this.userRepository.getList(); | ||||
|     for (const { id, profileImagePath } of users) { | ||||
|       track(profileImagePath); | ||||
|  | ||||
|       const entity = { entityId: id, entityType: PathEntityType.USER }; | ||||
|       if (profileImagePath && !hasFile(profileFiles, profileImagePath)) { | ||||
|         orphans.push({ ...entity, pathType: UserPathType.PROFILE, pathValue: profileImagePath }); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     const people = await this.personRepository.getAll(); | ||||
|     for (const { id, thumbnailPath } of people) { | ||||
|       track(thumbnailPath); | ||||
|       const entity = { entityId: id, entityType: PathEntityType.PERSON }; | ||||
|       if (thumbnailPath && !hasFile(thumbFiles, thumbnailPath)) { | ||||
|         orphans.push({ ...entity, pathType: PersonPathType.FACE, pathValue: thumbnailPath }); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     this.logger.log(`Found ${assetCount} assets, ${users.length} users, ${people.length} people`); | ||||
|  | ||||
|     const extras: string[] = []; | ||||
|     for (const file of allFiles) { | ||||
|       extras.push(file); | ||||
|     } | ||||
|  | ||||
|     // send as absolute paths | ||||
|     for (const orphan of orphans) { | ||||
|       orphan.pathValue = fullPath(orphan.pathValue); | ||||
|     } | ||||
|  | ||||
|     return { orphans, extras }; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -289,6 +289,9 @@ export class MetadataService { | ||||
|       }); | ||||
|       const checksum = this.cryptoRepository.hashSha1(video); | ||||
|  | ||||
|       const motionPath = this.storageCore.getAndroidMotionPath(asset); | ||||
|       this.storageCore.ensureFolders(motionPath); | ||||
|  | ||||
|       let motionAsset = await this.assetRepository.getByChecksum(asset.ownerId, checksum); | ||||
|       if (!motionAsset) { | ||||
|         const createdAt = asset.fileCreatedAt ?? asset.createdAt; | ||||
| @@ -300,7 +303,7 @@ export class MetadataService { | ||||
|           localDateTime: createdAt, | ||||
|           checksum, | ||||
|           ownerId: asset.ownerId, | ||||
|           originalPath: this.storageCore.getAndroidMotionPath(asset), | ||||
|           originalPath: motionPath, | ||||
|           originalFileName: asset.originalFileName, | ||||
|           isVisible: false, | ||||
|           isReadOnly: false, | ||||
|   | ||||
| @@ -14,6 +14,7 @@ export interface AssetSearchOptions { | ||||
|   trashedBefore?: Date; | ||||
|   type?: AssetType; | ||||
|   order?: 'ASC' | 'DESC'; | ||||
|   withDeleted?: boolean; | ||||
| } | ||||
|  | ||||
| export interface LivePhotoSearchOptions { | ||||
|   | ||||
| @@ -1,40 +1,20 @@ | ||||
| import { | ||||
|   newAssetRepositoryMock, | ||||
|   newMoveRepositoryMock, | ||||
|   newPersonRepositoryMock, | ||||
|   newStorageRepositoryMock, | ||||
|   newSystemConfigRepositoryMock, | ||||
|   newUserRepositoryMock, | ||||
| } from '@test'; | ||||
| import { newStorageRepositoryMock, newSystemConfigRepositoryMock, newUserRepositoryMock } from '@test'; | ||||
| import { serverVersion } from '../domain.constant'; | ||||
| import { | ||||
|   IAssetRepository, | ||||
|   IMoveRepository, | ||||
|   IPersonRepository, | ||||
|   IStorageRepository, | ||||
|   ISystemConfigRepository, | ||||
|   IUserRepository, | ||||
| } from '../repositories'; | ||||
| import { IStorageRepository, ISystemConfigRepository, IUserRepository } from '../repositories'; | ||||
| import { ServerInfoService } from './server-info.service'; | ||||
|  | ||||
| describe(ServerInfoService.name, () => { | ||||
|   let sut: ServerInfoService; | ||||
|   let assetMock: jest.Mocked<IAssetRepository>; | ||||
|   let configMock: jest.Mocked<ISystemConfigRepository>; | ||||
|   let moveMock: jest.Mocked<IMoveRepository>; | ||||
|   let personMock: jest.Mocked<IPersonRepository>; | ||||
|   let storageMock: jest.Mocked<IStorageRepository>; | ||||
|   let userMock: jest.Mocked<IUserRepository>; | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     assetMock = newAssetRepositoryMock(); | ||||
|     configMock = newSystemConfigRepositoryMock(); | ||||
|     moveMock = newMoveRepositoryMock(); | ||||
|     personMock = newPersonRepositoryMock(); | ||||
|     storageMock = newStorageRepositoryMock(); | ||||
|     userMock = newUserRepositoryMock(); | ||||
|  | ||||
|     sut = new ServerInfoService(assetMock, configMock, moveMock, personMock, userMock, storageMock); | ||||
|     sut = new ServerInfoService(configMock, userMock, storageMock); | ||||
|   }); | ||||
|  | ||||
|   it('should work', () => { | ||||
|   | ||||
| @@ -1,15 +1,7 @@ | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import { mimeTypes, serverVersion } from '../domain.constant'; | ||||
| import { asHumanReadable } from '../domain.util'; | ||||
| import { | ||||
|   IAssetRepository, | ||||
|   IMoveRepository, | ||||
|   IPersonRepository, | ||||
|   IStorageRepository, | ||||
|   ISystemConfigRepository, | ||||
|   IUserRepository, | ||||
|   UserStatsQueryResponse, | ||||
| } from '../repositories'; | ||||
| import { IStorageRepository, ISystemConfigRepository, IUserRepository, UserStatsQueryResponse } from '../repositories'; | ||||
| import { StorageCore, StorageFolder } from '../storage'; | ||||
| import { SystemConfigCore } from '../system-config'; | ||||
| import { | ||||
| @@ -25,22 +17,17 @@ import { | ||||
| @Injectable() | ||||
| export class ServerInfoService { | ||||
|   private configCore: SystemConfigCore; | ||||
|   private storageCore: StorageCore; | ||||
|  | ||||
|   constructor( | ||||
|     @Inject(IAssetRepository) assetRepository: IAssetRepository, | ||||
|     @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, | ||||
|     @Inject(IMoveRepository) moveRepository: IMoveRepository, | ||||
|     @Inject(IPersonRepository) personRepository: IPersonRepository, | ||||
|     @Inject(IUserRepository) private userRepository: IUserRepository, | ||||
|     @Inject(IStorageRepository) private storageRepository: IStorageRepository, | ||||
|   ) { | ||||
|     this.configCore = SystemConfigCore.create(configRepository); | ||||
|     this.storageCore = new StorageCore(storageRepository, assetRepository, moveRepository, personRepository); | ||||
|   } | ||||
|  | ||||
|   async getInfo(): Promise<ServerInfoResponseDto> { | ||||
|     const libraryBase = this.storageCore.getBaseFolder(StorageFolder.LIBRARY); | ||||
|     const libraryBase = StorageCore.getBaseFolder(StorageFolder.LIBRARY); | ||||
|     const diskInfo = await this.storageRepository.checkDiskUsage(libraryBase); | ||||
|  | ||||
|     const usagePercentage = (((diskInfo.total - diskInfo.free) / diskInfo.total) * 100).toFixed(2); | ||||
|   | ||||
| @@ -90,7 +90,7 @@ export class StorageTemplateService { | ||||
|     } | ||||
|  | ||||
|     this.logger.debug('Cleaning up empty directories...'); | ||||
|     const libraryFolder = this.storageCore.getBaseFolder(StorageFolder.LIBRARY); | ||||
|     const libraryFolder = StorageCore.getBaseFolder(StorageFolder.LIBRARY); | ||||
|     await this.storageRepository.removeEmptyDirs(libraryFolder); | ||||
|  | ||||
|     this.logger.log('Finished storage template migration'); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { AssetEntity, AssetPathType, PathType, PersonEntity, PersonPathType } from '@app/infra/entities'; | ||||
| import { Logger } from '@nestjs/common'; | ||||
| import { dirname, join } from 'node:path'; | ||||
| import { dirname, join, resolve } from 'node:path'; | ||||
| import { APP_MEDIA_LOCATION } from '../domain.constant'; | ||||
| import { IAssetRepository, IMoveRepository, IPersonRepository, IStorageRepository } from '../repositories'; | ||||
|  | ||||
| @@ -32,14 +32,14 @@ export class StorageCore { | ||||
|   ) {} | ||||
|  | ||||
|   getFolderLocation(folder: StorageFolder, userId: string) { | ||||
|     return join(this.getBaseFolder(folder), userId); | ||||
|     return join(StorageCore.getBaseFolder(folder), userId); | ||||
|   } | ||||
|  | ||||
|   getLibraryFolder(user: { storageLabel: string | null; id: string }) { | ||||
|     return join(this.getBaseFolder(StorageFolder.LIBRARY), user.storageLabel || user.id); | ||||
|     return join(StorageCore.getBaseFolder(StorageFolder.LIBRARY), user.storageLabel || user.id); | ||||
|   } | ||||
|  | ||||
|   getBaseFolder(folder: StorageFolder) { | ||||
|   static getBaseFolder(folder: StorageFolder) { | ||||
|     return join(APP_MEDIA_LOCATION, folder); | ||||
|   } | ||||
|  | ||||
| @@ -64,7 +64,11 @@ export class StorageCore { | ||||
|   } | ||||
|  | ||||
|   isAndroidMotionPath(originalPath: string) { | ||||
|     return originalPath.startsWith(this.getBaseFolder(StorageFolder.ENCODED_VIDEO)); | ||||
|     return originalPath.startsWith(StorageCore.getBaseFolder(StorageFolder.ENCODED_VIDEO)); | ||||
|   } | ||||
|  | ||||
|   static isImmichPath(path: string) { | ||||
|     return resolve(path).startsWith(resolve(APP_MEDIA_LOCATION)); | ||||
|   } | ||||
|  | ||||
|   async moveAssetFile(asset: AssetEntity, pathType: GeneratedAssetPath) { | ||||
| @@ -135,7 +139,7 @@ export class StorageCore { | ||||
|   } | ||||
|  | ||||
|   removeEmptyDirs(folder: StorageFolder) { | ||||
|     return this.repository.removeEmptyDirs(this.getBaseFolder(folder)); | ||||
|     return this.repository.removeEmptyDirs(StorageCore.getBaseFolder(folder)); | ||||
|   } | ||||
|  | ||||
|   private savePath(pathType: PathType, id: string, newPath: string) { | ||||
|   | ||||
| @@ -1,25 +1,14 @@ | ||||
| import { | ||||
|   newAssetRepositoryMock, | ||||
|   newMoveRepositoryMock, | ||||
|   newPersonRepositoryMock, | ||||
|   newStorageRepositoryMock, | ||||
| } from '@test'; | ||||
| import { IAssetRepository, IMoveRepository, IPersonRepository, IStorageRepository } from '../repositories'; | ||||
| import { newStorageRepositoryMock } from '@test'; | ||||
| import { IStorageRepository } from '../repositories'; | ||||
| import { StorageService } from './storage.service'; | ||||
|  | ||||
| describe(StorageService.name, () => { | ||||
|   let sut: StorageService; | ||||
|   let assetMock: jest.Mocked<IAssetRepository>; | ||||
|   let moveMock: jest.Mocked<IMoveRepository>; | ||||
|   let personMock: jest.Mocked<IPersonRepository>; | ||||
|   let storageMock: jest.Mocked<IStorageRepository>; | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     assetMock = newAssetRepositoryMock(); | ||||
|     moveMock = newMoveRepositoryMock(); | ||||
|     personMock = newPersonRepositoryMock(); | ||||
|     storageMock = newStorageRepositoryMock(); | ||||
|     sut = new StorageService(assetMock, moveMock, personMock, storageMock); | ||||
|     sut = new StorageService(storageMock); | ||||
|   }); | ||||
|  | ||||
|   it('should work', () => { | ||||
|   | ||||
| @@ -1,24 +1,16 @@ | ||||
| import { Inject, Injectable, Logger } from '@nestjs/common'; | ||||
| import { IDeleteFilesJob } from '../job'; | ||||
| import { IAssetRepository, IMoveRepository, IPersonRepository, IStorageRepository } from '../repositories'; | ||||
| import { IStorageRepository } from '../repositories'; | ||||
| import { StorageCore, StorageFolder } from './storage.core'; | ||||
|  | ||||
| @Injectable() | ||||
| export class StorageService { | ||||
|   private logger = new Logger(StorageService.name); | ||||
|   private storageCore: StorageCore; | ||||
|  | ||||
|   constructor( | ||||
|     @Inject(IAssetRepository) assetRepository: IAssetRepository, | ||||
|     @Inject(IMoveRepository) private moveRepository: IMoveRepository, | ||||
|     @Inject(IPersonRepository) personRepository: IPersonRepository, | ||||
|     @Inject(IStorageRepository) private storageRepository: IStorageRepository, | ||||
|   ) { | ||||
|     this.storageCore = new StorageCore(storageRepository, assetRepository, moveRepository, personRepository); | ||||
|   } | ||||
|   constructor(@Inject(IStorageRepository) private storageRepository: IStorageRepository) {} | ||||
|  | ||||
|   init() { | ||||
|     const libraryBase = this.storageCore.getBaseFolder(StorageFolder.LIBRARY); | ||||
|     const libraryBase = StorageCore.getBaseFolder(StorageFolder.LIBRARY); | ||||
|     this.storageRepository.mkdirSync(libraryBase); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,16 @@ | ||||
| import { AuditDeletesDto, AuditDeletesResponseDto, AuditService, AuthUserDto } from '@app/domain'; | ||||
| import { Controller, Get, Query } from '@nestjs/common'; | ||||
| import { | ||||
|   AuditDeletesDto, | ||||
|   AuditDeletesResponseDto, | ||||
|   AuditService, | ||||
|   AuthUserDto, | ||||
|   FileChecksumDto, | ||||
|   FileChecksumResponseDto, | ||||
|   FileReportDto, | ||||
|   FileReportFixDto, | ||||
| } from '@app/domain'; | ||||
| import { Body, Controller, Get, Post, Query } from '@nestjs/common'; | ||||
| import { ApiTags } from '@nestjs/swagger'; | ||||
| import { AuthUser, Authenticated } from '../app.guard'; | ||||
| import { AdminRoute, AuthUser, Authenticated } from '../app.guard'; | ||||
| import { UseValidation } from '../app.utils'; | ||||
|  | ||||
| @ApiTags('Audit') | ||||
| @@ -15,4 +24,22 @@ export class AuditController { | ||||
|   getAuditDeletes(@AuthUser() authUser: AuthUserDto, @Query() dto: AuditDeletesDto): Promise<AuditDeletesResponseDto> { | ||||
|     return this.service.getDeletes(authUser, dto); | ||||
|   } | ||||
|  | ||||
|   @AdminRoute() | ||||
|   @Get('file-report') | ||||
|   getAuditFiles(): Promise<FileReportDto> { | ||||
|     return this.service.getFileReport(); | ||||
|   } | ||||
|  | ||||
|   @AdminRoute() | ||||
|   @Post('file-report/checksum') | ||||
|   getFileChecksums(@Body() dto: FileChecksumDto): Promise<FileChecksumResponseDto[]> { | ||||
|     return this.service.getChecksums(dto); | ||||
|   } | ||||
|  | ||||
|   @AdminRoute() | ||||
|   @Post('file-report/fix') | ||||
|   fixAuditFiles(@Body() dto: FileReportFixDto): Promise<void> { | ||||
|     return this.service.fixItems(dto.items); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -34,4 +34,8 @@ export enum PersonPathType { | ||||
|   FACE = 'face', | ||||
| } | ||||
|  | ||||
| export type PathType = AssetPathType | PersonPathType; | ||||
| export enum UserPathType { | ||||
|   PROFILE = 'profile', | ||||
| } | ||||
|  | ||||
| export type PathType = AssetPathType | PersonPathType | UserPathType; | ||||
|   | ||||
| @@ -174,7 +174,7 @@ export class AssetRepository implements IAssetRepository { | ||||
|           person: true, | ||||
|         }, | ||||
|       }, | ||||
|       withDeleted: !!options.trashedBefore, | ||||
|       withDeleted: options.withDeleted ?? !!options.trashedBefore, | ||||
|       order: { | ||||
|         // Ensures correct order when paginating | ||||
|         createdAt: options.order ?? 'ASC', | ||||
|   | ||||
| @@ -19,6 +19,7 @@ import { | ||||
|   SystemConfigApi, | ||||
|   UserApi, | ||||
|   UserApiFp, | ||||
|   AuditApi, | ||||
| } from './open-api'; | ||||
| import { BASE_PATH } from './open-api/base'; | ||||
| import { DUMMY_BASE_URL, toPathString } from './open-api/common'; | ||||
| @@ -28,6 +29,7 @@ export class ImmichApi { | ||||
|   public albumApi: AlbumApi; | ||||
|   public libraryApi: LibraryApi; | ||||
|   public assetApi: AssetApi; | ||||
|   public auditApi: AuditApi; | ||||
|   public authenticationApi: AuthenticationApi; | ||||
|   public jobApi: JobApi; | ||||
|   public keyApi: APIKeyApi; | ||||
| @@ -51,6 +53,7 @@ export class ImmichApi { | ||||
|     this.config = new Configuration(params); | ||||
|  | ||||
|     this.albumApi = new AlbumApi(this.config); | ||||
|     this.auditApi = new AuditApi(this.config); | ||||
|     this.libraryApi = new LibraryApi(this.config); | ||||
|     this.assetApi = new AssetApi(this.config); | ||||
|     this.authenticationApi = new AuthenticationApi(this.config); | ||||
|   | ||||
							
								
								
									
										378
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										378
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							| @@ -1604,6 +1604,109 @@ export interface ExifResponseDto { | ||||
|      */ | ||||
|     'timeZone'?: string | null; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface FileChecksumDto | ||||
|  */ | ||||
| export interface FileChecksumDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {Array<string>} | ||||
|      * @memberof FileChecksumDto | ||||
|      */ | ||||
|     'filenames': Array<string>; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface FileChecksumResponseDto | ||||
|  */ | ||||
| export interface FileChecksumResponseDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof FileChecksumResponseDto | ||||
|      */ | ||||
|     'checksum': string; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof FileChecksumResponseDto | ||||
|      */ | ||||
|     'filename': string; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface FileReportDto | ||||
|  */ | ||||
| export interface FileReportDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {Array<string>} | ||||
|      * @memberof FileReportDto | ||||
|      */ | ||||
|     'extras': Array<string>; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {Array<FileReportItemDto>} | ||||
|      * @memberof FileReportDto | ||||
|      */ | ||||
|     'orphans': Array<FileReportItemDto>; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface FileReportFixDto | ||||
|  */ | ||||
| export interface FileReportFixDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {Array<FileReportItemDto>} | ||||
|      * @memberof FileReportFixDto | ||||
|      */ | ||||
|     'items': Array<FileReportItemDto>; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface FileReportItemDto | ||||
|  */ | ||||
| export interface FileReportItemDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof FileReportItemDto | ||||
|      */ | ||||
|     'checksum'?: string; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof FileReportItemDto | ||||
|      */ | ||||
|     'entityId': string; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {PathEntityType} | ||||
|      * @memberof FileReportItemDto | ||||
|      */ | ||||
|     'entityType': PathEntityType; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {PathType} | ||||
|      * @memberof FileReportItemDto | ||||
|      */ | ||||
|     'pathType': PathType; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof FileReportItemDto | ||||
|      */ | ||||
|     'pathValue': string; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
| @@ -2186,6 +2289,40 @@ export interface OAuthConfigResponseDto { | ||||
|      */ | ||||
|     'url'?: string; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @enum {string} | ||||
|  */ | ||||
| 
 | ||||
| export const PathEntityType = { | ||||
|     Asset: 'asset', | ||||
|     Person: 'person', | ||||
|     User: 'user' | ||||
| } as const; | ||||
| 
 | ||||
| export type PathEntityType = typeof PathEntityType[keyof typeof PathEntityType]; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @enum {string} | ||||
|  */ | ||||
| 
 | ||||
| export const PathType = { | ||||
|     Original: 'original', | ||||
|     JpegThumbnail: 'jpeg_thumbnail', | ||||
|     WebpThumbnail: 'webp_thumbnail', | ||||
|     EncodedVideo: 'encoded_video', | ||||
|     Sidecar: 'sidecar', | ||||
|     Face: 'face', | ||||
|     Profile: 'profile' | ||||
| } as const; | ||||
| 
 | ||||
| export type PathType = typeof PathType[keyof typeof PathType]; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
| @@ -8821,6 +8958,50 @@ export class AssetApi extends BaseAPI { | ||||
|  */ | ||||
| export const AuditApiAxiosParamCreator = function (configuration?: Configuration) { | ||||
|     return { | ||||
|         /** | ||||
|          *  | ||||
|          * @param {FileReportFixDto} fileReportFixDto  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         fixAuditFiles: async (fileReportFixDto: FileReportFixDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             // verify required parameter 'fileReportFixDto' is not null or undefined
 | ||||
|             assertParamExists('fixAuditFiles', 'fileReportFixDto', fileReportFixDto) | ||||
|             const localVarPath = `/audit/file-report/fix`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
|             if (configuration) { | ||||
|                 baseOptions = configuration.baseOptions; | ||||
|             } | ||||
| 
 | ||||
|             const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; | ||||
|             const localVarHeaderParameter = {} as any; | ||||
|             const localVarQueryParameter = {} as any; | ||||
| 
 | ||||
|             // authentication cookie required
 | ||||
| 
 | ||||
|             // authentication api_key required
 | ||||
|             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||
| 
 | ||||
|             // authentication bearer required
 | ||||
|             // http bearer authentication required
 | ||||
|             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             localVarHeaderParameter['Content-Type'] = 'application/json'; | ||||
| 
 | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
|             localVarRequestOptions.data = serializeDataIfNeeded(fileReportFixDto, localVarRequestOptions, configuration) | ||||
| 
 | ||||
|             return { | ||||
|                 url: toPathString(localVarUrlObj), | ||||
|                 options: localVarRequestOptions, | ||||
|             }; | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {EntityType} entityType  | ||||
| @@ -8875,6 +9056,88 @@ export const AuditApiAxiosParamCreator = function (configuration?: Configuration | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
| 
 | ||||
|             return { | ||||
|                 url: toPathString(localVarUrlObj), | ||||
|                 options: localVarRequestOptions, | ||||
|             }; | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAuditFiles: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/audit/file-report`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
|             if (configuration) { | ||||
|                 baseOptions = configuration.baseOptions; | ||||
|             } | ||||
| 
 | ||||
|             const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; | ||||
|             const localVarHeaderParameter = {} as any; | ||||
|             const localVarQueryParameter = {} as any; | ||||
| 
 | ||||
|             // authentication cookie required
 | ||||
| 
 | ||||
|             // authentication api_key required
 | ||||
|             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||
| 
 | ||||
|             // authentication bearer required
 | ||||
|             // http bearer authentication required
 | ||||
|             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
| 
 | ||||
|             return { | ||||
|                 url: toPathString(localVarUrlObj), | ||||
|                 options: localVarRequestOptions, | ||||
|             }; | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {FileChecksumDto} fileChecksumDto  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getFileChecksums: async (fileChecksumDto: FileChecksumDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             // verify required parameter 'fileChecksumDto' is not null or undefined
 | ||||
|             assertParamExists('getFileChecksums', 'fileChecksumDto', fileChecksumDto) | ||||
|             const localVarPath = `/audit/file-report/checksum`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
|             if (configuration) { | ||||
|                 baseOptions = configuration.baseOptions; | ||||
|             } | ||||
| 
 | ||||
|             const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; | ||||
|             const localVarHeaderParameter = {} as any; | ||||
|             const localVarQueryParameter = {} as any; | ||||
| 
 | ||||
|             // authentication cookie required
 | ||||
| 
 | ||||
|             // authentication api_key required
 | ||||
|             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||
| 
 | ||||
|             // authentication bearer required
 | ||||
|             // http bearer authentication required
 | ||||
|             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             localVarHeaderParameter['Content-Type'] = 'application/json'; | ||||
| 
 | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
|             localVarRequestOptions.data = serializeDataIfNeeded(fileChecksumDto, localVarRequestOptions, configuration) | ||||
| 
 | ||||
|             return { | ||||
|                 url: toPathString(localVarUrlObj), | ||||
|                 options: localVarRequestOptions, | ||||
| @@ -8890,6 +9153,16 @@ export const AuditApiAxiosParamCreator = function (configuration?: Configuration | ||||
| export const AuditApiFp = function(configuration?: Configuration) { | ||||
|     const localVarAxiosParamCreator = AuditApiAxiosParamCreator(configuration) | ||||
|     return { | ||||
|         /** | ||||
|          *  | ||||
|          * @param {FileReportFixDto} fileReportFixDto  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async fixAuditFiles(fileReportFixDto: FileReportFixDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.fixAuditFiles(fileReportFixDto, options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {EntityType} entityType  | ||||
| @@ -8902,6 +9175,25 @@ export const AuditApiFp = function(configuration?: Configuration) { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAuditDeletes(entityType, after, userId, options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async getAuditFiles(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<FileReportDto>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAuditFiles(options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {FileChecksumDto} fileChecksumDto  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async getFileChecksums(fileChecksumDto: FileChecksumDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<FileChecksumResponseDto>>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getFileChecksums(fileChecksumDto, options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| @@ -8912,6 +9204,15 @@ export const AuditApiFp = function(configuration?: Configuration) { | ||||
| export const AuditApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { | ||||
|     const localVarFp = AuditApiFp(configuration) | ||||
|     return { | ||||
|         /** | ||||
|          *  | ||||
|          * @param {AuditApiFixAuditFilesRequest} requestParameters Request parameters. | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         fixAuditFiles(requestParameters: AuditApiFixAuditFilesRequest, options?: AxiosRequestConfig): AxiosPromise<void> { | ||||
|             return localVarFp.fixAuditFiles(requestParameters.fileReportFixDto, options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {AuditApiGetAuditDeletesRequest} requestParameters Request parameters. | ||||
| @@ -8921,9 +9222,40 @@ export const AuditApiFactory = function (configuration?: Configuration, basePath | ||||
|         getAuditDeletes(requestParameters: AuditApiGetAuditDeletesRequest, options?: AxiosRequestConfig): AxiosPromise<AuditDeletesResponseDto> { | ||||
|             return localVarFp.getAuditDeletes(requestParameters.entityType, requestParameters.after, requestParameters.userId, options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAuditFiles(options?: AxiosRequestConfig): AxiosPromise<FileReportDto> { | ||||
|             return localVarFp.getAuditFiles(options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {AuditApiGetFileChecksumsRequest} requestParameters Request parameters. | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getFileChecksums(requestParameters: AuditApiGetFileChecksumsRequest, options?: AxiosRequestConfig): AxiosPromise<Array<FileChecksumResponseDto>> { | ||||
|             return localVarFp.getFileChecksums(requestParameters.fileChecksumDto, options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Request parameters for fixAuditFiles operation in AuditApi. | ||||
|  * @export | ||||
|  * @interface AuditApiFixAuditFilesRequest | ||||
|  */ | ||||
| export interface AuditApiFixAuditFilesRequest { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {FileReportFixDto} | ||||
|      * @memberof AuditApiFixAuditFiles | ||||
|      */ | ||||
|     readonly fileReportFixDto: FileReportFixDto | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Request parameters for getAuditDeletes operation in AuditApi. | ||||
|  * @export | ||||
| @@ -8952,6 +9284,20 @@ export interface AuditApiGetAuditDeletesRequest { | ||||
|     readonly userId?: string | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Request parameters for getFileChecksums operation in AuditApi. | ||||
|  * @export | ||||
|  * @interface AuditApiGetFileChecksumsRequest | ||||
|  */ | ||||
| export interface AuditApiGetFileChecksumsRequest { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {FileChecksumDto} | ||||
|      * @memberof AuditApiGetFileChecksums | ||||
|      */ | ||||
|     readonly fileChecksumDto: FileChecksumDto | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * AuditApi - object-oriented interface | ||||
|  * @export | ||||
| @@ -8959,6 +9305,17 @@ export interface AuditApiGetAuditDeletesRequest { | ||||
|  * @extends {BaseAPI} | ||||
|  */ | ||||
| export class AuditApi extends BaseAPI { | ||||
|     /** | ||||
|      *  | ||||
|      * @param {AuditApiFixAuditFilesRequest} requestParameters Request parameters. | ||||
|      * @param {*} [options] Override http request option. | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AuditApi | ||||
|      */ | ||||
|     public fixAuditFiles(requestParameters: AuditApiFixAuditFilesRequest, options?: AxiosRequestConfig) { | ||||
|         return AuditApiFp(this.configuration).fixAuditFiles(requestParameters.fileReportFixDto, options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @param {AuditApiGetAuditDeletesRequest} requestParameters Request parameters. | ||||
| @@ -8969,6 +9326,27 @@ export class AuditApi extends BaseAPI { | ||||
|     public getAuditDeletes(requestParameters: AuditApiGetAuditDeletesRequest, options?: AxiosRequestConfig) { | ||||
|         return AuditApiFp(this.configuration).getAuditDeletes(requestParameters.entityType, requestParameters.after, requestParameters.userId, options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @param {*} [options] Override http request option. | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AuditApi | ||||
|      */ | ||||
|     public getAuditFiles(options?: AxiosRequestConfig) { | ||||
|         return AuditApiFp(this.configuration).getAuditFiles(options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @param {AuditApiGetFileChecksumsRequest} requestParameters Request parameters. | ||||
|      * @param {*} [options] Override http request option. | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AuditApi | ||||
|      */ | ||||
|     public getFileChecksums(requestParameters: AuditApiGetFileChecksumsRequest, options?: AxiosRequestConfig) { | ||||
|         return AuditApiFp(this.configuration).getFileChecksums(requestParameters.fileChecksumDto, options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|   | ||||
							
								
								
									
										1
									
								
								web/src/lib/assets/empty-4.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								web/src/lib/assets/empty-4.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg width="900" height="600" viewBox="0 0 900 600" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="transparent" d="M0 0h900v600H0z"/><path d="M214.359 475.389c16.42 16.712 47.124 13.189 47.124 13.189s4.064-30.62-12.372-47.322c-16.419-16.712-47.109-13.198-47.109-13.198s-4.063 30.619 12.357 47.331z" fill="url(#a)"/><path d="M639.439 125.517c-17.194 9.808-41.345-.121-41.345-.121s3.743-25.827 20.946-35.623c17.194-9.808 41.335.11 41.335.11s-3.743 25.827-20.936 35.634z" fill="url(#b)"/><path d="M324.812 156.133c-17.672 17.987-50.72 14.194-50.72 14.194s-4.373-32.955 13.316-50.931c17.673-17.987 50.704-14.206 50.704-14.206s4.373 32.956-13.3 50.943z" fill="url(#c)"/><ellipse rx="15.17" ry="15.928" transform="matrix(1 0 0 -1 228.07 341.957)" fill="#E1E4E5"/><circle r="8.5" transform="matrix(1 0 0 -1 478.5 509.5)" fill="#9d9ea3"/><circle r="17.518" transform="matrix(1 0 0 -1 693.518 420.518)" fill="#9d9ea3"/><circle cx="708.183" cy="266.183" r="14.183" fill="#4F4F51"/><circle cx="247.603" cy="225.621" r="12.136" fill="#F8AE9D"/><ellipse cx="316.324" cy="510.867" rx="7.324" ry="6.867" fill="#E1E4E5"/><ellipse cx="664.796" cy="371.388" rx="9.796" ry="9.388" fill="#E1E4E5"/><circle cx="625.378" cy="479.378" r="11.377" fill="#E1E4E5"/><ellipse cx="401.025" cy="114.39" rx="5.309" ry="6.068" fill="#E1E4E5"/><circle cx="661.834" cy="300.834" r="5.58" transform="rotate(105 661.834 300.834)" fill="#E1E4E5"/><circle cx="654.769" cy="226.082" r="7.585" fill="#E1E4E5"/><ellipse cx="254.159" cy="284.946" rx="5.309" ry="4.551" fill="#E1E4E5"/><circle cx="521.363" cy="106.27" r="11.613" transform="rotate(105 521.363 106.27)" fill="#E1E4E5"/><path d="M162.314 308.103h-.149C161.284 320.589 152 320.781 152 320.781s10.238.2 10.238 14.628c0-14.428 10.238-14.628 10.238-14.628s-9.281-.192-10.162-12.678zm531.83-158.512h-.256c-1.518 21.504-17.507 21.835-17.507 21.835s17.632.345 17.632 25.192c0-24.847 17.632-25.192 17.632-25.192s-15.983-.331-17.501-21.835z" fill="#E1E4E5"/><path fill-rule="evenodd" clip-rule="evenodd" d="M553.714 397.505v56.123c0 20.672-16.743 37.416-37.415 37.416H329.22c-20.672 0-37.415-16.744-37.415-37.416V266.55c0-20.672 16.743-37.416 37.415-37.416h56.124" fill="url(#d)"/><path fill-rule="evenodd" clip-rule="evenodd" d="M363.07 155.431h214.049c26.28 0 47.566 21.286 47.566 47.566v214.049c0 26.28-21.286 47.566-47.566 47.566H363.07c-26.28 0-47.566-21.286-47.566-47.566V202.997c0-26.28 21.286-47.566 47.566-47.566z" fill="#9d9ea3"/><path d="m425.113 307.765 33.925 33.924 74.038-74.059" stroke="#fff" stroke-width="32.125" stroke-linecap="round" stroke-linejoin="round"/><defs><linearGradient id="a" x1="279.871" y1="532.474" x2="161.165" y2="346.391" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#EEE"/></linearGradient><linearGradient id="b" x1="573.046" y1="156.85" x2="712.364" y2="32.889" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#EEE"/></linearGradient><linearGradient id="c" x1="254.302" y1="217.573" x2="382.065" y2="17.293" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#EEE"/></linearGradient><linearGradient id="d" x1="417.175" y1="82.293" x2="425.251" y2="775.957" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#EEE"/></linearGradient></defs></svg> | ||||
| After Width: | Height: | Size: 3.3 KiB | 
| @@ -6,8 +6,9 @@ | ||||
|   import Button from './button.svelte'; | ||||
|  | ||||
|   export let color: Color = 'transparent-gray'; | ||||
|   export let disabled = false; | ||||
| </script> | ||||
|  | ||||
| <Button size="link" {color} shadow={false} rounded="lg" on:click> | ||||
| <Button size="link" {color} shadow={false} rounded="lg" {disabled} on:click> | ||||
|   <slot /> | ||||
| </Button> | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
|   import AccountMultipleOutline from 'svelte-material-icons/AccountMultipleOutline.svelte'; | ||||
|   import Cog from 'svelte-material-icons/Cog.svelte'; | ||||
|   import Server from 'svelte-material-icons/Server.svelte'; | ||||
|   import Tools from 'svelte-material-icons/Tools.svelte'; | ||||
|   import Sync from 'svelte-material-icons/Sync.svelte'; | ||||
| </script> | ||||
|  | ||||
| @@ -27,6 +28,9 @@ | ||||
|   <a data-sveltekit-preload-data="hover" href={AppRoute.ADMIN_STATS} draggable="false"> | ||||
|     <SideBarButton title="Server Stats" logo={Server} isSelected={$page.route.id === AppRoute.ADMIN_STATS} /> | ||||
|   </a> | ||||
|   <a data-sveltekit-preload-data="hover" href={AppRoute.ADMIN_REPAIR} draggable="false"> | ||||
|     <SideBarButton title="Repair" logo={Tools} isSelected={$page.route.id === AppRoute.ADMIN_REPAIR} /> | ||||
|   </a> | ||||
|   <div class="mb-6 mt-auto"> | ||||
|     <StatusBox /> | ||||
|   </div> | ||||
|   | ||||
| @@ -12,6 +12,7 @@ export enum AppRoute { | ||||
|   ADMIN_SETTINGS = '/admin/system-settings', | ||||
|   ADMIN_STATS = '/admin/server-status', | ||||
|   ADMIN_JOBS = '/admin/jobs-status', | ||||
|   ADMIN_REPAIR = '/admin/repair', | ||||
|  | ||||
|   ALBUMS = '/albums', | ||||
|   LIBRARIES = '/libraries', | ||||
|   | ||||
							
								
								
									
										26
									
								
								web/src/routes/admin/repair/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								web/src/routes/admin/repair/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import { AppRoute } from '$lib/constants'; | ||||
| import { redirect } from '@sveltejs/kit'; | ||||
| import type { PageServerLoad } from './$types'; | ||||
|  | ||||
| export const load = (async ({ parent, locals: { api } }) => { | ||||
|   const { user } = await parent(); | ||||
|  | ||||
|   if (!user) { | ||||
|     throw redirect(302, AppRoute.AUTH_LOGIN); | ||||
|   } else if (!user.isAdmin) { | ||||
|     throw redirect(302, AppRoute.PHOTOS); | ||||
|   } | ||||
|  | ||||
|   const { | ||||
|     data: { orphans, extras }, | ||||
|   } = await api.auditApi.getAuditFiles(); | ||||
|  | ||||
|   return { | ||||
|     user, | ||||
|     orphans, | ||||
|     extras, | ||||
|     meta: { | ||||
|       title: 'Repair', | ||||
|     }, | ||||
|   }; | ||||
| }) satisfies PageServerLoad; | ||||
							
								
								
									
										336
									
								
								web/src/routes/admin/repair/+page.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								web/src/routes/admin/repair/+page.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,336 @@ | ||||
| <script lang="ts"> | ||||
|   import empty4Url from '$lib/assets/empty-4.svg'; | ||||
|   import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; | ||||
|   import LinkButton from '$lib/components/elements/buttons/link-button.svelte'; | ||||
|   import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte'; | ||||
|   import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; | ||||
|   import { | ||||
|     NotificationType, | ||||
|     notificationController, | ||||
|   } from '$lib/components/shared-components/notification/notification'; | ||||
|   import { downloadManager } from '$lib/stores/download'; | ||||
|   import { downloadBlob } from '$lib/utils/asset-utils'; | ||||
|   import { handleError } from '$lib/utils/handle-error'; | ||||
|   import { FileReportItemDto, api, copyToClipboard } from '@api'; | ||||
|   import CheckAll from 'svelte-material-icons/CheckAll.svelte'; | ||||
|   import ContentCopy from 'svelte-material-icons/ContentCopy.svelte'; | ||||
|   import Download from 'svelte-material-icons/Download.svelte'; | ||||
|   import Refresh from 'svelte-material-icons/Refresh.svelte'; | ||||
|   import Wrench from 'svelte-material-icons/Wrench.svelte'; | ||||
|   import type { PageData } from './$types'; | ||||
|  | ||||
|   export let data: PageData; | ||||
|  | ||||
|   interface UntrackedFile { | ||||
|     filename: string; | ||||
|     checksum: string | null; | ||||
|   } | ||||
|  | ||||
|   interface Match { | ||||
|     orphan: FileReportItemDto; | ||||
|     extra: UntrackedFile; | ||||
|   } | ||||
|  | ||||
|   const normalize = (filenames: string[]) => filenames.map((filename) => ({ filename, checksum: null })); | ||||
|  | ||||
|   let checking = false; | ||||
|   let repairing = false; | ||||
|  | ||||
|   let orphans: FileReportItemDto[] = data.orphans; | ||||
|   let extras: UntrackedFile[] = normalize(data.extras); | ||||
|   let matches: Match[] = []; | ||||
|  | ||||
|   const handleDownload = () => { | ||||
|     if (extras.length > 0) { | ||||
|       const blob = new Blob([extras.map(({ filename }) => filename).join('\n')], { type: 'text/plain' }); | ||||
|       const downloadKey = 'untracked.txt'; | ||||
|       downloadManager.add(downloadKey, blob.size); | ||||
|       downloadManager.update(downloadKey, blob.size); | ||||
|       downloadBlob(blob, downloadKey); | ||||
|       setTimeout(() => downloadManager.clear(downloadKey), 5_000); | ||||
|     } | ||||
|  | ||||
|     if (orphans.length > 0) { | ||||
|       const blob = new Blob([JSON.stringify(orphans, null, 4)], { type: 'application/json' }); | ||||
|       const downloadKey = 'orphans.json'; | ||||
|       downloadManager.add(downloadKey, blob.size); | ||||
|       downloadManager.update(downloadKey, blob.size); | ||||
|       downloadBlob(blob, downloadKey); | ||||
|       setTimeout(() => downloadManager.clear(downloadKey), 5_000); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const handleRepair = async () => { | ||||
|     if (matches.length === 0) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     repairing = true; | ||||
|  | ||||
|     try { | ||||
|       await api.auditApi.fixAuditFiles({ | ||||
|         fileReportFixDto: { | ||||
|           items: matches.map(({ orphan, extra }) => ({ | ||||
|             entityId: orphan.entityId, | ||||
|             entityType: orphan.entityType, | ||||
|             pathType: orphan.pathType, | ||||
|             pathValue: extra.filename, | ||||
|           })), | ||||
|         }, | ||||
|       }); | ||||
|  | ||||
|       notificationController.show({ | ||||
|         type: NotificationType.Info, | ||||
|         message: `Repaired ${matches.length} items`, | ||||
|       }); | ||||
|  | ||||
|       matches = []; | ||||
|     } catch (error) { | ||||
|       handleError(error, 'Unable to repair items'); | ||||
|     } finally { | ||||
|       repairing = false; | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const handleSplit = (match: Match) => { | ||||
|     matches = matches.filter((_match) => _match !== match); | ||||
|     orphans = [match.orphan, ...orphans]; | ||||
|     extras = [match.extra, ...extras]; | ||||
|   }; | ||||
|  | ||||
|   const handleRefresh = async () => { | ||||
|     matches = []; | ||||
|     orphans = []; | ||||
|     extras = []; | ||||
|  | ||||
|     try { | ||||
|       const { data: report } = await api.auditApi.getAuditFiles(); | ||||
|  | ||||
|       orphans = report.orphans; | ||||
|       extras = normalize(report.extras); | ||||
|  | ||||
|       notificationController.show({ message: 'Refreshed', type: NotificationType.Info }); | ||||
|     } catch (error) { | ||||
|       handleError(error, 'Unable to load items'); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const handleCheckOne = async (filename: string) => { | ||||
|     try { | ||||
|       const matched = await loadAndMatch([filename]); | ||||
|       if (matched) { | ||||
|         notificationController.show({ message: `Matched 1 item`, type: NotificationType.Info }); | ||||
|       } | ||||
|     } catch (error) { | ||||
|       handleError(error, 'Unable to check item'); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const handleCheckAll = async () => { | ||||
|     checking = true; | ||||
|  | ||||
|     let count = 0; | ||||
|  | ||||
|     try { | ||||
|       const chunkSize = 10; | ||||
|       const filenames = [...extras.filter(({ checksum }) => !checksum).map(({ filename }) => filename)]; | ||||
|       for (let i = 0; i < filenames.length; i += chunkSize) { | ||||
|         count += await loadAndMatch(filenames.slice(i, i + chunkSize)); | ||||
|       } | ||||
|     } catch (error) { | ||||
|       handleError(error, 'Unable to check items'); | ||||
|     } finally { | ||||
|       checking = false; | ||||
|     } | ||||
|  | ||||
|     notificationController.show({ message: `Matched ${count} items`, type: NotificationType.Info }); | ||||
|   }; | ||||
|  | ||||
|   const loadAndMatch = async (filenames: string[]) => { | ||||
|     const { data: items } = await api.auditApi.getFileChecksums({ | ||||
|       fileChecksumDto: { filenames }, | ||||
|     }); | ||||
|  | ||||
|     let count = 0; | ||||
|  | ||||
|     for (const { checksum, filename } of items) { | ||||
|       const extra = extras.find((extra) => extra.filename === filename); | ||||
|       if (extra) { | ||||
|         extra.checksum = checksum; | ||||
|         extras = [...extras]; | ||||
|       } | ||||
|  | ||||
|       const orphan = orphans.find((orphan) => orphan.checksum === checksum); | ||||
|       if (orphan) { | ||||
|         count++; | ||||
|         matches = [...matches, { orphan, extra: { filename, checksum } }]; | ||||
|         orphans = orphans.filter((_orphan) => _orphan !== orphan); | ||||
|         extras = extras.filter((extra) => extra.filename !== filename); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return count; | ||||
|   }; | ||||
| </script> | ||||
|  | ||||
| <UserPageLayout user={data.user} title={data.meta.title} admin> | ||||
|   <svelte:fragment slot="sidebar" /> | ||||
|   <div class="flex justify-end gap-2" slot="buttons"> | ||||
|     <LinkButton on:click={() => handleRepair()} disabled={matches.length === 0 || repairing}> | ||||
|       <div class="flex place-items-center gap-2 text-sm"> | ||||
|         <Wrench size="18" /> | ||||
|         Repair All | ||||
|       </div> | ||||
|     </LinkButton> | ||||
|     <LinkButton on:click={() => handleCheckAll()} disabled={extras.length === 0 || checking}> | ||||
|       <div class="flex place-items-center gap-2 text-sm"> | ||||
|         <CheckAll size="18" /> | ||||
|         Check All | ||||
|       </div> | ||||
|     </LinkButton> | ||||
|     <LinkButton on:click={() => handleDownload()} disabled={extras.length + orphans.length === 0}> | ||||
|       <div class="flex place-items-center gap-2 text-sm"> | ||||
|         <Download size="18" /> | ||||
|         Export | ||||
|       </div> | ||||
|     </LinkButton> | ||||
|     <LinkButton on:click={() => handleRefresh()}> | ||||
|       <div class="flex place-items-center gap-2 text-sm"> | ||||
|         <Refresh size="18" /> | ||||
|         Refresh | ||||
|       </div> | ||||
|     </LinkButton> | ||||
|   </div> | ||||
|   <section id="setting-content" class="flex place-content-center sm:mx-4"> | ||||
|     <section class="w-full pb-28 sm:w-5/6 md:w-[850px]"> | ||||
|       {#if matches.length + extras.length + orphans.length === 0} | ||||
|         <div class="w-full"> | ||||
|           <EmptyPlaceholder | ||||
|             fullWidth | ||||
|             text="Untracked and missing files will show up here" | ||||
|             alt="Empty report" | ||||
|             src={empty4Url} | ||||
|           /> | ||||
|         </div> | ||||
|       {:else} | ||||
|         <div class="gap-2"> | ||||
|           <table class="table-fixed mt-5 w-full text-left"> | ||||
|             <thead | ||||
|               class="mb-4 flex w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" | ||||
|             > | ||||
|               <tr class="flex w-full place-items-center p-2 md:p-5"> | ||||
|                 <th class="w-full text-sm place-items-center font-medium flex justify-between" colspan="2"> | ||||
|                   <div class="px-3"> | ||||
|                     <p>MATCHES {matches.length ? `(${matches.length})` : ''}</p> | ||||
|                     <p class="text-gray-600 dark:text-gray-300 mt-1">These files are matched by their checksums</p> | ||||
|                   </div> | ||||
|                 </th> | ||||
|               </tr> | ||||
|             </thead> | ||||
|             <tbody | ||||
|               class="w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg max-h-[500px] block overflow-x-hidden" | ||||
|             > | ||||
|               {#each matches as match (match.extra.filename)} | ||||
|                 <tr | ||||
|                   class="w-full h-[75px] place-items-center border-[3px] border-transparent p-2 odd:bg-immich-gray even:bg-immich-bg hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5 flex justify-between" | ||||
|                   tabindex="0" | ||||
|                   on:click={() => handleSplit(match)} | ||||
|                 > | ||||
|                   <td class="text-sm text-ellipsis flex flex-col gap-1 font-mono"> | ||||
|                     <span>{match.orphan.pathValue} =></span> | ||||
|                     <span>{match.extra.filename}</span> | ||||
|                   </td> | ||||
|                   <td class="text-sm text-ellipsis d-flex font-mono"> | ||||
|                     <span>({match.orphan.entityType}/{match.orphan.pathType})</span> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|               {/each} | ||||
|             </tbody> | ||||
|           </table> | ||||
|  | ||||
|           <table class="table-fixed mt-5 w-full text-left"> | ||||
|             <thead | ||||
|               class="mb-4 flex w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" | ||||
|             > | ||||
|               <tr class="flex w-full place-items-center p-1 md:p-5"> | ||||
|                 <th class="w-full text-sm font-medium justify-between place-items-center flex" colspan="2"> | ||||
|                   <div class="px-3"> | ||||
|                     <p>OFFLINE PATHS {orphans.length ? `(${orphans.length})` : ''}</p> | ||||
|                     <p class="text-gray-600 dark:text-gray-300 mt-1"> | ||||
|                       These files are the results of manually deletion of the default upload library | ||||
|                     </p> | ||||
|                   </div> | ||||
|                 </th> | ||||
|               </tr> | ||||
|             </thead> | ||||
|             <tbody | ||||
|               class="w-full rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg overflow-y-auto max-h-[500px] block overflow-x-hidden" | ||||
|             > | ||||
|               {#each orphans as orphan, index (index)} | ||||
|                 <tr | ||||
|                   class="w-full h-[50px] place-items-center border-[3px] border-transparent odd:bg-immich-gray even:bg-immich-bg hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5 flex justify-between" | ||||
|                   tabindex="0" | ||||
|                   title={orphan.pathValue} | ||||
|                 > | ||||
|                   <td on:click={() => copyToClipboard(orphan.pathValue)}> | ||||
|                     <CircleIconButton logo={ContentCopy} size="18" /> | ||||
|                   </td> | ||||
|                   <td class="truncate text-sm font-mono text-left" title={orphan.pathValue}> | ||||
|                     {orphan.pathValue} | ||||
|                   </td> | ||||
|                   <td class="text-sm font-mono"> | ||||
|                     <span>({orphan.entityType})</span> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|               {/each} | ||||
|             </tbody> | ||||
|           </table> | ||||
|  | ||||
|           <table class="table-fixed mt-5 w-full text-left max-h-[300px]"> | ||||
|             <thead | ||||
|               class="mb-4 flex w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" | ||||
|             > | ||||
|               <tr class="flex w-full place-items-center p-2 md:p-5"> | ||||
|                 <th class="w-full text-sm font-medium place-items-center flex justify-between" colspan="2"> | ||||
|                   <div class="px-3"> | ||||
|                     <p>UNTRACKS FILES {extras.length ? `(${extras.length})` : ''}</p> | ||||
|                     <p class="text-gray-600 dark:text-gray-300 mt-1"> | ||||
|                       These files are not tracked by the application. They can be the results of failed moves, | ||||
|                       interrupted uploads, or left behind due to a bug | ||||
|                     </p> | ||||
|                   </div> | ||||
|                 </th> | ||||
|               </tr> | ||||
|             </thead> | ||||
|             <tbody | ||||
|               class="w-full rounded-md border-2 dark:border-immich-dark-gray dark:text-immich-dark-fg overflow-y-auto max-h-[500px] block overflow-x-hidden" | ||||
|             > | ||||
|               {#each extras as extra (extra.filename)} | ||||
|                 <tr | ||||
|                   class="flex h-[50px] w-full place-items-center border-[3px] border-transparent p-1 odd:bg-immich-gray even:bg-immich-bg hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5 justify-between" | ||||
|                   tabindex="0" | ||||
|                   on:click={() => handleCheckOne(extra.filename)} | ||||
|                   title={extra.filename} | ||||
|                 > | ||||
|                   <td on:click={() => copyToClipboard(extra.filename)}> | ||||
|                     <CircleIconButton logo={ContentCopy} size="18" /> | ||||
|                   </td> | ||||
|                   <td class="w-full text-md text-ellipsis flex justify-between pr-5"> | ||||
|                     <span class="text-ellipsis grow truncate font-mono text-sm pr-5" title={extra.filename} | ||||
|                       >{extra.filename}</span | ||||
|                     > | ||||
|                     <span class="text-sm font-mono dark:text-immich-dark-primary text-immich-primary pr-5"> | ||||
|                       {#if extra.checksum} | ||||
|                         [sha1:{extra.checksum}] | ||||
|                       {/if} | ||||
|                     </span> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|               {/each} | ||||
|             </tbody> | ||||
|           </table> | ||||
|         </div> | ||||
|       {/if} | ||||
|     </section> | ||||
|   </section> | ||||
| </UserPageLayout> | ||||
		Reference in New Issue
	
	Block a user