Merge branch 'main' of github.com:immich-app/immich into 4382-thumbnail-metadata
This commit is contained in:
commit
259ed35b62
89 changed files with 3436 additions and 237 deletions
392
cli/src/api/open-api/api.ts
generated
392
cli/src/api/open-api/api.ts
generated
|
@ -640,6 +640,12 @@ export interface AssetResponseDto {
|
|||
* @memberof AssetResponseDto
|
||||
*/
|
||||
'fileModifiedAt': string;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof AssetResponseDto
|
||||
*/
|
||||
'hasMetadata': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
|
@ -749,7 +755,7 @@ export interface AssetResponseDto {
|
|||
*/
|
||||
'tags'?: Array<TagResponseDto>;
|
||||
/**
|
||||
* base64 encoded thumbhash
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AssetResponseDto
|
||||
*/
|
||||
|
@ -1598,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
|
||||
|
@ -2180,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
|
||||
|
@ -2882,7 +3025,7 @@ export interface SharedLinkCreateDto {
|
|||
* @type {boolean}
|
||||
* @memberof SharedLinkCreateDto
|
||||
*/
|
||||
'showExif'?: boolean;
|
||||
'showMetadata'?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {SharedLinkType}
|
||||
|
@ -2927,7 +3070,7 @@ export interface SharedLinkEditDto {
|
|||
* @type {boolean}
|
||||
* @memberof SharedLinkEditDto
|
||||
*/
|
||||
'showExif'?: boolean;
|
||||
'showMetadata'?: boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
|
@ -2994,7 +3137,7 @@ export interface SharedLinkResponseDto {
|
|||
* @type {boolean}
|
||||
* @memberof SharedLinkResponseDto
|
||||
*/
|
||||
'showExif': boolean;
|
||||
'showMetadata': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {SharedLinkType}
|
||||
|
@ -8815,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
|
||||
|
@ -8869,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,
|
||||
|
@ -8884,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
|
||||
|
@ -8896,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);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8906,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.
|
||||
|
@ -8915,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
|
||||
|
@ -8946,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
|
||||
|
@ -8953,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.
|
||||
|
@ -8963,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)
|
||||
|
|
3
mobile/openapi/doc/AssetResponseDto.md
generated
3
mobile/openapi/doc/AssetResponseDto.md
generated
|
@ -15,6 +15,7 @@ Name | Type | Description | Notes
|
|||
**exifInfo** | [**ExifResponseDto**](ExifResponseDto.md) | | [optional]
|
||||
**fileCreatedAt** | [**DateTime**](DateTime.md) | |
|
||||
**fileModifiedAt** | [**DateTime**](DateTime.md) | |
|
||||
**hasMetadata** | **bool** | |
|
||||
**id** | **String** | |
|
||||
**isArchived** | **bool** | |
|
||||
**isExternal** | **bool** | |
|
||||
|
@ -33,7 +34,7 @@ Name | Type | Description | Notes
|
|||
**resized** | **bool** | |
|
||||
**smartInfo** | [**SmartInfoResponseDto**](SmartInfoResponseDto.md) | | [optional]
|
||||
**tags** | [**List<TagResponseDto>**](TagResponseDto.md) | | [optional] [default to const []]
|
||||
**thumbhash** | **String** | base64 encoded thumbhash |
|
||||
**thumbhash** | **String** | |
|
||||
**type** | [**AssetTypeEnum**](AssetTypeEnum.md) | |
|
||||
**updatedAt** | [**DateTime**](DateTime.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)
|
||||
|
||||
|
2
mobile/openapi/doc/SharedLinkCreateDto.md
generated
2
mobile/openapi/doc/SharedLinkCreateDto.md
generated
|
@ -14,7 +14,7 @@ Name | Type | Description | Notes
|
|||
**assetIds** | **List<String>** | | [optional] [default to const []]
|
||||
**description** | **String** | | [optional]
|
||||
**expiresAt** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**showExif** | **bool** | | [optional] [default to true]
|
||||
**showMetadata** | **bool** | | [optional] [default to true]
|
||||
**type** | [**SharedLinkType**](SharedLinkType.md) | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
|
2
mobile/openapi/doc/SharedLinkEditDto.md
generated
2
mobile/openapi/doc/SharedLinkEditDto.md
generated
|
@ -12,7 +12,7 @@ Name | Type | Description | Notes
|
|||
**allowUpload** | **bool** | | [optional]
|
||||
**description** | **String** | | [optional]
|
||||
**expiresAt** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**showExif** | **bool** | | [optional]
|
||||
**showMetadata** | **bool** | | [optional]
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
|
2
mobile/openapi/doc/SharedLinkResponseDto.md
generated
2
mobile/openapi/doc/SharedLinkResponseDto.md
generated
|
@ -17,7 +17,7 @@ Name | Type | Description | Notes
|
|||
**expiresAt** | [**DateTime**](DateTime.md) | |
|
||||
**id** | **String** | |
|
||||
**key** | **String** | |
|
||||
**showExif** | **bool** | |
|
||||
**showMetadata** | **bool** | |
|
||||
**type** | [**SharedLinkType**](SharedLinkType.md) | |
|
||||
**userId** | **String** | |
|
||||
|
||||
|
|
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();
|
||||
}
|
||||
|
|
11
mobile/openapi/lib/model/asset_response_dto.dart
generated
11
mobile/openapi/lib/model/asset_response_dto.dart
generated
|
@ -20,6 +20,7 @@ class AssetResponseDto {
|
|||
this.exifInfo,
|
||||
required this.fileCreatedAt,
|
||||
required this.fileModifiedAt,
|
||||
required this.hasMetadata,
|
||||
required this.id,
|
||||
required this.isArchived,
|
||||
required this.isExternal,
|
||||
|
@ -64,6 +65,8 @@ class AssetResponseDto {
|
|||
|
||||
DateTime fileModifiedAt;
|
||||
|
||||
bool hasMetadata;
|
||||
|
||||
String id;
|
||||
|
||||
bool isArchived;
|
||||
|
@ -112,7 +115,6 @@ class AssetResponseDto {
|
|||
|
||||
List<TagResponseDto> tags;
|
||||
|
||||
/// base64 encoded thumbhash
|
||||
String? thumbhash;
|
||||
|
||||
AssetTypeEnum type;
|
||||
|
@ -128,6 +130,7 @@ class AssetResponseDto {
|
|||
other.exifInfo == exifInfo &&
|
||||
other.fileCreatedAt == fileCreatedAt &&
|
||||
other.fileModifiedAt == fileModifiedAt &&
|
||||
other.hasMetadata == hasMetadata &&
|
||||
other.id == id &&
|
||||
other.isArchived == isArchived &&
|
||||
other.isExternal == isExternal &&
|
||||
|
@ -160,6 +163,7 @@ class AssetResponseDto {
|
|||
(exifInfo == null ? 0 : exifInfo!.hashCode) +
|
||||
(fileCreatedAt.hashCode) +
|
||||
(fileModifiedAt.hashCode) +
|
||||
(hasMetadata.hashCode) +
|
||||
(id.hashCode) +
|
||||
(isArchived.hashCode) +
|
||||
(isExternal.hashCode) +
|
||||
|
@ -183,7 +187,7 @@ class AssetResponseDto {
|
|||
(updatedAt.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isArchived=$isArchived, isExternal=$isExternal, isFavorite=$isFavorite, isOffline=$isOffline, isReadOnly=$isReadOnly, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]';
|
||||
String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isExternal=$isExternal, isFavorite=$isFavorite, isOffline=$isOffline, isReadOnly=$isReadOnly, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -198,6 +202,7 @@ class AssetResponseDto {
|
|||
}
|
||||
json[r'fileCreatedAt'] = this.fileCreatedAt.toUtc().toIso8601String();
|
||||
json[r'fileModifiedAt'] = this.fileModifiedAt.toUtc().toIso8601String();
|
||||
json[r'hasMetadata'] = this.hasMetadata;
|
||||
json[r'id'] = this.id;
|
||||
json[r'isArchived'] = this.isArchived;
|
||||
json[r'isExternal'] = this.isExternal;
|
||||
|
@ -253,6 +258,7 @@ class AssetResponseDto {
|
|||
exifInfo: ExifResponseDto.fromJson(json[r'exifInfo']),
|
||||
fileCreatedAt: mapDateTime(json, r'fileCreatedAt', '')!,
|
||||
fileModifiedAt: mapDateTime(json, r'fileModifiedAt', '')!,
|
||||
hasMetadata: mapValueOfType<bool>(json, r'hasMetadata')!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
isArchived: mapValueOfType<bool>(json, r'isArchived')!,
|
||||
isExternal: mapValueOfType<bool>(json, r'isExternal')!,
|
||||
|
@ -327,6 +333,7 @@ class AssetResponseDto {
|
|||
'duration',
|
||||
'fileCreatedAt',
|
||||
'fileModifiedAt',
|
||||
'hasMetadata',
|
||||
'id',
|
||||
'isArchived',
|
||||
'isExternal',
|
||||
|
|
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;
|
||||
}
|
||||
|
14
mobile/openapi/lib/model/shared_link_create_dto.dart
generated
14
mobile/openapi/lib/model/shared_link_create_dto.dart
generated
|
@ -19,7 +19,7 @@ class SharedLinkCreateDto {
|
|||
this.assetIds = const [],
|
||||
this.description,
|
||||
this.expiresAt,
|
||||
this.showExif = true,
|
||||
this.showMetadata = true,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
|
@ -47,7 +47,7 @@ class SharedLinkCreateDto {
|
|||
|
||||
DateTime? expiresAt;
|
||||
|
||||
bool showExif;
|
||||
bool showMetadata;
|
||||
|
||||
SharedLinkType type;
|
||||
|
||||
|
@ -59,7 +59,7 @@ class SharedLinkCreateDto {
|
|||
other.assetIds == assetIds &&
|
||||
other.description == description &&
|
||||
other.expiresAt == expiresAt &&
|
||||
other.showExif == showExif &&
|
||||
other.showMetadata == showMetadata &&
|
||||
other.type == type;
|
||||
|
||||
@override
|
||||
|
@ -71,11 +71,11 @@ class SharedLinkCreateDto {
|
|||
(assetIds.hashCode) +
|
||||
(description == null ? 0 : description!.hashCode) +
|
||||
(expiresAt == null ? 0 : expiresAt!.hashCode) +
|
||||
(showExif.hashCode) +
|
||||
(showMetadata.hashCode) +
|
||||
(type.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, showExif=$showExif, type=$type]';
|
||||
String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, showMetadata=$showMetadata, type=$type]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -97,7 +97,7 @@ class SharedLinkCreateDto {
|
|||
} else {
|
||||
// json[r'expiresAt'] = null;
|
||||
}
|
||||
json[r'showExif'] = this.showExif;
|
||||
json[r'showMetadata'] = this.showMetadata;
|
||||
json[r'type'] = this.type;
|
||||
return json;
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ class SharedLinkCreateDto {
|
|||
: const [],
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
expiresAt: mapDateTime(json, r'expiresAt', ''),
|
||||
showExif: mapValueOfType<bool>(json, r'showExif') ?? true,
|
||||
showMetadata: mapValueOfType<bool>(json, r'showMetadata') ?? true,
|
||||
type: SharedLinkType.fromJson(json[r'type'])!,
|
||||
);
|
||||
}
|
||||
|
|
18
mobile/openapi/lib/model/shared_link_edit_dto.dart
generated
18
mobile/openapi/lib/model/shared_link_edit_dto.dart
generated
|
@ -17,7 +17,7 @@ class SharedLinkEditDto {
|
|||
this.allowUpload,
|
||||
this.description,
|
||||
this.expiresAt,
|
||||
this.showExif,
|
||||
this.showMetadata,
|
||||
});
|
||||
|
||||
///
|
||||
|
@ -52,7 +52,7 @@ class SharedLinkEditDto {
|
|||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? showExif;
|
||||
bool? showMetadata;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SharedLinkEditDto &&
|
||||
|
@ -60,7 +60,7 @@ class SharedLinkEditDto {
|
|||
other.allowUpload == allowUpload &&
|
||||
other.description == description &&
|
||||
other.expiresAt == expiresAt &&
|
||||
other.showExif == showExif;
|
||||
other.showMetadata == showMetadata;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
|
@ -69,10 +69,10 @@ class SharedLinkEditDto {
|
|||
(allowUpload == null ? 0 : allowUpload!.hashCode) +
|
||||
(description == null ? 0 : description!.hashCode) +
|
||||
(expiresAt == null ? 0 : expiresAt!.hashCode) +
|
||||
(showExif == null ? 0 : showExif!.hashCode);
|
||||
(showMetadata == null ? 0 : showMetadata!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, description=$description, expiresAt=$expiresAt, showExif=$showExif]';
|
||||
String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, description=$description, expiresAt=$expiresAt, showMetadata=$showMetadata]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -96,10 +96,10 @@ class SharedLinkEditDto {
|
|||
} else {
|
||||
// json[r'expiresAt'] = null;
|
||||
}
|
||||
if (this.showExif != null) {
|
||||
json[r'showExif'] = this.showExif;
|
||||
if (this.showMetadata != null) {
|
||||
json[r'showMetadata'] = this.showMetadata;
|
||||
} else {
|
||||
// json[r'showExif'] = null;
|
||||
// json[r'showMetadata'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ class SharedLinkEditDto {
|
|||
allowUpload: mapValueOfType<bool>(json, r'allowUpload'),
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
expiresAt: mapDateTime(json, r'expiresAt', ''),
|
||||
showExif: mapValueOfType<bool>(json, r'showExif'),
|
||||
showMetadata: mapValueOfType<bool>(json, r'showMetadata'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -22,7 +22,7 @@ class SharedLinkResponseDto {
|
|||
required this.expiresAt,
|
||||
required this.id,
|
||||
required this.key,
|
||||
required this.showExif,
|
||||
required this.showMetadata,
|
||||
required this.type,
|
||||
required this.userId,
|
||||
});
|
||||
|
@ -51,7 +51,7 @@ class SharedLinkResponseDto {
|
|||
|
||||
String key;
|
||||
|
||||
bool showExif;
|
||||
bool showMetadata;
|
||||
|
||||
SharedLinkType type;
|
||||
|
||||
|
@ -68,7 +68,7 @@ class SharedLinkResponseDto {
|
|||
other.expiresAt == expiresAt &&
|
||||
other.id == id &&
|
||||
other.key == key &&
|
||||
other.showExif == showExif &&
|
||||
other.showMetadata == showMetadata &&
|
||||
other.type == type &&
|
||||
other.userId == userId;
|
||||
|
||||
|
@ -84,12 +84,12 @@ class SharedLinkResponseDto {
|
|||
(expiresAt == null ? 0 : expiresAt!.hashCode) +
|
||||
(id.hashCode) +
|
||||
(key.hashCode) +
|
||||
(showExif.hashCode) +
|
||||
(showMetadata.hashCode) +
|
||||
(type.hashCode) +
|
||||
(userId.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, showExif=$showExif, type=$type, userId=$userId]';
|
||||
String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, showMetadata=$showMetadata, type=$type, userId=$userId]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -114,7 +114,7 @@ class SharedLinkResponseDto {
|
|||
}
|
||||
json[r'id'] = this.id;
|
||||
json[r'key'] = this.key;
|
||||
json[r'showExif'] = this.showExif;
|
||||
json[r'showMetadata'] = this.showMetadata;
|
||||
json[r'type'] = this.type;
|
||||
json[r'userId'] = this.userId;
|
||||
return json;
|
||||
|
@ -137,7 +137,7 @@ class SharedLinkResponseDto {
|
|||
expiresAt: mapDateTime(json, r'expiresAt', ''),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
key: mapValueOfType<String>(json, r'key')!,
|
||||
showExif: mapValueOfType<bool>(json, r'showExif')!,
|
||||
showMetadata: mapValueOfType<bool>(json, r'showMetadata')!,
|
||||
type: SharedLinkType.fromJson(json[r'type'])!,
|
||||
userId: mapValueOfType<String>(json, r'userId')!,
|
||||
);
|
||||
|
@ -195,7 +195,7 @@ class SharedLinkResponseDto {
|
|||
'expiresAt',
|
||||
'id',
|
||||
'key',
|
||||
'showExif',
|
||||
'showMetadata',
|
||||
'type',
|
||||
'userId',
|
||||
};
|
||||
|
|
6
mobile/openapi/test/asset_response_dto_test.dart
generated
6
mobile/openapi/test/asset_response_dto_test.dart
generated
|
@ -52,6 +52,11 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// bool hasMetadata
|
||||
test('to test the property `hasMetadata`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String id
|
||||
test('to test the property `id`', () async {
|
||||
// TODO
|
||||
|
@ -142,7 +147,6 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// base64 encoded thumbhash
|
||||
// String thumbhash
|
||||
test('to test the property `thumbhash`', () async {
|
||||
// TODO
|
||||
|
|
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', () {
|
||||
|
||||
});
|
||||
|
||||
}
|
|
@ -46,8 +46,8 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// bool showExif (default value: true)
|
||||
test('to test the property `showExif`', () async {
|
||||
// bool showMetadata (default value: true)
|
||||
test('to test the property `showMetadata`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
|
|
@ -36,8 +36,8 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// bool showExif
|
||||
test('to test the property `showExif`', () async {
|
||||
// bool showMetadata
|
||||
test('to test the property `showMetadata`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
|
|
@ -61,8 +61,8 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// bool showExif
|
||||
test('to test the property `showExif`', () async {
|
||||
// bool showMetadata
|
||||
test('to test the property `showMetadata`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
@ -5770,6 +5882,9 @@
|
|||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"hasMetadata": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -5833,7 +5948,6 @@
|
|||
"type": "array"
|
||||
},
|
||||
"thumbhash": {
|
||||
"description": "base64 encoded thumbhash",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -5847,7 +5961,6 @@
|
|||
},
|
||||
"required": [
|
||||
"type",
|
||||
"id",
|
||||
"deviceAssetId",
|
||||
"deviceId",
|
||||
"ownerId",
|
||||
|
@ -5855,19 +5968,21 @@
|
|||
"originalPath",
|
||||
"originalFileName",
|
||||
"resized",
|
||||
"thumbhash",
|
||||
"fileCreatedAt",
|
||||
"fileModifiedAt",
|
||||
"updatedAt",
|
||||
"isFavorite",
|
||||
"isArchived",
|
||||
"isTrashed",
|
||||
"localDateTime",
|
||||
"isOffline",
|
||||
"isExternal",
|
||||
"isReadOnly",
|
||||
"checksum",
|
||||
"id",
|
||||
"thumbhash",
|
||||
"localDateTime",
|
||||
"duration",
|
||||
"checksum"
|
||||
"hasMetadata"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
@ -6577,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": {
|
||||
|
@ -7024,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": {
|
||||
|
@ -7599,7 +7825,7 @@
|
|||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"showExif": {
|
||||
"showMetadata": {
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -7628,7 +7854,7 @@
|
|||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"showExif": {
|
||||
"showMetadata": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
|
@ -7670,7 +7896,7 @@
|
|||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"showExif": {
|
||||
"showMetadata": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": {
|
||||
|
@ -7691,7 +7917,7 @@
|
|||
"assets",
|
||||
"allowUpload",
|
||||
"allowDownload",
|
||||
"showExif"
|
||||
"showMetadata"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
|
|
@ -47,6 +47,7 @@ import {
|
|||
BulkIdsDto,
|
||||
MapMarkerResponseDto,
|
||||
MemoryLaneResponseDto,
|
||||
SanitizedAssetResponseDto,
|
||||
TimeBucketResponseDto,
|
||||
mapAsset,
|
||||
} from './response-dto';
|
||||
|
@ -198,10 +199,17 @@ export class AssetService {
|
|||
return this.assetRepository.getTimeBuckets(dto);
|
||||
}
|
||||
|
||||
async getByTimeBucket(authUser: AuthUserDto, dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> {
|
||||
async getByTimeBucket(
|
||||
authUser: AuthUserDto,
|
||||
dto: TimeBucketAssetDto,
|
||||
): Promise<AssetResponseDto[] | SanitizedAssetResponseDto[]> {
|
||||
await this.timeBucketChecks(authUser, dto);
|
||||
const assets = await this.assetRepository.getByTimeBucket(dto.timeBucket, dto);
|
||||
return assets.map(mapAsset);
|
||||
if (authUser.isShowMetadata) {
|
||||
return assets.map((asset) => mapAsset(asset));
|
||||
} else {
|
||||
return assets.map((asset) => mapAsset(asset, true));
|
||||
}
|
||||
}
|
||||
|
||||
async downloadFile(authUser: AuthUserDto, id: string): Promise<ImmichReadStream> {
|
||||
|
|
|
@ -6,43 +6,62 @@ import { UserResponseDto, mapUser } from '../../user/response-dto/user-response.
|
|||
import { ExifResponseDto, mapExif } from './exif-response.dto';
|
||||
import { SmartInfoResponseDto, mapSmartInfo } from './smart-info-response.dto';
|
||||
|
||||
export class AssetResponseDto {
|
||||
export class SanitizedAssetResponseDto {
|
||||
id!: string;
|
||||
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
|
||||
type!: AssetType;
|
||||
thumbhash!: string | null;
|
||||
resized!: boolean;
|
||||
localDateTime!: Date;
|
||||
duration!: string;
|
||||
livePhotoVideoId?: string | null;
|
||||
hasMetadata!: boolean;
|
||||
}
|
||||
|
||||
export class AssetResponseDto extends SanitizedAssetResponseDto {
|
||||
deviceAssetId!: string;
|
||||
deviceId!: string;
|
||||
ownerId!: string;
|
||||
owner?: UserResponseDto;
|
||||
libraryId!: string;
|
||||
|
||||
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
|
||||
type!: AssetType;
|
||||
originalPath!: string;
|
||||
originalFileName!: string;
|
||||
resized!: boolean;
|
||||
/**base64 encoded thumbhash */
|
||||
thumbhash!: string | null;
|
||||
fileCreatedAt!: Date;
|
||||
fileModifiedAt!: Date;
|
||||
updatedAt!: Date;
|
||||
isFavorite!: boolean;
|
||||
isArchived!: boolean;
|
||||
isTrashed!: boolean;
|
||||
localDateTime!: Date;
|
||||
isOffline!: boolean;
|
||||
isExternal!: boolean;
|
||||
isReadOnly!: boolean;
|
||||
duration!: string;
|
||||
exifInfo?: ExifResponseDto;
|
||||
smartInfo?: SmartInfoResponseDto;
|
||||
livePhotoVideoId?: string | null;
|
||||
tags?: TagResponseDto[];
|
||||
people?: PersonResponseDto[];
|
||||
/**base64 encoded sha1 hash */
|
||||
checksum!: string;
|
||||
}
|
||||
|
||||
function _map(entity: AssetEntity, withExif: boolean): AssetResponseDto {
|
||||
export function mapAsset(entity: AssetEntity, stripMetadata = false): AssetResponseDto {
|
||||
const sanitizedAssetResponse: SanitizedAssetResponseDto = {
|
||||
id: entity.id,
|
||||
type: entity.type,
|
||||
thumbhash: entity.thumbhash?.toString('base64') ?? null,
|
||||
localDateTime: entity.localDateTime,
|
||||
resized: !!entity.resizePath,
|
||||
duration: entity.duration ?? '0:00:00.00000',
|
||||
livePhotoVideoId: entity.livePhotoVideoId,
|
||||
hasMetadata: false,
|
||||
};
|
||||
|
||||
if (stripMetadata) {
|
||||
return sanitizedAssetResponse as AssetResponseDto;
|
||||
}
|
||||
|
||||
return {
|
||||
...sanitizedAssetResponse,
|
||||
id: entity.id,
|
||||
deviceAssetId: entity.deviceAssetId,
|
||||
ownerId: entity.ownerId,
|
||||
|
@ -62,7 +81,7 @@ function _map(entity: AssetEntity, withExif: boolean): AssetResponseDto {
|
|||
isArchived: entity.isArchived,
|
||||
isTrashed: !!entity.deletedAt,
|
||||
duration: entity.duration ?? '0:00:00.00000',
|
||||
exifInfo: withExif ? (entity.exifInfo ? mapExif(entity.exifInfo) : undefined) : undefined,
|
||||
exifInfo: entity.exifInfo ? mapExif(entity.exifInfo) : undefined,
|
||||
smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined,
|
||||
livePhotoVideoId: entity.livePhotoVideoId,
|
||||
tags: entity.tags?.map(mapTag),
|
||||
|
@ -71,17 +90,10 @@ function _map(entity: AssetEntity, withExif: boolean): AssetResponseDto {
|
|||
isExternal: entity.isExternal,
|
||||
isOffline: entity.isOffline,
|
||||
isReadOnly: entity.isReadOnly,
|
||||
hasMetadata: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function mapAsset(entity: AssetEntity): AssetResponseDto {
|
||||
return _map(entity, true);
|
||||
}
|
||||
|
||||
export function mapAssetWithoutExif(entity: AssetEntity): AssetResponseDto {
|
||||
return _map(entity, false);
|
||||
}
|
||||
|
||||
export class MemoryLaneResponseDto {
|
||||
title!: string;
|
||||
assets!: AssetResponseDto[];
|
||||
|
|
|
@ -52,3 +52,15 @@ export function mapExif(entity: ExifEntity): ExifResponseDto {
|
|||
projectionType: entity.projectionType,
|
||||
};
|
||||
}
|
||||
|
||||
export function mapSanitizedExif(entity: ExifEntity): ExifResponseDto {
|
||||
return {
|
||||
fileSizeInByte: entity.fileSizeInByte ? parseInt(entity.fileSizeInByte.toString()) : null,
|
||||
orientation: entity.orientation,
|
||||
dateTimeOriginal: entity.dateTimeOriginal,
|
||||
timeZone: entity.timeZone,
|
||||
projectionType: entity.projectionType,
|
||||
exifImageWidth: entity.exifImageWidth,
|
||||
exifImageHeight: entity.exifImageHeight,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -380,7 +380,7 @@ export class AuthService {
|
|||
sharedLinkId: link.id,
|
||||
isAllowUpload: link.allowUpload,
|
||||
isAllowDownload: link.allowDownload,
|
||||
isShowExif: link.showExif,
|
||||
isShowMetadata: link.showExif,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -431,7 +431,7 @@ export class AuthService {
|
|||
isPublicUser: false,
|
||||
isAllowUpload: true,
|
||||
isAllowDownload: true,
|
||||
isShowExif: true,
|
||||
isShowMetadata: true,
|
||||
accessTokenId: token.id,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ export class AuthUserDto {
|
|||
sharedLinkId?: string;
|
||||
isAllowUpload?: boolean;
|
||||
isAllowDownload?: boolean;
|
||||
isShowExif?: boolean;
|
||||
isShowMetadata?: boolean;
|
||||
accessTokenId?: string;
|
||||
externalPath?: string | null;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -97,7 +97,7 @@ export class PersonService {
|
|||
async getAssets(authUser: AuthUserDto, id: string): Promise<AssetResponseDto[]> {
|
||||
await this.access.requirePermission(authUser, Permission.PERSON_READ, id);
|
||||
const assets = await this.repository.getAssets(id);
|
||||
return assets.map(mapAsset);
|
||||
return assets.map((asset) => mapAsset(asset));
|
||||
}
|
||||
|
||||
async update(authUser: AuthUserDto, id: string, dto: PersonUpdateDto): Promise<PersonResponseDto> {
|
||||
|
|
|
@ -14,6 +14,7 @@ export interface AssetSearchOptions {
|
|||
trashedBefore?: Date;
|
||||
type?: AssetType;
|
||||
order?: 'ASC' | 'DESC';
|
||||
withDeleted?: boolean;
|
||||
}
|
||||
|
||||
export interface LivePhotoSearchOptions {
|
||||
|
|
|
@ -154,7 +154,7 @@ export class SearchService {
|
|||
items: assets.items
|
||||
.map((item) => lookup[item.id])
|
||||
.filter((item) => !!item)
|
||||
.map(mapAsset),
|
||||
.map((asset) => mapAsset(asset)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -2,7 +2,7 @@ import { SharedLinkEntity, SharedLinkType } from '@app/infra/entities';
|
|||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import _ from 'lodash';
|
||||
import { AlbumResponseDto, mapAlbumWithoutAssets } from '../album';
|
||||
import { AssetResponseDto, mapAsset, mapAssetWithoutExif } from '../asset';
|
||||
import { AssetResponseDto, mapAsset } from '../asset';
|
||||
|
||||
export class SharedLinkResponseDto {
|
||||
id!: string;
|
||||
|
@ -17,8 +17,9 @@ export class SharedLinkResponseDto {
|
|||
assets!: AssetResponseDto[];
|
||||
album?: AlbumResponseDto;
|
||||
allowUpload!: boolean;
|
||||
|
||||
allowDownload!: boolean;
|
||||
showExif!: boolean;
|
||||
showMetadata!: boolean;
|
||||
}
|
||||
|
||||
export function mapSharedLink(sharedLink: SharedLinkEntity): SharedLinkResponseDto {
|
||||
|
@ -35,15 +36,15 @@ export function mapSharedLink(sharedLink: SharedLinkEntity): SharedLinkResponseD
|
|||
type: sharedLink.type,
|
||||
createdAt: sharedLink.createdAt,
|
||||
expiresAt: sharedLink.expiresAt,
|
||||
assets: assets.map(mapAsset),
|
||||
assets: assets.map((asset) => mapAsset(asset)),
|
||||
album: sharedLink.album ? mapAlbumWithoutAssets(sharedLink.album) : undefined,
|
||||
allowUpload: sharedLink.allowUpload,
|
||||
allowDownload: sharedLink.allowDownload,
|
||||
showExif: sharedLink.showExif,
|
||||
showMetadata: sharedLink.showExif,
|
||||
};
|
||||
}
|
||||
|
||||
export function mapSharedLinkWithNoExif(sharedLink: SharedLinkEntity): SharedLinkResponseDto {
|
||||
export function mapSharedLinkWithoutMetadata(sharedLink: SharedLinkEntity): SharedLinkResponseDto {
|
||||
const linkAssets = sharedLink.assets || [];
|
||||
const albumAssets = (sharedLink?.album?.assets || []).map((asset) => asset);
|
||||
|
||||
|
@ -57,10 +58,10 @@ export function mapSharedLinkWithNoExif(sharedLink: SharedLinkEntity): SharedLin
|
|||
type: sharedLink.type,
|
||||
createdAt: sharedLink.createdAt,
|
||||
expiresAt: sharedLink.expiresAt,
|
||||
assets: assets.map(mapAssetWithoutExif),
|
||||
assets: assets.map((asset) => mapAsset(asset, true)) as AssetResponseDto[],
|
||||
album: sharedLink.album ? mapAlbumWithoutAssets(sharedLink.album) : undefined,
|
||||
allowUpload: sharedLink.allowUpload,
|
||||
allowDownload: sharedLink.allowDownload,
|
||||
showExif: sharedLink.showExif,
|
||||
showMetadata: sharedLink.showExif,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ export class SharedLinkCreateDto {
|
|||
|
||||
@Optional()
|
||||
@IsBoolean()
|
||||
showExif?: boolean = true;
|
||||
showMetadata?: boolean = true;
|
||||
}
|
||||
|
||||
export class SharedLinkEditDto {
|
||||
|
@ -51,5 +51,5 @@ export class SharedLinkEditDto {
|
|||
allowDownload?: boolean;
|
||||
|
||||
@Optional()
|
||||
showExif?: boolean;
|
||||
showMetadata?: boolean;
|
||||
}
|
||||
|
|
|
@ -59,10 +59,10 @@ describe(SharedLinkService.name, () => {
|
|||
expect(shareMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
|
||||
});
|
||||
|
||||
it('should return not return exif', async () => {
|
||||
it('should not return metadata', async () => {
|
||||
const authDto = authStub.adminSharedLinkNoExif;
|
||||
shareMock.get.mockResolvedValue(sharedLinkStub.readonlyNoExif);
|
||||
await expect(sut.getMine(authDto)).resolves.toEqual(sharedLinkResponseStub.readonlyNoExif);
|
||||
await expect(sut.getMine(authDto)).resolves.toEqual(sharedLinkResponseStub.readonlyNoMetadata);
|
||||
expect(shareMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
|
||||
});
|
||||
});
|
||||
|
@ -137,7 +137,7 @@ describe(SharedLinkService.name, () => {
|
|||
await sut.create(authStub.admin, {
|
||||
type: SharedLinkType.INDIVIDUAL,
|
||||
assetIds: [assetStub.image.id],
|
||||
showExif: true,
|
||||
showMetadata: true,
|
||||
allowDownload: true,
|
||||
allowUpload: true,
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ import { AccessCore, Permission } from '../access';
|
|||
import { AssetIdErrorReason, AssetIdsDto, AssetIdsResponseDto } from '../asset';
|
||||
import { AuthUserDto } from '../auth';
|
||||
import { IAccessRepository, ICryptoRepository, ISharedLinkRepository } from '../repositories';
|
||||
import { SharedLinkResponseDto, mapSharedLink, mapSharedLinkWithNoExif } from './shared-link-response.dto';
|
||||
import { SharedLinkResponseDto, mapSharedLink, mapSharedLinkWithoutMetadata } from './shared-link-response.dto';
|
||||
import { SharedLinkCreateDto, SharedLinkEditDto } from './shared-link.dto';
|
||||
|
||||
@Injectable()
|
||||
|
@ -24,7 +24,7 @@ export class SharedLinkService {
|
|||
}
|
||||
|
||||
async getMine(authUser: AuthUserDto): Promise<SharedLinkResponseDto> {
|
||||
const { sharedLinkId: id, isPublicUser, isShowExif } = authUser;
|
||||
const { sharedLinkId: id, isPublicUser, isShowMetadata: isShowExif } = authUser;
|
||||
|
||||
if (!isPublicUser || !id) {
|
||||
throw new ForbiddenException();
|
||||
|
@ -69,7 +69,7 @@ export class SharedLinkService {
|
|||
expiresAt: dto.expiresAt || null,
|
||||
allowUpload: dto.allowUpload ?? true,
|
||||
allowDownload: dto.allowDownload ?? true,
|
||||
showExif: dto.showExif ?? true,
|
||||
showExif: dto.showMetadata ?? true,
|
||||
});
|
||||
|
||||
return this.map(sharedLink, { withExif: true });
|
||||
|
@ -84,7 +84,7 @@ export class SharedLinkService {
|
|||
expiresAt: dto.expiresAt,
|
||||
allowUpload: dto.allowUpload,
|
||||
allowDownload: dto.allowDownload,
|
||||
showExif: dto.showExif,
|
||||
showExif: dto.showMetadata,
|
||||
});
|
||||
return this.map(sharedLink, { withExif: true });
|
||||
}
|
||||
|
@ -157,6 +157,6 @@ export class SharedLinkService {
|
|||
}
|
||||
|
||||
private map(sharedLink: SharedLinkEntity, { withExif }: { withExif: boolean }) {
|
||||
return withExif ? mapSharedLink(sharedLink) : mapSharedLinkWithNoExif(sharedLink);
|
||||
return withExif ? mapSharedLink(sharedLink) : mapSharedLinkWithoutMetadata(sharedLink);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ export class TagService {
|
|||
async getAssets(authUser: AuthUserDto, id: string): Promise<AssetResponseDto[]> {
|
||||
await this.findOrFail(authUser, id);
|
||||
const assets = await this.repository.getAssets(authUser.id, id);
|
||||
return assets.map(mapAsset);
|
||||
return assets.map((asset) => mapAsset(asset));
|
||||
}
|
||||
|
||||
async addAssets(authUser: AuthUserDto, id: string, dto: AssetIdsDto): Promise<AssetIdsResponseDto[]> {
|
||||
|
|
|
@ -186,7 +186,7 @@ export class AssetController {
|
|||
@SharedLinkRoute()
|
||||
@Get('/assetById/:id')
|
||||
getAssetById(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto> {
|
||||
return this.assetService.getAssetById(authUser, id);
|
||||
return this.assetService.getAssetById(authUser, id) as Promise<AssetResponseDto>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,9 +10,9 @@ import {
|
|||
IStorageRepository,
|
||||
JobName,
|
||||
mapAsset,
|
||||
mapAssetWithoutExif,
|
||||
mimeTypes,
|
||||
Permission,
|
||||
SanitizedAssetResponseDto,
|
||||
UploadFile,
|
||||
} from '@app/domain';
|
||||
import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity, AssetType, LibraryType } from '@app/infra/entities';
|
||||
|
@ -187,12 +187,16 @@ export class AssetService {
|
|||
return assets.map((asset) => mapAsset(asset));
|
||||
}
|
||||
|
||||
public async getAssetById(authUser: AuthUserDto, assetId: string): Promise<AssetResponseDto> {
|
||||
public async getAssetById(
|
||||
authUser: AuthUserDto,
|
||||
assetId: string,
|
||||
): Promise<AssetResponseDto | SanitizedAssetResponseDto> {
|
||||
await this.access.requirePermission(authUser, Permission.ASSET_READ, assetId);
|
||||
|
||||
const allowExif = this.getExifPermission(authUser);
|
||||
const includeMetadata = this.getExifPermission(authUser);
|
||||
const asset = await this._assetRepository.getById(assetId);
|
||||
const data = allowExif ? mapAsset(asset) : mapAssetWithoutExif(asset);
|
||||
if (includeMetadata) {
|
||||
const data = mapAsset(asset);
|
||||
|
||||
if (data.ownerId !== authUser.id) {
|
||||
data.people = [];
|
||||
|
@ -203,6 +207,9 @@ export class AssetService {
|
|||
}
|
||||
|
||||
return data;
|
||||
} else {
|
||||
return mapAsset(asset, true);
|
||||
}
|
||||
}
|
||||
|
||||
async serveThumbnail(authUser: AuthUserDto, assetId: string, query: GetAssetThumbnailDto, res: Res) {
|
||||
|
@ -374,7 +381,7 @@ export class AssetService {
|
|||
}
|
||||
|
||||
getExifPermission(authUser: AuthUserDto) {
|
||||
return !authUser.isPublicUser || authUser.isShowExif;
|
||||
return !authUser.isPublicUser || authUser.isShowMetadata;
|
||||
}
|
||||
|
||||
private getThumbnailPath(asset: AssetEntity, format: GetAssetThumbnailFormatEnum) {
|
||||
|
|
|
@ -98,7 +98,7 @@ export class AssetController {
|
|||
@Authenticated({ isShared: true })
|
||||
@Get('time-bucket')
|
||||
getByTimeBucket(@AuthUser() authUser: AuthUserDto, @Query() dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> {
|
||||
return this.service.getByTimeBucket(authUser, dto);
|
||||
return this.service.getByTimeBucket(authUser, dto) as Promise<AssetResponseDto[]>;
|
||||
}
|
||||
|
||||
@Post('jobs')
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -72,7 +72,7 @@ export class PersonRepository implements IPersonRepository {
|
|||
.addOrderBy("NULLIF(person.name, '') IS NULL", 'ASC')
|
||||
.addOrderBy('COUNT(face.assetId)', 'DESC')
|
||||
.addOrderBy("NULLIF(person.name, '')", 'ASC', 'NULLS LAST')
|
||||
.having('COUNT(face.assetId) >= :faces', { faces: options?.minimumFaceCount || 1 })
|
||||
.having("person.name != '' OR COUNT(face.assetId) >= :faces", { faces: options?.minimumFaceCount || 1 })
|
||||
.groupBy('person.id')
|
||||
.limit(500);
|
||||
if (!options?.withHidden) {
|
||||
|
|
|
@ -10,4 +10,11 @@ export const sharedLinkApi = {
|
|||
expect(status).toBe(201);
|
||||
return body as SharedLinkResponseDto;
|
||||
},
|
||||
|
||||
getMySharedLink: async (server: any, key: string) => {
|
||||
const { status, body } = await request(server).get('/shared-link/me').query({ key });
|
||||
|
||||
expect(status).toBe(200);
|
||||
return body as SharedLinkResponseDto;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import { AlbumResponseDto, LoginResponseDto, SharedLinkResponseDto } from '@app/domain';
|
||||
import { PartnerController } from '@app/immich';
|
||||
import { SharedLinkType } from '@app/infra/entities';
|
||||
import { LibraryType, SharedLinkType } from '@app/infra/entities';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { api } from '@test/api';
|
||||
import { db } from '@test/db';
|
||||
import { errorStub, uuidStub } from '@test/fixtures';
|
||||
import { createTestApp } from '@test/test-utils';
|
||||
import {
|
||||
IMMICH_TEST_ASSET_PATH,
|
||||
IMMICH_TEST_ASSET_TEMP_PATH,
|
||||
createTestApp,
|
||||
restoreTempFolder,
|
||||
} from '@test/test-utils';
|
||||
import { cp } from 'fs/promises';
|
||||
import request from 'supertest';
|
||||
|
||||
const user1Dto = {
|
||||
|
@ -18,24 +24,22 @@ const user1Dto = {
|
|||
describe(`${PartnerController.name} (e2e)`, () => {
|
||||
let app: INestApplication;
|
||||
let server: any;
|
||||
let loginResponse: LoginResponseDto;
|
||||
let accessToken: string;
|
||||
let admin: LoginResponseDto;
|
||||
let user1: LoginResponseDto;
|
||||
let album: AlbumResponseDto;
|
||||
let sharedLink: SharedLinkResponseDto;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp();
|
||||
app = await createTestApp(true);
|
||||
server = app.getHttpServer();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.reset();
|
||||
await api.authApi.adminSignUp(server);
|
||||
loginResponse = await api.authApi.adminLogin(server);
|
||||
accessToken = loginResponse.accessToken;
|
||||
admin = await api.authApi.adminLogin(server);
|
||||
|
||||
await api.userApi.create(server, accessToken, user1Dto);
|
||||
await api.userApi.create(server, admin.accessToken, user1Dto);
|
||||
user1 = await api.authApi.login(server, { email: user1Dto.email, password: user1Dto.password });
|
||||
|
||||
album = await api.albumApi.create(server, user1.accessToken, { albumName: 'shared with link' });
|
||||
|
@ -48,6 +52,7 @@ describe(`${PartnerController.name} (e2e)`, () => {
|
|||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
await restoreTempFolder();
|
||||
});
|
||||
|
||||
describe('GET /shared-link', () => {
|
||||
|
@ -68,7 +73,9 @@ describe(`${PartnerController.name} (e2e)`, () => {
|
|||
});
|
||||
|
||||
it('should not get shared links created by other users', async () => {
|
||||
const { status, body } = await request(server).get('/shared-link').set('Authorization', `Bearer ${accessToken}`);
|
||||
const { status, body } = await request(server)
|
||||
.get('/shared-link')
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual([]);
|
||||
|
@ -77,7 +84,9 @@ describe(`${PartnerController.name} (e2e)`, () => {
|
|||
|
||||
describe('GET /shared-link/me', () => {
|
||||
it('should not require admin authentication', async () => {
|
||||
const { status } = await request(server).get('/shared-link/me').set('Authorization', `Bearer ${accessToken}`);
|
||||
const { status } = await request(server)
|
||||
.get('/shared-link/me')
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
|
||||
expect(status).toBe(403);
|
||||
});
|
||||
|
@ -104,7 +113,7 @@ describe(`${PartnerController.name} (e2e)`, () => {
|
|||
type: SharedLinkType.ALBUM,
|
||||
albumId: softDeletedAlbum.id,
|
||||
});
|
||||
await api.userApi.delete(server, accessToken, user1.userId);
|
||||
await api.userApi.delete(server, admin.accessToken, user1.userId);
|
||||
|
||||
const { status, body } = await request(server).get('/shared-link/me').query({ key: softDeletedAlbumLink.key });
|
||||
|
||||
|
@ -133,7 +142,7 @@ describe(`${PartnerController.name} (e2e)`, () => {
|
|||
it('should not get shared link by id if user has not created the link or it does not exist', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get(`/shared-link/${sharedLink.id}`)
|
||||
.set('Authorization', `Bearer ${accessToken}`);
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(expect.objectContaining({ message: 'Shared link not found' }));
|
||||
|
@ -248,4 +257,81 @@ describe(`${PartnerController.name} (e2e)`, () => {
|
|||
expect(status).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Shared link metadata', () => {
|
||||
beforeEach(async () => {
|
||||
await restoreTempFolder();
|
||||
|
||||
await cp(
|
||||
`${IMMICH_TEST_ASSET_PATH}/metadata/gps-position/thompson-springs.jpg`,
|
||||
`${IMMICH_TEST_ASSET_TEMP_PATH}/thompson-springs.jpg`,
|
||||
);
|
||||
|
||||
await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/');
|
||||
|
||||
const library = await api.libraryApi.create(server, admin.accessToken, {
|
||||
type: LibraryType.EXTERNAL,
|
||||
importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`],
|
||||
});
|
||||
|
||||
await api.libraryApi.scanLibrary(server, admin.accessToken, library.id);
|
||||
|
||||
const assets = await api.assetApi.getAllAssets(server, admin.accessToken);
|
||||
|
||||
expect(assets).toHaveLength(1);
|
||||
|
||||
album = await api.albumApi.create(server, admin.accessToken, { albumName: 'New album' });
|
||||
await api.albumApi.addAssets(server, admin.accessToken, album.id, { ids: [assets[0].id] });
|
||||
});
|
||||
|
||||
it('should return metadata for album shared link', async () => {
|
||||
const sharedLink = await api.sharedLinkApi.create(server, admin.accessToken, {
|
||||
type: SharedLinkType.ALBUM,
|
||||
albumId: album.id,
|
||||
});
|
||||
|
||||
const returnedLink = await api.sharedLinkApi.getMySharedLink(server, sharedLink.key);
|
||||
|
||||
expect(returnedLink.assets).toHaveLength(1);
|
||||
expect(returnedLink.album).toBeDefined();
|
||||
|
||||
const returnedAsset = returnedLink.assets[0];
|
||||
expect(returnedAsset).toEqual(
|
||||
expect.objectContaining({
|
||||
originalFileName: 'thompson-springs',
|
||||
resized: true,
|
||||
localDateTime: '2022-01-10T15:15:44.310Z',
|
||||
fileCreatedAt: '2022-01-10T19:15:44.310Z',
|
||||
exifInfo: expect.objectContaining({
|
||||
longitude: -108.400968333333,
|
||||
latitude: 39.115,
|
||||
orientation: '1',
|
||||
dateTimeOriginal: '2022-01-10T19:15:44.310Z',
|
||||
timeZone: 'UTC-4',
|
||||
state: 'Mesa County, Colorado',
|
||||
country: 'United States of America',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not return metadata for album shared link without metadata', async () => {
|
||||
const sharedLink = await api.sharedLinkApi.create(server, admin.accessToken, {
|
||||
type: SharedLinkType.ALBUM,
|
||||
albumId: album.id,
|
||||
showMetadata: false,
|
||||
});
|
||||
|
||||
const returnedLink = await api.sharedLinkApi.getMySharedLink(server, sharedLink.key);
|
||||
|
||||
expect(returnedLink.assets).toHaveLength(1);
|
||||
expect(returnedLink.album).toBeDefined();
|
||||
|
||||
const returnedAsset = returnedLink.assets[0];
|
||||
expect(returnedAsset).not.toHaveProperty('exifInfo');
|
||||
expect(returnedAsset).not.toHaveProperty('fileCreatedAt');
|
||||
expect(returnedAsset).not.toHaveProperty('originalFilename');
|
||||
expect(returnedAsset).not.toHaveProperty('originalPath');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
12
server/test/fixtures/auth.stub.ts
vendored
12
server/test/fixtures/auth.stub.ts
vendored
|
@ -48,7 +48,7 @@ export const authStub = {
|
|||
isPublicUser: false,
|
||||
isAllowUpload: true,
|
||||
isAllowDownload: true,
|
||||
isShowExif: true,
|
||||
isShowMetadata: true,
|
||||
accessTokenId: 'token-id',
|
||||
externalPath: null,
|
||||
}),
|
||||
|
@ -59,7 +59,7 @@ export const authStub = {
|
|||
isPublicUser: false,
|
||||
isAllowUpload: true,
|
||||
isAllowDownload: true,
|
||||
isShowExif: true,
|
||||
isShowMetadata: true,
|
||||
accessTokenId: 'token-id',
|
||||
externalPath: null,
|
||||
}),
|
||||
|
@ -70,7 +70,7 @@ export const authStub = {
|
|||
isPublicUser: false,
|
||||
isAllowUpload: true,
|
||||
isAllowDownload: true,
|
||||
isShowExif: true,
|
||||
isShowMetadata: true,
|
||||
accessTokenId: 'token-id',
|
||||
externalPath: '/data/user1',
|
||||
}),
|
||||
|
@ -81,7 +81,7 @@ export const authStub = {
|
|||
isAllowUpload: true,
|
||||
isAllowDownload: true,
|
||||
isPublicUser: true,
|
||||
isShowExif: true,
|
||||
isShowMetadata: true,
|
||||
sharedLinkId: '123',
|
||||
}),
|
||||
adminSharedLinkNoExif: Object.freeze<AuthUserDto>({
|
||||
|
@ -91,7 +91,7 @@ export const authStub = {
|
|||
isAllowUpload: true,
|
||||
isAllowDownload: true,
|
||||
isPublicUser: true,
|
||||
isShowExif: false,
|
||||
isShowMetadata: false,
|
||||
sharedLinkId: '123',
|
||||
}),
|
||||
readonlySharedLink: Object.freeze<AuthUserDto>({
|
||||
|
@ -101,7 +101,7 @@ export const authStub = {
|
|||
isAllowUpload: false,
|
||||
isAllowDownload: false,
|
||||
isPublicUser: true,
|
||||
isShowExif: true,
|
||||
isShowMetadata: true,
|
||||
sharedLinkId: '123',
|
||||
accessTokenId: 'token-id',
|
||||
}),
|
||||
|
|
24
server/test/fixtures/shared-link.stub.ts
vendored
24
server/test/fixtures/shared-link.stub.ts
vendored
|
@ -71,8 +71,20 @@ const assetResponse: AssetResponseDto = {
|
|||
checksum: 'ZmlsZSBoYXNo',
|
||||
isTrashed: false,
|
||||
libraryId: 'library-id',
|
||||
hasMetadata: true,
|
||||
};
|
||||
|
||||
const assetResponseWithoutMetadata = {
|
||||
id: 'id_1',
|
||||
type: AssetType.VIDEO,
|
||||
resized: false,
|
||||
thumbhash: null,
|
||||
localDateTime: today,
|
||||
duration: '0:00:00.00000',
|
||||
livePhotoVideoId: null,
|
||||
hasMetadata: false,
|
||||
} as AssetResponseDto;
|
||||
|
||||
const albumResponse: AlbumResponseDto = {
|
||||
albumName: 'Test Album',
|
||||
description: '',
|
||||
|
@ -253,7 +265,7 @@ export const sharedLinkResponseStub = {
|
|||
expiresAt: tomorrow,
|
||||
id: '123',
|
||||
key: sharedLinkBytes.toString('base64url'),
|
||||
showExif: true,
|
||||
showMetadata: true,
|
||||
type: SharedLinkType.ALBUM,
|
||||
userId: 'admin_id',
|
||||
}),
|
||||
|
@ -267,7 +279,7 @@ export const sharedLinkResponseStub = {
|
|||
expiresAt: yesterday,
|
||||
id: '123',
|
||||
key: sharedLinkBytes.toString('base64url'),
|
||||
showExif: true,
|
||||
showMetadata: true,
|
||||
type: SharedLinkType.ALBUM,
|
||||
userId: 'admin_id',
|
||||
}),
|
||||
|
@ -281,11 +293,11 @@ export const sharedLinkResponseStub = {
|
|||
description: null,
|
||||
allowUpload: false,
|
||||
allowDownload: false,
|
||||
showExif: true,
|
||||
showMetadata: true,
|
||||
album: albumResponse,
|
||||
assets: [assetResponse],
|
||||
}),
|
||||
readonlyNoExif: Object.freeze<SharedLinkResponseDto>({
|
||||
readonlyNoMetadata: Object.freeze<SharedLinkResponseDto>({
|
||||
id: '123',
|
||||
userId: 'admin_id',
|
||||
key: sharedLinkBytes.toString('base64url'),
|
||||
|
@ -295,8 +307,8 @@ export const sharedLinkResponseStub = {
|
|||
description: null,
|
||||
allowUpload: false,
|
||||
allowDownload: false,
|
||||
showExif: false,
|
||||
showMetadata: false,
|
||||
album: { ...albumResponse, startDate: assetResponse.fileCreatedAt, endDate: assetResponse.fileCreatedAt },
|
||||
assets: [{ ...assetResponse, exifInfo: undefined }],
|
||||
assets: [{ ...assetResponseWithoutMetadata, exifInfo: undefined }],
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
392
web/src/api/open-api/api.ts
generated
392
web/src/api/open-api/api.ts
generated
|
@ -640,6 +640,12 @@ export interface AssetResponseDto {
|
|||
* @memberof AssetResponseDto
|
||||
*/
|
||||
'fileModifiedAt': string;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof AssetResponseDto
|
||||
*/
|
||||
'hasMetadata': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
|
@ -749,7 +755,7 @@ export interface AssetResponseDto {
|
|||
*/
|
||||
'tags'?: Array<TagResponseDto>;
|
||||
/**
|
||||
* base64 encoded thumbhash
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AssetResponseDto
|
||||
*/
|
||||
|
@ -1598,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
|
||||
|
@ -2180,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
|
||||
|
@ -2882,7 +3025,7 @@ export interface SharedLinkCreateDto {
|
|||
* @type {boolean}
|
||||
* @memberof SharedLinkCreateDto
|
||||
*/
|
||||
'showExif'?: boolean;
|
||||
'showMetadata'?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {SharedLinkType}
|
||||
|
@ -2927,7 +3070,7 @@ export interface SharedLinkEditDto {
|
|||
* @type {boolean}
|
||||
* @memberof SharedLinkEditDto
|
||||
*/
|
||||
'showExif'?: boolean;
|
||||
'showMetadata'?: boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
|
@ -2994,7 +3137,7 @@ export interface SharedLinkResponseDto {
|
|||
* @type {boolean}
|
||||
* @memberof SharedLinkResponseDto
|
||||
*/
|
||||
'showExif': boolean;
|
||||
'showMetadata': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {SharedLinkType}
|
||||
|
@ -8815,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
|
||||
|
@ -8869,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,
|
||||
|
@ -8884,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
|
||||
|
@ -8896,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);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8906,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.
|
||||
|
@ -8915,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
|
||||
|
@ -8946,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
|
||||
|
@ -8953,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.
|
||||
|
@ -8963,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 |
|
@ -28,6 +28,7 @@
|
|||
export let showMotionPlayButton: boolean;
|
||||
export let isMotionPhotoPlaying = false;
|
||||
export let showDownloadButton: boolean;
|
||||
export let showDetailButton: boolean;
|
||||
export let showSlideshow = false;
|
||||
|
||||
const isOwner = asset.ownerId === $page.data.user?.id;
|
||||
|
@ -133,7 +134,14 @@
|
|||
title="Download"
|
||||
/>
|
||||
{/if}
|
||||
<CircleIconButton isOpacity={true} logo={InformationOutline} on:click={() => dispatch('showDetail')} title="Info" />
|
||||
{#if showDetailButton}
|
||||
<CircleIconButton
|
||||
isOpacity={true}
|
||||
logo={InformationOutline}
|
||||
on:click={() => dispatch('showDetail')}
|
||||
title="Info"
|
||||
/>
|
||||
{/if}
|
||||
{#if isOwner}
|
||||
<CircleIconButton
|
||||
isOpacity={true}
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
let shouldPlayMotionPhoto = false;
|
||||
let isShowProfileImageCrop = false;
|
||||
let shouldShowDownloadButton = sharedLink ? sharedLink.allowDownload : !asset.isOffline;
|
||||
let shouldShowDetailButton = asset.hasMetadata;
|
||||
let canCopyImagesToClipboard: boolean;
|
||||
|
||||
const onKeyboardPress = (keyInfo: KeyboardEvent) => handleKeyboardPress(keyInfo);
|
||||
|
@ -392,6 +393,7 @@
|
|||
showZoomButton={asset.type === AssetTypeEnum.Image}
|
||||
showMotionPlayButton={!!asset.livePhotoVideoId}
|
||||
showDownloadButton={shouldShowDownloadButton}
|
||||
showDetailButton={shouldShowDetailButton}
|
||||
showSlideshow={!!assetStore}
|
||||
on:goBack={closeViewer}
|
||||
on:showDetail={showDetailInfoHandler}
|
||||
|
@ -433,9 +435,9 @@
|
|||
on:close={closeViewer}
|
||||
on:onVideoEnded={() => (shouldPlayMotionPhoto = false)}
|
||||
/>
|
||||
{:else if asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR || asset.originalPath
|
||||
{:else if asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR || (asset.originalPath && asset.originalPath
|
||||
.toLowerCase()
|
||||
.endsWith('.insp')}
|
||||
.endsWith('.insp'))}
|
||||
<PanoramaViewer {asset} />
|
||||
{:else}
|
||||
<PhotoViewer {asset} on:close={closeViewer} />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
let description = '';
|
||||
let allowDownload = true;
|
||||
let allowUpload = false;
|
||||
let showExif = true;
|
||||
let showMetadata = true;
|
||||
let expirationTime = '';
|
||||
let shouldChangeExpirationTime = false;
|
||||
let canCopyImagesToClipboard = true;
|
||||
|
@ -41,7 +41,7 @@
|
|||
}
|
||||
allowUpload = editingLink.allowUpload;
|
||||
allowDownload = editingLink.allowDownload;
|
||||
showExif = editingLink.showExif;
|
||||
showMetadata = editingLink.showMetadata;
|
||||
|
||||
albumId = editingLink.album?.id;
|
||||
assetIds = editingLink.assets.map(({ id }) => id);
|
||||
|
@ -66,7 +66,7 @@
|
|||
allowUpload,
|
||||
description,
|
||||
allowDownload,
|
||||
showExif,
|
||||
showMetadata,
|
||||
},
|
||||
});
|
||||
sharedLink = `${window.location.origin}/share/${data.key}`;
|
||||
|
@ -119,9 +119,9 @@
|
|||
sharedLinkEditDto: {
|
||||
description,
|
||||
expiresAt: shouldChangeExpirationTime ? expirationDate : undefined,
|
||||
allowUpload: allowUpload,
|
||||
allowDownload: allowDownload,
|
||||
showExif: showExif,
|
||||
allowUpload,
|
||||
allowDownload,
|
||||
showMetadata,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -184,7 +184,7 @@
|
|||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<SettingSwitch bind:checked={showExif} title={'Show metadata'} />
|
||||
<SettingSwitch bind:checked={showMetadata} title={'Show metadata'} />
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -136,7 +136,7 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
{#if link.showExif}
|
||||
{#if link.showMetadata}
|
||||
<div
|
||||
class="flex w-[60px] place-content-center place-items-center rounded-full bg-immich-primary px-2 py-1 text-xs text-white dark:bg-immich-dark-primary dark:text-immich-dark-gray"
|
||||
>
|
||||
|
|
|
@ -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>
|
Loading…
Reference in a new issue