mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	refactor(server): shared links (#2632)
* refactor: rename share => shared-link * refactor: shared link crud methods * chore: open api
This commit is contained in:
		
							
								
								
									
										2
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							@@ -145,11 +145,11 @@ Class | Method | HTTP request | Description
 | 
			
		||||
*ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version | 
 | 
			
		||||
*ServerInfoApi* | [**getStats**](doc//ServerInfoApi.md#getstats) | **GET** /server-info/stats | 
 | 
			
		||||
*ServerInfoApi* | [**pingServer**](doc//ServerInfoApi.md#pingserver) | **GET** /server-info/ping | 
 | 
			
		||||
*ShareApi* | [**editSharedLink**](doc//ShareApi.md#editsharedlink) | **PATCH** /share/{id} | 
 | 
			
		||||
*ShareApi* | [**getAllSharedLinks**](doc//ShareApi.md#getallsharedlinks) | **GET** /share | 
 | 
			
		||||
*ShareApi* | [**getMySharedLink**](doc//ShareApi.md#getmysharedlink) | **GET** /share/me | 
 | 
			
		||||
*ShareApi* | [**getSharedLinkById**](doc//ShareApi.md#getsharedlinkbyid) | **GET** /share/{id} | 
 | 
			
		||||
*ShareApi* | [**removeSharedLink**](doc//ShareApi.md#removesharedlink) | **DELETE** /share/{id} | 
 | 
			
		||||
*ShareApi* | [**updateSharedLink**](doc//ShareApi.md#updatesharedlink) | **PATCH** /share/{id} | 
 | 
			
		||||
*SystemConfigApi* | [**getConfig**](doc//SystemConfigApi.md#getconfig) | **GET** /system-config | 
 | 
			
		||||
*SystemConfigApi* | [**getDefaults**](doc//SystemConfigApi.md#getdefaults) | **GET** /system-config/defaults | 
 | 
			
		||||
*SystemConfigApi* | [**getStorageTemplateOptions**](doc//SystemConfigApi.md#getstoragetemplateoptions) | **GET** /system-config/storage-template-options | 
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										116
									
								
								mobile/openapi/doc/ShareApi.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										116
									
								
								mobile/openapi/doc/ShareApi.md
									
									
									
										generated
									
									
									
								
							@@ -9,70 +9,13 @@ All URIs are relative to */api*
 | 
			
		||||
 | 
			
		||||
Method | HTTP request | Description
 | 
			
		||||
------------- | ------------- | -------------
 | 
			
		||||
[**editSharedLink**](ShareApi.md#editsharedlink) | **PATCH** /share/{id} | 
 | 
			
		||||
[**getAllSharedLinks**](ShareApi.md#getallsharedlinks) | **GET** /share | 
 | 
			
		||||
[**getMySharedLink**](ShareApi.md#getmysharedlink) | **GET** /share/me | 
 | 
			
		||||
[**getSharedLinkById**](ShareApi.md#getsharedlinkbyid) | **GET** /share/{id} | 
 | 
			
		||||
[**removeSharedLink**](ShareApi.md#removesharedlink) | **DELETE** /share/{id} | 
 | 
			
		||||
[**updateSharedLink**](ShareApi.md#updatesharedlink) | **PATCH** /share/{id} | 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# **editSharedLink**
 | 
			
		||||
> SharedLinkResponseDto editSharedLink(id, editSharedLinkDto)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 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 = ShareApi();
 | 
			
		||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | 
 | 
			
		||||
final editSharedLinkDto = EditSharedLinkDto(); // EditSharedLinkDto | 
 | 
			
		||||
 | 
			
		||||
try {
 | 
			
		||||
    final result = api_instance.editSharedLink(id, editSharedLinkDto);
 | 
			
		||||
    print(result);
 | 
			
		||||
} catch (e) {
 | 
			
		||||
    print('Exception when calling ShareApi->editSharedLink: $e\n');
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Parameters
 | 
			
		||||
 | 
			
		||||
Name | Type | Description  | Notes
 | 
			
		||||
------------- | ------------- | ------------- | -------------
 | 
			
		||||
 **id** | **String**|  | 
 | 
			
		||||
 **editSharedLinkDto** | [**EditSharedLinkDto**](EditSharedLinkDto.md)|  | 
 | 
			
		||||
 | 
			
		||||
### Return type
 | 
			
		||||
 | 
			
		||||
[**SharedLinkResponseDto**](SharedLinkResponseDto.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)
 | 
			
		||||
 | 
			
		||||
# **getAllSharedLinks**
 | 
			
		||||
> List<SharedLinkResponseDto> getAllSharedLinks()
 | 
			
		||||
 | 
			
		||||
@@ -288,3 +231,60 @@ void (empty response body)
 | 
			
		||||
 | 
			
		||||
[[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)
 | 
			
		||||
 | 
			
		||||
# **updateSharedLink**
 | 
			
		||||
> SharedLinkResponseDto updateSharedLink(id, editSharedLinkDto)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 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 = ShareApi();
 | 
			
		||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | 
 | 
			
		||||
final editSharedLinkDto = EditSharedLinkDto(); // EditSharedLinkDto | 
 | 
			
		||||
 | 
			
		||||
try {
 | 
			
		||||
    final result = api_instance.updateSharedLink(id, editSharedLinkDto);
 | 
			
		||||
    print(result);
 | 
			
		||||
} catch (e) {
 | 
			
		||||
    print('Exception when calling ShareApi->updateSharedLink: $e\n');
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Parameters
 | 
			
		||||
 | 
			
		||||
Name | Type | Description  | Notes
 | 
			
		||||
------------- | ------------- | ------------- | -------------
 | 
			
		||||
 **id** | **String**|  | 
 | 
			
		||||
 **editSharedLinkDto** | [**EditSharedLinkDto**](EditSharedLinkDto.md)|  | 
 | 
			
		||||
 | 
			
		||||
### Return type
 | 
			
		||||
 | 
			
		||||
[**SharedLinkResponseDto**](SharedLinkResponseDto.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)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										104
									
								
								mobile/openapi/lib/api/share_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										104
									
								
								mobile/openapi/lib/api/share_api.dart
									
									
									
										generated
									
									
									
								
							@@ -16,58 +16,6 @@ class ShareApi {
 | 
			
		||||
 | 
			
		||||
  final ApiClient apiClient;
 | 
			
		||||
 | 
			
		||||
  /// Performs an HTTP 'PATCH /share/{id}' operation and returns the [Response].
 | 
			
		||||
  /// Parameters:
 | 
			
		||||
  ///
 | 
			
		||||
  /// * [String] id (required):
 | 
			
		||||
  ///
 | 
			
		||||
  /// * [EditSharedLinkDto] editSharedLinkDto (required):
 | 
			
		||||
  Future<Response> editSharedLinkWithHttpInfo(String id, EditSharedLinkDto editSharedLinkDto,) async {
 | 
			
		||||
    // ignore: prefer_const_declarations
 | 
			
		||||
    final path = r'/share/{id}'
 | 
			
		||||
      .replaceAll('{id}', id);
 | 
			
		||||
 | 
			
		||||
    // ignore: prefer_final_locals
 | 
			
		||||
    Object? postBody = editSharedLinkDto;
 | 
			
		||||
 | 
			
		||||
    final queryParams = <QueryParam>[];
 | 
			
		||||
    final headerParams = <String, String>{};
 | 
			
		||||
    final formParams = <String, String>{};
 | 
			
		||||
 | 
			
		||||
    const contentTypes = <String>['application/json'];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return apiClient.invokeAPI(
 | 
			
		||||
      path,
 | 
			
		||||
      'PATCH',
 | 
			
		||||
      queryParams,
 | 
			
		||||
      postBody,
 | 
			
		||||
      headerParams,
 | 
			
		||||
      formParams,
 | 
			
		||||
      contentTypes.isEmpty ? null : contentTypes.first,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Parameters:
 | 
			
		||||
  ///
 | 
			
		||||
  /// * [String] id (required):
 | 
			
		||||
  ///
 | 
			
		||||
  /// * [EditSharedLinkDto] editSharedLinkDto (required):
 | 
			
		||||
  Future<SharedLinkResponseDto?> editSharedLink(String id, EditSharedLinkDto editSharedLinkDto,) async {
 | 
			
		||||
    final response = await editSharedLinkWithHttpInfo(id, editSharedLinkDto,);
 | 
			
		||||
    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), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
 | 
			
		||||
    
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Performs an HTTP 'GET /share' operation and returns the [Response].
 | 
			
		||||
  Future<Response> getAllSharedLinksWithHttpInfo() async {
 | 
			
		||||
    // ignore: prefer_const_declarations
 | 
			
		||||
@@ -250,4 +198,56 @@ class ShareApi {
 | 
			
		||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Performs an HTTP 'PATCH /share/{id}' operation and returns the [Response].
 | 
			
		||||
  /// Parameters:
 | 
			
		||||
  ///
 | 
			
		||||
  /// * [String] id (required):
 | 
			
		||||
  ///
 | 
			
		||||
  /// * [EditSharedLinkDto] editSharedLinkDto (required):
 | 
			
		||||
  Future<Response> updateSharedLinkWithHttpInfo(String id, EditSharedLinkDto editSharedLinkDto,) async {
 | 
			
		||||
    // ignore: prefer_const_declarations
 | 
			
		||||
    final path = r'/share/{id}'
 | 
			
		||||
      .replaceAll('{id}', id);
 | 
			
		||||
 | 
			
		||||
    // ignore: prefer_final_locals
 | 
			
		||||
    Object? postBody = editSharedLinkDto;
 | 
			
		||||
 | 
			
		||||
    final queryParams = <QueryParam>[];
 | 
			
		||||
    final headerParams = <String, String>{};
 | 
			
		||||
    final formParams = <String, String>{};
 | 
			
		||||
 | 
			
		||||
    const contentTypes = <String>['application/json'];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return apiClient.invokeAPI(
 | 
			
		||||
      path,
 | 
			
		||||
      'PATCH',
 | 
			
		||||
      queryParams,
 | 
			
		||||
      postBody,
 | 
			
		||||
      headerParams,
 | 
			
		||||
      formParams,
 | 
			
		||||
      contentTypes.isEmpty ? null : contentTypes.first,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Parameters:
 | 
			
		||||
  ///
 | 
			
		||||
  /// * [String] id (required):
 | 
			
		||||
  ///
 | 
			
		||||
  /// * [EditSharedLinkDto] editSharedLinkDto (required):
 | 
			
		||||
  Future<SharedLinkResponseDto?> updateSharedLink(String id, EditSharedLinkDto editSharedLinkDto,) async {
 | 
			
		||||
    final response = await updateSharedLinkWithHttpInfo(id, editSharedLinkDto,);
 | 
			
		||||
    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), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
 | 
			
		||||
    
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								mobile/openapi/test/share_api_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/test/share_api_test.dart
									
									
									
										generated
									
									
									
								
							@@ -17,11 +17,6 @@ void main() {
 | 
			
		||||
  // final instance = ShareApi();
 | 
			
		||||
 | 
			
		||||
  group('tests for ShareApi', () {
 | 
			
		||||
    //Future<SharedLinkResponseDto> editSharedLink(String id, EditSharedLinkDto editSharedLinkDto) async
 | 
			
		||||
    test('test editSharedLink', () async {
 | 
			
		||||
      // TODO
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    //Future<List<SharedLinkResponseDto>> getAllSharedLinks() async
 | 
			
		||||
    test('test getAllSharedLinks', () async {
 | 
			
		||||
      // TODO
 | 
			
		||||
@@ -42,5 +37,10 @@ void main() {
 | 
			
		||||
      // TODO
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    //Future<SharedLinkResponseDto> updateSharedLink(String id, EditSharedLinkDto editSharedLinkDto) async
 | 
			
		||||
    test('test updateSharedLink', () async {
 | 
			
		||||
      // TODO
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,13 +10,19 @@ import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
 | 
			
		||||
import { AddAssetsDto } from './dto/add-assets.dto';
 | 
			
		||||
import { DownloadService } from '../../modules/download/download.service';
 | 
			
		||||
import { DownloadDto } from '../asset/dto/download-library.dto';
 | 
			
		||||
import { ShareCore, ISharedLinkRepository, mapSharedLink, SharedLinkResponseDto, ICryptoRepository } from '@app/domain';
 | 
			
		||||
import {
 | 
			
		||||
  SharedLinkCore,
 | 
			
		||||
  ISharedLinkRepository,
 | 
			
		||||
  mapSharedLink,
 | 
			
		||||
  SharedLinkResponseDto,
 | 
			
		||||
  ICryptoRepository,
 | 
			
		||||
} from '@app/domain';
 | 
			
		||||
import { CreateAlbumShareLinkDto } from './dto/create-album-shared-link.dto';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class AlbumService {
 | 
			
		||||
  readonly logger = new Logger(AlbumService.name);
 | 
			
		||||
  private shareCore: ShareCore;
 | 
			
		||||
  private shareCore: SharedLinkCore;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    @Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
 | 
			
		||||
@@ -25,7 +31,7 @@ export class AlbumService {
 | 
			
		||||
    @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
 | 
			
		||||
    @Inject(IJobRepository) private jobRepository: IJobRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.shareCore = new ShareCore(sharedLinkRepository, cryptoRepository);
 | 
			
		||||
    this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _getAlbum({
 | 
			
		||||
 
 | 
			
		||||
@@ -229,7 +229,7 @@ describe('AssetService', () => {
 | 
			
		||||
 | 
			
		||||
      expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id);
 | 
			
		||||
      expect(sharedLinkRepositoryMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
 | 
			
		||||
      expect(sharedLinkRepositoryMock.save).not.toHaveBeenCalled();
 | 
			
		||||
      expect(sharedLinkRepositoryMock.update).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should add assets to a shared link', async () => {
 | 
			
		||||
@@ -241,13 +241,13 @@ describe('AssetService', () => {
 | 
			
		||||
      assetRepositoryMock.getById.mockResolvedValue(asset1);
 | 
			
		||||
      sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      sharedLinkRepositoryMock.hasAssetAccess.mockResolvedValue(true);
 | 
			
		||||
      sharedLinkRepositoryMock.save.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      sharedLinkRepositoryMock.update.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
 | 
			
		||||
      await expect(sut.addAssetsToSharedLink(authDto, dto)).resolves.toEqual(sharedLinkResponseStub.valid);
 | 
			
		||||
 | 
			
		||||
      expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id);
 | 
			
		||||
      expect(sharedLinkRepositoryMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
 | 
			
		||||
      expect(sharedLinkRepositoryMock.save).toHaveBeenCalled();
 | 
			
		||||
      expect(sharedLinkRepositoryMock.update).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should remove assets from a shared link', async () => {
 | 
			
		||||
@@ -259,13 +259,13 @@ describe('AssetService', () => {
 | 
			
		||||
      assetRepositoryMock.getById.mockResolvedValue(asset1);
 | 
			
		||||
      sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      sharedLinkRepositoryMock.hasAssetAccess.mockResolvedValue(true);
 | 
			
		||||
      sharedLinkRepositoryMock.save.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      sharedLinkRepositoryMock.update.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
 | 
			
		||||
      await expect(sut.removeAssetsFromSharedLink(authDto, dto)).resolves.toEqual(sharedLinkResponseStub.valid);
 | 
			
		||||
 | 
			
		||||
      expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id);
 | 
			
		||||
      expect(sharedLinkRepositoryMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
 | 
			
		||||
      expect(sharedLinkRepositoryMock.save).toHaveBeenCalled();
 | 
			
		||||
      expect(sharedLinkRepositoryMock.update).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ import { ICryptoRepository, IJobRepository } from '@app/domain';
 | 
			
		||||
import { DownloadService } from '../../modules/download/download.service';
 | 
			
		||||
import { DownloadDto } from './dto/download-library.dto';
 | 
			
		||||
import { IAlbumRepository } from '../album/album-repository';
 | 
			
		||||
import { ShareCore } from '@app/domain';
 | 
			
		||||
import { SharedLinkCore } from '@app/domain';
 | 
			
		||||
import { IPartnerRepository } from '@app/domain';
 | 
			
		||||
import { ISharedLinkRepository } from '@app/domain';
 | 
			
		||||
import { DownloadFilesDto } from './dto/download-files.dto';
 | 
			
		||||
@@ -80,7 +80,7 @@ interface ServableFile {
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class AssetService {
 | 
			
		||||
  readonly logger = new Logger(AssetService.name);
 | 
			
		||||
  private shareCore: ShareCore;
 | 
			
		||||
  private shareCore: SharedLinkCore;
 | 
			
		||||
  private assetCore: AssetCore;
 | 
			
		||||
  private partnerCore: PartnerCore;
 | 
			
		||||
 | 
			
		||||
@@ -97,7 +97,7 @@ export class AssetService {
 | 
			
		||||
    @Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.assetCore = new AssetCore(_assetRepository, jobRepository);
 | 
			
		||||
    this.shareCore = new ShareCore(sharedLinkRepository, cryptoRepository);
 | 
			
		||||
    this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository);
 | 
			
		||||
    this.partnerCore = new PartnerCore(partnerRepository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { AuthUserDto, EditSharedLinkDto, SharedLinkResponseDto, ShareService } from '@app/domain';
 | 
			
		||||
import { AuthUserDto, EditSharedLinkDto, SharedLinkResponseDto, SharedLinkService } from '@app/domain';
 | 
			
		||||
import { Body, Controller, Delete, Get, Param, Patch } from '@nestjs/common';
 | 
			
		||||
import { ApiTags } from '@nestjs/swagger';
 | 
			
		||||
import { GetAuthUser } from '../decorators/auth-user.decorator';
 | 
			
		||||
@@ -11,7 +11,7 @@ import { UUIDParamDto } from './dto/uuid-param.dto';
 | 
			
		||||
@Authenticated()
 | 
			
		||||
@UseValidation()
 | 
			
		||||
export class SharedLinkController {
 | 
			
		||||
  constructor(private readonly service: ShareService) {}
 | 
			
		||||
  constructor(private readonly service: SharedLinkService) {}
 | 
			
		||||
 | 
			
		||||
  @Get()
 | 
			
		||||
  getAllSharedLinks(@GetAuthUser() authUser: AuthUserDto): Promise<SharedLinkResponseDto[]> {
 | 
			
		||||
@@ -29,20 +29,20 @@ export class SharedLinkController {
 | 
			
		||||
    @GetAuthUser() authUser: AuthUserDto,
 | 
			
		||||
    @Param() { id }: UUIDParamDto,
 | 
			
		||||
  ): Promise<SharedLinkResponseDto> {
 | 
			
		||||
    return this.service.getById(authUser, id, true);
 | 
			
		||||
    return this.service.get(authUser, id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Patch(':id')
 | 
			
		||||
  updateSharedLink(
 | 
			
		||||
    @GetAuthUser() authUser: AuthUserDto,
 | 
			
		||||
    @Param() { id }: UUIDParamDto,
 | 
			
		||||
    @Body() dto: EditSharedLinkDto,
 | 
			
		||||
  ): Promise<SharedLinkResponseDto> {
 | 
			
		||||
    return this.service.update(authUser, id, dto);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Delete(':id')
 | 
			
		||||
  removeSharedLink(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
 | 
			
		||||
    return this.service.remove(authUser, id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Patch(':id')
 | 
			
		||||
  editSharedLink(
 | 
			
		||||
    @GetAuthUser() authUser: AuthUserDto,
 | 
			
		||||
    @Param() { id }: UUIDParamDto,
 | 
			
		||||
    @Body() dto: EditSharedLinkDto,
 | 
			
		||||
  ): Promise<SharedLinkResponseDto> {
 | 
			
		||||
    return this.service.edit(authUser, id, dto);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1565,41 +1565,8 @@
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      "delete": {
 | 
			
		||||
        "operationId": "removeSharedLink",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "name": "id",
 | 
			
		||||
            "required": true,
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "schema": {
 | 
			
		||||
              "format": "uuid",
 | 
			
		||||
              "type": "string"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "200": {
 | 
			
		||||
            "description": ""
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "share"
 | 
			
		||||
        ],
 | 
			
		||||
        "security": [
 | 
			
		||||
          {
 | 
			
		||||
            "bearer": []
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "cookie": []
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "api_key": []
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      "patch": {
 | 
			
		||||
        "operationId": "editSharedLink",
 | 
			
		||||
        "operationId": "updateSharedLink",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "name": "id",
 | 
			
		||||
@@ -1647,6 +1614,39 @@
 | 
			
		||||
            "api_key": []
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      "delete": {
 | 
			
		||||
        "operationId": "removeSharedLink",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "name": "id",
 | 
			
		||||
            "required": true,
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "schema": {
 | 
			
		||||
              "format": "uuid",
 | 
			
		||||
              "type": "string"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "200": {
 | 
			
		||||
            "description": ""
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "share"
 | 
			
		||||
        ],
 | 
			
		||||
        "security": [
 | 
			
		||||
          {
 | 
			
		||||
            "bearer": []
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "cookie": []
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "api_key": []
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/system-config": {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ import {
 | 
			
		||||
} from '../../test';
 | 
			
		||||
import { IKeyRepository } from '../api-key';
 | 
			
		||||
import { ICryptoRepository } from '../crypto/crypto.repository';
 | 
			
		||||
import { ISharedLinkRepository } from '../share';
 | 
			
		||||
import { ISharedLinkRepository } from '../shared-link';
 | 
			
		||||
import { ISystemConfigRepository } from '../system-config';
 | 
			
		||||
import { IUserRepository } from '../user';
 | 
			
		||||
import { IUserTokenRepository } from '../user-token';
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ import { AuthUserDto, ChangePasswordDto, LoginCredentialDto, SignUpDto } from '.
 | 
			
		||||
import { AdminSignupResponseDto, LoginResponseDto, LogoutResponseDto, mapAdminSignupResponse } from './response-dto';
 | 
			
		||||
import { IUserTokenRepository, UserTokenCore } from '../user-token';
 | 
			
		||||
import cookieParser from 'cookie';
 | 
			
		||||
import { ISharedLinkRepository, ShareCore } from '../share';
 | 
			
		||||
import { ISharedLinkRepository, SharedLinkCore } from '../shared-link';
 | 
			
		||||
import { APIKeyCore } from '../api-key/api-key.core';
 | 
			
		||||
import { IKeyRepository } from '../api-key';
 | 
			
		||||
import { AuthDeviceResponseDto, mapUserToken } from './response-dto';
 | 
			
		||||
@@ -29,7 +29,7 @@ export class AuthService {
 | 
			
		||||
  private authCore: AuthCore;
 | 
			
		||||
  private oauthCore: OAuthCore;
 | 
			
		||||
  private userCore: UserCore;
 | 
			
		||||
  private shareCore: ShareCore;
 | 
			
		||||
  private shareCore: SharedLinkCore;
 | 
			
		||||
  private keyCore: APIKeyCore;
 | 
			
		||||
 | 
			
		||||
  private logger = new Logger(AuthService.name);
 | 
			
		||||
@@ -48,7 +48,7 @@ export class AuthService {
 | 
			
		||||
    this.authCore = new AuthCore(cryptoRepository, configRepository, userTokenRepository, initialConfig);
 | 
			
		||||
    this.oauthCore = new OAuthCore(configRepository, initialConfig);
 | 
			
		||||
    this.userCore = new UserCore(userRepository, cryptoRepository);
 | 
			
		||||
    this.shareCore = new ShareCore(shareRepository, cryptoRepository);
 | 
			
		||||
    this.shareCore = new SharedLinkCore(shareRepository, cryptoRepository);
 | 
			
		||||
    this.keyCore = new APIKeyCore(cryptoRepository, keyRepository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import { PartnerService } from './partner';
 | 
			
		||||
import { PersonService } from './person';
 | 
			
		||||
import { SearchService } from './search';
 | 
			
		||||
import { ServerInfoService } from './server-info';
 | 
			
		||||
import { ShareService } from './share';
 | 
			
		||||
import { SharedLinkService } from './shared-link';
 | 
			
		||||
import { SmartInfoService } from './smart-info';
 | 
			
		||||
import { StorageService } from './storage';
 | 
			
		||||
import { StorageTemplateService } from './storage-template';
 | 
			
		||||
@@ -34,7 +34,7 @@ const providers: Provider[] = [
 | 
			
		||||
  PartnerService,
 | 
			
		||||
  SearchService,
 | 
			
		||||
  ServerInfoService,
 | 
			
		||||
  ShareService,
 | 
			
		||||
  SharedLinkService,
 | 
			
		||||
  SmartInfoService,
 | 
			
		||||
  StorageService,
 | 
			
		||||
  StorageTemplateService,
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ export * from './person';
 | 
			
		||||
export * from './search';
 | 
			
		||||
export * from './server-info';
 | 
			
		||||
export * from './partner';
 | 
			
		||||
export * from './share';
 | 
			
		||||
export * from './shared-link';
 | 
			
		||||
export * from './smart-info';
 | 
			
		||||
export * from './storage';
 | 
			
		||||
export * from './storage-template';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,127 +0,0 @@
 | 
			
		||||
import { BadRequestException, ForbiddenException } from '@nestjs/common';
 | 
			
		||||
import {
 | 
			
		||||
  authStub,
 | 
			
		||||
  newCryptoRepositoryMock,
 | 
			
		||||
  newSharedLinkRepositoryMock,
 | 
			
		||||
  sharedLinkResponseStub,
 | 
			
		||||
  sharedLinkStub,
 | 
			
		||||
} from '../../test';
 | 
			
		||||
import { ICryptoRepository } from '../crypto';
 | 
			
		||||
import { ShareService } from './share.service';
 | 
			
		||||
import { ISharedLinkRepository } from './shared-link.repository';
 | 
			
		||||
 | 
			
		||||
describe(ShareService.name, () => {
 | 
			
		||||
  let sut: ShareService;
 | 
			
		||||
  let cryptoMock: jest.Mocked<ICryptoRepository>;
 | 
			
		||||
  let shareMock: jest.Mocked<ISharedLinkRepository>;
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
    cryptoMock = newCryptoRepositoryMock();
 | 
			
		||||
    shareMock = newSharedLinkRepositoryMock();
 | 
			
		||||
 | 
			
		||||
    sut = new ShareService(cryptoMock, shareMock);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should work', () => {
 | 
			
		||||
    expect(sut).toBeDefined();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('getAll', () => {
 | 
			
		||||
    it('should return all keys for a user', async () => {
 | 
			
		||||
      shareMock.getAll.mockResolvedValue([sharedLinkStub.expired, sharedLinkStub.valid]);
 | 
			
		||||
      await expect(sut.getAll(authStub.user1)).resolves.toEqual([
 | 
			
		||||
        sharedLinkResponseStub.expired,
 | 
			
		||||
        sharedLinkResponseStub.valid,
 | 
			
		||||
      ]);
 | 
			
		||||
      expect(shareMock.getAll).toHaveBeenCalledWith(authStub.user1.id);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('getMine', () => {
 | 
			
		||||
    it('should only work for a public user', async () => {
 | 
			
		||||
      await expect(sut.getMine(authStub.admin)).rejects.toBeInstanceOf(ForbiddenException);
 | 
			
		||||
      expect(shareMock.get).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should return the key for the public user (auth dto)', async () => {
 | 
			
		||||
      const authDto = authStub.adminSharedLink;
 | 
			
		||||
      shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      await expect(sut.getMine(authDto)).resolves.toEqual(sharedLinkResponseStub.valid);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('get', () => {
 | 
			
		||||
    it('should not work on a missing key', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(null);
 | 
			
		||||
      await expect(sut.getById(authStub.user1, sharedLinkStub.valid.id, true)).rejects.toBeInstanceOf(
 | 
			
		||||
        BadRequestException,
 | 
			
		||||
      );
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | 
			
		||||
      expect(shareMock.remove).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should get a key by id', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      await expect(sut.getById(authStub.user1, sharedLinkStub.valid.id, false)).resolves.toEqual(
 | 
			
		||||
        sharedLinkResponseStub.valid,
 | 
			
		||||
      );
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should include exif', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(sharedLinkStub.readonly);
 | 
			
		||||
      await expect(sut.getById(authStub.user1, sharedLinkStub.readonly.id, true)).resolves.toEqual(
 | 
			
		||||
        sharedLinkResponseStub.readonly,
 | 
			
		||||
      );
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.readonly.id);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should exclude exif', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(sharedLinkStub.readonly);
 | 
			
		||||
      await expect(sut.getById(authStub.user1, sharedLinkStub.readonly.id, false)).resolves.toEqual(
 | 
			
		||||
        sharedLinkResponseStub.readonlyNoExif,
 | 
			
		||||
      );
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.readonly.id);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('remove', () => {
 | 
			
		||||
    it('should not work on a missing key', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(null);
 | 
			
		||||
      await expect(sut.remove(authStub.user1, sharedLinkStub.valid.id)).rejects.toBeInstanceOf(BadRequestException);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | 
			
		||||
      expect(shareMock.remove).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should remove a key', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      await sut.remove(authStub.user1, sharedLinkStub.valid.id);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | 
			
		||||
      expect(shareMock.remove).toHaveBeenCalledWith(sharedLinkStub.valid);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('edit', () => {
 | 
			
		||||
    it('should not work on a missing key', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(null);
 | 
			
		||||
      await expect(sut.edit(authStub.user1, sharedLinkStub.valid.id, {})).rejects.toBeInstanceOf(BadRequestException);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | 
			
		||||
      expect(shareMock.save).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should edit a key', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      shareMock.save.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      const dto = { allowDownload: false };
 | 
			
		||||
      await sut.edit(authStub.user1, sharedLinkStub.valid.id, dto);
 | 
			
		||||
      // await expect(sut.edit(authStub.user1, sharedLinkStub.valid.id, dto)).rejects.toBeInstanceOf(BadRequestException);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | 
			
		||||
      expect(shareMock.save).toHaveBeenCalledWith({
 | 
			
		||||
        id: sharedLinkStub.valid.id,
 | 
			
		||||
        userId: authStub.user1.id,
 | 
			
		||||
        allowDownload: false,
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@@ -1,60 +0,0 @@
 | 
			
		||||
import { BadRequestException, ForbiddenException, Inject, Injectable, Logger } from '@nestjs/common';
 | 
			
		||||
import { AuthUserDto } from '../auth';
 | 
			
		||||
import { ICryptoRepository } from '../crypto';
 | 
			
		||||
import { EditSharedLinkDto } from './dto';
 | 
			
		||||
import { mapSharedLink, mapSharedLinkWithNoExif, SharedLinkResponseDto } from './response-dto';
 | 
			
		||||
import { ShareCore } from './share.core';
 | 
			
		||||
import { ISharedLinkRepository } from './shared-link.repository';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class ShareService {
 | 
			
		||||
  readonly logger = new Logger(ShareService.name);
 | 
			
		||||
  private shareCore: ShareCore;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
 | 
			
		||||
    @Inject(ISharedLinkRepository) sharedLinkRepository: ISharedLinkRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.shareCore = new ShareCore(sharedLinkRepository, cryptoRepository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async getAll(authUser: AuthUserDto): Promise<SharedLinkResponseDto[]> {
 | 
			
		||||
    const links = await this.shareCore.getAll(authUser.id);
 | 
			
		||||
    return links.map(mapSharedLink);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async getMine(authUser: AuthUserDto): Promise<SharedLinkResponseDto> {
 | 
			
		||||
    if (!authUser.isPublicUser || !authUser.sharedLinkId) {
 | 
			
		||||
      throw new ForbiddenException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let allowExif = true;
 | 
			
		||||
    if (authUser.isShowExif != undefined) {
 | 
			
		||||
      allowExif = authUser.isShowExif;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.getById(authUser, authUser.sharedLinkId, allowExif);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async getById(authUser: AuthUserDto, id: string, allowExif: boolean): Promise<SharedLinkResponseDto> {
 | 
			
		||||
    const link = await this.shareCore.get(authUser.id, id);
 | 
			
		||||
    if (!link) {
 | 
			
		||||
      throw new BadRequestException('Shared link not found');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (allowExif) {
 | 
			
		||||
      return mapSharedLink(link);
 | 
			
		||||
    } else {
 | 
			
		||||
      return mapSharedLinkWithNoExif(link);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async remove(authUser: AuthUserDto, id: string): Promise<void> {
 | 
			
		||||
    await this.shareCore.remove(authUser.id, id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async edit(authUser: AuthUserDto, id: string, dto: EditSharedLinkDto) {
 | 
			
		||||
    const link = await this.shareCore.save(authUser.id, id, dto);
 | 
			
		||||
    return mapSharedLink(link);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
export * from './dto';
 | 
			
		||||
export * from './response-dto';
 | 
			
		||||
export * from './share.core';
 | 
			
		||||
export * from './share.service';
 | 
			
		||||
export * from './shared-link.core';
 | 
			
		||||
export * from './shared-link.service';
 | 
			
		||||
export * from './shared-link.repository';
 | 
			
		||||
@@ -5,19 +5,12 @@ import { ICryptoRepository } from '../crypto';
 | 
			
		||||
import { CreateSharedLinkDto } from './dto';
 | 
			
		||||
import { ISharedLinkRepository } from './shared-link.repository';
 | 
			
		||||
 | 
			
		||||
export class ShareCore {
 | 
			
		||||
  readonly logger = new Logger(ShareCore.name);
 | 
			
		||||
export class SharedLinkCore {
 | 
			
		||||
  readonly logger = new Logger(SharedLinkCore.name);
 | 
			
		||||
 | 
			
		||||
  constructor(private repository: ISharedLinkRepository, private cryptoRepository: ICryptoRepository) {}
 | 
			
		||||
 | 
			
		||||
  getAll(userId: string): Promise<SharedLinkEntity[]> {
 | 
			
		||||
    return this.repository.getAll(userId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get(userId: string, id: string): Promise<SharedLinkEntity | null> {
 | 
			
		||||
    return this.repository.get(userId, id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // TODO: move to SharedLinkController/SharedLinkService
 | 
			
		||||
  create(userId: string, dto: CreateSharedLinkDto): Promise<SharedLinkEntity> {
 | 
			
		||||
    return this.repository.create({
 | 
			
		||||
      key: Buffer.from(this.cryptoRepository.randomBytes(50)),
 | 
			
		||||
@@ -34,42 +27,24 @@ export class ShareCore {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async save(userId: string, id: string, entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity> {
 | 
			
		||||
    const link = await this.get(userId, id);
 | 
			
		||||
    if (!link) {
 | 
			
		||||
      throw new BadRequestException('Shared link not found');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.repository.save({ ...entity, userId, id });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async remove(userId: string, id: string): Promise<void> {
 | 
			
		||||
    const link = await this.get(userId, id);
 | 
			
		||||
    if (!link) {
 | 
			
		||||
      throw new BadRequestException('Shared link not found');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await this.repository.remove(link);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async addAssets(userId: string, id: string, assets: AssetEntity[]) {
 | 
			
		||||
    const link = await this.get(userId, id);
 | 
			
		||||
    const link = await this.repository.get(userId, id);
 | 
			
		||||
    if (!link) {
 | 
			
		||||
      throw new BadRequestException('Shared link not found');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.repository.save({ ...link, assets: [...link.assets, ...assets] });
 | 
			
		||||
    return this.repository.update({ ...link, assets: [...link.assets, ...assets] });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async removeAssets(userId: string, id: string, assets: AssetEntity[]) {
 | 
			
		||||
    const link = await this.get(userId, id);
 | 
			
		||||
    const link = await this.repository.get(userId, id);
 | 
			
		||||
    if (!link) {
 | 
			
		||||
      throw new BadRequestException('Shared link not found');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const newAssets = link.assets.filter((asset) => assets.find((a) => a.id === asset.id));
 | 
			
		||||
 | 
			
		||||
    return this.repository.save({ ...link, assets: newAssets });
 | 
			
		||||
    return this.repository.update({ ...link, assets: newAssets });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async hasAssetAccess(id: string, assetId: string): Promise<boolean> {
 | 
			
		||||
@@ -7,7 +7,7 @@ export interface ISharedLinkRepository {
 | 
			
		||||
  get(userId: string, id: string): Promise<SharedLinkEntity | null>;
 | 
			
		||||
  getByKey(key: Buffer): Promise<SharedLinkEntity | null>;
 | 
			
		||||
  create(entity: Omit<SharedLinkEntity, 'id' | 'user'>): Promise<SharedLinkEntity>;
 | 
			
		||||
  update(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity>;
 | 
			
		||||
  remove(entity: SharedLinkEntity): Promise<void>;
 | 
			
		||||
  save(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity>;
 | 
			
		||||
  hasAssetAccess(id: string, assetId: string): Promise<boolean>;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										103
									
								
								server/libs/domain/src/shared-link/shared-link.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								server/libs/domain/src/shared-link/shared-link.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
import { BadRequestException, ForbiddenException } from '@nestjs/common';
 | 
			
		||||
import { authStub, newSharedLinkRepositoryMock, sharedLinkResponseStub, sharedLinkStub } from '../../test';
 | 
			
		||||
import { SharedLinkService } from './shared-link.service';
 | 
			
		||||
import { ISharedLinkRepository } from './shared-link.repository';
 | 
			
		||||
 | 
			
		||||
describe(SharedLinkService.name, () => {
 | 
			
		||||
  let sut: SharedLinkService;
 | 
			
		||||
  let shareMock: jest.Mocked<ISharedLinkRepository>;
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
    shareMock = newSharedLinkRepositoryMock();
 | 
			
		||||
 | 
			
		||||
    sut = new SharedLinkService(shareMock);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should work', () => {
 | 
			
		||||
    expect(sut).toBeDefined();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('getAll', () => {
 | 
			
		||||
    it('should return all shared links for a user', async () => {
 | 
			
		||||
      shareMock.getAll.mockResolvedValue([sharedLinkStub.expired, sharedLinkStub.valid]);
 | 
			
		||||
      await expect(sut.getAll(authStub.user1)).resolves.toEqual([
 | 
			
		||||
        sharedLinkResponseStub.expired,
 | 
			
		||||
        sharedLinkResponseStub.valid,
 | 
			
		||||
      ]);
 | 
			
		||||
      expect(shareMock.getAll).toHaveBeenCalledWith(authStub.user1.id);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('getMine', () => {
 | 
			
		||||
    it('should only work for a public user', async () => {
 | 
			
		||||
      await expect(sut.getMine(authStub.admin)).rejects.toBeInstanceOf(ForbiddenException);
 | 
			
		||||
      expect(shareMock.get).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should return the shared link for the public user', async () => {
 | 
			
		||||
      const authDto = authStub.adminSharedLink;
 | 
			
		||||
      shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      await expect(sut.getMine(authDto)).resolves.toEqual(sharedLinkResponseStub.valid);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should return not return exif', async () => {
 | 
			
		||||
      const authDto = authStub.adminSharedLinkNoExif;
 | 
			
		||||
      shareMock.get.mockResolvedValue(sharedLinkStub.readonlyNoExif);
 | 
			
		||||
      await expect(sut.getMine(authDto)).resolves.toEqual(sharedLinkResponseStub.readonlyNoExif);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('get', () => {
 | 
			
		||||
    it('should throw an error for an invalid shared link', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(null);
 | 
			
		||||
      await expect(sut.get(authStub.user1, 'missing-id')).rejects.toBeInstanceOf(BadRequestException);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, 'missing-id');
 | 
			
		||||
      expect(shareMock.update).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should get a shared link by id', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      await expect(sut.get(authStub.user1, sharedLinkStub.valid.id)).resolves.toEqual(sharedLinkResponseStub.valid);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('update', () => {
 | 
			
		||||
    it('should throw an error for an invalid shared link', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(null);
 | 
			
		||||
      await expect(sut.update(authStub.user1, 'missing-id', {})).rejects.toBeInstanceOf(BadRequestException);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, 'missing-id');
 | 
			
		||||
      expect(shareMock.update).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should update a shared link', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      shareMock.update.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      await sut.update(authStub.user1, sharedLinkStub.valid.id, { allowDownload: false });
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | 
			
		||||
      expect(shareMock.update).toHaveBeenCalledWith({
 | 
			
		||||
        id: sharedLinkStub.valid.id,
 | 
			
		||||
        userId: authStub.user1.id,
 | 
			
		||||
        allowDownload: false,
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('remove', () => {
 | 
			
		||||
    it('should throw an error for an invalid shared link', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(null);
 | 
			
		||||
      await expect(sut.remove(authStub.user1, 'missing-id')).rejects.toBeInstanceOf(BadRequestException);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, 'missing-id');
 | 
			
		||||
      expect(shareMock.update).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should remove a key', async () => {
 | 
			
		||||
      shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | 
			
		||||
      await sut.remove(authStub.user1, sharedLinkStub.valid.id);
 | 
			
		||||
      expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | 
			
		||||
      expect(shareMock.remove).toHaveBeenCalledWith(sharedLinkStub.valid);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										63
									
								
								server/libs/domain/src/shared-link/shared-link.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								server/libs/domain/src/shared-link/shared-link.service.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
import { SharedLinkEntity } from '@app/infra/entities';
 | 
			
		||||
import { BadRequestException, ForbiddenException, Inject, Injectable } from '@nestjs/common';
 | 
			
		||||
import { AuthUserDto } from '../auth';
 | 
			
		||||
import { EditSharedLinkDto } from './dto';
 | 
			
		||||
import { mapSharedLink, mapSharedLinkWithNoExif, SharedLinkResponseDto } from './response-dto';
 | 
			
		||||
import { ISharedLinkRepository } from './shared-link.repository';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class SharedLinkService {
 | 
			
		||||
  constructor(@Inject(ISharedLinkRepository) private repository: ISharedLinkRepository) {}
 | 
			
		||||
 | 
			
		||||
  async getAll(authUser: AuthUserDto): Promise<SharedLinkResponseDto[]> {
 | 
			
		||||
    return this.repository.getAll(authUser.id).then((links) => links.map(mapSharedLink));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async getMine(authUser: AuthUserDto): Promise<SharedLinkResponseDto> {
 | 
			
		||||
    const { sharedLinkId: id, isPublicUser, isShowExif } = authUser;
 | 
			
		||||
 | 
			
		||||
    if (!isPublicUser || !id) {
 | 
			
		||||
      throw new ForbiddenException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const sharedLink = await this.findOrFail(authUser, id);
 | 
			
		||||
 | 
			
		||||
    return this.map(sharedLink, { withExif: isShowExif ?? true });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async get(authUser: AuthUserDto, id: string): Promise<SharedLinkResponseDto> {
 | 
			
		||||
    const sharedLink = await this.findOrFail(authUser, id);
 | 
			
		||||
    return this.map(sharedLink, { withExif: true });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async update(authUser: AuthUserDto, id: string, dto: EditSharedLinkDto) {
 | 
			
		||||
    await this.findOrFail(authUser, id);
 | 
			
		||||
    const sharedLink = await this.repository.update({
 | 
			
		||||
      id,
 | 
			
		||||
      userId: authUser.id,
 | 
			
		||||
      description: dto.description,
 | 
			
		||||
      expiresAt: dto.expiresAt,
 | 
			
		||||
      allowUpload: dto.allowUpload,
 | 
			
		||||
      allowDownload: dto.allowDownload,
 | 
			
		||||
      showExif: dto.showExif,
 | 
			
		||||
    });
 | 
			
		||||
    return this.map(sharedLink, { withExif: true });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async remove(authUser: AuthUserDto, id: string): Promise<void> {
 | 
			
		||||
    const sharedLink = await this.findOrFail(authUser, id);
 | 
			
		||||
    await this.repository.remove(sharedLink);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async findOrFail(authUser: AuthUserDto, id: string) {
 | 
			
		||||
    const sharedLink = await this.repository.get(authUser.id, id);
 | 
			
		||||
    if (!sharedLink) {
 | 
			
		||||
      throw new BadRequestException('Shared link not found');
 | 
			
		||||
    }
 | 
			
		||||
    return sharedLink;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private map(sharedLink: SharedLinkEntity, { withExif }: { withExif: boolean }) {
 | 
			
		||||
    return withExif ? mapSharedLink(sharedLink) : mapSharedLinkWithNoExif(sharedLink);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -71,6 +71,16 @@ export const authStub = {
 | 
			
		||||
    isShowExif: true,
 | 
			
		||||
    sharedLinkId: '123',
 | 
			
		||||
  }),
 | 
			
		||||
  adminSharedLinkNoExif: Object.freeze<AuthUserDto>({
 | 
			
		||||
    id: 'admin_id',
 | 
			
		||||
    email: 'admin@test.com',
 | 
			
		||||
    isAdmin: true,
 | 
			
		||||
    isAllowUpload: true,
 | 
			
		||||
    isAllowDownload: true,
 | 
			
		||||
    isPublicUser: true,
 | 
			
		||||
    isShowExif: false,
 | 
			
		||||
    sharedLinkId: '123',
 | 
			
		||||
  }),
 | 
			
		||||
  readonlySharedLink: Object.freeze<AuthUserDto>({
 | 
			
		||||
    id: 'admin_id',
 | 
			
		||||
    email: 'admin@test.com',
 | 
			
		||||
@@ -690,7 +700,7 @@ export const sharedLinkStub = {
 | 
			
		||||
    showExif: true,
 | 
			
		||||
    assets: [],
 | 
			
		||||
  } as SharedLinkEntity),
 | 
			
		||||
  readonly: Object.freeze<SharedLinkEntity>({
 | 
			
		||||
  readonlyNoExif: Object.freeze<SharedLinkEntity>({
 | 
			
		||||
    id: '123',
 | 
			
		||||
    userId: authStub.admin.id,
 | 
			
		||||
    user: userEntityStub.admin,
 | 
			
		||||
@@ -700,7 +710,7 @@ export const sharedLinkStub = {
 | 
			
		||||
    expiresAt: tomorrow,
 | 
			
		||||
    allowUpload: false,
 | 
			
		||||
    allowDownload: false,
 | 
			
		||||
    showExif: true,
 | 
			
		||||
    showExif: false,
 | 
			
		||||
    assets: [],
 | 
			
		||||
    album: {
 | 
			
		||||
      id: 'album-123',
 | 
			
		||||
@@ -834,7 +844,7 @@ export const sharedLinkResponseStub = {
 | 
			
		||||
    description: undefined,
 | 
			
		||||
    allowUpload: false,
 | 
			
		||||
    allowDownload: false,
 | 
			
		||||
    showExif: true,
 | 
			
		||||
    showExif: false,
 | 
			
		||||
    album: albumResponse,
 | 
			
		||||
    assets: [{ ...assetResponse, exifInfo: undefined }],
 | 
			
		||||
  }),
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ export const newSharedLinkRepositoryMock = (): jest.Mocked<ISharedLinkRepository
 | 
			
		||||
    getByKey: jest.fn(),
 | 
			
		||||
    create: jest.fn(),
 | 
			
		||||
    remove: jest.fn(),
 | 
			
		||||
    save: jest.fn(),
 | 
			
		||||
    update: jest.fn(),
 | 
			
		||||
    hasAssetAccess: jest.fn(),
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,12 @@
 | 
			
		||||
import { ISharedLinkRepository } from '@app/domain';
 | 
			
		||||
import { Injectable, Logger } from '@nestjs/common';
 | 
			
		||||
import { Injectable } from '@nestjs/common';
 | 
			
		||||
import { InjectRepository } from '@nestjs/typeorm';
 | 
			
		||||
import { Repository } from 'typeorm';
 | 
			
		||||
import { SharedLinkEntity } from '../entities';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class SharedLinkRepository implements ISharedLinkRepository {
 | 
			
		||||
  readonly logger = new Logger(SharedLinkRepository.name);
 | 
			
		||||
  constructor(
 | 
			
		||||
    @InjectRepository(SharedLinkEntity)
 | 
			
		||||
    private readonly repository: Repository<SharedLinkEntity>,
 | 
			
		||||
  ) {}
 | 
			
		||||
  constructor(@InjectRepository(SharedLinkEntity) private repository: Repository<SharedLinkEntity>) {}
 | 
			
		||||
 | 
			
		||||
  get(userId: string, id: string): Promise<SharedLinkEntity | null> {
 | 
			
		||||
    return this.repository.findOne({
 | 
			
		||||
@@ -78,30 +74,22 @@ export class SharedLinkRepository implements ISharedLinkRepository {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  create(entity: Omit<SharedLinkEntity, 'id'>): Promise<SharedLinkEntity> {
 | 
			
		||||
    return this.repository.save(entity);
 | 
			
		||||
  create(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity> {
 | 
			
		||||
    return this.save(entity);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  update(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity> {
 | 
			
		||||
    return this.save(entity);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async remove(entity: SharedLinkEntity): Promise<void> {
 | 
			
		||||
    await this.repository.remove(entity);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async save(entity: SharedLinkEntity): Promise<SharedLinkEntity> {
 | 
			
		||||
    await this.repository.save(entity);
 | 
			
		||||
    return this.repository.findOneOrFail({ where: { id: entity.id } });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async hasAssetAccess(id: string, assetId: string): Promise<boolean> {
 | 
			
		||||
    const count1 = await this.repository.count({
 | 
			
		||||
      where: {
 | 
			
		||||
        id,
 | 
			
		||||
        assets: {
 | 
			
		||||
          id: assetId,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const count2 = await this.repository.count({
 | 
			
		||||
    return (
 | 
			
		||||
      // album asset
 | 
			
		||||
      (await this.repository.exist({
 | 
			
		||||
        where: {
 | 
			
		||||
          id,
 | 
			
		||||
          album: {
 | 
			
		||||
@@ -110,8 +98,21 @@ export class SharedLinkRepository implements ISharedLinkRepository {
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
      })) ||
 | 
			
		||||
      // individual asset
 | 
			
		||||
      (await this.repository.exist({
 | 
			
		||||
        where: {
 | 
			
		||||
          id,
 | 
			
		||||
          assets: {
 | 
			
		||||
            id: assetId,
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      }))
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    return Boolean(count1 + count2);
 | 
			
		||||
  private async save(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity> {
 | 
			
		||||
    await this.repository.save(entity);
 | 
			
		||||
    return this.repository.findOneOrFail({ where: { id: entity.id } });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										202
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										202
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							@@ -9984,54 +9984,6 @@ export class ServerInfoApi extends BaseAPI {
 | 
			
		||||
 */
 | 
			
		||||
export const ShareApiAxiosParamCreator = function (configuration?: Configuration) {
 | 
			
		||||
    return {
 | 
			
		||||
        /**
 | 
			
		||||
         * 
 | 
			
		||||
         * @param {string} id 
 | 
			
		||||
         * @param {EditSharedLinkDto} editSharedLinkDto 
 | 
			
		||||
         * @param {*} [options] Override http request option.
 | 
			
		||||
         * @throws {RequiredError}
 | 
			
		||||
         */
 | 
			
		||||
        editSharedLink: async (id: string, editSharedLinkDto: EditSharedLinkDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
 | 
			
		||||
            // verify required parameter 'id' is not null or undefined
 | 
			
		||||
            assertParamExists('editSharedLink', 'id', id)
 | 
			
		||||
            // verify required parameter 'editSharedLinkDto' is not null or undefined
 | 
			
		||||
            assertParamExists('editSharedLink', 'editSharedLinkDto', editSharedLinkDto)
 | 
			
		||||
            const localVarPath = `/share/{id}`
 | 
			
		||||
                .replace(`{${"id"}}`, encodeURIComponent(String(id)));
 | 
			
		||||
            // 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: 'PATCH', ...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(editSharedLinkDto, localVarRequestOptions, configuration)
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                url: toPathString(localVarUrlObj),
 | 
			
		||||
                options: localVarRequestOptions,
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
         * 
 | 
			
		||||
         * @param {*} [options] Override http request option.
 | 
			
		||||
@@ -10192,6 +10144,54 @@ export const ShareApiAxiosParamCreator = function (configuration?: Configuration
 | 
			
		||||
            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
 | 
			
		||||
            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                url: toPathString(localVarUrlObj),
 | 
			
		||||
                options: localVarRequestOptions,
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
         * 
 | 
			
		||||
         * @param {string} id 
 | 
			
		||||
         * @param {EditSharedLinkDto} editSharedLinkDto 
 | 
			
		||||
         * @param {*} [options] Override http request option.
 | 
			
		||||
         * @throws {RequiredError}
 | 
			
		||||
         */
 | 
			
		||||
        updateSharedLink: async (id: string, editSharedLinkDto: EditSharedLinkDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
 | 
			
		||||
            // verify required parameter 'id' is not null or undefined
 | 
			
		||||
            assertParamExists('updateSharedLink', 'id', id)
 | 
			
		||||
            // verify required parameter 'editSharedLinkDto' is not null or undefined
 | 
			
		||||
            assertParamExists('updateSharedLink', 'editSharedLinkDto', editSharedLinkDto)
 | 
			
		||||
            const localVarPath = `/share/{id}`
 | 
			
		||||
                .replace(`{${"id"}}`, encodeURIComponent(String(id)));
 | 
			
		||||
            // 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: 'PATCH', ...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(editSharedLinkDto, localVarRequestOptions, configuration)
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                url: toPathString(localVarUrlObj),
 | 
			
		||||
                options: localVarRequestOptions,
 | 
			
		||||
@@ -10207,17 +10207,6 @@ export const ShareApiAxiosParamCreator = function (configuration?: Configuration
 | 
			
		||||
export const ShareApiFp = function(configuration?: Configuration) {
 | 
			
		||||
    const localVarAxiosParamCreator = ShareApiAxiosParamCreator(configuration)
 | 
			
		||||
    return {
 | 
			
		||||
        /**
 | 
			
		||||
         * 
 | 
			
		||||
         * @param {string} id 
 | 
			
		||||
         * @param {EditSharedLinkDto} editSharedLinkDto 
 | 
			
		||||
         * @param {*} [options] Override http request option.
 | 
			
		||||
         * @throws {RequiredError}
 | 
			
		||||
         */
 | 
			
		||||
        async editSharedLink(id: string, editSharedLinkDto: EditSharedLinkDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SharedLinkResponseDto>> {
 | 
			
		||||
            const localVarAxiosArgs = await localVarAxiosParamCreator.editSharedLink(id, editSharedLinkDto, options);
 | 
			
		||||
            return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
         * 
 | 
			
		||||
         * @param {*} [options] Override http request option.
 | 
			
		||||
@@ -10257,6 +10246,17 @@ export const ShareApiFp = function(configuration?: Configuration) {
 | 
			
		||||
            const localVarAxiosArgs = await localVarAxiosParamCreator.removeSharedLink(id, options);
 | 
			
		||||
            return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
         * 
 | 
			
		||||
         * @param {string} id 
 | 
			
		||||
         * @param {EditSharedLinkDto} editSharedLinkDto 
 | 
			
		||||
         * @param {*} [options] Override http request option.
 | 
			
		||||
         * @throws {RequiredError}
 | 
			
		||||
         */
 | 
			
		||||
        async updateSharedLink(id: string, editSharedLinkDto: EditSharedLinkDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SharedLinkResponseDto>> {
 | 
			
		||||
            const localVarAxiosArgs = await localVarAxiosParamCreator.updateSharedLink(id, editSharedLinkDto, options);
 | 
			
		||||
            return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -10267,16 +10267,6 @@ export const ShareApiFp = function(configuration?: Configuration) {
 | 
			
		||||
export const ShareApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
 | 
			
		||||
    const localVarFp = ShareApiFp(configuration)
 | 
			
		||||
    return {
 | 
			
		||||
        /**
 | 
			
		||||
         * 
 | 
			
		||||
         * @param {string} id 
 | 
			
		||||
         * @param {EditSharedLinkDto} editSharedLinkDto 
 | 
			
		||||
         * @param {*} [options] Override http request option.
 | 
			
		||||
         * @throws {RequiredError}
 | 
			
		||||
         */
 | 
			
		||||
        editSharedLink(id: string, editSharedLinkDto: EditSharedLinkDto, options?: any): AxiosPromise<SharedLinkResponseDto> {
 | 
			
		||||
            return localVarFp.editSharedLink(id, editSharedLinkDto, options).then((request) => request(axios, basePath));
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
         * 
 | 
			
		||||
         * @param {*} [options] Override http request option.
 | 
			
		||||
@@ -10312,30 +10302,19 @@ export const ShareApiFactory = function (configuration?: Configuration, basePath
 | 
			
		||||
        removeSharedLink(id: string, options?: any): AxiosPromise<void> {
 | 
			
		||||
            return localVarFp.removeSharedLink(id, options).then((request) => request(axios, basePath));
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
         * 
 | 
			
		||||
         * @param {string} id 
 | 
			
		||||
         * @param {EditSharedLinkDto} editSharedLinkDto 
 | 
			
		||||
         * @param {*} [options] Override http request option.
 | 
			
		||||
         * @throws {RequiredError}
 | 
			
		||||
         */
 | 
			
		||||
        updateSharedLink(id: string, editSharedLinkDto: EditSharedLinkDto, options?: any): AxiosPromise<SharedLinkResponseDto> {
 | 
			
		||||
            return localVarFp.updateSharedLink(id, editSharedLinkDto, options).then((request) => request(axios, basePath));
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Request parameters for editSharedLink operation in ShareApi.
 | 
			
		||||
 * @export
 | 
			
		||||
 * @interface ShareApiEditSharedLinkRequest
 | 
			
		||||
 */
 | 
			
		||||
export interface ShareApiEditSharedLinkRequest {
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     * @type {string}
 | 
			
		||||
     * @memberof ShareApiEditSharedLink
 | 
			
		||||
     */
 | 
			
		||||
    readonly id: string
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     * @type {EditSharedLinkDto}
 | 
			
		||||
     * @memberof ShareApiEditSharedLink
 | 
			
		||||
     */
 | 
			
		||||
    readonly editSharedLinkDto: EditSharedLinkDto
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Request parameters for getMySharedLink operation in ShareApi.
 | 
			
		||||
 * @export
 | 
			
		||||
@@ -10378,6 +10357,27 @@ export interface ShareApiRemoveSharedLinkRequest {
 | 
			
		||||
    readonly id: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Request parameters for updateSharedLink operation in ShareApi.
 | 
			
		||||
 * @export
 | 
			
		||||
 * @interface ShareApiUpdateSharedLinkRequest
 | 
			
		||||
 */
 | 
			
		||||
export interface ShareApiUpdateSharedLinkRequest {
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     * @type {string}
 | 
			
		||||
     * @memberof ShareApiUpdateSharedLink
 | 
			
		||||
     */
 | 
			
		||||
    readonly id: string
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     * @type {EditSharedLinkDto}
 | 
			
		||||
     * @memberof ShareApiUpdateSharedLink
 | 
			
		||||
     */
 | 
			
		||||
    readonly editSharedLinkDto: EditSharedLinkDto
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ShareApi - object-oriented interface
 | 
			
		||||
 * @export
 | 
			
		||||
@@ -10385,17 +10385,6 @@ export interface ShareApiRemoveSharedLinkRequest {
 | 
			
		||||
 * @extends {BaseAPI}
 | 
			
		||||
 */
 | 
			
		||||
export class ShareApi extends BaseAPI {
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     * @param {ShareApiEditSharedLinkRequest} requestParameters Request parameters.
 | 
			
		||||
     * @param {*} [options] Override http request option.
 | 
			
		||||
     * @throws {RequiredError}
 | 
			
		||||
     * @memberof ShareApi
 | 
			
		||||
     */
 | 
			
		||||
    public editSharedLink(requestParameters: ShareApiEditSharedLinkRequest, options?: AxiosRequestConfig) {
 | 
			
		||||
        return ShareApiFp(this.configuration).editSharedLink(requestParameters.id, requestParameters.editSharedLinkDto, options).then((request) => request(this.axios, this.basePath));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     * @param {*} [options] Override http request option.
 | 
			
		||||
@@ -10438,6 +10427,17 @@ export class ShareApi extends BaseAPI {
 | 
			
		||||
    public removeSharedLink(requestParameters: ShareApiRemoveSharedLinkRequest, options?: AxiosRequestConfig) {
 | 
			
		||||
        return ShareApiFp(this.configuration).removeSharedLink(requestParameters.id, options).then((request) => request(this.axios, this.basePath));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     * @param {ShareApiUpdateSharedLinkRequest} requestParameters Request parameters.
 | 
			
		||||
     * @param {*} [options] Override http request option.
 | 
			
		||||
     * @throws {RequiredError}
 | 
			
		||||
     * @memberof ShareApi
 | 
			
		||||
     */
 | 
			
		||||
    public updateSharedLink(requestParameters: ShareApiUpdateSharedLinkRequest, options?: AxiosRequestConfig) {
 | 
			
		||||
        return ShareApiFp(this.configuration).updateSharedLink(requestParameters.id, requestParameters.editSharedLinkDto, options).then((request) => request(this.axios, this.basePath));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -137,7 +137,7 @@
 | 
			
		||||
					? new Date(currentTime + expirationTime).toISOString()
 | 
			
		||||
					: null;
 | 
			
		||||
 | 
			
		||||
				await api.shareApi.editSharedLink({
 | 
			
		||||
				await api.shareApi.updateSharedLink({
 | 
			
		||||
					id: editingLink.id,
 | 
			
		||||
					editSharedLinkDto: {
 | 
			
		||||
						description,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user