feat(server): Add support for client-side hashing (#2072)

* Modify controller DTOs

* Can check duplicates on server side

* Remove deviceassetid and deviceid

* Remove device ids from file uploader

* Add db migration for removed device ids

* Don't sanitize checksum

* Convert asset checksum to string

* Make checksum not optional for asset

* Use enums when rejecting duplicates

* Cleanup

* Return of the device id, but optional

* Don't use deviceId for upload folder

* Use checksum in thumb path

* Only use asset id in thumb path

* Openapi generation

* Put deviceAssetId back in asset response dto

* Add missing checksum in test fixture

* Add another missing checksum in test fixture

* Cleanup asset repository

* Add back previous /exists endpoint

* Require checksum to not be null

* Correctly set deviceId in db

* Remove index

* Fix compilation errors

* Make device id nullabel in asset response dto

* Reduce PR scope

* Revert asset service

* Reorder imports

* Reorder imports

* Reduce PR scope

* Reduce PR scope

* Reduce PR scope

* Reduce PR scope

* Reduce PR scope

* Update openapi

* Reduce PR scope

* refactor: asset bulk upload check

* chore: regenreate open-api

* chore: fix tests

* chore: tests

* update migrations and regenerate api

* Feat: use checksum in web file uploader

* Change to wasm-crypto

* Use crypto api for checksumming in web uploader

* Minor cleanup of file upload

* feat(web): pause and resume jobs

* Make device asset id not nullable again

* Cleanup

* Device id not nullable in response dto

* Update API specs

* Bump api specs

* Remove old TODO comment

* Remove NOT NULL constraint on checksum index

* Fix requested pubspec changes

* Remove unneeded import

* Update server/apps/immich/src/api-v1/asset/asset.service.ts

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>

* Update server/apps/immich/src/api-v1/asset/asset-repository.ts

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>

* Remove unneeded check

* Update server/apps/immich/src/api-v1/asset/asset-repository.ts

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>

* Remove hashing in the web uploader

* Cleanup file uploader

* Remove varchar from asset entity fields

* Return 200 from bulk upload check

* Put device asset id back into asset repository

* Merge migrations

* Revert pubspec lock

* Update openapi specs

* Merge upstream changes

* Fix failing asset service tests

* Fix formatting issue

* Cleanup migrations

* Remove newline from pubspec

* Revert newline

* Checkout main version

* Revert again

* Only return AssetCheck

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
This commit is contained in:
Jonathan Jogenfors 2023-05-24 23:08:21 +02:00 committed by GitHub
parent 49b74e9091
commit 1b54c4f8e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 1396 additions and 58 deletions

View file

@ -346,6 +346,96 @@ export interface AllJobStatusResponseDto {
*/
'recognize-faces-queue': JobStatusDto;
}
/**
*
* @export
* @interface AssetBulkUploadCheckDto
*/
export interface AssetBulkUploadCheckDto {
/**
*
* @type {Array<AssetBulkUploadCheckItem>}
* @memberof AssetBulkUploadCheckDto
*/
'assets': Array<AssetBulkUploadCheckItem>;
}
/**
*
* @export
* @interface AssetBulkUploadCheckItem
*/
export interface AssetBulkUploadCheckItem {
/**
*
* @type {string}
* @memberof AssetBulkUploadCheckItem
*/
'id': string;
/**
*
* @type {string}
* @memberof AssetBulkUploadCheckItem
*/
'checksum': string;
}
/**
*
* @export
* @interface AssetBulkUploadCheckResponseDto
*/
export interface AssetBulkUploadCheckResponseDto {
/**
*
* @type {Array<AssetBulkUploadCheckResult>}
* @memberof AssetBulkUploadCheckResponseDto
*/
'results': Array<AssetBulkUploadCheckResult>;
}
/**
*
* @export
* @interface AssetBulkUploadCheckResult
*/
export interface AssetBulkUploadCheckResult {
/**
*
* @type {string}
* @memberof AssetBulkUploadCheckResult
*/
'id': string;
/**
*
* @type {string}
* @memberof AssetBulkUploadCheckResult
*/
'action': AssetBulkUploadCheckResultActionEnum;
/**
*
* @type {string}
* @memberof AssetBulkUploadCheckResult
*/
'reason'?: AssetBulkUploadCheckResultReasonEnum;
/**
*
* @type {string}
* @memberof AssetBulkUploadCheckResult
*/
'assetId'?: string;
}
export const AssetBulkUploadCheckResultActionEnum = {
Accept: 'accept',
Reject: 'reject'
} as const;
export type AssetBulkUploadCheckResultActionEnum = typeof AssetBulkUploadCheckResultActionEnum[keyof typeof AssetBulkUploadCheckResultActionEnum];
export const AssetBulkUploadCheckResultReasonEnum = {
Duplicate: 'duplicate',
UnsupportedFormat: 'unsupported-format'
} as const;
export type AssetBulkUploadCheckResultReasonEnum = typeof AssetBulkUploadCheckResultReasonEnum[keyof typeof AssetBulkUploadCheckResultReasonEnum];
/**
*
* @export
@ -4120,6 +4210,50 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
options: localVarRequestOptions,
};
},
/**
* Checks if assets exist by checksums
* @param {AssetBulkUploadCheckDto} assetBulkUploadCheckDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
bulkUploadCheck: async (assetBulkUploadCheckDto: AssetBulkUploadCheckDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'assetBulkUploadCheckDto' is not null or undefined
assertParamExists('bulkUploadCheck', 'assetBulkUploadCheckDto', assetBulkUploadCheckDto)
const localVarPath = `/asset/bulk-upload-check`;
// 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(assetBulkUploadCheckDto, localVarRequestOptions, configuration)
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Check duplicated asset before uploading - for Web upload used
* @param {CheckDuplicateAssetDto} checkDuplicateAssetDto
@ -5312,6 +5446,16 @@ export const AssetApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.addAssetsToSharedLink(addAssetsDto, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
* Checks if assets exist by checksums
* @param {AssetBulkUploadCheckDto} assetBulkUploadCheckDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async bulkUploadCheck(assetBulkUploadCheckDto: AssetBulkUploadCheckDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetBulkUploadCheckResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.bulkUploadCheck(assetBulkUploadCheckDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
* Check duplicated asset before uploading - for Web upload used
* @param {CheckDuplicateAssetDto} checkDuplicateAssetDto
@ -5595,6 +5739,15 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
addAssetsToSharedLink(addAssetsDto: AddAssetsDto, key?: string, options?: any): AxiosPromise<SharedLinkResponseDto> {
return localVarFp.addAssetsToSharedLink(addAssetsDto, key, options).then((request) => request(axios, basePath));
},
/**
* Checks if assets exist by checksums
* @param {AssetBulkUploadCheckDto} assetBulkUploadCheckDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
bulkUploadCheck(assetBulkUploadCheckDto: AssetBulkUploadCheckDto, options?: any): AxiosPromise<AssetBulkUploadCheckResponseDto> {
return localVarFp.bulkUploadCheck(assetBulkUploadCheckDto, options).then((request) => request(axios, basePath));
},
/**
* Check duplicated asset before uploading - for Web upload used
* @param {CheckDuplicateAssetDto} checkDuplicateAssetDto
@ -5856,6 +6009,17 @@ export class AssetApi extends BaseAPI {
return AssetApiFp(this.configuration).addAssetsToSharedLink(addAssetsDto, key, options).then((request) => request(this.axios, this.basePath));
}
/**
* Checks if assets exist by checksums
* @param {AssetBulkUploadCheckDto} assetBulkUploadCheckDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof AssetApi
*/
public bulkUploadCheck(assetBulkUploadCheckDto: AssetBulkUploadCheckDto, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).bulkUploadCheck(assetBulkUploadCheckDto, options).then((request) => request(this.axios, this.basePath));
}
/**
* Check duplicated asset before uploading - for Web upload used
* @param {CheckDuplicateAssetDto} checkDuplicateAssetDto