mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	refactor(server, web): create shared link (#2879)
* refactor: shared links * chore: open api * fix: tsc error
This commit is contained in:
		
							
								
								
									
										21
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							| @@ -37,8 +37,6 @@ doc/CheckDuplicateAssetResponseDto.md | ||||
| doc/CheckExistingAssetsDto.md | ||||
| doc/CheckExistingAssetsResponseDto.md | ||||
| doc/CreateAlbumDto.md | ||||
| doc/CreateAlbumShareLinkDto.md | ||||
| doc/CreateAssetsShareLinkDto.md | ||||
| doc/CreateProfileImageResponseDto.md | ||||
| doc/CreateTagDto.md | ||||
| doc/CreateUserDto.md | ||||
| @@ -48,7 +46,6 @@ doc/DeleteAssetDto.md | ||||
| doc/DeleteAssetResponseDto.md | ||||
| doc/DeleteAssetStatus.md | ||||
| doc/DownloadFilesDto.md | ||||
| doc/EditSharedLinkDto.md | ||||
| doc/ExifResponseDto.md | ||||
| doc/GetAssetByTimeBucketDto.md | ||||
| doc/GetAssetCountByTimeBucketDto.md | ||||
| @@ -89,7 +86,9 @@ doc/ServerInfoResponseDto.md | ||||
| doc/ServerPingResponse.md | ||||
| doc/ServerStatsResponseDto.md | ||||
| doc/ServerVersionReponseDto.md | ||||
| doc/ShareApi.md | ||||
| doc/SharedLinkApi.md | ||||
| doc/SharedLinkCreateDto.md | ||||
| doc/SharedLinkEditDto.md | ||||
| doc/SharedLinkResponseDto.md | ||||
| doc/SharedLinkType.md | ||||
| doc/SignUpDto.md | ||||
| @@ -128,7 +127,7 @@ lib/api/partner_api.dart | ||||
| lib/api/person_api.dart | ||||
| lib/api/search_api.dart | ||||
| lib/api/server_info_api.dart | ||||
| lib/api/share_api.dart | ||||
| lib/api/shared_link_api.dart | ||||
| lib/api/system_config_api.dart | ||||
| lib/api/tag_api.dart | ||||
| lib/api/user_api.dart | ||||
| @@ -170,8 +169,6 @@ lib/model/check_duplicate_asset_response_dto.dart | ||||
| lib/model/check_existing_assets_dto.dart | ||||
| lib/model/check_existing_assets_response_dto.dart | ||||
| lib/model/create_album_dto.dart | ||||
| lib/model/create_album_share_link_dto.dart | ||||
| lib/model/create_assets_share_link_dto.dart | ||||
| lib/model/create_profile_image_response_dto.dart | ||||
| lib/model/create_tag_dto.dart | ||||
| lib/model/create_user_dto.dart | ||||
| @@ -181,7 +178,6 @@ lib/model/delete_asset_dto.dart | ||||
| lib/model/delete_asset_response_dto.dart | ||||
| lib/model/delete_asset_status.dart | ||||
| lib/model/download_files_dto.dart | ||||
| lib/model/edit_shared_link_dto.dart | ||||
| lib/model/exif_response_dto.dart | ||||
| lib/model/get_asset_by_time_bucket_dto.dart | ||||
| lib/model/get_asset_count_by_time_bucket_dto.dart | ||||
| @@ -216,6 +212,8 @@ lib/model/server_info_response_dto.dart | ||||
| lib/model/server_ping_response.dart | ||||
| lib/model/server_stats_response_dto.dart | ||||
| lib/model/server_version_reponse_dto.dart | ||||
| lib/model/shared_link_create_dto.dart | ||||
| lib/model/shared_link_edit_dto.dart | ||||
| lib/model/shared_link_response_dto.dart | ||||
| lib/model/shared_link_type.dart | ||||
| lib/model/sign_up_dto.dart | ||||
| @@ -274,8 +272,6 @@ test/check_duplicate_asset_response_dto_test.dart | ||||
| test/check_existing_assets_dto_test.dart | ||||
| test/check_existing_assets_response_dto_test.dart | ||||
| test/create_album_dto_test.dart | ||||
| test/create_album_share_link_dto_test.dart | ||||
| test/create_assets_share_link_dto_test.dart | ||||
| test/create_profile_image_response_dto_test.dart | ||||
| test/create_tag_dto_test.dart | ||||
| test/create_user_dto_test.dart | ||||
| @@ -285,7 +281,6 @@ test/delete_asset_dto_test.dart | ||||
| test/delete_asset_response_dto_test.dart | ||||
| test/delete_asset_status_test.dart | ||||
| test/download_files_dto_test.dart | ||||
| test/edit_shared_link_dto_test.dart | ||||
| test/exif_response_dto_test.dart | ||||
| test/get_asset_by_time_bucket_dto_test.dart | ||||
| test/get_asset_count_by_time_bucket_dto_test.dart | ||||
| @@ -326,7 +321,9 @@ test/server_info_response_dto_test.dart | ||||
| test/server_ping_response_test.dart | ||||
| test/server_stats_response_dto_test.dart | ||||
| test/server_version_reponse_dto_test.dart | ||||
| test/share_api_test.dart | ||||
| test/shared_link_api_test.dart | ||||
| test/shared_link_create_dto_test.dart | ||||
| test/shared_link_edit_dto_test.dart | ||||
| test/shared_link_response_dto_test.dart | ||||
| test/shared_link_type_test.dart | ||||
| test/sign_up_dto_test.dart | ||||
|   | ||||
							
								
								
									
										22
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										22
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							| @@ -80,7 +80,6 @@ Class | Method | HTTP request | Description | ||||
| *AlbumApi* | [**addAssetsToAlbum**](doc//AlbumApi.md#addassetstoalbum) | **PUT** /album/{id}/assets |  | ||||
| *AlbumApi* | [**addUsersToAlbum**](doc//AlbumApi.md#adduserstoalbum) | **PUT** /album/{id}/users |  | ||||
| *AlbumApi* | [**createAlbum**](doc//AlbumApi.md#createalbum) | **POST** /album |  | ||||
| *AlbumApi* | [**createAlbumSharedLink**](doc//AlbumApi.md#createalbumsharedlink) | **POST** /album/create-shared-link |  | ||||
| *AlbumApi* | [**deleteAlbum**](doc//AlbumApi.md#deletealbum) | **DELETE** /album/{id} |  | ||||
| *AlbumApi* | [**downloadArchive**](doc//AlbumApi.md#downloadarchive) | **GET** /album/{id}/download |  | ||||
| *AlbumApi* | [**getAlbumCount**](doc//AlbumApi.md#getalbumcount) | **GET** /album/count |  | ||||
| @@ -89,11 +88,9 @@ Class | Method | HTTP request | Description | ||||
| *AlbumApi* | [**removeAssetFromAlbum**](doc//AlbumApi.md#removeassetfromalbum) | **DELETE** /album/{id}/assets |  | ||||
| *AlbumApi* | [**removeUserFromAlbum**](doc//AlbumApi.md#removeuserfromalbum) | **DELETE** /album/{id}/user/{userId} |  | ||||
| *AlbumApi* | [**updateAlbumInfo**](doc//AlbumApi.md#updatealbuminfo) | **PATCH** /album/{id} |  | ||||
| *AssetApi* | [**addAssetsToSharedLink**](doc//AssetApi.md#addassetstosharedlink) | **PATCH** /asset/shared-link/add |  | ||||
| *AssetApi* | [**bulkUploadCheck**](doc//AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |  | ||||
| *AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |  | ||||
| *AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |  | ||||
| *AssetApi* | [**createAssetsSharedLink**](doc//AssetApi.md#createassetssharedlink) | **POST** /asset/shared-link |  | ||||
| *AssetApi* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset |  | ||||
| *AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download/{id} |  | ||||
| *AssetApi* | [**downloadFiles**](doc//AssetApi.md#downloadfiles) | **POST** /asset/download-files |  | ||||
| @@ -111,7 +108,6 @@ Class | Method | HTTP request | Description | ||||
| *AssetApi* | [**getMapMarkers**](doc//AssetApi.md#getmapmarkers) | **GET** /asset/map-marker |  | ||||
| *AssetApi* | [**getMemoryLane**](doc//AssetApi.md#getmemorylane) | **GET** /asset/memory-lane |  | ||||
| *AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |  | ||||
| *AssetApi* | [**removeAssetsFromSharedLink**](doc//AssetApi.md#removeassetsfromsharedlink) | **PATCH** /asset/shared-link/remove |  | ||||
| *AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search |  | ||||
| *AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} |  | ||||
| *AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{id} |  | ||||
| @@ -146,11 +142,14 @@ 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* | [**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} |  | ||||
| *SharedLinkApi* | [**addSharedLinkAssets**](doc//SharedLinkApi.md#addsharedlinkassets) | **PUT** /shared-link/{id}/assets |  | ||||
| *SharedLinkApi* | [**createSharedLink**](doc//SharedLinkApi.md#createsharedlink) | **POST** /shared-link |  | ||||
| *SharedLinkApi* | [**getAllSharedLinks**](doc//SharedLinkApi.md#getallsharedlinks) | **GET** /shared-link |  | ||||
| *SharedLinkApi* | [**getMySharedLink**](doc//SharedLinkApi.md#getmysharedlink) | **GET** /shared-link/me |  | ||||
| *SharedLinkApi* | [**getSharedLinkById**](doc//SharedLinkApi.md#getsharedlinkbyid) | **GET** /shared-link/{id} |  | ||||
| *SharedLinkApi* | [**removeSharedLink**](doc//SharedLinkApi.md#removesharedlink) | **DELETE** /shared-link/{id} |  | ||||
| *SharedLinkApi* | [**removeSharedLinkAssets**](doc//SharedLinkApi.md#removesharedlinkassets) | **DELETE** /shared-link/{id}/assets |  | ||||
| *SharedLinkApi* | [**updateSharedLink**](doc//SharedLinkApi.md#updatesharedlink) | **PATCH** /shared-link/{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 |  | ||||
| @@ -207,8 +206,6 @@ Class | Method | HTTP request | Description | ||||
|  - [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md) | ||||
|  - [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md) | ||||
|  - [CreateAlbumDto](doc//CreateAlbumDto.md) | ||||
|  - [CreateAlbumShareLinkDto](doc//CreateAlbumShareLinkDto.md) | ||||
|  - [CreateAssetsShareLinkDto](doc//CreateAssetsShareLinkDto.md) | ||||
|  - [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md) | ||||
|  - [CreateTagDto](doc//CreateTagDto.md) | ||||
|  - [CreateUserDto](doc//CreateUserDto.md) | ||||
| @@ -218,7 +215,6 @@ Class | Method | HTTP request | Description | ||||
|  - [DeleteAssetResponseDto](doc//DeleteAssetResponseDto.md) | ||||
|  - [DeleteAssetStatus](doc//DeleteAssetStatus.md) | ||||
|  - [DownloadFilesDto](doc//DownloadFilesDto.md) | ||||
|  - [EditSharedLinkDto](doc//EditSharedLinkDto.md) | ||||
|  - [ExifResponseDto](doc//ExifResponseDto.md) | ||||
|  - [GetAssetByTimeBucketDto](doc//GetAssetByTimeBucketDto.md) | ||||
|  - [GetAssetCountByTimeBucketDto](doc//GetAssetCountByTimeBucketDto.md) | ||||
| @@ -253,6 +249,8 @@ Class | Method | HTTP request | Description | ||||
|  - [ServerPingResponse](doc//ServerPingResponse.md) | ||||
|  - [ServerStatsResponseDto](doc//ServerStatsResponseDto.md) | ||||
|  - [ServerVersionReponseDto](doc//ServerVersionReponseDto.md) | ||||
|  - [SharedLinkCreateDto](doc//SharedLinkCreateDto.md) | ||||
|  - [SharedLinkEditDto](doc//SharedLinkEditDto.md) | ||||
|  - [SharedLinkResponseDto](doc//SharedLinkResponseDto.md) | ||||
|  - [SharedLinkType](doc//SharedLinkType.md) | ||||
|  - [SignUpDto](doc//SignUpDto.md) | ||||
|   | ||||
							
								
								
									
										56
									
								
								mobile/openapi/doc/AlbumApi.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										56
									
								
								mobile/openapi/doc/AlbumApi.md
									
									
									
										generated
									
									
									
								
							| @@ -12,7 +12,6 @@ Method | HTTP request | Description | ||||
| [**addAssetsToAlbum**](AlbumApi.md#addassetstoalbum) | **PUT** /album/{id}/assets |  | ||||
| [**addUsersToAlbum**](AlbumApi.md#adduserstoalbum) | **PUT** /album/{id}/users |  | ||||
| [**createAlbum**](AlbumApi.md#createalbum) | **POST** /album |  | ||||
| [**createAlbumSharedLink**](AlbumApi.md#createalbumsharedlink) | **POST** /album/create-shared-link |  | ||||
| [**deleteAlbum**](AlbumApi.md#deletealbum) | **DELETE** /album/{id} |  | ||||
| [**downloadArchive**](AlbumApi.md#downloadarchive) | **GET** /album/{id}/download |  | ||||
| [**getAlbumCount**](AlbumApi.md#getalbumcount) | **GET** /album/count |  | ||||
| @@ -194,61 +193,6 @@ 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) | ||||
| 
 | ||||
| # **createAlbumSharedLink** | ||||
| > SharedLinkResponseDto createAlbumSharedLink(createAlbumShareLinkDto) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### 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 = AlbumApi(); | ||||
| final createAlbumShareLinkDto = CreateAlbumShareLinkDto(); // CreateAlbumShareLinkDto |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.createAlbumSharedLink(createAlbumShareLinkDto); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AlbumApi->createAlbumSharedLink: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| 
 | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **createAlbumShareLinkDto** | [**CreateAlbumShareLinkDto**](CreateAlbumShareLinkDto.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) | ||||
| 
 | ||||
| # **deleteAlbum** | ||||
| > deleteAlbum(id) | ||||
| 
 | ||||
|   | ||||
							
								
								
									
										172
									
								
								mobile/openapi/doc/AssetApi.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										172
									
								
								mobile/openapi/doc/AssetApi.md
									
									
									
										generated
									
									
									
								
							| @@ -9,11 +9,9 @@ All URIs are relative to */api* | ||||
| 
 | ||||
| Method | HTTP request | Description | ||||
| ------------- | ------------- | ------------- | ||||
| [**addAssetsToSharedLink**](AssetApi.md#addassetstosharedlink) | **PATCH** /asset/shared-link/add |  | ||||
| [**bulkUploadCheck**](AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |  | ||||
| [**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |  | ||||
| [**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |  | ||||
| [**createAssetsSharedLink**](AssetApi.md#createassetssharedlink) | **POST** /asset/shared-link |  | ||||
| [**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset |  | ||||
| [**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download/{id} |  | ||||
| [**downloadFiles**](AssetApi.md#downloadfiles) | **POST** /asset/download-files |  | ||||
| @@ -31,70 +29,12 @@ Method | HTTP request | Description | ||||
| [**getMapMarkers**](AssetApi.md#getmapmarkers) | **GET** /asset/map-marker |  | ||||
| [**getMemoryLane**](AssetApi.md#getmemorylane) | **GET** /asset/memory-lane |  | ||||
| [**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |  | ||||
| [**removeAssetsFromSharedLink**](AssetApi.md#removeassetsfromsharedlink) | **PATCH** /asset/shared-link/remove |  | ||||
| [**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search |  | ||||
| [**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{id} |  | ||||
| [**updateAsset**](AssetApi.md#updateasset) | **PUT** /asset/{id} |  | ||||
| [**uploadFile**](AssetApi.md#uploadfile) | **POST** /asset/upload |  | ||||
| 
 | ||||
| 
 | ||||
| # **addAssetsToSharedLink** | ||||
| > SharedLinkResponseDto addAssetsToSharedLink(addAssetsDto, key) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### 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 = AssetApi(); | ||||
| final addAssetsDto = AddAssetsDto(); // AddAssetsDto |  | ||||
| final key = key_example; // String |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.addAssetsToSharedLink(addAssetsDto, key); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AssetApi->addAssetsToSharedLink: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| 
 | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **addAssetsDto** | [**AddAssetsDto**](AddAssetsDto.md)|  |  | ||||
|  **key** | **String**|  | [optional]  | ||||
| 
 | ||||
| ### 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) | ||||
| 
 | ||||
| # **bulkUploadCheck** | ||||
| > AssetBulkUploadCheckResponseDto bulkUploadCheck(assetBulkUploadCheckDto) | ||||
| 
 | ||||
| @@ -268,61 +208,6 @@ 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) | ||||
| 
 | ||||
| # **createAssetsSharedLink** | ||||
| > SharedLinkResponseDto createAssetsSharedLink(createAssetsShareLinkDto) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### 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 = AssetApi(); | ||||
| final createAssetsShareLinkDto = CreateAssetsShareLinkDto(); // CreateAssetsShareLinkDto |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.createAssetsSharedLink(createAssetsShareLinkDto); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AssetApi->createAssetsSharedLink: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| 
 | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **createAssetsShareLinkDto** | [**CreateAssetsShareLinkDto**](CreateAssetsShareLinkDto.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) | ||||
| 
 | ||||
| # **deleteAsset** | ||||
| > List<DeleteAssetResponseDto> deleteAsset(deleteAssetDto) | ||||
| 
 | ||||
| @@ -1274,63 +1159,6 @@ 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) | ||||
| 
 | ||||
| # **removeAssetsFromSharedLink** | ||||
| > SharedLinkResponseDto removeAssetsFromSharedLink(removeAssetsDto, key) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### 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 = AssetApi(); | ||||
| final removeAssetsDto = RemoveAssetsDto(); // RemoveAssetsDto |  | ||||
| final key = key_example; // String |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.removeAssetsFromSharedLink(removeAssetsDto, key); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AssetApi->removeAssetsFromSharedLink: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| 
 | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **removeAssetsDto** | [**RemoveAssetsDto**](RemoveAssetsDto.md)|  |  | ||||
|  **key** | **String**|  | [optional]  | ||||
| 
 | ||||
| ### 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) | ||||
| 
 | ||||
| # **searchAsset** | ||||
| > List<AssetResponseDto> searchAsset(searchAssetDto) | ||||
| 
 | ||||
|   | ||||
							
								
								
									
										20
									
								
								mobile/openapi/doc/CreateAssetsShareLinkDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								mobile/openapi/doc/CreateAssetsShareLinkDto.md
									
									
									
										generated
									
									
									
								
							| @@ -1,20 +0,0 @@ | ||||
| # openapi.model.CreateAssetsShareLinkDto | ||||
| 
 | ||||
| ## Load the model package | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| ``` | ||||
| 
 | ||||
| ## Properties | ||||
| Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| **assetIds** | **List<String>** |  | [default to const []] | ||||
| **expiresAt** | [**DateTime**](DateTime.md) |  | [optional]  | ||||
| **allowUpload** | **bool** |  | [optional]  | ||||
| **allowDownload** | **bool** |  | [optional]  | ||||
| **showExif** | **bool** |  | [optional]  | ||||
| **description** | **String** |  | [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) | ||||
| 
 | ||||
| 
 | ||||
| @@ -1,4 +1,4 @@ | ||||
| # openapi.api.ShareApi | ||||
| # openapi.api.SharedLinkApi | ||||
| 
 | ||||
| ## Load the API package | ||||
| ```dart | ||||
| @@ -9,13 +9,130 @@ All URIs are relative to */api* | ||||
| 
 | ||||
| Method | HTTP request | Description | ||||
| ------------- | ------------- | ------------- | ||||
| [**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} |  | ||||
| [**addSharedLinkAssets**](SharedLinkApi.md#addsharedlinkassets) | **PUT** /shared-link/{id}/assets |  | ||||
| [**createSharedLink**](SharedLinkApi.md#createsharedlink) | **POST** /shared-link |  | ||||
| [**getAllSharedLinks**](SharedLinkApi.md#getallsharedlinks) | **GET** /shared-link |  | ||||
| [**getMySharedLink**](SharedLinkApi.md#getmysharedlink) | **GET** /shared-link/me |  | ||||
| [**getSharedLinkById**](SharedLinkApi.md#getsharedlinkbyid) | **GET** /shared-link/{id} |  | ||||
| [**removeSharedLink**](SharedLinkApi.md#removesharedlink) | **DELETE** /shared-link/{id} |  | ||||
| [**removeSharedLinkAssets**](SharedLinkApi.md#removesharedlinkassets) | **DELETE** /shared-link/{id}/assets |  | ||||
| [**updateSharedLink**](SharedLinkApi.md#updatesharedlink) | **PATCH** /shared-link/{id} |  | ||||
| 
 | ||||
| 
 | ||||
| # **addSharedLinkAssets** | ||||
| > List<AssetIdsResponseDto> addSharedLinkAssets(id, assetIdsDto, key) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### 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 = SharedLinkApi(); | ||||
| final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |  | ||||
| final assetIdsDto = AssetIdsDto(); // AssetIdsDto |  | ||||
| final key = key_example; // String |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.addSharedLinkAssets(id, assetIdsDto, key); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling SharedLinkApi->addSharedLinkAssets: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| 
 | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **id** | **String**|  |  | ||||
|  **assetIdsDto** | [**AssetIdsDto**](AssetIdsDto.md)|  |  | ||||
|  **key** | **String**|  | [optional]  | ||||
| 
 | ||||
| ### Return type | ||||
| 
 | ||||
| [**List<AssetIdsResponseDto>**](AssetIdsResponseDto.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) | ||||
| 
 | ||||
| # **createSharedLink** | ||||
| > SharedLinkResponseDto createSharedLink(sharedLinkCreateDto) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### 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 = SharedLinkApi(); | ||||
| final sharedLinkCreateDto = SharedLinkCreateDto(); // SharedLinkCreateDto |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.createSharedLink(sharedLinkCreateDto); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling SharedLinkApi->createSharedLink: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| 
 | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **sharedLinkCreateDto** | [**SharedLinkCreateDto**](SharedLinkCreateDto.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() | ||||
| 
 | ||||
| @@ -39,13 +156,13 @@ import 'package:openapi/api.dart'; | ||||
| // String yourTokenGeneratorFunction() { ... } | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||
| 
 | ||||
| final api_instance = ShareApi(); | ||||
| final api_instance = SharedLinkApi(); | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.getAllSharedLinks(); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling ShareApi->getAllSharedLinks: $e\n'); | ||||
|     print('Exception when calling SharedLinkApi->getAllSharedLinks: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| @@ -90,14 +207,14 @@ import 'package:openapi/api.dart'; | ||||
| // String yourTokenGeneratorFunction() { ... } | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||
| 
 | ||||
| final api_instance = ShareApi(); | ||||
| final api_instance = SharedLinkApi(); | ||||
| final key = key_example; // String |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.getMySharedLink(key); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling ShareApi->getMySharedLink: $e\n'); | ||||
|     print('Exception when calling SharedLinkApi->getMySharedLink: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| @@ -145,14 +262,14 @@ import 'package:openapi/api.dart'; | ||||
| // String yourTokenGeneratorFunction() { ... } | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||
| 
 | ||||
| final api_instance = ShareApi(); | ||||
| final api_instance = SharedLinkApi(); | ||||
| final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.getSharedLinkById(id); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling ShareApi->getSharedLinkById: $e\n'); | ||||
|     print('Exception when calling SharedLinkApi->getSharedLinkById: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| @@ -200,13 +317,13 @@ import 'package:openapi/api.dart'; | ||||
| // String yourTokenGeneratorFunction() { ... } | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||
| 
 | ||||
| final api_instance = ShareApi(); | ||||
| final api_instance = SharedLinkApi(); | ||||
| final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |  | ||||
| 
 | ||||
| try { | ||||
|     api_instance.removeSharedLink(id); | ||||
| } catch (e) { | ||||
|     print('Exception when calling ShareApi->removeSharedLink: $e\n'); | ||||
|     print('Exception when calling SharedLinkApi->removeSharedLink: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| @@ -231,8 +348,8 @@ 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) | ||||
| # **removeSharedLinkAssets** | ||||
| > List<AssetIdsResponseDto> removeSharedLinkAssets(id, assetIdsDto, key) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @@ -254,15 +371,16 @@ import 'package:openapi/api.dart'; | ||||
| // String yourTokenGeneratorFunction() { ... } | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||
| 
 | ||||
| final api_instance = ShareApi(); | ||||
| final api_instance = SharedLinkApi(); | ||||
| final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |  | ||||
| final editSharedLinkDto = EditSharedLinkDto(); // EditSharedLinkDto |  | ||||
| final assetIdsDto = AssetIdsDto(); // AssetIdsDto |  | ||||
| final key = key_example; // String |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.updateSharedLink(id, editSharedLinkDto); | ||||
|     final result = api_instance.removeSharedLinkAssets(id, assetIdsDto, key); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling ShareApi->updateSharedLink: $e\n'); | ||||
|     print('Exception when calling SharedLinkApi->removeSharedLinkAssets: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| @@ -271,7 +389,65 @@ try { | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **id** | **String**|  |  | ||||
|  **editSharedLinkDto** | [**EditSharedLinkDto**](EditSharedLinkDto.md)|  |  | ||||
|  **assetIdsDto** | [**AssetIdsDto**](AssetIdsDto.md)|  |  | ||||
|  **key** | **String**|  | [optional]  | ||||
| 
 | ||||
| ### Return type | ||||
| 
 | ||||
| [**List<AssetIdsResponseDto>**](AssetIdsResponseDto.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) | ||||
| 
 | ||||
| # **updateSharedLink** | ||||
| > SharedLinkResponseDto updateSharedLink(id, sharedLinkEditDto) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### 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 = SharedLinkApi(); | ||||
| final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |  | ||||
| final sharedLinkEditDto = SharedLinkEditDto(); // SharedLinkEditDto |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.updateSharedLink(id, sharedLinkEditDto); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling SharedLinkApi->updateSharedLink: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| 
 | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **id** | **String**|  |  | ||||
|  **sharedLinkEditDto** | [**SharedLinkEditDto**](SharedLinkEditDto.md)|  |  | ||||
| 
 | ||||
| ### Return type | ||||
| 
 | ||||
| @@ -1,4 +1,4 @@ | ||||
| # openapi.model.CreateAlbumShareLinkDto | ||||
| # openapi.model.SharedLinkCreateDto | ||||
| 
 | ||||
| ## Load the model package | ||||
| ```dart | ||||
| @@ -8,12 +8,14 @@ import 'package:openapi/api.dart'; | ||||
| ## Properties | ||||
| Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| **albumId** | **String** |  |  | ||||
| **expiresAt** | [**DateTime**](DateTime.md) |  | [optional]  | ||||
| **allowUpload** | **bool** |  | [optional]  | ||||
| **allowDownload** | **bool** |  | [optional]  | ||||
| **showExif** | **bool** |  | [optional]  | ||||
| **type** | [**SharedLinkType**](SharedLinkType.md) |  |  | ||||
| **assetIds** | **List<String>** |  | [optional] [default to const []] | ||||
| **albumId** | **String** |  | [optional]  | ||||
| **description** | **String** |  | [optional]  | ||||
| **expiresAt** | [**DateTime**](DateTime.md) |  | [optional]  | ||||
| **allowUpload** | **bool** |  | [optional] [default to false] | ||||
| **allowDownload** | **bool** |  | [optional] [default to true] | ||||
| **showExif** | **bool** |  | [optional] [default to true] | ||||
| 
 | ||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||
| 
 | ||||
| @@ -1,4 +1,4 @@ | ||||
| # openapi.model.EditSharedLinkDto | ||||
| # openapi.model.SharedLinkEditDto | ||||
| 
 | ||||
| ## Load the model package | ||||
| ```dart | ||||
							
								
								
									
										2
									
								
								mobile/openapi/doc/SharedLinkResponseDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/doc/SharedLinkResponseDto.md
									
									
									
										generated
									
									
									
								
							| @@ -10,7 +10,7 @@ Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| **type** | [**SharedLinkType**](SharedLinkType.md) |  |  | ||||
| **id** | **String** |  |  | ||||
| **description** | **String** |  | [optional]  | ||||
| **description** | **String** |  |  | ||||
| **userId** | **String** |  |  | ||||
| **key** | **String** |  |  | ||||
| **createdAt** | [**DateTime**](DateTime.md) |  |  | ||||
|   | ||||
							
								
								
									
										7
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							| @@ -38,7 +38,7 @@ part 'api/partner_api.dart'; | ||||
| part 'api/person_api.dart'; | ||||
| part 'api/search_api.dart'; | ||||
| part 'api/server_info_api.dart'; | ||||
| part 'api/share_api.dart'; | ||||
| part 'api/shared_link_api.dart'; | ||||
| part 'api/system_config_api.dart'; | ||||
| part 'api/tag_api.dart'; | ||||
| part 'api/user_api.dart'; | ||||
| @@ -73,8 +73,6 @@ part 'model/check_duplicate_asset_response_dto.dart'; | ||||
| part 'model/check_existing_assets_dto.dart'; | ||||
| part 'model/check_existing_assets_response_dto.dart'; | ||||
| part 'model/create_album_dto.dart'; | ||||
| part 'model/create_album_share_link_dto.dart'; | ||||
| part 'model/create_assets_share_link_dto.dart'; | ||||
| part 'model/create_profile_image_response_dto.dart'; | ||||
| part 'model/create_tag_dto.dart'; | ||||
| part 'model/create_user_dto.dart'; | ||||
| @@ -84,7 +82,6 @@ part 'model/delete_asset_dto.dart'; | ||||
| part 'model/delete_asset_response_dto.dart'; | ||||
| part 'model/delete_asset_status.dart'; | ||||
| part 'model/download_files_dto.dart'; | ||||
| part 'model/edit_shared_link_dto.dart'; | ||||
| part 'model/exif_response_dto.dart'; | ||||
| part 'model/get_asset_by_time_bucket_dto.dart'; | ||||
| part 'model/get_asset_count_by_time_bucket_dto.dart'; | ||||
| @@ -119,6 +116,8 @@ part 'model/server_info_response_dto.dart'; | ||||
| part 'model/server_ping_response.dart'; | ||||
| part 'model/server_stats_response_dto.dart'; | ||||
| part 'model/server_version_reponse_dto.dart'; | ||||
| part 'model/shared_link_create_dto.dart'; | ||||
| part 'model/shared_link_edit_dto.dart'; | ||||
| part 'model/shared_link_response_dto.dart'; | ||||
| part 'model/shared_link_type.dart'; | ||||
| part 'model/sign_up_dto.dart'; | ||||
|   | ||||
							
								
								
									
										47
									
								
								mobile/openapi/lib/api/album_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										47
									
								
								mobile/openapi/lib/api/album_api.dart
									
									
									
										generated
									
									
									
								
							| @@ -175,53 +175,6 @@ class AlbumApi { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'POST /album/create-shared-link' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [CreateAlbumShareLinkDto] createAlbumShareLinkDto (required): | ||||
|   Future<Response> createAlbumSharedLinkWithHttpInfo(CreateAlbumShareLinkDto createAlbumShareLinkDto,) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/album/create-shared-link'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody = createAlbumShareLinkDto; | ||||
| 
 | ||||
|     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: | ||||
|   /// | ||||
|   /// * [CreateAlbumShareLinkDto] createAlbumShareLinkDto (required): | ||||
|   Future<SharedLinkResponseDto?> createAlbumSharedLink(CreateAlbumShareLinkDto createAlbumShareLinkDto,) async { | ||||
|     final response = await createAlbumSharedLinkWithHttpInfo(createAlbumShareLinkDto,); | ||||
|     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 'DELETE /album/{id}' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   | ||||
							
								
								
									
										157
									
								
								mobile/openapi/lib/api/asset_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										157
									
								
								mobile/openapi/lib/api/asset_api.dart
									
									
									
										generated
									
									
									
								
							| @@ -16,61 +16,6 @@ class AssetApi { | ||||
| 
 | ||||
|   final ApiClient apiClient; | ||||
| 
 | ||||
|   /// Performs an HTTP 'PATCH /asset/shared-link/add' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [AddAssetsDto] addAssetsDto (required): | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<Response> addAssetsToSharedLinkWithHttpInfo(AddAssetsDto addAssetsDto, { String? key, }) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/asset/shared-link/add'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody = addAssetsDto; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     if (key != null) { | ||||
|       queryParams.addAll(_queryParams('', 'key', key)); | ||||
|     } | ||||
| 
 | ||||
|     const contentTypes = <String>['application/json']; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'PATCH', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [AddAssetsDto] addAssetsDto (required): | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<SharedLinkResponseDto?> addAssetsToSharedLink(AddAssetsDto addAssetsDto, { String? key, }) async { | ||||
|     final response = await addAssetsToSharedLinkWithHttpInfo(addAssetsDto,  key: key, ); | ||||
|     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; | ||||
|   } | ||||
| 
 | ||||
|   /// Checks if assets exist by checksums | ||||
|   /// | ||||
|   /// Note: This method returns the HTTP [Response]. | ||||
| @@ -235,53 +180,6 @@ class AssetApi { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'POST /asset/shared-link' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [CreateAssetsShareLinkDto] createAssetsShareLinkDto (required): | ||||
|   Future<Response> createAssetsSharedLinkWithHttpInfo(CreateAssetsShareLinkDto createAssetsShareLinkDto,) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/asset/shared-link'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody = createAssetsShareLinkDto; | ||||
| 
 | ||||
|     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: | ||||
|   /// | ||||
|   /// * [CreateAssetsShareLinkDto] createAssetsShareLinkDto (required): | ||||
|   Future<SharedLinkResponseDto?> createAssetsSharedLink(CreateAssetsShareLinkDto createAssetsShareLinkDto,) async { | ||||
|     final response = await createAssetsSharedLinkWithHttpInfo(createAssetsShareLinkDto,); | ||||
|     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 'DELETE /asset' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
| @@ -1225,61 +1123,6 @@ class AssetApi { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'PATCH /asset/shared-link/remove' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [RemoveAssetsDto] removeAssetsDto (required): | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<Response> removeAssetsFromSharedLinkWithHttpInfo(RemoveAssetsDto removeAssetsDto, { String? key, }) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/asset/shared-link/remove'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody = removeAssetsDto; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     if (key != null) { | ||||
|       queryParams.addAll(_queryParams('', 'key', key)); | ||||
|     } | ||||
| 
 | ||||
|     const contentTypes = <String>['application/json']; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'PATCH', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [RemoveAssetsDto] removeAssetsDto (required): | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<SharedLinkResponseDto?> removeAssetsFromSharedLink(RemoveAssetsDto removeAssetsDto, { String? key, }) async { | ||||
|     final response = await removeAssetsFromSharedLinkWithHttpInfo(removeAssetsDto,  key: key, ); | ||||
|     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 'POST /asset/search' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   | ||||
							
								
								
									
										253
									
								
								mobile/openapi/lib/api/share_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										253
									
								
								mobile/openapi/lib/api/share_api.dart
									
									
									
										generated
									
									
									
								
							| @@ -1,253 +0,0 @@ | ||||
| // | ||||
| // 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 ShareApi { | ||||
|   ShareApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient; | ||||
| 
 | ||||
|   final ApiClient apiClient; | ||||
| 
 | ||||
|   /// Performs an HTTP 'GET /share' operation and returns the [Response]. | ||||
|   Future<Response> getAllSharedLinksWithHttpInfo() async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/share'; | ||||
| 
 | ||||
|     // 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<List<SharedLinkResponseDto>?> getAllSharedLinks() async { | ||||
|     final response = await getAllSharedLinksWithHttpInfo(); | ||||
|     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<SharedLinkResponseDto>') as List) | ||||
|         .cast<SharedLinkResponseDto>() | ||||
|         .toList(); | ||||
| 
 | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'GET /share/me' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<Response> getMySharedLinkWithHttpInfo({ String? key, }) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/share/me'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     if (key != null) { | ||||
|       queryParams.addAll(_queryParams('', 'key', key)); | ||||
|     } | ||||
| 
 | ||||
|     const contentTypes = <String>[]; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'GET', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<SharedLinkResponseDto?> getMySharedLink({ String? key, }) async { | ||||
|     final response = await getMySharedLinkWithHttpInfo( key: key, ); | ||||
|     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/{id}' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   Future<Response> getSharedLinkByIdWithHttpInfo(String id,) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/share/{id}' | ||||
|       .replaceAll('{id}', id); | ||||
| 
 | ||||
|     // 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, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   Future<SharedLinkResponseDto?> getSharedLinkById(String id,) async { | ||||
|     final response = await getSharedLinkByIdWithHttpInfo(id,); | ||||
|     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 'DELETE /share/{id}' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   Future<Response> removeSharedLinkWithHttpInfo(String id,) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/share/{id}' | ||||
|       .replaceAll('{id}', id); | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     const contentTypes = <String>[]; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'DELETE', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   Future<void> removeSharedLink(String id,) async { | ||||
|     final response = await removeSharedLinkWithHttpInfo(id,); | ||||
|     if (response.statusCode >= HttpStatus.badRequest) { | ||||
|       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; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										426
									
								
								mobile/openapi/lib/api/shared_link_api.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										426
									
								
								mobile/openapi/lib/api/shared_link_api.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,426 @@ | ||||
| // | ||||
| // 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 SharedLinkApi { | ||||
|   SharedLinkApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient; | ||||
| 
 | ||||
|   final ApiClient apiClient; | ||||
| 
 | ||||
|   /// Performs an HTTP 'PUT /shared-link/{id}/assets' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   /// | ||||
|   /// * [AssetIdsDto] assetIdsDto (required): | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<Response> addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/shared-link/{id}/assets' | ||||
|       .replaceAll('{id}', id); | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody = assetIdsDto; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     if (key != null) { | ||||
|       queryParams.addAll(_queryParams('', 'key', key)); | ||||
|     } | ||||
| 
 | ||||
|     const contentTypes = <String>['application/json']; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'PUT', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   /// | ||||
|   /// * [AssetIdsDto] assetIdsDto (required): | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<List<AssetIdsResponseDto>?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async { | ||||
|     final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto,  key: key, ); | ||||
|     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<AssetIdsResponseDto>') as List) | ||||
|         .cast<AssetIdsResponseDto>() | ||||
|         .toList(); | ||||
| 
 | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'POST /shared-link' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): | ||||
|   Future<Response> createSharedLinkWithHttpInfo(SharedLinkCreateDto sharedLinkCreateDto,) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/shared-link'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody = sharedLinkCreateDto; | ||||
| 
 | ||||
|     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: | ||||
|   /// | ||||
|   /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): | ||||
|   Future<SharedLinkResponseDto?> createSharedLink(SharedLinkCreateDto sharedLinkCreateDto,) async { | ||||
|     final response = await createSharedLinkWithHttpInfo(sharedLinkCreateDto,); | ||||
|     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 /shared-link' operation and returns the [Response]. | ||||
|   Future<Response> getAllSharedLinksWithHttpInfo() async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/shared-link'; | ||||
| 
 | ||||
|     // 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<List<SharedLinkResponseDto>?> getAllSharedLinks() async { | ||||
|     final response = await getAllSharedLinksWithHttpInfo(); | ||||
|     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<SharedLinkResponseDto>') as List) | ||||
|         .cast<SharedLinkResponseDto>() | ||||
|         .toList(); | ||||
| 
 | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'GET /shared-link/me' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<Response> getMySharedLinkWithHttpInfo({ String? key, }) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/shared-link/me'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     if (key != null) { | ||||
|       queryParams.addAll(_queryParams('', 'key', key)); | ||||
|     } | ||||
| 
 | ||||
|     const contentTypes = <String>[]; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'GET', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<SharedLinkResponseDto?> getMySharedLink({ String? key, }) async { | ||||
|     final response = await getMySharedLinkWithHttpInfo( key: key, ); | ||||
|     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 /shared-link/{id}' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   Future<Response> getSharedLinkByIdWithHttpInfo(String id,) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/shared-link/{id}' | ||||
|       .replaceAll('{id}', id); | ||||
| 
 | ||||
|     // 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, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   Future<SharedLinkResponseDto?> getSharedLinkById(String id,) async { | ||||
|     final response = await getSharedLinkByIdWithHttpInfo(id,); | ||||
|     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 'DELETE /shared-link/{id}' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   Future<Response> removeSharedLinkWithHttpInfo(String id,) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/shared-link/{id}' | ||||
|       .replaceAll('{id}', id); | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     const contentTypes = <String>[]; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'DELETE', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   Future<void> removeSharedLink(String id,) async { | ||||
|     final response = await removeSharedLinkWithHttpInfo(id,); | ||||
|     if (response.statusCode >= HttpStatus.badRequest) { | ||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'DELETE /shared-link/{id}/assets' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   /// | ||||
|   /// * [AssetIdsDto] assetIdsDto (required): | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<Response> removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/shared-link/{id}/assets' | ||||
|       .replaceAll('{id}', id); | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody = assetIdsDto; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     if (key != null) { | ||||
|       queryParams.addAll(_queryParams('', 'key', key)); | ||||
|     } | ||||
| 
 | ||||
|     const contentTypes = <String>['application/json']; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'DELETE', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   /// | ||||
|   /// * [AssetIdsDto] assetIdsDto (required): | ||||
|   /// | ||||
|   /// * [String] key: | ||||
|   Future<List<AssetIdsResponseDto>?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async { | ||||
|     final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto,  key: key, ); | ||||
|     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<AssetIdsResponseDto>') as List) | ||||
|         .cast<AssetIdsResponseDto>() | ||||
|         .toList(); | ||||
| 
 | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'PATCH /shared-link/{id}' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] id (required): | ||||
|   /// | ||||
|   /// * [SharedLinkEditDto] sharedLinkEditDto (required): | ||||
|   Future<Response> updateSharedLinkWithHttpInfo(String id, SharedLinkEditDto sharedLinkEditDto,) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/shared-link/{id}' | ||||
|       .replaceAll('{id}', id); | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody = sharedLinkEditDto; | ||||
| 
 | ||||
|     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): | ||||
|   /// | ||||
|   /// * [SharedLinkEditDto] sharedLinkEditDto (required): | ||||
|   Future<SharedLinkResponseDto?> updateSharedLink(String id, SharedLinkEditDto sharedLinkEditDto,) async { | ||||
|     final response = await updateSharedLinkWithHttpInfo(id, sharedLinkEditDto,); | ||||
|     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/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							| @@ -241,10 +241,6 @@ class ApiClient { | ||||
|           return CheckExistingAssetsResponseDto.fromJson(value); | ||||
|         case 'CreateAlbumDto': | ||||
|           return CreateAlbumDto.fromJson(value); | ||||
|         case 'CreateAlbumShareLinkDto': | ||||
|           return CreateAlbumShareLinkDto.fromJson(value); | ||||
|         case 'CreateAssetsShareLinkDto': | ||||
|           return CreateAssetsShareLinkDto.fromJson(value); | ||||
|         case 'CreateProfileImageResponseDto': | ||||
|           return CreateProfileImageResponseDto.fromJson(value); | ||||
|         case 'CreateTagDto': | ||||
| @@ -263,8 +259,6 @@ class ApiClient { | ||||
|           return DeleteAssetStatusTypeTransformer().decode(value); | ||||
|         case 'DownloadFilesDto': | ||||
|           return DownloadFilesDto.fromJson(value); | ||||
|         case 'EditSharedLinkDto': | ||||
|           return EditSharedLinkDto.fromJson(value); | ||||
|         case 'ExifResponseDto': | ||||
|           return ExifResponseDto.fromJson(value); | ||||
|         case 'GetAssetByTimeBucketDto': | ||||
| @@ -333,6 +327,10 @@ class ApiClient { | ||||
|           return ServerStatsResponseDto.fromJson(value); | ||||
|         case 'ServerVersionReponseDto': | ||||
|           return ServerVersionReponseDto.fromJson(value); | ||||
|         case 'SharedLinkCreateDto': | ||||
|           return SharedLinkCreateDto.fromJson(value); | ||||
|         case 'SharedLinkEditDto': | ||||
|           return SharedLinkEditDto.fromJson(value); | ||||
|         case 'SharedLinkResponseDto': | ||||
|           return SharedLinkResponseDto.fromJson(value); | ||||
|         case 'SharedLinkType': | ||||
|   | ||||
| @@ -1,194 +0,0 @@ | ||||
| // | ||||
| // 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 CreateAlbumShareLinkDto { | ||||
|   /// Returns a new [CreateAlbumShareLinkDto] instance. | ||||
|   CreateAlbumShareLinkDto({ | ||||
|     required this.albumId, | ||||
|     this.expiresAt, | ||||
|     this.allowUpload, | ||||
|     this.allowDownload, | ||||
|     this.showExif, | ||||
|     this.description, | ||||
|   }); | ||||
| 
 | ||||
|   String albumId; | ||||
| 
 | ||||
|   /// | ||||
|   /// 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. | ||||
|   /// | ||||
|   DateTime? expiresAt; | ||||
| 
 | ||||
|   /// | ||||
|   /// 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. | ||||
|   /// | ||||
|   bool? allowUpload; | ||||
| 
 | ||||
|   /// | ||||
|   /// 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. | ||||
|   /// | ||||
|   bool? allowDownload; | ||||
| 
 | ||||
|   /// | ||||
|   /// 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. | ||||
|   /// | ||||
|   bool? showExif; | ||||
| 
 | ||||
|   /// | ||||
|   /// 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? description; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => identical(this, other) || other is CreateAlbumShareLinkDto && | ||||
|      other.albumId == albumId && | ||||
|      other.expiresAt == expiresAt && | ||||
|      other.allowUpload == allowUpload && | ||||
|      other.allowDownload == allowDownload && | ||||
|      other.showExif == showExif && | ||||
|      other.description == description; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (albumId.hashCode) + | ||||
|     (expiresAt == null ? 0 : expiresAt!.hashCode) + | ||||
|     (allowUpload == null ? 0 : allowUpload!.hashCode) + | ||||
|     (allowDownload == null ? 0 : allowDownload!.hashCode) + | ||||
|     (showExif == null ? 0 : showExif!.hashCode) + | ||||
|     (description == null ? 0 : description!.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'CreateAlbumShareLinkDto[albumId=$albumId, expiresAt=$expiresAt, allowUpload=$allowUpload, allowDownload=$allowDownload, showExif=$showExif, description=$description]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|       json[r'albumId'] = this.albumId; | ||||
|     if (this.expiresAt != null) { | ||||
|       json[r'expiresAt'] = this.expiresAt!.toUtc().toIso8601String(); | ||||
|     } else { | ||||
|       // json[r'expiresAt'] = null; | ||||
|     } | ||||
|     if (this.allowUpload != null) { | ||||
|       json[r'allowUpload'] = this.allowUpload; | ||||
|     } else { | ||||
|       // json[r'allowUpload'] = null; | ||||
|     } | ||||
|     if (this.allowDownload != null) { | ||||
|       json[r'allowDownload'] = this.allowDownload; | ||||
|     } else { | ||||
|       // json[r'allowDownload'] = null; | ||||
|     } | ||||
|     if (this.showExif != null) { | ||||
|       json[r'showExif'] = this.showExif; | ||||
|     } else { | ||||
|       // json[r'showExif'] = null; | ||||
|     } | ||||
|     if (this.description != null) { | ||||
|       json[r'description'] = this.description; | ||||
|     } else { | ||||
|       // json[r'description'] = null; | ||||
|     } | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
|   /// Returns a new [CreateAlbumShareLinkDto] instance and imports its values from | ||||
|   /// [value] if it's a [Map], null otherwise. | ||||
|   // ignore: prefer_constructors_over_static_methods | ||||
|   static CreateAlbumShareLinkDto? fromJson(dynamic value) { | ||||
|     if (value is Map) { | ||||
|       final json = value.cast<String, dynamic>(); | ||||
| 
 | ||||
|       // Ensure that the map contains the required keys. | ||||
|       // Note 1: the values aren't checked for validity beyond being non-null. | ||||
|       // Note 2: this code is stripped in release mode! | ||||
|       assert(() { | ||||
|         requiredKeys.forEach((key) { | ||||
|           assert(json.containsKey(key), 'Required key "CreateAlbumShareLinkDto[$key]" is missing from JSON.'); | ||||
|           assert(json[key] != null, 'Required key "CreateAlbumShareLinkDto[$key]" has a null value in JSON.'); | ||||
|         }); | ||||
|         return true; | ||||
|       }()); | ||||
| 
 | ||||
|       return CreateAlbumShareLinkDto( | ||||
|         albumId: mapValueOfType<String>(json, r'albumId')!, | ||||
|         expiresAt: mapDateTime(json, r'expiresAt', ''), | ||||
|         allowUpload: mapValueOfType<bool>(json, r'allowUpload'), | ||||
|         allowDownload: mapValueOfType<bool>(json, r'allowDownload'), | ||||
|         showExif: mapValueOfType<bool>(json, r'showExif'), | ||||
|         description: mapValueOfType<String>(json, r'description'), | ||||
|       ); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<CreateAlbumShareLinkDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <CreateAlbumShareLinkDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = CreateAlbumShareLinkDto.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| 
 | ||||
|   static Map<String, CreateAlbumShareLinkDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, CreateAlbumShareLinkDto>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = CreateAlbumShareLinkDto.fromJson(entry.value); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of CreateAlbumShareLinkDto-objects as value to a dart map | ||||
|   static Map<String, List<CreateAlbumShareLinkDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<CreateAlbumShareLinkDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       // ignore: parameter_assignments | ||||
|       json = json.cast<String, dynamic>(); | ||||
|       for (final entry in json.entries) { | ||||
|         map[entry.key] = CreateAlbumShareLinkDto.listFromJson(entry.value, growable: growable,); | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   /// The list of required keys that must be present in a JSON. | ||||
|   static const requiredKeys = <String>{ | ||||
|     'albumId', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| @@ -10,17 +10,21 @@ | ||||
| 
 | ||||
| part of openapi.api; | ||||
| 
 | ||||
| class CreateAssetsShareLinkDto { | ||||
|   /// Returns a new [CreateAssetsShareLinkDto] instance. | ||||
|   CreateAssetsShareLinkDto({ | ||||
| class SharedLinkCreateDto { | ||||
|   /// Returns a new [SharedLinkCreateDto] instance. | ||||
|   SharedLinkCreateDto({ | ||||
|     required this.type, | ||||
|     this.assetIds = const [], | ||||
|     this.expiresAt, | ||||
|     this.allowUpload, | ||||
|     this.allowDownload, | ||||
|     this.showExif, | ||||
|     this.albumId, | ||||
|     this.description, | ||||
|     this.expiresAt, | ||||
|     this.allowUpload = false, | ||||
|     this.allowDownload = true, | ||||
|     this.showExif = true, | ||||
|   }); | ||||
| 
 | ||||
|   SharedLinkType type; | ||||
| 
 | ||||
|   List<String> assetIds; | ||||
| 
 | ||||
|   /// | ||||
| @@ -29,31 +33,7 @@ class CreateAssetsShareLinkDto { | ||||
|   /// source code must fall back to having a nullable type. | ||||
|   /// Consider adding a "default:" property in the specification file to hide this note. | ||||
|   /// | ||||
|   DateTime? expiresAt; | ||||
| 
 | ||||
|   /// | ||||
|   /// 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. | ||||
|   /// | ||||
|   bool? allowUpload; | ||||
| 
 | ||||
|   /// | ||||
|   /// 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. | ||||
|   /// | ||||
|   bool? allowDownload; | ||||
| 
 | ||||
|   /// | ||||
|   /// 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. | ||||
|   /// | ||||
|   bool? showExif; | ||||
|   String? albumId; | ||||
| 
 | ||||
|   /// | ||||
|   /// Please note: This property should have been non-nullable! Since the specification file | ||||
| @@ -63,63 +43,69 @@ class CreateAssetsShareLinkDto { | ||||
|   /// | ||||
|   String? description; | ||||
| 
 | ||||
|   DateTime? expiresAt; | ||||
| 
 | ||||
|   bool allowUpload; | ||||
| 
 | ||||
|   bool allowDownload; | ||||
| 
 | ||||
|   bool showExif; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => identical(this, other) || other is CreateAssetsShareLinkDto && | ||||
|   bool operator ==(Object other) => identical(this, other) || other is SharedLinkCreateDto && | ||||
|      other.type == type && | ||||
|      other.assetIds == assetIds && | ||||
|      other.albumId == albumId && | ||||
|      other.description == description && | ||||
|      other.expiresAt == expiresAt && | ||||
|      other.allowUpload == allowUpload && | ||||
|      other.allowDownload == allowDownload && | ||||
|      other.showExif == showExif && | ||||
|      other.description == description; | ||||
|      other.showExif == showExif; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (type.hashCode) + | ||||
|     (assetIds.hashCode) + | ||||
|     (albumId == null ? 0 : albumId!.hashCode) + | ||||
|     (description == null ? 0 : description!.hashCode) + | ||||
|     (expiresAt == null ? 0 : expiresAt!.hashCode) + | ||||
|     (allowUpload == null ? 0 : allowUpload!.hashCode) + | ||||
|     (allowDownload == null ? 0 : allowDownload!.hashCode) + | ||||
|     (showExif == null ? 0 : showExif!.hashCode) + | ||||
|     (description == null ? 0 : description!.hashCode); | ||||
|     (allowUpload.hashCode) + | ||||
|     (allowDownload.hashCode) + | ||||
|     (showExif.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'CreateAssetsShareLinkDto[assetIds=$assetIds, expiresAt=$expiresAt, allowUpload=$allowUpload, allowDownload=$allowDownload, showExif=$showExif, description=$description]'; | ||||
|   String toString() => 'SharedLinkCreateDto[type=$type, assetIds=$assetIds, albumId=$albumId, description=$description, expiresAt=$expiresAt, allowUpload=$allowUpload, allowDownload=$allowDownload, showExif=$showExif]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|       json[r'type'] = this.type; | ||||
|       json[r'assetIds'] = this.assetIds; | ||||
|     if (this.expiresAt != null) { | ||||
|       json[r'expiresAt'] = this.expiresAt!.toUtc().toIso8601String(); | ||||
|     if (this.albumId != null) { | ||||
|       json[r'albumId'] = this.albumId; | ||||
|     } else { | ||||
|       // json[r'expiresAt'] = null; | ||||
|     } | ||||
|     if (this.allowUpload != null) { | ||||
|       json[r'allowUpload'] = this.allowUpload; | ||||
|     } else { | ||||
|       // json[r'allowUpload'] = null; | ||||
|     } | ||||
|     if (this.allowDownload != null) { | ||||
|       json[r'allowDownload'] = this.allowDownload; | ||||
|     } else { | ||||
|       // json[r'allowDownload'] = null; | ||||
|     } | ||||
|     if (this.showExif != null) { | ||||
|       json[r'showExif'] = this.showExif; | ||||
|     } else { | ||||
|       // json[r'showExif'] = null; | ||||
|       // json[r'albumId'] = null; | ||||
|     } | ||||
|     if (this.description != null) { | ||||
|       json[r'description'] = this.description; | ||||
|     } else { | ||||
|       // json[r'description'] = null; | ||||
|     } | ||||
|     if (this.expiresAt != null) { | ||||
|       json[r'expiresAt'] = this.expiresAt!.toUtc().toIso8601String(); | ||||
|     } else { | ||||
|       // json[r'expiresAt'] = null; | ||||
|     } | ||||
|       json[r'allowUpload'] = this.allowUpload; | ||||
|       json[r'allowDownload'] = this.allowDownload; | ||||
|       json[r'showExif'] = this.showExif; | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
|   /// Returns a new [CreateAssetsShareLinkDto] instance and imports its values from | ||||
|   /// Returns a new [SharedLinkCreateDto] instance and imports its values from | ||||
|   /// [value] if it's a [Map], null otherwise. | ||||
|   // ignore: prefer_constructors_over_static_methods | ||||
|   static CreateAssetsShareLinkDto? fromJson(dynamic value) { | ||||
|   static SharedLinkCreateDto? fromJson(dynamic value) { | ||||
|     if (value is Map) { | ||||
|       final json = value.cast<String, dynamic>(); | ||||
| 
 | ||||
| @@ -128,31 +114,33 @@ class CreateAssetsShareLinkDto { | ||||
|       // Note 2: this code is stripped in release mode! | ||||
|       assert(() { | ||||
|         requiredKeys.forEach((key) { | ||||
|           assert(json.containsKey(key), 'Required key "CreateAssetsShareLinkDto[$key]" is missing from JSON.'); | ||||
|           assert(json[key] != null, 'Required key "CreateAssetsShareLinkDto[$key]" has a null value in JSON.'); | ||||
|           assert(json.containsKey(key), 'Required key "SharedLinkCreateDto[$key]" is missing from JSON.'); | ||||
|           assert(json[key] != null, 'Required key "SharedLinkCreateDto[$key]" has a null value in JSON.'); | ||||
|         }); | ||||
|         return true; | ||||
|       }()); | ||||
| 
 | ||||
|       return CreateAssetsShareLinkDto( | ||||
|       return SharedLinkCreateDto( | ||||
|         type: SharedLinkType.fromJson(json[r'type'])!, | ||||
|         assetIds: json[r'assetIds'] is Iterable | ||||
|             ? (json[r'assetIds'] as Iterable).cast<String>().toList(growable: false) | ||||
|             : const [], | ||||
|         expiresAt: mapDateTime(json, r'expiresAt', ''), | ||||
|         allowUpload: mapValueOfType<bool>(json, r'allowUpload'), | ||||
|         allowDownload: mapValueOfType<bool>(json, r'allowDownload'), | ||||
|         showExif: mapValueOfType<bool>(json, r'showExif'), | ||||
|         albumId: mapValueOfType<String>(json, r'albumId'), | ||||
|         description: mapValueOfType<String>(json, r'description'), | ||||
|         expiresAt: mapDateTime(json, r'expiresAt', ''), | ||||
|         allowUpload: mapValueOfType<bool>(json, r'allowUpload') ?? false, | ||||
|         allowDownload: mapValueOfType<bool>(json, r'allowDownload') ?? true, | ||||
|         showExif: mapValueOfType<bool>(json, r'showExif') ?? true, | ||||
|       ); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<CreateAssetsShareLinkDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <CreateAssetsShareLinkDto>[]; | ||||
|   static List<SharedLinkCreateDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <SharedLinkCreateDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = CreateAssetsShareLinkDto.fromJson(row); | ||||
|         final value = SharedLinkCreateDto.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
| @@ -161,12 +149,12 @@ class CreateAssetsShareLinkDto { | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| 
 | ||||
|   static Map<String, CreateAssetsShareLinkDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, CreateAssetsShareLinkDto>{}; | ||||
|   static Map<String, SharedLinkCreateDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, SharedLinkCreateDto>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = CreateAssetsShareLinkDto.fromJson(entry.value); | ||||
|         final value = SharedLinkCreateDto.fromJson(entry.value); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
| @@ -175,14 +163,14 @@ class CreateAssetsShareLinkDto { | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of CreateAssetsShareLinkDto-objects as value to a dart map | ||||
|   static Map<String, List<CreateAssetsShareLinkDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<CreateAssetsShareLinkDto>>{}; | ||||
|   // maps a json object with a list of SharedLinkCreateDto-objects as value to a dart map | ||||
|   static Map<String, List<SharedLinkCreateDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<SharedLinkCreateDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       // ignore: parameter_assignments | ||||
|       json = json.cast<String, dynamic>(); | ||||
|       for (final entry in json.entries) { | ||||
|         map[entry.key] = CreateAssetsShareLinkDto.listFromJson(entry.value, growable: growable,); | ||||
|         map[entry.key] = SharedLinkCreateDto.listFromJson(entry.value, growable: growable,); | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
| @@ -190,7 +178,7 @@ class CreateAssetsShareLinkDto { | ||||
| 
 | ||||
|   /// The list of required keys that must be present in a JSON. | ||||
|   static const requiredKeys = <String>{ | ||||
|     'assetIds', | ||||
|     'type', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| @@ -10,9 +10,9 @@ | ||||
| 
 | ||||
| part of openapi.api; | ||||
| 
 | ||||
| class EditSharedLinkDto { | ||||
|   /// Returns a new [EditSharedLinkDto] instance. | ||||
|   EditSharedLinkDto({ | ||||
| class SharedLinkEditDto { | ||||
|   /// Returns a new [SharedLinkEditDto] instance. | ||||
|   SharedLinkEditDto({ | ||||
|     this.description, | ||||
|     this.expiresAt, | ||||
|     this.allowUpload, | ||||
| @@ -55,7 +55,7 @@ class EditSharedLinkDto { | ||||
|   bool? showExif; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => identical(this, other) || other is EditSharedLinkDto && | ||||
|   bool operator ==(Object other) => identical(this, other) || other is SharedLinkEditDto && | ||||
|      other.description == description && | ||||
|      other.expiresAt == expiresAt && | ||||
|      other.allowUpload == allowUpload && | ||||
| @@ -72,7 +72,7 @@ class EditSharedLinkDto { | ||||
|     (showExif == null ? 0 : showExif!.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'EditSharedLinkDto[description=$description, expiresAt=$expiresAt, allowUpload=$allowUpload, allowDownload=$allowDownload, showExif=$showExif]'; | ||||
|   String toString() => 'SharedLinkEditDto[description=$description, expiresAt=$expiresAt, allowUpload=$allowUpload, allowDownload=$allowDownload, showExif=$showExif]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
| @@ -104,10 +104,10 @@ class EditSharedLinkDto { | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
|   /// Returns a new [EditSharedLinkDto] instance and imports its values from | ||||
|   /// Returns a new [SharedLinkEditDto] instance and imports its values from | ||||
|   /// [value] if it's a [Map], null otherwise. | ||||
|   // ignore: prefer_constructors_over_static_methods | ||||
|   static EditSharedLinkDto? fromJson(dynamic value) { | ||||
|   static SharedLinkEditDto? fromJson(dynamic value) { | ||||
|     if (value is Map) { | ||||
|       final json = value.cast<String, dynamic>(); | ||||
| 
 | ||||
| @@ -116,13 +116,13 @@ class EditSharedLinkDto { | ||||
|       // Note 2: this code is stripped in release mode! | ||||
|       assert(() { | ||||
|         requiredKeys.forEach((key) { | ||||
|           assert(json.containsKey(key), 'Required key "EditSharedLinkDto[$key]" is missing from JSON.'); | ||||
|           assert(json[key] != null, 'Required key "EditSharedLinkDto[$key]" has a null value in JSON.'); | ||||
|           assert(json.containsKey(key), 'Required key "SharedLinkEditDto[$key]" is missing from JSON.'); | ||||
|           assert(json[key] != null, 'Required key "SharedLinkEditDto[$key]" has a null value in JSON.'); | ||||
|         }); | ||||
|         return true; | ||||
|       }()); | ||||
| 
 | ||||
|       return EditSharedLinkDto( | ||||
|       return SharedLinkEditDto( | ||||
|         description: mapValueOfType<String>(json, r'description'), | ||||
|         expiresAt: mapDateTime(json, r'expiresAt', ''), | ||||
|         allowUpload: mapValueOfType<bool>(json, r'allowUpload'), | ||||
| @@ -133,11 +133,11 @@ class EditSharedLinkDto { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<EditSharedLinkDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <EditSharedLinkDto>[]; | ||||
|   static List<SharedLinkEditDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <SharedLinkEditDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = EditSharedLinkDto.fromJson(row); | ||||
|         final value = SharedLinkEditDto.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
| @@ -146,12 +146,12 @@ class EditSharedLinkDto { | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| 
 | ||||
|   static Map<String, EditSharedLinkDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, EditSharedLinkDto>{}; | ||||
|   static Map<String, SharedLinkEditDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, SharedLinkEditDto>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = EditSharedLinkDto.fromJson(entry.value); | ||||
|         final value = SharedLinkEditDto.fromJson(entry.value); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
| @@ -160,14 +160,14 @@ class EditSharedLinkDto { | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of EditSharedLinkDto-objects as value to a dart map | ||||
|   static Map<String, List<EditSharedLinkDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<EditSharedLinkDto>>{}; | ||||
|   // maps a json object with a list of SharedLinkEditDto-objects as value to a dart map | ||||
|   static Map<String, List<SharedLinkEditDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<SharedLinkEditDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       // ignore: parameter_assignments | ||||
|       json = json.cast<String, dynamic>(); | ||||
|       for (final entry in json.entries) { | ||||
|         map[entry.key] = EditSharedLinkDto.listFromJson(entry.value, growable: growable,); | ||||
|         map[entry.key] = SharedLinkEditDto.listFromJson(entry.value, growable: growable,); | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
| @@ -15,7 +15,7 @@ class SharedLinkResponseDto { | ||||
|   SharedLinkResponseDto({ | ||||
|     required this.type, | ||||
|     required this.id, | ||||
|     this.description, | ||||
|     required this.description, | ||||
|     required this.userId, | ||||
|     required this.key, | ||||
|     required this.createdAt, | ||||
| @@ -31,12 +31,6 @@ class SharedLinkResponseDto { | ||||
| 
 | ||||
|   String id; | ||||
| 
 | ||||
|   /// | ||||
|   /// 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? description; | ||||
| 
 | ||||
|   String userId; | ||||
| @@ -206,6 +200,7 @@ class SharedLinkResponseDto { | ||||
|   static const requiredKeys = <String>{ | ||||
|     'type', | ||||
|     'id', | ||||
|     'description', | ||||
|     'userId', | ||||
|     'key', | ||||
|     'createdAt', | ||||
|   | ||||
							
								
								
									
										5
									
								
								mobile/openapi/test/album_api_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								mobile/openapi/test/album_api_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -32,11 +32,6 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<SharedLinkResponseDto> createAlbumSharedLink(CreateAlbumShareLinkDto createAlbumShareLinkDto) async | ||||
|     test('test createAlbumSharedLink', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future deleteAlbum(String id) async | ||||
|     test('test deleteAlbum', () async { | ||||
|       // TODO | ||||
|   | ||||
							
								
								
									
										15
									
								
								mobile/openapi/test/asset_api_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										15
									
								
								mobile/openapi/test/asset_api_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -17,11 +17,6 @@ void main() { | ||||
|   // final instance = AssetApi(); | ||||
| 
 | ||||
|   group('tests for AssetApi', () { | ||||
|     //Future<SharedLinkResponseDto> addAssetsToSharedLink(AddAssetsDto addAssetsDto, { String key }) async | ||||
|     test('test addAssetsToSharedLink', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // Checks if assets exist by checksums | ||||
|     // | ||||
|     //Future<AssetBulkUploadCheckResponseDto> bulkUploadCheck(AssetBulkUploadCheckDto assetBulkUploadCheckDto) async | ||||
| @@ -43,11 +38,6 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<SharedLinkResponseDto> createAssetsSharedLink(CreateAssetsShareLinkDto createAssetsShareLinkDto) async | ||||
|     test('test createAssetsSharedLink', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<List<DeleteAssetResponseDto>> deleteAsset(DeleteAssetDto deleteAssetDto) async | ||||
|     test('test deleteAsset', () async { | ||||
|       // TODO | ||||
| @@ -141,11 +131,6 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<SharedLinkResponseDto> removeAssetsFromSharedLink(RemoveAssetsDto removeAssetsDto, { String key }) async | ||||
|     test('test removeAssetsFromSharedLink', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<List<AssetResponseDto>> searchAsset(SearchAssetDto searchAssetDto) async | ||||
|     test('test searchAsset', () async { | ||||
|       // TODO | ||||
|   | ||||
| @@ -1,52 +0,0 @@ | ||||
| // | ||||
| // 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 CreateAlbumShareLinkDto | ||||
| void main() { | ||||
|   // final instance = CreateAlbumShareLinkDto(); | ||||
| 
 | ||||
|   group('test CreateAlbumShareLinkDto', () { | ||||
|     // String albumId | ||||
|     test('to test the property `albumId`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // DateTime expiresAt | ||||
|     test('to test the property `expiresAt`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool allowUpload | ||||
|     test('to test the property `allowUpload`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool allowDownload | ||||
|     test('to test the property `allowDownload`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool showExif | ||||
|     test('to test the property `showExif`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // String description | ||||
|     test('to test the property `description`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
| 
 | ||||
|   }); | ||||
| 
 | ||||
| } | ||||
| @@ -12,11 +12,21 @@ import 'package:openapi/api.dart'; | ||||
| import 'package:test/test.dart'; | ||||
| 
 | ||||
| 
 | ||||
| /// tests for ShareApi | ||||
| /// tests for SharedLinkApi | ||||
| void main() { | ||||
|   // final instance = ShareApi(); | ||||
|   // final instance = SharedLinkApi(); | ||||
| 
 | ||||
|   group('tests for SharedLinkApi', () { | ||||
|     //Future<List<AssetIdsResponseDto>> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String key }) async | ||||
|     test('test addSharedLinkAssets', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<SharedLinkResponseDto> createSharedLink(SharedLinkCreateDto sharedLinkCreateDto) async | ||||
|     test('test createSharedLink', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|   group('tests for ShareApi', () { | ||||
|     //Future<List<SharedLinkResponseDto>> getAllSharedLinks() async | ||||
|     test('test getAllSharedLinks', () async { | ||||
|       // TODO | ||||
| @@ -37,7 +47,12 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<SharedLinkResponseDto> updateSharedLink(String id, EditSharedLinkDto editSharedLinkDto) async | ||||
|     //Future<List<AssetIdsResponseDto>> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String key }) async | ||||
|     test('test removeSharedLinkAssets', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<SharedLinkResponseDto> updateSharedLink(String id, SharedLinkEditDto sharedLinkEditDto) async | ||||
|     test('test updateSharedLink', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| @@ -11,33 +11,23 @@ | ||||
| import 'package:openapi/api.dart'; | ||||
| import 'package:test/test.dart'; | ||||
| 
 | ||||
| // tests for CreateAssetsShareLinkDto | ||||
| // tests for SharedLinkCreateDto | ||||
| void main() { | ||||
|   // final instance = CreateAssetsShareLinkDto(); | ||||
|   // final instance = SharedLinkCreateDto(); | ||||
| 
 | ||||
|   group('test SharedLinkCreateDto', () { | ||||
|     // SharedLinkType type | ||||
|     test('to test the property `type`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|   group('test CreateAssetsShareLinkDto', () { | ||||
|     // List<String> assetIds (default value: const []) | ||||
|     test('to test the property `assetIds`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // DateTime expiresAt | ||||
|     test('to test the property `expiresAt`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool allowUpload | ||||
|     test('to test the property `allowUpload`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool allowDownload | ||||
|     test('to test the property `allowDownload`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool showExif | ||||
|     test('to test the property `showExif`', () async { | ||||
|     // String albumId | ||||
|     test('to test the property `albumId`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
| @@ -46,6 +36,26 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // DateTime expiresAt | ||||
|     test('to test the property `expiresAt`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool allowUpload (default value: false) | ||||
|     test('to test the property `allowUpload`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool allowDownload (default value: true) | ||||
|     test('to test the property `allowDownload`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool showExif (default value: true) | ||||
|     test('to test the property `showExif`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
| 
 | ||||
|   }); | ||||
| 
 | ||||
| @@ -11,11 +11,11 @@ | ||||
| import 'package:openapi/api.dart'; | ||||
| import 'package:test/test.dart'; | ||||
| 
 | ||||
| // tests for EditSharedLinkDto | ||||
| // tests for SharedLinkEditDto | ||||
| void main() { | ||||
|   // final instance = EditSharedLinkDto(); | ||||
|   // final instance = SharedLinkEditDto(); | ||||
| 
 | ||||
|   group('test EditSharedLinkDto', () { | ||||
|   group('test SharedLinkEditDto', () { | ||||
|     // String description | ||||
|     test('to test the property `description`', () async { | ||||
|       // TODO | ||||
| @@ -1,7 +1,14 @@ | ||||
| import { AlbumResponseDto, AuthService, CreateAlbumDto, SharedLinkResponseDto, UserService } from '@app/domain'; | ||||
| import { CreateAlbumShareLinkDto } from '@app/immich/api-v1/album/dto/create-album-shared-link.dto'; | ||||
| import { | ||||
|   AlbumResponseDto, | ||||
|   AuthService, | ||||
|   CreateAlbumDto, | ||||
|   SharedLinkCreateDto, | ||||
|   SharedLinkResponseDto, | ||||
|   UserService, | ||||
| } from '@app/domain'; | ||||
| import { AppModule } from '@app/immich/app.module'; | ||||
| import { AuthUserDto } from '@app/immich/decorators/auth-user.decorator'; | ||||
| import { SharedLinkType } from '@app/infra/entities'; | ||||
| import { INestApplication } from '@nestjs/common'; | ||||
| import { Test, TestingModule } from '@nestjs/testing'; | ||||
| import request from 'supertest'; | ||||
| @@ -14,8 +21,10 @@ async function _createAlbum(app: INestApplication, data: CreateAlbumDto) { | ||||
|   return res.body as AlbumResponseDto; | ||||
| } | ||||
|  | ||||
| async function _createAlbumSharedLink(app: INestApplication, data: CreateAlbumShareLinkDto) { | ||||
|   const res = await request(app.getHttpServer()).post('/album/create-shared-link').send(data); | ||||
| async function _createAlbumSharedLink(app: INestApplication, data: Omit<SharedLinkCreateDto, 'type'>) { | ||||
|   const res = await request(app.getHttpServer()) | ||||
|     .post('/shared-link') | ||||
|     .send({ ...data, type: SharedLinkType.ALBUM }); | ||||
|   expect(res.status).toEqual(201); | ||||
|   return res.body as SharedLinkResponseDto; | ||||
| } | ||||
|   | ||||
| @@ -127,48 +127,6 @@ | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/album/create-shared-link": { | ||||
|       "post": { | ||||
|         "operationId": "createAlbumSharedLink", | ||||
|         "parameters": [], | ||||
|         "requestBody": { | ||||
|           "required": true, | ||||
|           "content": { | ||||
|             "application/json": { | ||||
|               "schema": { | ||||
|                 "$ref": "#/components/schemas/CreateAlbumShareLinkDto" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "responses": { | ||||
|           "201": { | ||||
|             "description": "", | ||||
|             "content": { | ||||
|               "application/json": { | ||||
|                 "schema": { | ||||
|                   "$ref": "#/components/schemas/SharedLinkResponseDto" | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "Album" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
|             "bearer": [] | ||||
|           }, | ||||
|           { | ||||
|             "cookie": [] | ||||
|           }, | ||||
|           { | ||||
|             "api_key": [] | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/album/{id}": { | ||||
|       "patch": { | ||||
|         "operationId": "updateAlbumInfo", | ||||
| @@ -1660,150 +1618,6 @@ | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/asset/shared-link": { | ||||
|       "post": { | ||||
|         "operationId": "createAssetsSharedLink", | ||||
|         "parameters": [], | ||||
|         "requestBody": { | ||||
|           "required": true, | ||||
|           "content": { | ||||
|             "application/json": { | ||||
|               "schema": { | ||||
|                 "$ref": "#/components/schemas/CreateAssetsShareLinkDto" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "responses": { | ||||
|           "201": { | ||||
|             "description": "", | ||||
|             "content": { | ||||
|               "application/json": { | ||||
|                 "schema": { | ||||
|                   "$ref": "#/components/schemas/SharedLinkResponseDto" | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "Asset" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
|             "bearer": [] | ||||
|           }, | ||||
|           { | ||||
|             "cookie": [] | ||||
|           }, | ||||
|           { | ||||
|             "api_key": [] | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/asset/shared-link/add": { | ||||
|       "patch": { | ||||
|         "operationId": "addAssetsToSharedLink", | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "name": "key", | ||||
|             "required": false, | ||||
|             "in": "query", | ||||
|             "schema": { | ||||
|               "type": "string" | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         "requestBody": { | ||||
|           "required": true, | ||||
|           "content": { | ||||
|             "application/json": { | ||||
|               "schema": { | ||||
|                 "$ref": "#/components/schemas/AddAssetsDto" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "responses": { | ||||
|           "200": { | ||||
|             "description": "", | ||||
|             "content": { | ||||
|               "application/json": { | ||||
|                 "schema": { | ||||
|                   "$ref": "#/components/schemas/SharedLinkResponseDto" | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "Asset" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
|             "bearer": [] | ||||
|           }, | ||||
|           { | ||||
|             "cookie": [] | ||||
|           }, | ||||
|           { | ||||
|             "api_key": [] | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/asset/shared-link/remove": { | ||||
|       "patch": { | ||||
|         "operationId": "removeAssetsFromSharedLink", | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "name": "key", | ||||
|             "required": false, | ||||
|             "in": "query", | ||||
|             "schema": { | ||||
|               "type": "string" | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         "requestBody": { | ||||
|           "required": true, | ||||
|           "content": { | ||||
|             "application/json": { | ||||
|               "schema": { | ||||
|                 "$ref": "#/components/schemas/RemoveAssetsDto" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "responses": { | ||||
|           "200": { | ||||
|             "description": "", | ||||
|             "content": { | ||||
|               "application/json": { | ||||
|                 "schema": { | ||||
|                   "$ref": "#/components/schemas/SharedLinkResponseDto" | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "Asset" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
|             "bearer": [] | ||||
|           }, | ||||
|           { | ||||
|             "cookie": [] | ||||
|           }, | ||||
|           { | ||||
|             "api_key": [] | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/asset/stat/archive": { | ||||
|       "get": { | ||||
|         "operationId": "getArchivedAssetCountByUserId", | ||||
| @@ -3264,7 +3078,7 @@ | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/share": { | ||||
|     "/shared-link": { | ||||
|       "get": { | ||||
|         "operationId": "getAllSharedLinks", | ||||
|         "parameters": [], | ||||
| @@ -3284,7 +3098,47 @@ | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "share" | ||||
|           "Shared Link" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
|             "bearer": [] | ||||
|           }, | ||||
|           { | ||||
|             "cookie": [] | ||||
|           }, | ||||
|           { | ||||
|             "api_key": [] | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       "post": { | ||||
|         "operationId": "createSharedLink", | ||||
|         "parameters": [], | ||||
|         "requestBody": { | ||||
|           "required": true, | ||||
|           "content": { | ||||
|             "application/json": { | ||||
|               "schema": { | ||||
|                 "$ref": "#/components/schemas/SharedLinkCreateDto" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "responses": { | ||||
|           "201": { | ||||
|             "description": "", | ||||
|             "content": { | ||||
|               "application/json": { | ||||
|                 "schema": { | ||||
|                   "$ref": "#/components/schemas/SharedLinkResponseDto" | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "Shared Link" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
| @@ -3299,7 +3153,7 @@ | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/share/me": { | ||||
|     "/shared-link/me": { | ||||
|       "get": { | ||||
|         "operationId": "getMySharedLink", | ||||
|         "parameters": [ | ||||
| @@ -3325,7 +3179,7 @@ | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "share" | ||||
|           "Shared Link" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
| @@ -3340,7 +3194,7 @@ | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/share/{id}": { | ||||
|     "/shared-link/{id}": { | ||||
|       "get": { | ||||
|         "operationId": "getSharedLinkById", | ||||
|         "parameters": [ | ||||
| @@ -3367,7 +3221,7 @@ | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "share" | ||||
|           "Shared Link" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
| @@ -3399,7 +3253,7 @@ | ||||
|           "content": { | ||||
|             "application/json": { | ||||
|               "schema": { | ||||
|                 "$ref": "#/components/schemas/EditSharedLinkDto" | ||||
|                 "$ref": "#/components/schemas/SharedLinkEditDto" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
| @@ -3417,7 +3271,7 @@ | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "share" | ||||
|           "Shared Link" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
| @@ -3450,7 +3304,131 @@ | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "share" | ||||
|           "Shared Link" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
|             "bearer": [] | ||||
|           }, | ||||
|           { | ||||
|             "cookie": [] | ||||
|           }, | ||||
|           { | ||||
|             "api_key": [] | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/shared-link/{id}/assets": { | ||||
|       "put": { | ||||
|         "operationId": "addSharedLinkAssets", | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "name": "id", | ||||
|             "required": true, | ||||
|             "in": "path", | ||||
|             "schema": { | ||||
|               "format": "uuid", | ||||
|               "type": "string" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "name": "key", | ||||
|             "required": false, | ||||
|             "in": "query", | ||||
|             "schema": { | ||||
|               "type": "string" | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         "requestBody": { | ||||
|           "required": true, | ||||
|           "content": { | ||||
|             "application/json": { | ||||
|               "schema": { | ||||
|                 "$ref": "#/components/schemas/AssetIdsDto" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "responses": { | ||||
|           "200": { | ||||
|             "description": "", | ||||
|             "content": { | ||||
|               "application/json": { | ||||
|                 "schema": { | ||||
|                   "type": "array", | ||||
|                   "items": { | ||||
|                     "$ref": "#/components/schemas/AssetIdsResponseDto" | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "Shared Link" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
|             "bearer": [] | ||||
|           }, | ||||
|           { | ||||
|             "cookie": [] | ||||
|           }, | ||||
|           { | ||||
|             "api_key": [] | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       "delete": { | ||||
|         "operationId": "removeSharedLinkAssets", | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "name": "id", | ||||
|             "required": true, | ||||
|             "in": "path", | ||||
|             "schema": { | ||||
|               "format": "uuid", | ||||
|               "type": "string" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "name": "key", | ||||
|             "required": false, | ||||
|             "in": "query", | ||||
|             "schema": { | ||||
|               "type": "string" | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         "requestBody": { | ||||
|           "required": true, | ||||
|           "content": { | ||||
|             "application/json": { | ||||
|               "schema": { | ||||
|                 "$ref": "#/components/schemas/AssetIdsDto" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "responses": { | ||||
|           "200": { | ||||
|             "description": "", | ||||
|             "content": { | ||||
|               "application/json": { | ||||
|                 "schema": { | ||||
|                   "type": "array", | ||||
|                   "items": { | ||||
|                     "$ref": "#/components/schemas/AssetIdsResponseDto" | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "Shared Link" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
| @@ -5089,34 +5067,6 @@ | ||||
|           "albumName" | ||||
|         ] | ||||
|       }, | ||||
|       "CreateAlbumShareLinkDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "albumId": { | ||||
|             "type": "string", | ||||
|             "format": "uuid" | ||||
|           }, | ||||
|           "expiresAt": { | ||||
|             "format": "date-time", | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "allowUpload": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "allowDownload": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "showExif": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "description": { | ||||
|             "type": "string" | ||||
|           } | ||||
|         }, | ||||
|         "required": [ | ||||
|           "albumId" | ||||
|         ] | ||||
|       }, | ||||
|       "CreateAssetDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
| @@ -5176,42 +5126,6 @@ | ||||
|           "fileExtension" | ||||
|         ] | ||||
|       }, | ||||
|       "CreateAssetsShareLinkDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "assetIds": { | ||||
|             "title": "Array asset IDs to be shared", | ||||
|             "example": [ | ||||
|               "bf973405-3f2a-48d2-a687-2ed4167164be", | ||||
|               "dd41870b-5d00-46d2-924e-1d8489a0aa0f", | ||||
|               "fad77c3f-deef-4e7e-9608-14c1aa4e559a" | ||||
|             ], | ||||
|             "type": "array", | ||||
|             "items": { | ||||
|               "type": "string" | ||||
|             } | ||||
|           }, | ||||
|           "expiresAt": { | ||||
|             "format": "date-time", | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "allowUpload": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "allowDownload": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "showExif": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "description": { | ||||
|             "type": "string" | ||||
|           } | ||||
|         }, | ||||
|         "required": [ | ||||
|           "assetIds" | ||||
|         ] | ||||
|       }, | ||||
|       "CreateProfileImageDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
| @@ -5392,28 +5306,6 @@ | ||||
|           "assetIds" | ||||
|         ] | ||||
|       }, | ||||
|       "EditSharedLinkDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "description": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "expiresAt": { | ||||
|             "format": "date-time", | ||||
|             "type": "string", | ||||
|             "nullable": true | ||||
|           }, | ||||
|           "allowUpload": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "allowDownload": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "showExif": { | ||||
|             "type": "boolean" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "ExifResponseDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
| @@ -6160,6 +6052,71 @@ | ||||
|           "patch" | ||||
|         ] | ||||
|       }, | ||||
|       "SharedLinkCreateDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "type": { | ||||
|             "$ref": "#/components/schemas/SharedLinkType" | ||||
|           }, | ||||
|           "assetIds": { | ||||
|             "type": "array", | ||||
|             "items": { | ||||
|               "type": "string", | ||||
|               "format": "uuid" | ||||
|             } | ||||
|           }, | ||||
|           "albumId": { | ||||
|             "type": "string", | ||||
|             "format": "uuid" | ||||
|           }, | ||||
|           "description": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "expiresAt": { | ||||
|             "format": "date-time", | ||||
|             "type": "string", | ||||
|             "nullable": true, | ||||
|             "default": null | ||||
|           }, | ||||
|           "allowUpload": { | ||||
|             "type": "boolean", | ||||
|             "default": false | ||||
|           }, | ||||
|           "allowDownload": { | ||||
|             "type": "boolean", | ||||
|             "default": true | ||||
|           }, | ||||
|           "showExif": { | ||||
|             "type": "boolean", | ||||
|             "default": true | ||||
|           } | ||||
|         }, | ||||
|         "required": [ | ||||
|           "type" | ||||
|         ] | ||||
|       }, | ||||
|       "SharedLinkEditDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "description": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "expiresAt": { | ||||
|             "format": "date-time", | ||||
|             "type": "string", | ||||
|             "nullable": true | ||||
|           }, | ||||
|           "allowUpload": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "allowDownload": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "showExif": { | ||||
|             "type": "boolean" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "SharedLinkResponseDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
| @@ -6170,7 +6127,8 @@ | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "description": { | ||||
|             "type": "string" | ||||
|             "type": "string", | ||||
|             "nullable": true | ||||
|           }, | ||||
|           "userId": { | ||||
|             "type": "string" | ||||
| @@ -6209,6 +6167,7 @@ | ||||
|         "required": [ | ||||
|           "type", | ||||
|           "id", | ||||
|           "description", | ||||
|           "userId", | ||||
|           "key", | ||||
|           "createdAt", | ||||
|   | ||||
| @@ -2,8 +2,11 @@ export const IAccessRepository = 'IAccessRepository'; | ||||
|  | ||||
| export interface IAccessRepository { | ||||
|   hasPartnerAccess(userId: string, partnerId: string): Promise<boolean>; | ||||
|  | ||||
|   hasAlbumAssetAccess(userId: string, assetId: string): Promise<boolean>; | ||||
|   hasOwnerAssetAccess(userId: string, assetId: string): Promise<boolean>; | ||||
|   hasPartnerAssetAccess(userId: string, assetId: string): Promise<boolean>; | ||||
|   hasSharedLinkAssetAccess(userId: string, assetId: string): Promise<boolean>; | ||||
|  | ||||
|   hasAlbumOwnerAccess(userId: string, albumId: string): Promise<boolean>; | ||||
| } | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import { IKeyRepository } from '../api-key'; | ||||
| import { APIKeyCore } from '../api-key/api-key.core'; | ||||
| import { ICryptoRepository } from '../crypto/crypto.repository'; | ||||
| import { OAuthCore } from '../oauth/oauth.core'; | ||||
| import { ISharedLinkRepository, SharedLinkCore } from '../shared-link'; | ||||
| import { ISharedLinkRepository } from '../shared-link'; | ||||
| import { INITIAL_SYSTEM_CONFIG, ISystemConfigRepository } from '../system-config'; | ||||
| import { IUserRepository, UserCore } from '../user'; | ||||
| import { IUserTokenRepository, UserTokenCore } from '../user-token'; | ||||
| @@ -35,7 +35,6 @@ export class AuthService { | ||||
|   private authCore: AuthCore; | ||||
|   private oauthCore: OAuthCore; | ||||
|   private userCore: UserCore; | ||||
|   private shareCore: SharedLinkCore; | ||||
|   private keyCore: APIKeyCore; | ||||
|  | ||||
|   private logger = new Logger(AuthService.name); | ||||
| @@ -45,7 +44,7 @@ export class AuthService { | ||||
|     @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, | ||||
|     @Inject(IUserRepository) userRepository: IUserRepository, | ||||
|     @Inject(IUserTokenRepository) userTokenRepository: IUserTokenRepository, | ||||
|     @Inject(ISharedLinkRepository) shareRepository: ISharedLinkRepository, | ||||
|     @Inject(ISharedLinkRepository) private sharedLinkRepository: ISharedLinkRepository, | ||||
|     @Inject(IKeyRepository) keyRepository: IKeyRepository, | ||||
|     @Inject(INITIAL_SYSTEM_CONFIG) | ||||
|     initialConfig: SystemConfig, | ||||
| @@ -54,7 +53,6 @@ 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 SharedLinkCore(shareRepository, cryptoRepository); | ||||
|     this.keyCore = new APIKeyCore(cryptoRepository, keyRepository); | ||||
|   } | ||||
|  | ||||
| @@ -147,7 +145,7 @@ export class AuthService { | ||||
|     const apiKey = (headers[IMMICH_API_KEY_HEADER] || params.apiKey) as string; | ||||
|  | ||||
|     if (shareKey) { | ||||
|       return this.shareCore.validate(shareKey); | ||||
|       return this.validateSharedLink(shareKey); | ||||
|     } | ||||
|  | ||||
|     if (userToken) { | ||||
| @@ -193,4 +191,29 @@ export class AuthService { | ||||
|     const cookies = cookieParser.parse(headers.cookie || ''); | ||||
|     return cookies[IMMICH_ACCESS_COOKIE] || null; | ||||
|   } | ||||
|  | ||||
|   async validateSharedLink(key: string | string[]): Promise<AuthUserDto | null> { | ||||
|     key = Array.isArray(key) ? key[0] : key; | ||||
|  | ||||
|     const bytes = Buffer.from(key, key.length === 100 ? 'hex' : 'base64url'); | ||||
|     const link = await this.sharedLinkRepository.getByKey(bytes); | ||||
|     if (link) { | ||||
|       if (!link.expiresAt || new Date(link.expiresAt) > new Date()) { | ||||
|         const user = link.user; | ||||
|         if (user) { | ||||
|           return { | ||||
|             id: user.id, | ||||
|             email: user.email, | ||||
|             isAdmin: user.isAdmin, | ||||
|             isPublicUser: true, | ||||
|             sharedLinkId: link.id, | ||||
|             isAllowUpload: link.allowUpload, | ||||
|             isAllowDownload: link.allowDownload, | ||||
|             isShowExif: link.showExif, | ||||
|           }; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     throw new UnauthorizedException('Invalid share key'); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,12 +0,0 @@ | ||||
| import { AlbumEntity, AssetEntity, SharedLinkType } from '@app/infra/entities'; | ||||
|  | ||||
| export class CreateSharedLinkDto { | ||||
|   description?: string; | ||||
|   expiresAt?: Date; | ||||
|   type!: SharedLinkType; | ||||
|   assets!: AssetEntity[]; | ||||
|   album?: AlbumEntity; | ||||
|   allowUpload?: boolean; | ||||
|   allowDownload?: boolean; | ||||
|   showExif?: boolean; | ||||
| } | ||||
| @@ -1,18 +0,0 @@ | ||||
| import { IsOptional } from 'class-validator'; | ||||
|  | ||||
| export class EditSharedLinkDto { | ||||
|   @IsOptional() | ||||
|   description?: string; | ||||
|  | ||||
|   @IsOptional() | ||||
|   expiresAt?: Date | null; | ||||
|  | ||||
|   @IsOptional() | ||||
|   allowUpload?: boolean; | ||||
|  | ||||
|   @IsOptional() | ||||
|   allowDownload?: boolean; | ||||
|  | ||||
|   @IsOptional() | ||||
|   showExif?: boolean; | ||||
| } | ||||
| @@ -1,2 +0,0 @@ | ||||
| export * from './create-shared-link.dto'; | ||||
| export * from './edit-shared-link.dto'; | ||||
| @@ -1,5 +1,4 @@ | ||||
| export * from './dto'; | ||||
| export * from './response-dto'; | ||||
| export * from './shared-link.core'; | ||||
| export * from './shared-link-response.dto'; | ||||
| export * from './shared-link.dto'; | ||||
| export * from './shared-link.repository'; | ||||
| export * from './shared-link.service'; | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| export * from './shared-link-response.dto'; | ||||
| @@ -1,12 +1,12 @@ | ||||
| import { SharedLinkEntity, SharedLinkType } from '@app/infra/entities'; | ||||
| import { ApiProperty } from '@nestjs/swagger'; | ||||
| import _ from 'lodash'; | ||||
| import { AlbumResponseDto, mapAlbumExcludeAssetInfo } from '../../album'; | ||||
| import { AssetResponseDto, mapAsset, mapAssetWithoutExif } from '../../asset'; | ||||
| import { AlbumResponseDto, mapAlbumExcludeAssetInfo } from '../album'; | ||||
| import { AssetResponseDto, mapAsset, mapAssetWithoutExif } from '../asset'; | ||||
| 
 | ||||
| export class SharedLinkResponseDto { | ||||
|   id!: string; | ||||
|   description?: string; | ||||
|   description!: string | null; | ||||
|   userId!: string; | ||||
|   key!: string; | ||||
| 
 | ||||
| @@ -1,80 +0,0 @@ | ||||
| import { AssetEntity, SharedLinkEntity } from '@app/infra/entities'; | ||||
| import { BadRequestException, ForbiddenException, Logger, UnauthorizedException } from '@nestjs/common'; | ||||
| import { AuthUserDto } from '../auth'; | ||||
| import { ICryptoRepository } from '../crypto'; | ||||
| import { CreateSharedLinkDto } from './dto'; | ||||
| import { ISharedLinkRepository } from './shared-link.repository'; | ||||
|  | ||||
| export class SharedLinkCore { | ||||
|   readonly logger = new Logger(SharedLinkCore.name); | ||||
|  | ||||
|   constructor(private repository: ISharedLinkRepository, private cryptoRepository: ICryptoRepository) {} | ||||
|  | ||||
|   // TODO: move to SharedLinkController/SharedLinkService | ||||
|   create(userId: string, dto: CreateSharedLinkDto): Promise<SharedLinkEntity> { | ||||
|     return this.repository.create({ | ||||
|       key: Buffer.from(this.cryptoRepository.randomBytes(50)), | ||||
|       description: dto.description, | ||||
|       userId, | ||||
|       createdAt: new Date(), | ||||
|       expiresAt: dto.expiresAt ?? null, | ||||
|       type: dto.type, | ||||
|       assets: dto.assets, | ||||
|       album: dto.album, | ||||
|       allowUpload: dto.allowUpload ?? false, | ||||
|       allowDownload: dto.allowDownload ?? true, | ||||
|       showExif: dto.showExif ?? true, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   async addAssets(userId: string, id: string, assets: AssetEntity[]) { | ||||
|     const link = await this.repository.get(userId, id); | ||||
|     if (!link) { | ||||
|       throw new BadRequestException('Shared link not found'); | ||||
|     } | ||||
|  | ||||
|     return this.repository.update({ ...link, assets: [...link.assets, ...assets] }); | ||||
|   } | ||||
|  | ||||
|   async removeAssets(userId: string, id: string, assets: AssetEntity[]) { | ||||
|     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.update({ ...link, assets: newAssets }); | ||||
|   } | ||||
|  | ||||
|   checkDownloadAccess(user: AuthUserDto) { | ||||
|     if (user.isPublicUser && !user.isAllowDownload) { | ||||
|       throw new ForbiddenException(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   async validate(key: string | string[]): Promise<AuthUserDto | null> { | ||||
|     key = Array.isArray(key) ? key[0] : key; | ||||
|  | ||||
|     const bytes = Buffer.from(key, key.length === 100 ? 'hex' : 'base64url'); | ||||
|     const link = await this.repository.getByKey(bytes); | ||||
|     if (link) { | ||||
|       if (!link.expiresAt || new Date(link.expiresAt) > new Date()) { | ||||
|         const user = link.user; | ||||
|         if (user) { | ||||
|           return { | ||||
|             id: user.id, | ||||
|             email: user.email, | ||||
|             isAdmin: user.isAdmin, | ||||
|             isPublicUser: true, | ||||
|             sharedLinkId: link.id, | ||||
|             isAllowUpload: link.allowUpload, | ||||
|             isAllowDownload: link.allowDownload, | ||||
|             isShowExif: link.showExif, | ||||
|           }; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     throw new UnauthorizedException('Invalid share key'); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										53
									
								
								server/src/domain/shared-link/shared-link.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								server/src/domain/shared-link/shared-link.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| import { SharedLinkType } from '@app/infra/entities'; | ||||
| import { ApiProperty } from '@nestjs/swagger'; | ||||
| import { IsBoolean, IsDate, IsEnum, IsOptional, IsString } from 'class-validator'; | ||||
| import { ValidateUUID } from '../../immich/decorators/validate-uuid.decorator'; | ||||
|  | ||||
| export class SharedLinkCreateDto { | ||||
|   @IsEnum(SharedLinkType) | ||||
|   @ApiProperty({ enum: SharedLinkType, enumName: 'SharedLinkType' }) | ||||
|   type!: SharedLinkType; | ||||
|  | ||||
|   @ValidateUUID({ each: true, optional: true }) | ||||
|   assetIds?: string[]; | ||||
|  | ||||
|   @ValidateUUID({ optional: true }) | ||||
|   albumId?: string; | ||||
|  | ||||
|   @IsString() | ||||
|   @IsOptional() | ||||
|   description?: string; | ||||
|  | ||||
|   @IsDate() | ||||
|   @IsOptional() | ||||
|   expiresAt?: Date | null = null; | ||||
|  | ||||
|   @IsOptional() | ||||
|   @IsBoolean() | ||||
|   allowUpload?: boolean = false; | ||||
|  | ||||
|   @IsOptional() | ||||
|   @IsBoolean() | ||||
|   allowDownload?: boolean = true; | ||||
|  | ||||
|   @IsOptional() | ||||
|   @IsBoolean() | ||||
|   showExif?: boolean = true; | ||||
| } | ||||
|  | ||||
| export class SharedLinkEditDto { | ||||
|   @IsOptional() | ||||
|   description?: string; | ||||
|  | ||||
|   @IsOptional() | ||||
|   expiresAt?: Date | null; | ||||
|  | ||||
|   @IsOptional() | ||||
|   allowUpload?: boolean; | ||||
|  | ||||
|   @IsOptional() | ||||
|   allowDownload?: boolean; | ||||
|  | ||||
|   @IsOptional() | ||||
|   showExif?: boolean; | ||||
| } | ||||
| @@ -6,7 +6,7 @@ export interface ISharedLinkRepository { | ||||
|   getAll(userId: string): Promise<SharedLinkEntity[]>; | ||||
|   get(userId: string, id: string): Promise<SharedLinkEntity | null>; | ||||
|   getByKey(key: Buffer): Promise<SharedLinkEntity | null>; | ||||
|   create(entity: Omit<SharedLinkEntity, 'id' | 'user'>): Promise<SharedLinkEntity>; | ||||
|   create(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity>; | ||||
|   update(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity>; | ||||
|   remove(entity: SharedLinkEntity): Promise<void>; | ||||
| } | ||||
|   | ||||
| @@ -1,16 +1,33 @@ | ||||
| import { BadRequestException, ForbiddenException } from '@nestjs/common'; | ||||
| import { authStub, newSharedLinkRepositoryMock, sharedLinkResponseStub, sharedLinkStub } from '@test'; | ||||
| import { | ||||
|   albumStub, | ||||
|   assetEntityStub, | ||||
|   authStub, | ||||
|   newAccessRepositoryMock, | ||||
|   newCryptoRepositoryMock, | ||||
|   newSharedLinkRepositoryMock, | ||||
|   sharedLinkResponseStub, | ||||
|   sharedLinkStub, | ||||
| } from '@test'; | ||||
| import { when } from 'jest-when'; | ||||
| import _ from 'lodash'; | ||||
| import { SharedLinkType } from '../../infra/entities/shared-link.entity'; | ||||
| import { AssetIdErrorReason, IAccessRepository, ICryptoRepository } from '../index'; | ||||
| import { ISharedLinkRepository } from './shared-link.repository'; | ||||
| import { SharedLinkService } from './shared-link.service'; | ||||
|  | ||||
| describe(SharedLinkService.name, () => { | ||||
|   let sut: SharedLinkService; | ||||
|   let accessMock: jest.Mocked<IAccessRepository>; | ||||
|   let cryptoMock: jest.Mocked<ICryptoRepository>; | ||||
|   let shareMock: jest.Mocked<ISharedLinkRepository>; | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     accessMock = newAccessRepositoryMock(); | ||||
|     cryptoMock = newCryptoRepositoryMock(); | ||||
|     shareMock = newSharedLinkRepositoryMock(); | ||||
|  | ||||
|     sut = new SharedLinkService(shareMock); | ||||
|     sut = new SharedLinkService(accessMock, cryptoMock, shareMock); | ||||
|   }); | ||||
|  | ||||
|   it('should work', () => { | ||||
| @@ -64,6 +81,82 @@ describe(SharedLinkService.name, () => { | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('create', () => { | ||||
|     it('should not allow an album shared link without an albumId', async () => { | ||||
|       await expect(sut.create(authStub.admin, { type: SharedLinkType.ALBUM, assetIds: [] })).rejects.toBeInstanceOf( | ||||
|         BadRequestException, | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('should not allow non-owners to create album shared links', async () => { | ||||
|       accessMock.hasAlbumOwnerAccess.mockResolvedValue(false); | ||||
|       await expect( | ||||
|         sut.create(authStub.admin, { type: SharedLinkType.ALBUM, assetIds: [], albumId: 'album-1' }), | ||||
|       ).rejects.toBeInstanceOf(BadRequestException); | ||||
|     }); | ||||
|  | ||||
|     it('should not allow individual shared links with no assets', async () => { | ||||
|       await expect( | ||||
|         sut.create(authStub.admin, { type: SharedLinkType.INDIVIDUAL, assetIds: [] }), | ||||
|       ).rejects.toBeInstanceOf(BadRequestException); | ||||
|     }); | ||||
|  | ||||
|     it('should require asset ownership to make an individual shared link', async () => { | ||||
|       accessMock.hasOwnerAssetAccess.mockResolvedValue(false); | ||||
|       await expect( | ||||
|         sut.create(authStub.admin, { type: SharedLinkType.INDIVIDUAL, assetIds: ['asset-1'] }), | ||||
|       ).rejects.toBeInstanceOf(BadRequestException); | ||||
|     }); | ||||
|  | ||||
|     it('should create an album shared link', async () => { | ||||
|       accessMock.hasAlbumOwnerAccess.mockResolvedValue(true); | ||||
|       shareMock.create.mockResolvedValue(sharedLinkStub.valid); | ||||
|  | ||||
|       await sut.create(authStub.admin, { type: SharedLinkType.ALBUM, albumId: albumStub.oneAsset.id }); | ||||
|  | ||||
|       expect(accessMock.hasAlbumOwnerAccess).toHaveBeenCalledWith(authStub.admin.id, albumStub.oneAsset.id); | ||||
|       expect(shareMock.create).toHaveBeenCalledWith({ | ||||
|         type: SharedLinkType.ALBUM, | ||||
|         userId: authStub.admin.id, | ||||
|         albumId: albumStub.oneAsset.id, | ||||
|         allowDownload: true, | ||||
|         allowUpload: true, | ||||
|         assets: [], | ||||
|         description: null, | ||||
|         expiresAt: null, | ||||
|         showExif: true, | ||||
|         key: Buffer.from('random-bytes', 'utf8'), | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     it('should create an individual shared link', async () => { | ||||
|       accessMock.hasOwnerAssetAccess.mockResolvedValue(true); | ||||
|       shareMock.create.mockResolvedValue(sharedLinkStub.individual); | ||||
|  | ||||
|       await sut.create(authStub.admin, { | ||||
|         type: SharedLinkType.INDIVIDUAL, | ||||
|         assetIds: [assetEntityStub.image.id], | ||||
|         showExif: true, | ||||
|         allowDownload: true, | ||||
|         allowUpload: true, | ||||
|       }); | ||||
|  | ||||
|       expect(accessMock.hasOwnerAssetAccess).toHaveBeenCalledWith(authStub.admin.id, assetEntityStub.image.id); | ||||
|       expect(shareMock.create).toHaveBeenCalledWith({ | ||||
|         type: SharedLinkType.INDIVIDUAL, | ||||
|         userId: authStub.admin.id, | ||||
|         albumId: null, | ||||
|         allowDownload: true, | ||||
|         allowUpload: true, | ||||
|         assets: [{ id: assetEntityStub.image.id }], | ||||
|         description: null, | ||||
|         expiresAt: null, | ||||
|         showExif: true, | ||||
|         key: Buffer.from('random-bytes', 'utf8'), | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('update', () => { | ||||
|     it('should throw an error for an invalid shared link', async () => { | ||||
|       shareMock.get.mockResolvedValue(null); | ||||
| @@ -100,4 +193,58 @@ describe(SharedLinkService.name, () => { | ||||
|       expect(shareMock.remove).toHaveBeenCalledWith(sharedLinkStub.valid); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('addAssets', () => { | ||||
|     it('should not work on album shared links', async () => { | ||||
|       shareMock.get.mockResolvedValue(sharedLinkStub.valid); | ||||
|       await expect(sut.addAssets(authStub.admin, 'link-1', { assetIds: ['asset-1'] })).rejects.toBeInstanceOf( | ||||
|         BadRequestException, | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('should add assets to a shared link', async () => { | ||||
|       shareMock.get.mockResolvedValue(_.cloneDeep(sharedLinkStub.individual)); | ||||
|       shareMock.create.mockResolvedValue(sharedLinkStub.individual); | ||||
|  | ||||
|       when(accessMock.hasOwnerAssetAccess).calledWith(authStub.admin.id, 'asset-2').mockResolvedValue(false); | ||||
|       when(accessMock.hasOwnerAssetAccess).calledWith(authStub.admin.id, 'asset-3').mockResolvedValue(true); | ||||
|  | ||||
|       await expect( | ||||
|         sut.addAssets(authStub.admin, 'link-1', { assetIds: [assetEntityStub.image.id, 'asset-2', 'asset-3'] }), | ||||
|       ).resolves.toEqual([ | ||||
|         { assetId: assetEntityStub.image.id, success: false, error: AssetIdErrorReason.DUPLICATE }, | ||||
|         { assetId: 'asset-2', success: false, error: AssetIdErrorReason.NO_PERMISSION }, | ||||
|         { assetId: 'asset-3', success: true }, | ||||
|       ]); | ||||
|  | ||||
|       expect(accessMock.hasOwnerAssetAccess).toHaveBeenCalledTimes(2); | ||||
|       expect(shareMock.update).toHaveBeenCalledWith({ | ||||
|         ...sharedLinkStub.individual, | ||||
|         assets: [assetEntityStub.image, { id: 'asset-3' }], | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('removeAssets', () => { | ||||
|     it('should not work on album shared links', async () => { | ||||
|       shareMock.get.mockResolvedValue(sharedLinkStub.valid); | ||||
|       await expect(sut.removeAssets(authStub.admin, 'link-1', { assetIds: ['asset-1'] })).rejects.toBeInstanceOf( | ||||
|         BadRequestException, | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('should remove assets from a shared link', async () => { | ||||
|       shareMock.get.mockResolvedValue(_.cloneDeep(sharedLinkStub.individual)); | ||||
|       shareMock.create.mockResolvedValue(sharedLinkStub.individual); | ||||
|  | ||||
|       await expect( | ||||
|         sut.removeAssets(authStub.admin, 'link-1', { assetIds: [assetEntityStub.image.id, 'asset-2'] }), | ||||
|       ).resolves.toEqual([ | ||||
|         { assetId: assetEntityStub.image.id, success: true }, | ||||
|         { assetId: 'asset-2', success: false, error: AssetIdErrorReason.NOT_FOUND }, | ||||
|       ]); | ||||
|  | ||||
|       expect(shareMock.update).toHaveBeenCalledWith({ ...sharedLinkStub.individual, assets: [] }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -1,15 +1,22 @@ | ||||
| import { SharedLinkEntity } from '@app/infra/entities'; | ||||
| import { AssetEntity, SharedLinkEntity, SharedLinkType } from '@app/infra/entities'; | ||||
| import { BadRequestException, ForbiddenException, Inject, Injectable } from '@nestjs/common'; | ||||
| import { IAccessRepository } from '../access'; | ||||
| import { AssetIdErrorReason, AssetIdsDto, AssetIdsResponseDto } from '../asset'; | ||||
| import { AuthUserDto } from '../auth'; | ||||
| import { EditSharedLinkDto } from './dto'; | ||||
| import { mapSharedLink, mapSharedLinkWithNoExif, SharedLinkResponseDto } from './response-dto'; | ||||
| import { ICryptoRepository } from '../crypto'; | ||||
| import { mapSharedLink, mapSharedLinkWithNoExif, SharedLinkResponseDto } from './shared-link-response.dto'; | ||||
| import { SharedLinkCreateDto, SharedLinkEditDto } from './shared-link.dto'; | ||||
| import { ISharedLinkRepository } from './shared-link.repository'; | ||||
|  | ||||
| @Injectable() | ||||
| export class SharedLinkService { | ||||
|   constructor(@Inject(ISharedLinkRepository) private repository: ISharedLinkRepository) {} | ||||
|   constructor( | ||||
|     @Inject(IAccessRepository) private accessRepository: IAccessRepository, | ||||
|     @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, | ||||
|     @Inject(ISharedLinkRepository) private repository: ISharedLinkRepository, | ||||
|   ) {} | ||||
|  | ||||
|   async getAll(authUser: AuthUserDto): Promise<SharedLinkResponseDto[]> { | ||||
|   getAll(authUser: AuthUserDto): Promise<SharedLinkResponseDto[]> { | ||||
|     return this.repository.getAll(authUser.id).then((links) => links.map(mapSharedLink)); | ||||
|   } | ||||
|  | ||||
| @@ -30,7 +37,52 @@ export class SharedLinkService { | ||||
|     return this.map(sharedLink, { withExif: true }); | ||||
|   } | ||||
|  | ||||
|   async update(authUser: AuthUserDto, id: string, dto: EditSharedLinkDto) { | ||||
|   async create(authUser: AuthUserDto, dto: SharedLinkCreateDto): Promise<SharedLinkResponseDto> { | ||||
|     switch (dto.type) { | ||||
|       case SharedLinkType.ALBUM: | ||||
|         if (!dto.albumId) { | ||||
|           throw new BadRequestException('Invalid albumId'); | ||||
|         } | ||||
|  | ||||
|         const isAlbumOwner = await this.accessRepository.hasAlbumOwnerAccess(authUser.id, dto.albumId); | ||||
|         if (!isAlbumOwner) { | ||||
|           throw new BadRequestException('Invalid albumId'); | ||||
|         } | ||||
|  | ||||
|         break; | ||||
|  | ||||
|       case SharedLinkType.INDIVIDUAL: | ||||
|         if (!dto.assetIds || dto.assetIds.length === 0) { | ||||
|           throw new BadRequestException('Invalid assetIds'); | ||||
|         } | ||||
|  | ||||
|         for (const assetId of dto.assetIds) { | ||||
|           const hasAccess = await this.accessRepository.hasOwnerAssetAccess(authUser.id, assetId); | ||||
|           if (!hasAccess) { | ||||
|             throw new BadRequestException(`No access to assetId: ${assetId}`); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     const sharedLink = await this.repository.create({ | ||||
|       key: this.cryptoRepository.randomBytes(50), | ||||
|       userId: authUser.id, | ||||
|       type: dto.type, | ||||
|       albumId: dto.albumId || null, | ||||
|       assets: (dto.assetIds || []).map((id) => ({ id } as AssetEntity)), | ||||
|       description: dto.description || null, | ||||
|       expiresAt: dto.expiresAt || null, | ||||
|       allowUpload: dto.allowUpload ?? true, | ||||
|       allowDownload: dto.allowDownload ?? true, | ||||
|       showExif: dto.showExif ?? true, | ||||
|     }); | ||||
|  | ||||
|     return this.map(sharedLink, { withExif: true }); | ||||
|   } | ||||
|  | ||||
|   async update(authUser: AuthUserDto, id: string, dto: SharedLinkEditDto) { | ||||
|     await this.findOrFail(authUser, id); | ||||
|     const sharedLink = await this.repository.update({ | ||||
|       id, | ||||
| @@ -57,6 +109,60 @@ export class SharedLinkService { | ||||
|     return sharedLink; | ||||
|   } | ||||
|  | ||||
|   async addAssets(authUser: AuthUserDto, id: string, dto: AssetIdsDto): Promise<AssetIdsResponseDto[]> { | ||||
|     const sharedLink = await this.findOrFail(authUser, id); | ||||
|  | ||||
|     if (sharedLink.type !== SharedLinkType.INDIVIDUAL) { | ||||
|       throw new BadRequestException('Invalid shared link type'); | ||||
|     } | ||||
|  | ||||
|     const results: AssetIdsResponseDto[] = []; | ||||
|     for (const assetId of dto.assetIds) { | ||||
|       const hasAsset = sharedLink.assets.find((asset) => asset.id === assetId); | ||||
|       if (hasAsset) { | ||||
|         results.push({ assetId, success: false, error: AssetIdErrorReason.DUPLICATE }); | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       const hasAccess = await this.accessRepository.hasOwnerAssetAccess(authUser.id, assetId); | ||||
|       if (!hasAccess) { | ||||
|         results.push({ assetId, success: false, error: AssetIdErrorReason.NO_PERMISSION }); | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       results.push({ assetId, success: true }); | ||||
|       sharedLink.assets.push({ id: assetId } as AssetEntity); | ||||
|     } | ||||
|  | ||||
|     await this.repository.update(sharedLink); | ||||
|  | ||||
|     return results; | ||||
|   } | ||||
|  | ||||
|   async removeAssets(authUser: AuthUserDto, id: string, dto: AssetIdsDto): Promise<AssetIdsResponseDto[]> { | ||||
|     const sharedLink = await this.findOrFail(authUser, id); | ||||
|  | ||||
|     if (sharedLink.type !== SharedLinkType.INDIVIDUAL) { | ||||
|       throw new BadRequestException('Invalid shared link type'); | ||||
|     } | ||||
|  | ||||
|     const results: AssetIdsResponseDto[] = []; | ||||
|     for (const assetId of dto.assetIds) { | ||||
|       const hasAsset = sharedLink.assets.find((asset) => asset.id === assetId); | ||||
|       if (!hasAsset) { | ||||
|         results.push({ assetId, success: false, error: AssetIdErrorReason.NOT_FOUND }); | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       results.push({ assetId, success: true }); | ||||
|       sharedLink.assets = sharedLink.assets.filter((asset) => asset.id !== assetId); | ||||
|     } | ||||
|  | ||||
|     await this.repository.update(sharedLink); | ||||
|  | ||||
|     return results; | ||||
|   } | ||||
|  | ||||
|   private map(sharedLink: SharedLinkEntity, { withExif }: { withExif: boolean }) { | ||||
|     return withExif ? mapSharedLink(sharedLink) : mapSharedLinkWithNoExif(sharedLink); | ||||
|   } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { AlbumResponseDto } from '@app/domain'; | ||||
| import { Body, Controller, Delete, Get, Param, Post, Put, Query, Response } from '@nestjs/common'; | ||||
| import { Body, Controller, Delete, Get, Param, Put, Query, Response } from '@nestjs/common'; | ||||
| import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; | ||||
| import { Response as Res } from 'express'; | ||||
| import { handleDownload } from '../../app.utils'; | ||||
| @@ -10,7 +10,6 @@ import { UseValidation } from '../../decorators/use-validation.decorator'; | ||||
| import { DownloadDto } from '../asset/dto/download-library.dto'; | ||||
| import { AlbumService } from './album.service'; | ||||
| import { AddAssetsDto } from './dto/add-assets.dto'; | ||||
| import { CreateAlbumShareLinkDto as CreateAlbumSharedLinkDto } from './dto/create-album-shared-link.dto'; | ||||
| import { RemoveAssetsDto } from './dto/remove-assets.dto'; | ||||
| import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto'; | ||||
|  | ||||
| @@ -59,9 +58,4 @@ export class AlbumController { | ||||
|   ) { | ||||
|     return this.service.downloadArchive(authUser, id, dto).then((download) => handleDownload(download, res)); | ||||
|   } | ||||
|  | ||||
|   @Post('create-shared-link') | ||||
|   createAlbumSharedLink(@AuthUser() authUser: AuthUserDto, @Body() dto: CreateAlbumSharedLinkDto) { | ||||
|     return this.service.createSharedLink(authUser, dto); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { AlbumResponseDto, ICryptoRepository, ISharedLinkRepository, mapUser } from '@app/domain'; | ||||
| import { AlbumResponseDto, mapUser } from '@app/domain'; | ||||
| import { AlbumEntity, UserEntity } from '@app/infra/entities'; | ||||
| import { ForbiddenException, NotFoundException } from '@nestjs/common'; | ||||
| import { newCryptoRepositoryMock, newSharedLinkRepositoryMock, userEntityStub } from '@test'; | ||||
| import { userEntityStub } from '@test'; | ||||
| import { AuthUserDto } from '../../decorators/auth-user.decorator'; | ||||
| import { DownloadService } from '../../modules/download/download.service'; | ||||
| import { IAlbumRepository } from './album-repository'; | ||||
| @@ -11,9 +11,7 @@ import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto'; | ||||
| describe('Album service', () => { | ||||
|   let sut: AlbumService; | ||||
|   let albumRepositoryMock: jest.Mocked<IAlbumRepository>; | ||||
|   let sharedLinkRepositoryMock: jest.Mocked<ISharedLinkRepository>; | ||||
|   let downloadServiceMock: jest.Mocked<Partial<DownloadService>>; | ||||
|   let cryptoMock: jest.Mocked<ICryptoRepository>; | ||||
|  | ||||
|   const authUser: AuthUserDto = Object.freeze({ | ||||
|     id: '1111', | ||||
| @@ -99,20 +97,11 @@ describe('Album service', () => { | ||||
|       updateThumbnails: jest.fn(), | ||||
|     }; | ||||
|  | ||||
|     sharedLinkRepositoryMock = newSharedLinkRepositoryMock(); | ||||
|  | ||||
|     downloadServiceMock = { | ||||
|       downloadArchive: jest.fn(), | ||||
|     }; | ||||
|  | ||||
|     cryptoMock = newCryptoRepositoryMock(); | ||||
|  | ||||
|     sut = new AlbumService( | ||||
|       albumRepositoryMock, | ||||
|       sharedLinkRepositoryMock, | ||||
|       downloadServiceMock as DownloadService, | ||||
|       cryptoMock, | ||||
|     ); | ||||
|     sut = new AlbumService(albumRepositoryMock, downloadServiceMock as DownloadService); | ||||
|   }); | ||||
|  | ||||
|   it('gets an owned album', async () => { | ||||
|   | ||||
| @@ -1,36 +1,22 @@ | ||||
| import { | ||||
|   AlbumResponseDto, | ||||
|   ICryptoRepository, | ||||
|   ISharedLinkRepository, | ||||
|   mapAlbum, | ||||
|   mapSharedLink, | ||||
|   SharedLinkCore, | ||||
|   SharedLinkResponseDto, | ||||
| } from '@app/domain'; | ||||
| import { AlbumEntity, SharedLinkType } from '@app/infra/entities'; | ||||
| import { AlbumResponseDto, mapAlbum } from '@app/domain'; | ||||
| import { AlbumEntity } from '@app/infra/entities'; | ||||
| import { BadRequestException, ForbiddenException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common'; | ||||
| import { AuthUserDto } from '../../decorators/auth-user.decorator'; | ||||
| import { DownloadService } from '../../modules/download/download.service'; | ||||
| import { DownloadDto } from '../asset/dto/download-library.dto'; | ||||
| import { IAlbumRepository } from './album-repository'; | ||||
| import { AddAssetsDto } from './dto/add-assets.dto'; | ||||
| import { CreateAlbumShareLinkDto } from './dto/create-album-shared-link.dto'; | ||||
| import { RemoveAssetsDto } from './dto/remove-assets.dto'; | ||||
| import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto'; | ||||
|  | ||||
| @Injectable() | ||||
| export class AlbumService { | ||||
|   readonly logger = new Logger(AlbumService.name); | ||||
|   private shareCore: SharedLinkCore; | ||||
|   private logger = new Logger(AlbumService.name); | ||||
|  | ||||
|   constructor( | ||||
|     @Inject(IAlbumRepository) private albumRepository: IAlbumRepository, | ||||
|     @Inject(ISharedLinkRepository) sharedLinkRepository: ISharedLinkRepository, | ||||
|     private downloadService: DownloadService, | ||||
|     @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository, | ||||
|   ) { | ||||
|     this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository); | ||||
|   } | ||||
|   ) {} | ||||
|  | ||||
|   private async _getAlbum({ | ||||
|     authUser, | ||||
| @@ -91,7 +77,7 @@ export class AlbumService { | ||||
|   } | ||||
|  | ||||
|   async downloadArchive(authUser: AuthUserDto, albumId: string, dto: DownloadDto) { | ||||
|     this.shareCore.checkDownloadAccess(authUser); | ||||
|     this.checkDownloadAccess(authUser); | ||||
|  | ||||
|     const album = await this._getAlbum({ authUser, albumId, validateIsOwner: false }); | ||||
|     const assets = (album.assets || []).map((asset) => asset).slice(dto.skip || 0); | ||||
| @@ -99,20 +85,9 @@ export class AlbumService { | ||||
|     return this.downloadService.downloadArchive(album.albumName, assets); | ||||
|   } | ||||
|  | ||||
|   async createSharedLink(authUser: AuthUserDto, dto: CreateAlbumShareLinkDto): Promise<SharedLinkResponseDto> { | ||||
|     const album = await this._getAlbum({ authUser, albumId: dto.albumId }); | ||||
|  | ||||
|     const sharedLink = await this.shareCore.create(authUser.id, { | ||||
|       type: SharedLinkType.ALBUM, | ||||
|       expiresAt: dto.expiresAt, | ||||
|       allowUpload: dto.allowUpload, | ||||
|       album, | ||||
|       assets: [], | ||||
|       description: dto.description, | ||||
|       allowDownload: dto.allowDownload, | ||||
|       showExif: dto.showExif, | ||||
|     }); | ||||
|  | ||||
|     return mapSharedLink(sharedLink); | ||||
|   private checkDownloadAccess(authUser: AuthUserDto) { | ||||
|     if (authUser.isPublicUser && !authUser.isAllowDownload) { | ||||
|       throw new ForbiddenException(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,35 +0,0 @@ | ||||
| import { ValidateUUID } from '@app/immich/decorators/validate-uuid.decorator'; | ||||
| import { ApiProperty } from '@nestjs/swagger'; | ||||
| import { Type } from 'class-transformer'; | ||||
| import { IsBoolean, IsDate, IsOptional, IsString } from 'class-validator'; | ||||
|  | ||||
| export class CreateAlbumShareLinkDto { | ||||
|   @ValidateUUID() | ||||
|   albumId!: string; | ||||
|  | ||||
|   @IsOptional() | ||||
|   @IsDate() | ||||
|   @Type(() => Date) | ||||
|   @ApiProperty() | ||||
|   expiresAt?: Date; | ||||
|  | ||||
|   @IsBoolean() | ||||
|   @IsOptional() | ||||
|   @ApiProperty() | ||||
|   allowUpload?: boolean; | ||||
|  | ||||
|   @IsBoolean() | ||||
|   @IsOptional() | ||||
|   @ApiProperty() | ||||
|   allowDownload?: boolean; | ||||
|  | ||||
|   @IsBoolean() | ||||
|   @IsOptional() | ||||
|   @ApiProperty() | ||||
|   showExif?: boolean; | ||||
|  | ||||
|   @IsString() | ||||
|   @IsOptional() | ||||
|   @ApiProperty() | ||||
|   description?: string; | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { AssetResponseDto, ImmichReadStream, SharedLinkResponseDto } from '@app/domain'; | ||||
| import { AssetResponseDto, ImmichReadStream } from '@app/domain'; | ||||
| import { | ||||
|   Body, | ||||
|   Controller, | ||||
| @@ -10,7 +10,6 @@ import { | ||||
|   HttpStatus, | ||||
|   Param, | ||||
|   ParseFilePipe, | ||||
|   Patch, | ||||
|   Post, | ||||
|   Put, | ||||
|   Query, | ||||
| @@ -28,15 +27,12 @@ import { assetUploadOption, ImmichFile } from '../../config/asset-upload.config' | ||||
| import { UUIDParamDto } from '../../controllers/dto/uuid-param.dto'; | ||||
| import { AuthUser, AuthUserDto } from '../../decorators/auth-user.decorator'; | ||||
| import { Authenticated, SharedLinkRoute } from '../../decorators/authenticated.decorator'; | ||||
| import { AddAssetsDto } from '../album/dto/add-assets.dto'; | ||||
| import { RemoveAssetsDto } from '../album/dto/remove-assets.dto'; | ||||
| import FileNotEmptyValidator from '../validation/file-not-empty-validator'; | ||||
| import { AssetService } from './asset.service'; | ||||
| import { AssetBulkUploadCheckDto } from './dto/asset-check.dto'; | ||||
| import { AssetSearchDto } from './dto/asset-search.dto'; | ||||
| import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto'; | ||||
| import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; | ||||
| import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto'; | ||||
| import { CreateAssetDto, mapToUploadFile } from './dto/create-asset.dto'; | ||||
| import { DeleteAssetDto } from './dto/delete-asset.dto'; | ||||
| import { DeviceIdDto } from './dto/device-id.dto'; | ||||
| @@ -319,30 +315,4 @@ export class AssetController { | ||||
|   ): Promise<AssetBulkUploadCheckResponseDto> { | ||||
|     return this.assetService.bulkUploadCheck(authUser, dto); | ||||
|   } | ||||
|  | ||||
|   @Post('/shared-link') | ||||
|   createAssetsSharedLink( | ||||
|     @AuthUser() authUser: AuthUserDto, | ||||
|     @Body(ValidationPipe) dto: CreateAssetsShareLinkDto, | ||||
|   ): Promise<SharedLinkResponseDto> { | ||||
|     return this.assetService.createAssetsSharedLink(authUser, dto); | ||||
|   } | ||||
|  | ||||
|   @SharedLinkRoute() | ||||
|   @Patch('/shared-link/add') | ||||
|   addAssetsToSharedLink( | ||||
|     @AuthUser() authUser: AuthUserDto, | ||||
|     @Body(ValidationPipe) dto: AddAssetsDto, | ||||
|   ): Promise<SharedLinkResponseDto> { | ||||
|     return this.assetService.addAssetsToSharedLink(authUser, dto); | ||||
|   } | ||||
|  | ||||
|   @SharedLinkRoute() | ||||
|   @Patch('/shared-link/remove') | ||||
|   removeAssetsFromSharedLink( | ||||
|     @AuthUser() authUser: AuthUserDto, | ||||
|     @Body(ValidationPipe) dto: RemoveAssetsDto, | ||||
|   ): Promise<SharedLinkResponseDto> { | ||||
|     return this.assetService.removeAssetsFromSharedLink(authUser, dto); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,31 +1,19 @@ | ||||
| import { | ||||
|   IAccessRepository, | ||||
|   ICryptoRepository, | ||||
|   IJobRepository, | ||||
|   ISharedLinkRepository, | ||||
|   IStorageRepository, | ||||
|   JobName, | ||||
| } from '@app/domain'; | ||||
| import { IAccessRepository, IJobRepository, IStorageRepository, JobName } from '@app/domain'; | ||||
| import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities'; | ||||
| import { BadRequestException, ForbiddenException } from '@nestjs/common'; | ||||
| import { ForbiddenException } from '@nestjs/common'; | ||||
| import { | ||||
|   assetEntityStub, | ||||
|   authStub, | ||||
|   fileStub, | ||||
|   newAccessRepositoryMock, | ||||
|   newCryptoRepositoryMock, | ||||
|   newJobRepositoryMock, | ||||
|   newSharedLinkRepositoryMock, | ||||
|   newStorageRepositoryMock, | ||||
|   sharedLinkResponseStub, | ||||
|   sharedLinkStub, | ||||
| } from '@test'; | ||||
| import { when } from 'jest-when'; | ||||
| import { QueryFailedError, Repository } from 'typeorm'; | ||||
| import { DownloadService } from '../../modules/download/download.service'; | ||||
| import { IAssetRepository } from './asset-repository'; | ||||
| import { AssetService } from './asset.service'; | ||||
| import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto'; | ||||
| import { CreateAssetDto } from './dto/create-asset.dto'; | ||||
| import { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto'; | ||||
| import { AssetRejectReason, AssetUploadAction } from './response-dto/asset-check-response.dto'; | ||||
| @@ -134,8 +122,6 @@ describe('AssetService', () => { | ||||
|   let accessMock: jest.Mocked<IAccessRepository>; | ||||
|   let assetRepositoryMock: jest.Mocked<IAssetRepository>; | ||||
|   let downloadServiceMock: jest.Mocked<Partial<DownloadService>>; | ||||
|   let sharedLinkRepositoryMock: jest.Mocked<ISharedLinkRepository>; | ||||
|   let cryptoMock: jest.Mocked<ICryptoRepository>; | ||||
|   let jobMock: jest.Mocked<IJobRepository>; | ||||
|   let storageMock: jest.Mocked<IStorageRepository>; | ||||
|  | ||||
| @@ -165,9 +151,7 @@ describe('AssetService', () => { | ||||
|     }; | ||||
|  | ||||
|     accessMock = newAccessRepositoryMock(); | ||||
|     sharedLinkRepositoryMock = newSharedLinkRepositoryMock(); | ||||
|     jobMock = newJobRepositoryMock(); | ||||
|     cryptoMock = newCryptoRepositoryMock(); | ||||
|     storageMock = newStorageRepositoryMock(); | ||||
|  | ||||
|     sut = new AssetService( | ||||
| @@ -175,9 +159,7 @@ describe('AssetService', () => { | ||||
|       assetRepositoryMock, | ||||
|       a, | ||||
|       downloadServiceMock as DownloadService, | ||||
|       sharedLinkRepositoryMock, | ||||
|       jobMock, | ||||
|       cryptoMock, | ||||
|       storageMock, | ||||
|     ); | ||||
|  | ||||
| @@ -189,77 +171,6 @@ describe('AssetService', () => { | ||||
|       .mockResolvedValue(assetEntityStub.livePhotoMotionAsset); | ||||
|   }); | ||||
|  | ||||
|   describe('createAssetsSharedLink', () => { | ||||
|     it('should create an individual share link', async () => { | ||||
|       const asset1 = _getAsset_1(); | ||||
|       const dto: CreateAssetsShareLinkDto = { assetIds: [asset1.id] }; | ||||
|  | ||||
|       assetRepositoryMock.getById.mockResolvedValue(asset1); | ||||
|       accessMock.hasOwnerAssetAccess.mockResolvedValue(true); | ||||
|       sharedLinkRepositoryMock.create.mockResolvedValue(sharedLinkStub.valid); | ||||
|  | ||||
|       await expect(sut.createAssetsSharedLink(authStub.user1, dto)).resolves.toEqual(sharedLinkResponseStub.valid); | ||||
|  | ||||
|       expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id); | ||||
|       expect(accessMock.hasOwnerAssetAccess).toHaveBeenCalledWith(authStub.user1.id, asset1.id); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('updateAssetsInSharedLink', () => { | ||||
|     it('should require a valid shared link', async () => { | ||||
|       const asset1 = _getAsset_1(); | ||||
|  | ||||
|       const authDto = authStub.adminSharedLink; | ||||
|       const dto = { assetIds: [asset1.id] }; | ||||
|  | ||||
|       assetRepositoryMock.getById.mockResolvedValue(asset1); | ||||
|       sharedLinkRepositoryMock.get.mockResolvedValue(null); | ||||
|       accessMock.hasSharedLinkAssetAccess.mockResolvedValue(true); | ||||
|  | ||||
|       await expect(sut.addAssetsToSharedLink(authDto, dto)).rejects.toBeInstanceOf(BadRequestException); | ||||
|  | ||||
|       expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id); | ||||
|       expect(sharedLinkRepositoryMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId); | ||||
|       expect(sharedLinkRepositoryMock.update).not.toHaveBeenCalled(); | ||||
|     }); | ||||
|  | ||||
|     it('should add assets to a shared link', async () => { | ||||
|       const asset1 = _getAsset_1(); | ||||
|  | ||||
|       const authDto = authStub.adminSharedLink; | ||||
|       const dto = { assetIds: [asset1.id] }; | ||||
|  | ||||
|       assetRepositoryMock.getById.mockResolvedValue(asset1); | ||||
|       sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid); | ||||
|       accessMock.hasSharedLinkAssetAccess.mockResolvedValue(true); | ||||
|       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.update).toHaveBeenCalled(); | ||||
|     }); | ||||
|  | ||||
|     it('should remove assets from a shared link', async () => { | ||||
|       const asset1 = _getAsset_1(); | ||||
|  | ||||
|       const authDto = authStub.adminSharedLink; | ||||
|       const dto = { assetIds: [asset1.id] }; | ||||
|  | ||||
|       assetRepositoryMock.getById.mockResolvedValue(asset1); | ||||
|       sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid); | ||||
|       accessMock.hasSharedLinkAssetAccess.mockResolvedValue(true); | ||||
|       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.update).toHaveBeenCalled(); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('uploadFile', () => { | ||||
|     it('should handle a file upload', async () => { | ||||
|       const assetEntity = _getAsset_1(); | ||||
|   | ||||
| @@ -2,19 +2,14 @@ import { | ||||
|   AssetResponseDto, | ||||
|   getLivePhotoMotionFilename, | ||||
|   IAccessRepository, | ||||
|   ICryptoRepository, | ||||
|   IJobRepository, | ||||
|   ImmichReadStream, | ||||
|   ISharedLinkRepository, | ||||
|   IStorageRepository, | ||||
|   JobName, | ||||
|   mapAsset, | ||||
|   mapAssetWithoutExif, | ||||
|   mapSharedLink, | ||||
|   SharedLinkCore, | ||||
|   SharedLinkResponseDto, | ||||
| } from '@app/domain'; | ||||
| import { AssetEntity, AssetType, SharedLinkType } from '@app/infra/entities'; | ||||
| import { AssetEntity, AssetType } from '@app/infra/entities'; | ||||
| import { | ||||
|   BadRequestException, | ||||
|   ForbiddenException, | ||||
| @@ -33,15 +28,12 @@ import { QueryFailedError, Repository } from 'typeorm'; | ||||
| import { promisify } from 'util'; | ||||
| import { AuthUserDto } from '../../decorators/auth-user.decorator'; | ||||
| import { DownloadService } from '../../modules/download/download.service'; | ||||
| import { AddAssetsDto } from '../album/dto/add-assets.dto'; | ||||
| import { RemoveAssetsDto } from '../album/dto/remove-assets.dto'; | ||||
| import { IAssetRepository } from './asset-repository'; | ||||
| import { AssetCore } from './asset.core'; | ||||
| import { AssetBulkUploadCheckDto } from './dto/asset-check.dto'; | ||||
| import { AssetSearchDto } from './dto/asset-search.dto'; | ||||
| import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto'; | ||||
| import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; | ||||
| import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto'; | ||||
| import { CreateAssetDto, UploadFile } from './dto/create-asset.dto'; | ||||
| import { DeleteAssetDto } from './dto/delete-asset.dto'; | ||||
| import { DownloadFilesDto } from './dto/download-files.dto'; | ||||
| @@ -80,22 +72,17 @@ interface ServableFile { | ||||
| @Injectable() | ||||
| export class AssetService { | ||||
|   readonly logger = new Logger(AssetService.name); | ||||
|   private shareCore: SharedLinkCore; | ||||
|   private assetCore: AssetCore; | ||||
|  | ||||
|   constructor( | ||||
|     @Inject(IAccessRepository) private accessRepository: IAccessRepository, | ||||
|     @Inject(IAssetRepository) private _assetRepository: IAssetRepository, | ||||
|     @InjectRepository(AssetEntity) | ||||
|     private assetRepository: Repository<AssetEntity>, | ||||
|     @InjectRepository(AssetEntity) private assetRepository: Repository<AssetEntity>, | ||||
|     private downloadService: DownloadService, | ||||
|     @Inject(ISharedLinkRepository) sharedLinkRepository: ISharedLinkRepository, | ||||
|     @Inject(IJobRepository) private jobRepository: IJobRepository, | ||||
|     @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository, | ||||
|     @Inject(IStorageRepository) private storageRepository: IStorageRepository, | ||||
|   ) { | ||||
|     this.assetCore = new AssetCore(_assetRepository, jobRepository); | ||||
|     this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository); | ||||
|   } | ||||
|  | ||||
|   public async uploadFile( | ||||
| @@ -608,61 +595,9 @@ export class AssetService { | ||||
|   } | ||||
|  | ||||
|   private checkDownloadAccess(authUser: AuthUserDto) { | ||||
|     this.shareCore.checkDownloadAccess(authUser); | ||||
|   } | ||||
|  | ||||
|   async createAssetsSharedLink(authUser: AuthUserDto, dto: CreateAssetsShareLinkDto): Promise<SharedLinkResponseDto> { | ||||
|     const assets = []; | ||||
|  | ||||
|     await this.checkAssetsAccess(authUser, dto.assetIds); | ||||
|     for (const assetId of dto.assetIds) { | ||||
|       const asset = await this._assetRepository.getById(assetId); | ||||
|       assets.push(asset); | ||||
|     } | ||||
|  | ||||
|     const sharedLink = await this.shareCore.create(authUser.id, { | ||||
|       type: SharedLinkType.INDIVIDUAL, | ||||
|       expiresAt: dto.expiresAt, | ||||
|       allowUpload: dto.allowUpload, | ||||
|       assets, | ||||
|       description: dto.description, | ||||
|       allowDownload: dto.allowDownload, | ||||
|       showExif: dto.showExif, | ||||
|     }); | ||||
|  | ||||
|     return mapSharedLink(sharedLink); | ||||
|   } | ||||
|  | ||||
|   async addAssetsToSharedLink(authUser: AuthUserDto, dto: AddAssetsDto): Promise<SharedLinkResponseDto> { | ||||
|     if (!authUser.sharedLinkId) { | ||||
|     if (authUser.isPublicUser && !authUser.isAllowDownload) { | ||||
|       throw new ForbiddenException(); | ||||
|     } | ||||
|  | ||||
|     const assets = []; | ||||
|  | ||||
|     for (const assetId of dto.assetIds) { | ||||
|       const asset = await this._assetRepository.getById(assetId); | ||||
|       assets.push(asset); | ||||
|     } | ||||
|  | ||||
|     const updatedLink = await this.shareCore.addAssets(authUser.id, authUser.sharedLinkId, assets); | ||||
|     return mapSharedLink(updatedLink); | ||||
|   } | ||||
|  | ||||
|   async removeAssetsFromSharedLink(authUser: AuthUserDto, dto: RemoveAssetsDto): Promise<SharedLinkResponseDto> { | ||||
|     if (!authUser.sharedLinkId) { | ||||
|       throw new ForbiddenException(); | ||||
|     } | ||||
|  | ||||
|     const assets = []; | ||||
|  | ||||
|     for (const assetId of dto.assetIds) { | ||||
|       const asset = await this._assetRepository.getById(assetId); | ||||
|       assets.push(asset); | ||||
|     } | ||||
|  | ||||
|     const updatedLink = await this.shareCore.removeAssets(authUser.id, authUser.sharedLinkId, assets); | ||||
|     return mapSharedLink(updatedLink); | ||||
|   } | ||||
|  | ||||
|   getExifPermission(authUser: AuthUserDto) { | ||||
|   | ||||
| @@ -1,41 +0,0 @@ | ||||
| import { ApiProperty } from '@nestjs/swagger'; | ||||
| import { Type } from 'class-transformer'; | ||||
| import { IsArray, IsBoolean, IsDate, IsNotEmpty, IsOptional, IsString } from 'class-validator'; | ||||
|  | ||||
| export class CreateAssetsShareLinkDto { | ||||
|   @IsArray() | ||||
|   @IsString({ each: true }) | ||||
|   @IsNotEmpty({ each: true }) | ||||
|   @ApiProperty({ | ||||
|     isArray: true, | ||||
|     type: String, | ||||
|     title: 'Array asset IDs to be shared', | ||||
|     example: [ | ||||
|       'bf973405-3f2a-48d2-a687-2ed4167164be', | ||||
|       'dd41870b-5d00-46d2-924e-1d8489a0aa0f', | ||||
|       'fad77c3f-deef-4e7e-9608-14c1aa4e559a', | ||||
|     ], | ||||
|   }) | ||||
|   assetIds!: string[]; | ||||
|  | ||||
|   @IsDate() | ||||
|   @Type(() => Date) | ||||
|   @IsOptional() | ||||
|   expiresAt?: Date; | ||||
|  | ||||
|   @IsBoolean() | ||||
|   @IsOptional() | ||||
|   allowUpload?: boolean; | ||||
|  | ||||
|   @IsBoolean() | ||||
|   @IsOptional() | ||||
|   allowDownload?: boolean; | ||||
|  | ||||
|   @IsBoolean() | ||||
|   @IsOptional() | ||||
|   showExif?: boolean; | ||||
|  | ||||
|   @IsString() | ||||
|   @IsOptional() | ||||
|   description?: string; | ||||
| } | ||||
| @@ -1,13 +1,21 @@ | ||||
| import { AuthUserDto, EditSharedLinkDto, SharedLinkResponseDto, SharedLinkService } from '@app/domain'; | ||||
| import { Body, Controller, Delete, Get, Param, Patch } from '@nestjs/common'; | ||||
| import { | ||||
|   AssetIdsDto, | ||||
|   AssetIdsResponseDto, | ||||
|   AuthUserDto, | ||||
|   SharedLinkCreateDto, | ||||
|   SharedLinkEditDto, | ||||
|   SharedLinkResponseDto, | ||||
|   SharedLinkService, | ||||
| } from '@app/domain'; | ||||
| import { Body, Controller, Delete, Get, Param, Patch, Post, Put } from '@nestjs/common'; | ||||
| import { ApiTags } from '@nestjs/swagger'; | ||||
| import { AuthUser } from '../decorators/auth-user.decorator'; | ||||
| import { Authenticated, SharedLinkRoute } from '../decorators/authenticated.decorator'; | ||||
| import { UseValidation } from '../decorators/use-validation.decorator'; | ||||
| import { UUIDParamDto } from './dto/uuid-param.dto'; | ||||
|  | ||||
| @ApiTags('share') | ||||
| @Controller('share') | ||||
| @ApiTags('Shared Link') | ||||
| @Controller('shared-link') | ||||
| @Authenticated() | ||||
| @UseValidation() | ||||
| export class SharedLinkController { | ||||
| @@ -29,11 +37,16 @@ export class SharedLinkController { | ||||
|     return this.service.get(authUser, id); | ||||
|   } | ||||
|  | ||||
|   @Post() | ||||
|   createSharedLink(@AuthUser() authUser: AuthUserDto, @Body() dto: SharedLinkCreateDto) { | ||||
|     return this.service.create(authUser, dto); | ||||
|   } | ||||
|  | ||||
|   @Patch(':id') | ||||
|   updateSharedLink( | ||||
|     @AuthUser() authUser: AuthUserDto, | ||||
|     @Param() { id }: UUIDParamDto, | ||||
|     @Body() dto: EditSharedLinkDto, | ||||
|     @Body() dto: SharedLinkEditDto, | ||||
|   ): Promise<SharedLinkResponseDto> { | ||||
|     return this.service.update(authUser, id, dto); | ||||
|   } | ||||
| @@ -42,4 +55,24 @@ export class SharedLinkController { | ||||
|   removeSharedLink(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> { | ||||
|     return this.service.remove(authUser, id); | ||||
|   } | ||||
|  | ||||
|   @SharedLinkRoute() | ||||
|   @Put(':id/assets') | ||||
|   addSharedLinkAssets( | ||||
|     @AuthUser() authUser: AuthUserDto, | ||||
|     @Param() { id }: UUIDParamDto, | ||||
|     @Body() dto: AssetIdsDto, | ||||
|   ): Promise<AssetIdsResponseDto[]> { | ||||
|     return this.service.addAssets(authUser, id, dto); | ||||
|   } | ||||
|  | ||||
|   @SharedLinkRoute() | ||||
|   @Delete(':id/assets') | ||||
|   removeSharedLinkAssets( | ||||
|     @AuthUser() authUser: AuthUserDto, | ||||
|     @Param() { id }: UUIDParamDto, | ||||
|     @Body() dto: AssetIdsDto, | ||||
|   ): Promise<AssetIdsResponseDto[]> { | ||||
|     return this.service.removeAssets(authUser, id, dto); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -18,8 +18,8 @@ export class SharedLinkEntity { | ||||
|   @PrimaryGeneratedColumn('uuid') | ||||
|   id!: string; | ||||
|  | ||||
|   @Column({ nullable: true }) | ||||
|   description?: string; | ||||
|   @Column({ type: 'varchar', nullable: true }) | ||||
|   description!: string | null; | ||||
|  | ||||
|   @Column() | ||||
|   userId!: string; | ||||
| @@ -55,6 +55,9 @@ export class SharedLinkEntity { | ||||
|   @Index('IDX_sharedlink_albumId') | ||||
|   @ManyToOne(() => AlbumEntity, (album) => album.sharedLinks, { onDelete: 'CASCADE', onUpdate: 'CASCADE' }) | ||||
|   album?: AlbumEntity; | ||||
|  | ||||
|   @Column({ type: 'varchar', nullable: true }) | ||||
|   albumId!: string | null; | ||||
| } | ||||
|  | ||||
| export enum SharedLinkType { | ||||
|   | ||||
| @@ -95,4 +95,13 @@ export class AccessRepository implements IAccessRepository { | ||||
|       })) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   hasAlbumOwnerAccess(userId: string, albumId: string): Promise<boolean> { | ||||
|     return this.albumRepository.exist({ | ||||
|       where: { | ||||
|         id: albumId, | ||||
|         ownerId: userId, | ||||
|       }, | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -777,6 +777,21 @@ export const loginResponseStub = { | ||||
| }; | ||||
|  | ||||
| export const sharedLinkStub = { | ||||
|   individual: Object.freeze({ | ||||
|     id: '123', | ||||
|     userId: authStub.admin.id, | ||||
|     user: userEntityStub.admin, | ||||
|     key: sharedLinkBytes, | ||||
|     type: SharedLinkType.INDIVIDUAL, | ||||
|     createdAt: today, | ||||
|     expiresAt: tomorrow, | ||||
|     allowUpload: true, | ||||
|     allowDownload: true, | ||||
|     showExif: true, | ||||
|     album: undefined, | ||||
|     description: null, | ||||
|     assets: [assetEntityStub.image], | ||||
|   } as SharedLinkEntity), | ||||
|   valid: Object.freeze({ | ||||
|     id: '123', | ||||
|     userId: authStub.admin.id, | ||||
| @@ -789,6 +804,8 @@ export const sharedLinkStub = { | ||||
|     allowDownload: true, | ||||
|     showExif: true, | ||||
|     album: undefined, | ||||
|     albumId: null, | ||||
|     description: null, | ||||
|     assets: [], | ||||
|   } as SharedLinkEntity), | ||||
|   expired: Object.freeze({ | ||||
| @@ -802,6 +819,8 @@ export const sharedLinkStub = { | ||||
|     allowUpload: true, | ||||
|     allowDownload: true, | ||||
|     showExif: true, | ||||
|     description: null, | ||||
|     albumId: null, | ||||
|     assets: [], | ||||
|   } as SharedLinkEntity), | ||||
|   readonlyNoExif: Object.freeze<SharedLinkEntity>({ | ||||
| @@ -815,7 +834,9 @@ export const sharedLinkStub = { | ||||
|     allowUpload: false, | ||||
|     allowDownload: false, | ||||
|     showExif: false, | ||||
|     description: null, | ||||
|     assets: [], | ||||
|     albumId: 'album-123', | ||||
|     album: { | ||||
|       id: 'album-123', | ||||
|       ownerId: authStub.admin.id, | ||||
| @@ -903,7 +924,7 @@ export const sharedLinkResponseStub = { | ||||
|     allowUpload: true, | ||||
|     assets: [], | ||||
|     createdAt: today, | ||||
|     description: undefined, | ||||
|     description: null, | ||||
|     expiresAt: tomorrow, | ||||
|     id: '123', | ||||
|     key: sharedLinkBytes.toString('base64url'), | ||||
| @@ -917,7 +938,7 @@ export const sharedLinkResponseStub = { | ||||
|     allowUpload: true, | ||||
|     assets: [], | ||||
|     createdAt: today, | ||||
|     description: undefined, | ||||
|     description: null, | ||||
|     expiresAt: yesterday, | ||||
|     id: '123', | ||||
|     key: sharedLinkBytes.toString('base64url'), | ||||
| @@ -932,7 +953,7 @@ export const sharedLinkResponseStub = { | ||||
|     type: SharedLinkType.ALBUM, | ||||
|     createdAt: today, | ||||
|     expiresAt: tomorrow, | ||||
|     description: undefined, | ||||
|     description: null, | ||||
|     allowUpload: false, | ||||
|     allowDownload: false, | ||||
|     showExif: true, | ||||
| @@ -946,7 +967,7 @@ export const sharedLinkResponseStub = { | ||||
|     type: SharedLinkType.ALBUM, | ||||
|     createdAt: today, | ||||
|     expiresAt: tomorrow, | ||||
|     description: undefined, | ||||
|     description: null, | ||||
|     allowUpload: false, | ||||
|     allowDownload: false, | ||||
|     showExif: false, | ||||
|   | ||||
| @@ -3,9 +3,12 @@ import { IAccessRepository } from '@app/domain'; | ||||
| export const newAccessRepositoryMock = (): jest.Mocked<IAccessRepository> => { | ||||
|   return { | ||||
|     hasPartnerAccess: jest.fn(), | ||||
|  | ||||
|     hasAlbumAssetAccess: jest.fn(), | ||||
|     hasOwnerAssetAccess: jest.fn(), | ||||
|     hasPartnerAssetAccess: jest.fn(), | ||||
|     hasSharedLinkAssetAccess: jest.fn(), | ||||
|  | ||||
|     hasAlbumOwnerAccess: jest.fn(), | ||||
|   }; | ||||
| }; | ||||
|   | ||||
| @@ -7,16 +7,16 @@ import { | ||||
| 	Configuration, | ||||
| 	ConfigurationParameters, | ||||
| 	JobApi, | ||||
| 	JobName, | ||||
| 	OAuthApi, | ||||
| 	PersonApi, | ||||
| 	PartnerApi, | ||||
| 	PersonApi, | ||||
| 	SearchApi, | ||||
| 	ServerInfoApi, | ||||
| 	ShareApi, | ||||
| 	SharedLinkApi, | ||||
| 	SystemConfigApi, | ||||
| 	UserApi, | ||||
| 	UserApiFp, | ||||
| 	JobName | ||||
| 	UserApiFp | ||||
| } from './open-api'; | ||||
| import { BASE_PATH } from './open-api/base'; | ||||
| import { DUMMY_BASE_URL, toPathString } from './open-api/common'; | ||||
| @@ -32,7 +32,7 @@ export class ImmichApi { | ||||
| 	public partnerApi: PartnerApi; | ||||
| 	public searchApi: SearchApi; | ||||
| 	public serverInfoApi: ServerInfoApi; | ||||
| 	public shareApi: ShareApi; | ||||
| 	public sharedLinkApi: SharedLinkApi; | ||||
| 	public personApi: PersonApi; | ||||
| 	public systemConfigApi: SystemConfigApi; | ||||
| 	public userApi: UserApi; | ||||
| @@ -51,7 +51,7 @@ export class ImmichApi { | ||||
| 		this.partnerApi = new PartnerApi(this.config); | ||||
| 		this.searchApi = new SearchApi(this.config); | ||||
| 		this.serverInfoApi = new ServerInfoApi(this.config); | ||||
| 		this.shareApi = new ShareApi(this.config); | ||||
| 		this.sharedLinkApi = new SharedLinkApi(this.config); | ||||
| 		this.personApi = new PersonApi(this.config); | ||||
| 		this.systemConfigApi = new SystemConfigApi(this.config); | ||||
| 		this.userApi = new UserApi(this.config); | ||||
|   | ||||
							
								
								
									
										1045
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1045
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -31,7 +31,7 @@ | ||||
| 	}); | ||||
|  | ||||
| 	const getSharedLinks = async () => { | ||||
| 		const { data } = await api.shareApi.getAllSharedLinks(); | ||||
| 		const { data } = await api.sharedLinkApi.getAllSharedLinks(); | ||||
|  | ||||
| 		sharedLinks = data.filter((link) => link.album?.id === album.id); | ||||
| 	}; | ||||
|   | ||||
| @@ -1,34 +1,65 @@ | ||||
| <script lang="ts"> | ||||
| 	import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; | ||||
| 	import { AssetResponseDto, SharedLinkResponseDto, api } from '@api'; | ||||
| 	import { SharedLinkResponseDto, api } from '@api'; | ||||
| 	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte'; | ||||
| 	import ConfirmDialogue from '../../shared-components/confirm-dialogue.svelte'; | ||||
| 	import { getAssetControlContext } from '../asset-select-control-bar.svelte'; | ||||
| 	import { | ||||
| 		NotificationType, | ||||
| 		notificationController | ||||
| 	} from '../../shared-components/notification/notification'; | ||||
| 	import { handleError } from '../../../utils/handle-error'; | ||||
|  | ||||
| 	export let sharedLink: SharedLinkResponseDto; | ||||
| 	export let allAssets: AssetResponseDto[]; | ||||
|  | ||||
| 	let removing = false; | ||||
|  | ||||
| 	const { getAssets, clearSelect } = getAssetControlContext(); | ||||
|  | ||||
| 	const handleRemoveAssetsFromSharedLink = async () => { | ||||
| 		if (window.confirm('Do you want to remove selected assets from the shared link?')) { | ||||
| 			// TODO: Rename API method or change functionality. The assetIds passed | ||||
| 			// in are kept instead of removed. | ||||
| 			const assetsToKeep = allAssets.filter((a) => !getAssets().has(a)); | ||||
| 			await api.assetApi.removeAssetsFromSharedLink({ | ||||
| 				removeAssetsDto: { | ||||
| 					assetIds: assetsToKeep.map((a) => a.id) | ||||
| 	const handleRemove = async () => { | ||||
| 		try { | ||||
| 			const { data: results } = await api.sharedLinkApi.removeSharedLinkAssets({ | ||||
| 				id: sharedLink.id, | ||||
| 				assetIdsDto: { | ||||
| 					assetIds: Array.from(getAssets()).map((asset) => asset.id) | ||||
| 				}, | ||||
| 				key: sharedLink?.key | ||||
| 				key: sharedLink.key | ||||
| 			}); | ||||
|  | ||||
| 			for (const result of results) { | ||||
| 				if (!result.success) { | ||||
| 					continue; | ||||
| 				} | ||||
|  | ||||
| 				sharedLink.assets = sharedLink.assets.filter((asset) => asset.id !== result.assetId); | ||||
| 			} | ||||
|  | ||||
| 			const count = results.filter((item) => item.success).length; | ||||
|  | ||||
| 			notificationController.show({ | ||||
| 				type: NotificationType.Info, | ||||
| 				message: `Removed ${count} assets` | ||||
| 			}); | ||||
|  | ||||
| 			sharedLink.assets = assetsToKeep; | ||||
| 			clearSelect(); | ||||
| 		} catch (error) { | ||||
| 			handleError(error, 'Unable to remove assets from shared link'); | ||||
| 		} | ||||
| 	}; | ||||
| </script> | ||||
|  | ||||
| <CircleIconButton | ||||
| 	title="Remove from album" | ||||
| 	on:click={handleRemoveAssetsFromSharedLink} | ||||
| 	title="Remove from shared link" | ||||
| 	on:click={() => (removing = true)} | ||||
| 	logo={DeleteOutline} | ||||
| /> | ||||
|  | ||||
| {#if removing} | ||||
| 	<ConfirmDialogue | ||||
| 		title="Remove Assets?" | ||||
| 		prompt="Are you sure you want to remove {getAssets().size} asset(s) from this shared link?" | ||||
| 		confirmText="Remove" | ||||
| 		on:confirm={() => handleRemove()} | ||||
| 		on:cancel={() => (removing = false)} | ||||
| 	/> | ||||
| {/if} | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
| 		notificationController, | ||||
| 		NotificationType | ||||
| 	} from '../shared-components/notification/notification'; | ||||
| 	import { handleError } from '../../utils/handle-error'; | ||||
|  | ||||
| 	export let sharedLink: SharedLinkResponseDto; | ||||
| 	export let isOwned: boolean; | ||||
| @@ -26,43 +27,40 @@ | ||||
| 	$: assets = sharedLink.assets; | ||||
| 	$: isMultiSelectionMode = selectedAssets.size > 0; | ||||
|  | ||||
| 	const clearMultiSelectAssetAssetHandler = () => { | ||||
| 		selectedAssets = new Set(); | ||||
| 	}; | ||||
|  | ||||
| 	const downloadAssets = async () => { | ||||
| 		await bulkDownload('immich-shared', assets, undefined, sharedLink?.key); | ||||
| 		await bulkDownload('immich-shared', assets, undefined, sharedLink.key); | ||||
| 	}; | ||||
|  | ||||
| 	const handleUploadAssets = async () => { | ||||
| 		try { | ||||
| 			const results = await openFileUploadDialog(undefined, sharedLink?.key); | ||||
| 			const results = await openFileUploadDialog(undefined, sharedLink.key); | ||||
|  | ||||
| 			const assetIds = results.filter((id) => !!id) as string[]; | ||||
|  | ||||
| 			await api.assetApi.addAssetsToSharedLink({ | ||||
| 				addAssetsDto: { | ||||
| 					assetIds | ||||
| 			const { data } = await api.sharedLinkApi.addSharedLinkAssets({ | ||||
| 				id: sharedLink.id, | ||||
| 				assetIdsDto: { | ||||
| 					assetIds: results.filter((id) => !!id) as string[] | ||||
| 				}, | ||||
| 				key: sharedLink?.key | ||||
| 				key: sharedLink.key | ||||
| 			}); | ||||
|  | ||||
| 			const added = data.filter((item) => item.success).length; | ||||
|  | ||||
| 			notificationController.show({ | ||||
| 				message: `Successfully add ${assetIds.length} to the shared link`, | ||||
| 				message: `Added ${added} assets`, | ||||
| 				type: NotificationType.Info | ||||
| 			}); | ||||
| 		} catch (e) { | ||||
| 			console.error('handleUploadAssets', e); | ||||
| 			handleError(e, 'Unable to add assets to shared link'); | ||||
| 		} | ||||
| 	}; | ||||
| </script> | ||||
|  | ||||
| <section class="bg-immich-bg dark:bg-immich-dark-bg"> | ||||
| 	{#if isMultiSelectionMode} | ||||
| 		<AssetSelectControlBar assets={selectedAssets} clearSelect={clearMultiSelectAssetAssetHandler}> | ||||
| 		<AssetSelectControlBar assets={selectedAssets} clearSelect={() => (selectedAssets = new Set())}> | ||||
| 			<DownloadAction filename="immich-shared" sharedLinkKey={sharedLink.key} /> | ||||
| 			{#if isOwned} | ||||
| 				<RemoveFromSharedLink bind:sharedLink allAssets={assets} /> | ||||
| 				<RemoveFromSharedLink bind:sharedLink /> | ||||
| 			{/if} | ||||
| 		</AssetSelectControlBar> | ||||
| 	{:else} | ||||
|   | ||||
| @@ -7,31 +7,31 @@ | ||||
| 	import { handleError } from '$lib/utils/handle-error'; | ||||
| 	import { | ||||
| 		AlbumResponseDto, | ||||
| 		api, | ||||
| 		AssetResponseDto, | ||||
| 		SharedLinkResponseDto, | ||||
| 		SharedLinkType, | ||||
| 		api | ||||
| 		SharedLinkType | ||||
| 	} from '@api'; | ||||
| 	import { createEventDispatcher, onMount } from 'svelte'; | ||||
| 	import Link from 'svelte-material-icons/Link.svelte'; | ||||
| 	import BaseModal from '../base-modal.svelte'; | ||||
| 	import type { ImmichDropDownOption } from '../dropdown-button.svelte'; | ||||
| 	import DropdownButton from '../dropdown-button.svelte'; | ||||
| 	import { NotificationType, notificationController } from '../notification/notification'; | ||||
| 	import { notificationController, NotificationType } from '../notification/notification'; | ||||
|  | ||||
| 	export let shareType: SharedLinkType; | ||||
| 	export let sharedAssets: AssetResponseDto[] = []; | ||||
| 	export let album: AlbumResponseDto | undefined = undefined; | ||||
| 	export let editingLink: SharedLinkResponseDto | undefined = undefined; | ||||
|  | ||||
| 	let isShowSharedLink = false; | ||||
| 	let expirationTime = ''; | ||||
| 	let isAllowUpload = false; | ||||
| 	let sharedLink = ''; | ||||
| 	let sharedLink: string | null = null; | ||||
| 	let description = ''; | ||||
| 	let allowDownload = true; | ||||
| 	let allowUpload = false; | ||||
| 	let showExif = true; | ||||
| 	let expirationTime = ''; | ||||
| 	let shouldChangeExpirationTime = false; | ||||
| 	let isAllowDownload = true; | ||||
| 	let shouldShowExif = true; | ||||
|  | ||||
| 	const dispatch = createEventDispatcher(); | ||||
|  | ||||
| 	const expiredDateOption: ImmichDropDownOption = { | ||||
| @@ -44,9 +44,9 @@ | ||||
| 			if (editingLink.description) { | ||||
| 				description = editingLink.description; | ||||
| 			} | ||||
| 			isAllowUpload = editingLink.allowUpload; | ||||
| 			isAllowDownload = editingLink.allowDownload; | ||||
| 			shouldShowExif = editingLink.showExif; | ||||
| 			allowUpload = editingLink.allowUpload; | ||||
| 			allowDownload = editingLink.allowDownload; | ||||
| 			showExif = editingLink.showExif; | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| @@ -58,49 +58,32 @@ | ||||
| 			: undefined; | ||||
|  | ||||
| 		try { | ||||
| 			if (shareType === SharedLinkType.Album && album) { | ||||
| 				const { data } = await api.albumApi.createAlbumSharedLink({ | ||||
| 					createAlbumShareLinkDto: { | ||||
| 						albumId: album.id, | ||||
| 						expiresAt: expirationDate, | ||||
| 						allowUpload: isAllowUpload, | ||||
| 						description: description, | ||||
| 						allowDownload: isAllowDownload, | ||||
| 						showExif: shouldShowExif | ||||
| 					} | ||||
| 				}); | ||||
| 				buildSharedLink(data); | ||||
| 			} else { | ||||
| 				const { data } = await api.assetApi.createAssetsSharedLink({ | ||||
| 					createAssetsShareLinkDto: { | ||||
| 						assetIds: sharedAssets.map((a) => a.id), | ||||
| 						expiresAt: expirationDate, | ||||
| 						allowUpload: isAllowUpload, | ||||
| 						description: description, | ||||
| 						allowDownload: isAllowDownload, | ||||
| 						showExif: shouldShowExif | ||||
| 					} | ||||
| 				}); | ||||
| 				buildSharedLink(data); | ||||
| 			} | ||||
| 			const { data } = await api.sharedLinkApi.createSharedLink({ | ||||
| 				sharedLinkCreateDto: { | ||||
| 					type: shareType, | ||||
| 					albumId: album ? album.id : undefined, | ||||
| 					assetIds: sharedAssets.map((a) => a.id), | ||||
| 					expiresAt: expirationDate, | ||||
| 					allowUpload, | ||||
| 					description, | ||||
| 					allowDownload, | ||||
| 					showExif | ||||
| 				} | ||||
| 			}); | ||||
| 			sharedLink = `${window.location.origin}/share/${data.key}`; | ||||
| 		} catch (e) { | ||||
| 			handleError(e, 'Failed to create shared link'); | ||||
| 		} | ||||
|  | ||||
| 		isShowSharedLink = true; | ||||
| 	}; | ||||
|  | ||||
| 	const buildSharedLink = (createdLink: SharedLinkResponseDto) => { | ||||
| 		sharedLink = `${window.location.origin}/share/${createdLink.key}`; | ||||
| 	}; | ||||
|  | ||||
| 	const handleCopy = async () => { | ||||
| 		if (!sharedLink) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		try { | ||||
| 			await navigator.clipboard.writeText(sharedLink); | ||||
| 			notificationController.show({ | ||||
| 				message: 'Copied to clipboard!', | ||||
| 				type: NotificationType.Info | ||||
| 			}); | ||||
| 			notificationController.show({ message: 'Copied to clipboard!', type: NotificationType.Info }); | ||||
| 		} catch (e) { | ||||
| 			handleError( | ||||
| 				e, | ||||
| @@ -129,34 +112,36 @@ | ||||
| 	}; | ||||
|  | ||||
| 	const handleEditLink = async () => { | ||||
| 		if (editingLink) { | ||||
| 			try { | ||||
| 				const expirationTime = getExpirationTimeInMillisecond(); | ||||
| 				const currentTime = new Date().getTime(); | ||||
| 				const expirationDate: string | null = expirationTime | ||||
| 					? new Date(currentTime + expirationTime).toISOString() | ||||
| 					: null; | ||||
| 		if (!editingLink) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 				await api.shareApi.updateSharedLink({ | ||||
| 					id: editingLink.id, | ||||
| 					editSharedLinkDto: { | ||||
| 						description, | ||||
| 						expiresAt: shouldChangeExpirationTime ? expirationDate : undefined, | ||||
| 						allowUpload: isAllowUpload, | ||||
| 						allowDownload: isAllowDownload, | ||||
| 						showExif: shouldShowExif | ||||
| 					} | ||||
| 				}); | ||||
| 		try { | ||||
| 			const expirationTime = getExpirationTimeInMillisecond(); | ||||
| 			const currentTime = new Date().getTime(); | ||||
| 			const expirationDate: string | null = expirationTime | ||||
| 				? new Date(currentTime + expirationTime).toISOString() | ||||
| 				: null; | ||||
|  | ||||
| 				notificationController.show({ | ||||
| 					type: NotificationType.Info, | ||||
| 					message: 'Edited' | ||||
| 				}); | ||||
| 			await api.sharedLinkApi.updateSharedLink({ | ||||
| 				id: editingLink.id, | ||||
| 				sharedLinkEditDto: { | ||||
| 					description, | ||||
| 					expiresAt: shouldChangeExpirationTime ? expirationDate : undefined, | ||||
| 					allowUpload: allowUpload, | ||||
| 					allowDownload: allowDownload, | ||||
| 					showExif: showExif | ||||
| 				} | ||||
| 			}); | ||||
|  | ||||
| 				dispatch('close'); | ||||
| 			} catch (e) { | ||||
| 				handleError(e, 'Failed to edit shared link'); | ||||
| 			} | ||||
| 			notificationController.show({ | ||||
| 				type: NotificationType.Info, | ||||
| 				message: 'Edited' | ||||
| 			}); | ||||
|  | ||||
| 			dispatch('close'); | ||||
| 		} catch (e) { | ||||
| 			handleError(e, 'Failed to edit shared link'); | ||||
| 		} | ||||
| 	}; | ||||
| </script> | ||||
| @@ -212,15 +197,15 @@ | ||||
| 				</div> | ||||
|  | ||||
| 				<div class="my-3"> | ||||
| 					<SettingSwitch bind:checked={shouldShowExif} title={'Show metadata'} /> | ||||
| 					<SettingSwitch bind:checked={showExif} title={'Show metadata'} /> | ||||
| 				</div> | ||||
|  | ||||
| 				<div class="my-3"> | ||||
| 					<SettingSwitch bind:checked={isAllowDownload} title={'Allow public user to download'} /> | ||||
| 					<SettingSwitch bind:checked={allowDownload} title={'Allow public user to download'} /> | ||||
| 				</div> | ||||
|  | ||||
| 				<div class="my-3"> | ||||
| 					<SettingSwitch bind:checked={isAllowUpload} title={'Allow public user to upload'} /> | ||||
| 					<SettingSwitch bind:checked={allowUpload} title={'Allow public user to upload'} /> | ||||
| 				</div> | ||||
|  | ||||
| 				<div class="text-sm"> | ||||
| @@ -248,7 +233,7 @@ | ||||
| 	<hr /> | ||||
|  | ||||
| 	<section class="m-6"> | ||||
| 		{#if !isShowSharedLink} | ||||
| 		{#if !sharedLink} | ||||
| 			{#if editingLink} | ||||
| 				<div class="flex justify-end"> | ||||
| 					<Button size="sm" rounded="lg" on:click={handleEditLink}>Confirm</Button> | ||||
| @@ -258,9 +243,7 @@ | ||||
| 					<Button size="sm" rounded="lg" on:click={handleCreateSharedLink}>Create link</Button> | ||||
| 				</div> | ||||
| 			{/if} | ||||
| 		{/if} | ||||
|  | ||||
| 		{#if isShowSharedLink} | ||||
| 		{:else} | ||||
| 			<div class="flex w-full gap-4"> | ||||
| 				<input class="immich-form-input w-full" bind:value={sharedLink} disabled /> | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ export const load = (async ({ params, locals: { api } }) => { | ||||
| 	const { key } = params; | ||||
|  | ||||
| 	try { | ||||
| 		const { data: sharedLink } = await api.shareApi.getMySharedLink({ key }); | ||||
| 		const { data: sharedLink } = await api.sharedLinkApi.getMySharedLink({ key }); | ||||
|  | ||||
| 		const assetCount = sharedLink.assets.length; | ||||
| 		const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id; | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| <script lang="ts"> | ||||
| 	import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte'; | ||||
| 	import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte'; | ||||
|  | ||||
| 	import { api, SharedLinkResponseDto } from '@api'; | ||||
| 	import { goto } from '$app/navigation'; | ||||
| 	import SharedLinkCard from '$lib/components/sharedlinks-page/shared-link-card.svelte'; | ||||
| @@ -11,53 +10,45 @@ | ||||
| 	} from '$lib/components/shared-components/notification/notification'; | ||||
| 	import { onMount } from 'svelte'; | ||||
| 	import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte'; | ||||
| 	import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; | ||||
| 	import { handleError } from '$lib/utils/handle-error'; | ||||
| 	import { AppRoute } from '$lib/constants'; | ||||
|  | ||||
| 	let sharedLinks: SharedLinkResponseDto[] = []; | ||||
| 	let showEditForm = false; | ||||
| 	let editSharedLink: SharedLinkResponseDto; | ||||
| 	let editSharedLink: SharedLinkResponseDto | null = null; | ||||
|  | ||||
| 	onMount(async () => { | ||||
| 		sharedLinks = await getSharedLinks(); | ||||
| 	}); | ||||
| 	let deleteLinkId: string | null = null; | ||||
|  | ||||
| 	const getSharedLinks = async () => { | ||||
| 		const { data: sharedLinks } = await api.shareApi.getAllSharedLinks(); | ||||
|  | ||||
| 		return sharedLinks; | ||||
| 	const refresh = async () => { | ||||
| 		const { data } = await api.sharedLinkApi.getAllSharedLinks(); | ||||
| 		sharedLinks = data; | ||||
| 	}; | ||||
|  | ||||
| 	const handleDeleteLink = async (linkId: string) => { | ||||
| 		if (window.confirm('Do you want to delete the shared link? ')) { | ||||
| 			try { | ||||
| 				await api.shareApi.removeSharedLink({ id: linkId }); | ||||
| 				notificationController.show({ | ||||
| 					message: 'Shared link deleted', | ||||
| 					type: NotificationType.Info | ||||
| 				}); | ||||
| 	onMount(async () => { | ||||
| 		await refresh(); | ||||
| 	}); | ||||
|  | ||||
| 				sharedLinks = await getSharedLinks(); | ||||
| 			} catch (e) { | ||||
| 				console.error(e); | ||||
| 				notificationController.show({ | ||||
| 					message: 'Failed to delete shared link', | ||||
| 					type: NotificationType.Error | ||||
| 				}); | ||||
| 			} | ||||
| 	const handleDeleteLink = async () => { | ||||
| 		if (!deleteLinkId) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		try { | ||||
| 			await api.sharedLinkApi.removeSharedLink({ id: deleteLinkId }); | ||||
| 			notificationController.show({ message: 'Deleted shared link', type: NotificationType.Info }); | ||||
| 			deleteLinkId = null; | ||||
| 			refresh(); | ||||
| 		} catch (error) { | ||||
| 			handleError(error, 'Unable to delete shared link'); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	const handleEditLink = async (id: string) => { | ||||
| 		const { data } = await api.shareApi.getSharedLinkById({ id }); | ||||
| 		editSharedLink = data; | ||||
| 		showEditForm = true; | ||||
| 	}; | ||||
|  | ||||
| 	const handleEditDone = async () => { | ||||
| 		sharedLinks = await getSharedLinks(); | ||||
| 		showEditForm = false; | ||||
| 		refresh(); | ||||
| 		editSharedLink = null; | ||||
| 	}; | ||||
|  | ||||
| 	const handleCopy = async (key: string) => { | ||||
| 	const handleCopyLink = async (key: string) => { | ||||
| 		const link = `${window.location.origin}/share/${key}`; | ||||
| 		await navigator.clipboard.writeText(link); | ||||
| 		notificationController.show({ | ||||
| @@ -67,7 +58,7 @@ | ||||
| 	}; | ||||
| </script> | ||||
|  | ||||
| <ControlAppBar backIcon={ArrowLeft} on:close-button-click={() => goto('/sharing')}> | ||||
| <ControlAppBar backIcon={ArrowLeft} on:close-button-click={() => goto(AppRoute.SHARING)}> | ||||
| 	<svelte:fragment slot="leading">Shared links</svelte:fragment> | ||||
| </ControlAppBar> | ||||
|  | ||||
| @@ -86,16 +77,16 @@ | ||||
| 			{#each sharedLinks as link (link.id)} | ||||
| 				<SharedLinkCard | ||||
| 					{link} | ||||
| 					on:delete={() => handleDeleteLink(link.id)} | ||||
| 					on:edit={() => handleEditLink(link.id)} | ||||
| 					on:copy={() => handleCopy(link.key)} | ||||
| 					on:delete={() => (deleteLinkId = link.id)} | ||||
| 					on:edit={() => (editSharedLink = link)} | ||||
| 					on:copy={() => handleCopyLink(link.key)} | ||||
| 				/> | ||||
| 			{/each} | ||||
| 		</div> | ||||
| 	{/if} | ||||
| </section> | ||||
|  | ||||
| {#if showEditForm} | ||||
| {#if editSharedLink} | ||||
| 	<CreateSharedLinkModal | ||||
| 		editingLink={editSharedLink} | ||||
| 		shareType={editSharedLink.type} | ||||
| @@ -103,3 +94,13 @@ | ||||
| 		on:close={handleEditDone} | ||||
| 	/> | ||||
| {/if} | ||||
|  | ||||
| {#if deleteLinkId} | ||||
| 	<ConfirmDialogue | ||||
| 		title="Delete Shared Link" | ||||
| 		prompt="Are you want to delete this shared link?" | ||||
| 		confirmText="Delete" | ||||
| 		on:confirm={() => handleDeleteLink()} | ||||
| 		on:cancel={() => (deleteLinkId = null)} | ||||
| 	/> | ||||
| {/if} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user