refactor(server, web): create shared link (#2879)

* refactor: shared links

* chore: open api

* fix: tsc error
This commit is contained in:
Jason Rasmussen 2023-06-20 21:08:43 -04:00 committed by GitHub
parent 746ca5d5ed
commit 868f629f32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 2150 additions and 2642 deletions

View File

@ -37,8 +37,6 @@ doc/CheckDuplicateAssetResponseDto.md
doc/CheckExistingAssetsDto.md doc/CheckExistingAssetsDto.md
doc/CheckExistingAssetsResponseDto.md doc/CheckExistingAssetsResponseDto.md
doc/CreateAlbumDto.md doc/CreateAlbumDto.md
doc/CreateAlbumShareLinkDto.md
doc/CreateAssetsShareLinkDto.md
doc/CreateProfileImageResponseDto.md doc/CreateProfileImageResponseDto.md
doc/CreateTagDto.md doc/CreateTagDto.md
doc/CreateUserDto.md doc/CreateUserDto.md
@ -48,7 +46,6 @@ doc/DeleteAssetDto.md
doc/DeleteAssetResponseDto.md doc/DeleteAssetResponseDto.md
doc/DeleteAssetStatus.md doc/DeleteAssetStatus.md
doc/DownloadFilesDto.md doc/DownloadFilesDto.md
doc/EditSharedLinkDto.md
doc/ExifResponseDto.md doc/ExifResponseDto.md
doc/GetAssetByTimeBucketDto.md doc/GetAssetByTimeBucketDto.md
doc/GetAssetCountByTimeBucketDto.md doc/GetAssetCountByTimeBucketDto.md
@ -89,7 +86,9 @@ doc/ServerInfoResponseDto.md
doc/ServerPingResponse.md doc/ServerPingResponse.md
doc/ServerStatsResponseDto.md doc/ServerStatsResponseDto.md
doc/ServerVersionReponseDto.md doc/ServerVersionReponseDto.md
doc/ShareApi.md doc/SharedLinkApi.md
doc/SharedLinkCreateDto.md
doc/SharedLinkEditDto.md
doc/SharedLinkResponseDto.md doc/SharedLinkResponseDto.md
doc/SharedLinkType.md doc/SharedLinkType.md
doc/SignUpDto.md doc/SignUpDto.md
@ -128,7 +127,7 @@ lib/api/partner_api.dart
lib/api/person_api.dart lib/api/person_api.dart
lib/api/search_api.dart lib/api/search_api.dart
lib/api/server_info_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/system_config_api.dart
lib/api/tag_api.dart lib/api/tag_api.dart
lib/api/user_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_dto.dart
lib/model/check_existing_assets_response_dto.dart lib/model/check_existing_assets_response_dto.dart
lib/model/create_album_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_profile_image_response_dto.dart
lib/model/create_tag_dto.dart lib/model/create_tag_dto.dart
lib/model/create_user_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_response_dto.dart
lib/model/delete_asset_status.dart lib/model/delete_asset_status.dart
lib/model/download_files_dto.dart lib/model/download_files_dto.dart
lib/model/edit_shared_link_dto.dart
lib/model/exif_response_dto.dart lib/model/exif_response_dto.dart
lib/model/get_asset_by_time_bucket_dto.dart lib/model/get_asset_by_time_bucket_dto.dart
lib/model/get_asset_count_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_ping_response.dart
lib/model/server_stats_response_dto.dart lib/model/server_stats_response_dto.dart
lib/model/server_version_reponse_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_response_dto.dart
lib/model/shared_link_type.dart lib/model/shared_link_type.dart
lib/model/sign_up_dto.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_dto_test.dart
test/check_existing_assets_response_dto_test.dart test/check_existing_assets_response_dto_test.dart
test/create_album_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_profile_image_response_dto_test.dart
test/create_tag_dto_test.dart test/create_tag_dto_test.dart
test/create_user_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_response_dto_test.dart
test/delete_asset_status_test.dart test/delete_asset_status_test.dart
test/download_files_dto_test.dart test/download_files_dto_test.dart
test/edit_shared_link_dto_test.dart
test/exif_response_dto_test.dart test/exif_response_dto_test.dart
test/get_asset_by_time_bucket_dto_test.dart test/get_asset_by_time_bucket_dto_test.dart
test/get_asset_count_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_ping_response_test.dart
test/server_stats_response_dto_test.dart test/server_stats_response_dto_test.dart
test/server_version_reponse_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_response_dto_test.dart
test/shared_link_type_test.dart test/shared_link_type_test.dart
test/sign_up_dto_test.dart test/sign_up_dto_test.dart

View File

@ -80,7 +80,6 @@ Class | Method | HTTP request | Description
*AlbumApi* | [**addAssetsToAlbum**](doc//AlbumApi.md#addassetstoalbum) | **PUT** /album/{id}/assets | *AlbumApi* | [**addAssetsToAlbum**](doc//AlbumApi.md#addassetstoalbum) | **PUT** /album/{id}/assets |
*AlbumApi* | [**addUsersToAlbum**](doc//AlbumApi.md#adduserstoalbum) | **PUT** /album/{id}/users | *AlbumApi* | [**addUsersToAlbum**](doc//AlbumApi.md#adduserstoalbum) | **PUT** /album/{id}/users |
*AlbumApi* | [**createAlbum**](doc//AlbumApi.md#createalbum) | **POST** /album | *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* | [**deleteAlbum**](doc//AlbumApi.md#deletealbum) | **DELETE** /album/{id} |
*AlbumApi* | [**downloadArchive**](doc//AlbumApi.md#downloadarchive) | **GET** /album/{id}/download | *AlbumApi* | [**downloadArchive**](doc//AlbumApi.md#downloadarchive) | **GET** /album/{id}/download |
*AlbumApi* | [**getAlbumCount**](doc//AlbumApi.md#getalbumcount) | **GET** /album/count | *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* | [**removeAssetFromAlbum**](doc//AlbumApi.md#removeassetfromalbum) | **DELETE** /album/{id}/assets |
*AlbumApi* | [**removeUserFromAlbum**](doc//AlbumApi.md#removeuserfromalbum) | **DELETE** /album/{id}/user/{userId} | *AlbumApi* | [**removeUserFromAlbum**](doc//AlbumApi.md#removeuserfromalbum) | **DELETE** /album/{id}/user/{userId} |
*AlbumApi* | [**updateAlbumInfo**](doc//AlbumApi.md#updatealbuminfo) | **PATCH** /album/{id} | *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* | [**bulkUploadCheck**](doc//AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |
*AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check | *AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |
*AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist | *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* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset |
*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download/{id} | *AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download/{id} |
*AssetApi* | [**downloadFiles**](doc//AssetApi.md#downloadfiles) | **POST** /asset/download-files | *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* | [**getMapMarkers**](doc//AssetApi.md#getmapmarkers) | **GET** /asset/map-marker |
*AssetApi* | [**getMemoryLane**](doc//AssetApi.md#getmemorylane) | **GET** /asset/memory-lane | *AssetApi* | [**getMemoryLane**](doc//AssetApi.md#getmemorylane) | **GET** /asset/memory-lane |
*AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} | *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* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search |
*AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} | *AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} |
*AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{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* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
*ServerInfoApi* | [**getStats**](doc//ServerInfoApi.md#getstats) | **GET** /server-info/stats | *ServerInfoApi* | [**getStats**](doc//ServerInfoApi.md#getstats) | **GET** /server-info/stats |
*ServerInfoApi* | [**pingServer**](doc//ServerInfoApi.md#pingserver) | **GET** /server-info/ping | *ServerInfoApi* | [**pingServer**](doc//ServerInfoApi.md#pingserver) | **GET** /server-info/ping |
*ShareApi* | [**getAllSharedLinks**](doc//ShareApi.md#getallsharedlinks) | **GET** /share | *SharedLinkApi* | [**addSharedLinkAssets**](doc//SharedLinkApi.md#addsharedlinkassets) | **PUT** /shared-link/{id}/assets |
*ShareApi* | [**getMySharedLink**](doc//ShareApi.md#getmysharedlink) | **GET** /share/me | *SharedLinkApi* | [**createSharedLink**](doc//SharedLinkApi.md#createsharedlink) | **POST** /shared-link |
*ShareApi* | [**getSharedLinkById**](doc//ShareApi.md#getsharedlinkbyid) | **GET** /share/{id} | *SharedLinkApi* | [**getAllSharedLinks**](doc//SharedLinkApi.md#getallsharedlinks) | **GET** /shared-link |
*ShareApi* | [**removeSharedLink**](doc//ShareApi.md#removesharedlink) | **DELETE** /share/{id} | *SharedLinkApi* | [**getMySharedLink**](doc//SharedLinkApi.md#getmysharedlink) | **GET** /shared-link/me |
*ShareApi* | [**updateSharedLink**](doc//ShareApi.md#updatesharedlink) | **PATCH** /share/{id} | *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* | [**getConfig**](doc//SystemConfigApi.md#getconfig) | **GET** /system-config |
*SystemConfigApi* | [**getDefaults**](doc//SystemConfigApi.md#getdefaults) | **GET** /system-config/defaults | *SystemConfigApi* | [**getDefaults**](doc//SystemConfigApi.md#getdefaults) | **GET** /system-config/defaults |
*SystemConfigApi* | [**getStorageTemplateOptions**](doc//SystemConfigApi.md#getstoragetemplateoptions) | **GET** /system-config/storage-template-options | *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) - [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md)
- [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md) - [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md)
- [CreateAlbumDto](doc//CreateAlbumDto.md) - [CreateAlbumDto](doc//CreateAlbumDto.md)
- [CreateAlbumShareLinkDto](doc//CreateAlbumShareLinkDto.md)
- [CreateAssetsShareLinkDto](doc//CreateAssetsShareLinkDto.md)
- [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md) - [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md)
- [CreateTagDto](doc//CreateTagDto.md) - [CreateTagDto](doc//CreateTagDto.md)
- [CreateUserDto](doc//CreateUserDto.md) - [CreateUserDto](doc//CreateUserDto.md)
@ -218,7 +215,6 @@ Class | Method | HTTP request | Description
- [DeleteAssetResponseDto](doc//DeleteAssetResponseDto.md) - [DeleteAssetResponseDto](doc//DeleteAssetResponseDto.md)
- [DeleteAssetStatus](doc//DeleteAssetStatus.md) - [DeleteAssetStatus](doc//DeleteAssetStatus.md)
- [DownloadFilesDto](doc//DownloadFilesDto.md) - [DownloadFilesDto](doc//DownloadFilesDto.md)
- [EditSharedLinkDto](doc//EditSharedLinkDto.md)
- [ExifResponseDto](doc//ExifResponseDto.md) - [ExifResponseDto](doc//ExifResponseDto.md)
- [GetAssetByTimeBucketDto](doc//GetAssetByTimeBucketDto.md) - [GetAssetByTimeBucketDto](doc//GetAssetByTimeBucketDto.md)
- [GetAssetCountByTimeBucketDto](doc//GetAssetCountByTimeBucketDto.md) - [GetAssetCountByTimeBucketDto](doc//GetAssetCountByTimeBucketDto.md)
@ -253,6 +249,8 @@ Class | Method | HTTP request | Description
- [ServerPingResponse](doc//ServerPingResponse.md) - [ServerPingResponse](doc//ServerPingResponse.md)
- [ServerStatsResponseDto](doc//ServerStatsResponseDto.md) - [ServerStatsResponseDto](doc//ServerStatsResponseDto.md)
- [ServerVersionReponseDto](doc//ServerVersionReponseDto.md) - [ServerVersionReponseDto](doc//ServerVersionReponseDto.md)
- [SharedLinkCreateDto](doc//SharedLinkCreateDto.md)
- [SharedLinkEditDto](doc//SharedLinkEditDto.md)
- [SharedLinkResponseDto](doc//SharedLinkResponseDto.md) - [SharedLinkResponseDto](doc//SharedLinkResponseDto.md)
- [SharedLinkType](doc//SharedLinkType.md) - [SharedLinkType](doc//SharedLinkType.md)
- [SignUpDto](doc//SignUpDto.md) - [SignUpDto](doc//SignUpDto.md)

View File

@ -12,7 +12,6 @@ Method | HTTP request | Description
[**addAssetsToAlbum**](AlbumApi.md#addassetstoalbum) | **PUT** /album/{id}/assets | [**addAssetsToAlbum**](AlbumApi.md#addassetstoalbum) | **PUT** /album/{id}/assets |
[**addUsersToAlbum**](AlbumApi.md#adduserstoalbum) | **PUT** /album/{id}/users | [**addUsersToAlbum**](AlbumApi.md#adduserstoalbum) | **PUT** /album/{id}/users |
[**createAlbum**](AlbumApi.md#createalbum) | **POST** /album | [**createAlbum**](AlbumApi.md#createalbum) | **POST** /album |
[**createAlbumSharedLink**](AlbumApi.md#createalbumsharedlink) | **POST** /album/create-shared-link |
[**deleteAlbum**](AlbumApi.md#deletealbum) | **DELETE** /album/{id} | [**deleteAlbum**](AlbumApi.md#deletealbum) | **DELETE** /album/{id} |
[**downloadArchive**](AlbumApi.md#downloadarchive) | **GET** /album/{id}/download | [**downloadArchive**](AlbumApi.md#downloadarchive) | **GET** /album/{id}/download |
[**getAlbumCount**](AlbumApi.md#getalbumcount) | **GET** /album/count | [**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) [[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**
> deleteAlbum(id) > deleteAlbum(id)

View File

@ -9,11 +9,9 @@ All URIs are relative to */api*
Method | HTTP request | Description Method | HTTP request | Description
------------- | ------------- | ------------- ------------- | ------------- | -------------
[**addAssetsToSharedLink**](AssetApi.md#addassetstosharedlink) | **PATCH** /asset/shared-link/add |
[**bulkUploadCheck**](AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check | [**bulkUploadCheck**](AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |
[**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check | [**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |
[**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist | [**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |
[**createAssetsSharedLink**](AssetApi.md#createassetssharedlink) | **POST** /asset/shared-link |
[**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset | [**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset |
[**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download/{id} | [**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download/{id} |
[**downloadFiles**](AssetApi.md#downloadfiles) | **POST** /asset/download-files | [**downloadFiles**](AssetApi.md#downloadfiles) | **POST** /asset/download-files |
@ -31,70 +29,12 @@ Method | HTTP request | Description
[**getMapMarkers**](AssetApi.md#getmapmarkers) | **GET** /asset/map-marker | [**getMapMarkers**](AssetApi.md#getmapmarkers) | **GET** /asset/map-marker |
[**getMemoryLane**](AssetApi.md#getmemorylane) | **GET** /asset/memory-lane | [**getMemoryLane**](AssetApi.md#getmemorylane) | **GET** /asset/memory-lane |
[**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} | [**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
[**removeAssetsFromSharedLink**](AssetApi.md#removeassetsfromsharedlink) | **PATCH** /asset/shared-link/remove |
[**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search | [**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search |
[**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{id} | [**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{id} |
[**updateAsset**](AssetApi.md#updateasset) | **PUT** /asset/{id} | [**updateAsset**](AssetApi.md#updateasset) | **PUT** /asset/{id} |
[**uploadFile**](AssetApi.md#uploadfile) | **POST** /asset/upload | [**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** # **bulkUploadCheck**
> AssetBulkUploadCheckResponseDto bulkUploadCheck(assetBulkUploadCheckDto) > 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) [[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** # **deleteAsset**
> List<DeleteAssetResponseDto> deleteAsset(deleteAssetDto) > 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) [[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** # **searchAsset**
> List<AssetResponseDto> searchAsset(searchAssetDto) > List<AssetResponseDto> searchAsset(searchAssetDto)

View File

@ -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)

View File

@ -1,4 +1,4 @@
# openapi.api.ShareApi # openapi.api.SharedLinkApi
## Load the API package ## Load the API package
```dart ```dart
@ -9,13 +9,130 @@ All URIs are relative to */api*
Method | HTTP request | Description Method | HTTP request | Description
------------- | ------------- | ------------- ------------- | ------------- | -------------
[**getAllSharedLinks**](ShareApi.md#getallsharedlinks) | **GET** /share | [**addSharedLinkAssets**](SharedLinkApi.md#addsharedlinkassets) | **PUT** /shared-link/{id}/assets |
[**getMySharedLink**](ShareApi.md#getmysharedlink) | **GET** /share/me | [**createSharedLink**](SharedLinkApi.md#createsharedlink) | **POST** /shared-link |
[**getSharedLinkById**](ShareApi.md#getsharedlinkbyid) | **GET** /share/{id} | [**getAllSharedLinks**](SharedLinkApi.md#getallsharedlinks) | **GET** /shared-link |
[**removeSharedLink**](ShareApi.md#removesharedlink) | **DELETE** /share/{id} | [**getMySharedLink**](SharedLinkApi.md#getmysharedlink) | **GET** /shared-link/me |
[**updateSharedLink**](ShareApi.md#updatesharedlink) | **PATCH** /share/{id} | [**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** # **getAllSharedLinks**
> List<SharedLinkResponseDto> getAllSharedLinks() > List<SharedLinkResponseDto> getAllSharedLinks()
@ -39,13 +156,13 @@ import 'package:openapi/api.dart';
// String yourTokenGeneratorFunction() { ... } // String yourTokenGeneratorFunction() { ... }
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = ShareApi(); final api_instance = SharedLinkApi();
try { try {
final result = api_instance.getAllSharedLinks(); final result = api_instance.getAllSharedLinks();
print(result); print(result);
} catch (e) { } 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() { ... } // String yourTokenGeneratorFunction() { ... }
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = ShareApi(); final api_instance = SharedLinkApi();
final key = key_example; // String | final key = key_example; // String |
try { try {
final result = api_instance.getMySharedLink(key); final result = api_instance.getMySharedLink(key);
print(result); print(result);
} catch (e) { } 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() { ... } // String yourTokenGeneratorFunction() { ... }
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = ShareApi(); final api_instance = SharedLinkApi();
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
try { try {
final result = api_instance.getSharedLinkById(id); final result = api_instance.getSharedLinkById(id);
print(result); print(result);
} catch (e) { } 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() { ... } // String yourTokenGeneratorFunction() { ... }
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = ShareApi(); final api_instance = SharedLinkApi();
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
try { try {
api_instance.removeSharedLink(id); api_instance.removeSharedLink(id);
} catch (e) { } 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) [[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** # **removeSharedLinkAssets**
> SharedLinkResponseDto updateSharedLink(id, editSharedLinkDto) > List<AssetIdsResponseDto> removeSharedLinkAssets(id, assetIdsDto, key)
@ -254,15 +371,16 @@ import 'package:openapi/api.dart';
// String yourTokenGeneratorFunction() { ... } // String yourTokenGeneratorFunction() { ... }
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = ShareApi(); final api_instance = SharedLinkApi();
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final editSharedLinkDto = EditSharedLinkDto(); // EditSharedLinkDto | final assetIdsDto = AssetIdsDto(); // AssetIdsDto |
final key = key_example; // String |
try { try {
final result = api_instance.updateSharedLink(id, editSharedLinkDto); final result = api_instance.removeSharedLinkAssets(id, assetIdsDto, key);
print(result); print(result);
} catch (e) { } 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 Name | Type | Description | Notes
------------- | ------------- | ------------- | ------------- ------------- | ------------- | ------------- | -------------
**id** | **String**| | **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 ### Return type

View File

@ -1,4 +1,4 @@
# openapi.model.CreateAlbumShareLinkDto # openapi.model.SharedLinkCreateDto
## Load the model package ## Load the model package
```dart ```dart
@ -8,12 +8,14 @@ import 'package:openapi/api.dart';
## Properties ## Properties
Name | Type | Description | Notes Name | Type | Description | Notes
------------ | ------------- | ------------- | ------------- ------------ | ------------- | ------------- | -------------
**albumId** | **String** | | **type** | [**SharedLinkType**](SharedLinkType.md) | |
**expiresAt** | [**DateTime**](DateTime.md) | | [optional] **assetIds** | **List<String>** | | [optional] [default to const []]
**allowUpload** | **bool** | | [optional] **albumId** | **String** | | [optional]
**allowDownload** | **bool** | | [optional]
**showExif** | **bool** | | [optional]
**description** | **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) [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -1,4 +1,4 @@
# openapi.model.EditSharedLinkDto # openapi.model.SharedLinkEditDto
## Load the model package ## Load the model package
```dart ```dart

View File

@ -10,7 +10,7 @@ Name | Type | Description | Notes
------------ | ------------- | ------------- | ------------- ------------ | ------------- | ------------- | -------------
**type** | [**SharedLinkType**](SharedLinkType.md) | | **type** | [**SharedLinkType**](SharedLinkType.md) | |
**id** | **String** | | **id** | **String** | |
**description** | **String** | | [optional] **description** | **String** | |
**userId** | **String** | | **userId** | **String** | |
**key** | **String** | | **key** | **String** | |
**createdAt** | [**DateTime**](DateTime.md) | | **createdAt** | [**DateTime**](DateTime.md) | |

View File

@ -38,7 +38,7 @@ part 'api/partner_api.dart';
part 'api/person_api.dart'; part 'api/person_api.dart';
part 'api/search_api.dart'; part 'api/search_api.dart';
part 'api/server_info_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/system_config_api.dart';
part 'api/tag_api.dart'; part 'api/tag_api.dart';
part 'api/user_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_dto.dart';
part 'model/check_existing_assets_response_dto.dart'; part 'model/check_existing_assets_response_dto.dart';
part 'model/create_album_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_profile_image_response_dto.dart';
part 'model/create_tag_dto.dart'; part 'model/create_tag_dto.dart';
part 'model/create_user_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_response_dto.dart';
part 'model/delete_asset_status.dart'; part 'model/delete_asset_status.dart';
part 'model/download_files_dto.dart'; part 'model/download_files_dto.dart';
part 'model/edit_shared_link_dto.dart';
part 'model/exif_response_dto.dart'; part 'model/exif_response_dto.dart';
part 'model/get_asset_by_time_bucket_dto.dart'; part 'model/get_asset_by_time_bucket_dto.dart';
part 'model/get_asset_count_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_ping_response.dart';
part 'model/server_stats_response_dto.dart'; part 'model/server_stats_response_dto.dart';
part 'model/server_version_reponse_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_response_dto.dart';
part 'model/shared_link_type.dart'; part 'model/shared_link_type.dart';
part 'model/sign_up_dto.dart'; part 'model/sign_up_dto.dart';

View File

@ -175,53 +175,6 @@ class AlbumApi {
return null; 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]. /// Performs an HTTP 'DELETE /album/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///

View File

@ -16,61 +16,6 @@ class AssetApi {
final ApiClient apiClient; 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 /// Checks if assets exist by checksums
/// ///
/// Note: This method returns the HTTP [Response]. /// Note: This method returns the HTTP [Response].
@ -235,53 +180,6 @@ class AssetApi {
return null; 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]. /// Performs an HTTP 'DELETE /asset' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
@ -1225,61 +1123,6 @@ class AssetApi {
return null; 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]. /// Performs an HTTP 'POST /asset/search' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///

View File

@ -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;
}
}

View 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;
}
}

View File

@ -241,10 +241,6 @@ class ApiClient {
return CheckExistingAssetsResponseDto.fromJson(value); return CheckExistingAssetsResponseDto.fromJson(value);
case 'CreateAlbumDto': case 'CreateAlbumDto':
return CreateAlbumDto.fromJson(value); return CreateAlbumDto.fromJson(value);
case 'CreateAlbumShareLinkDto':
return CreateAlbumShareLinkDto.fromJson(value);
case 'CreateAssetsShareLinkDto':
return CreateAssetsShareLinkDto.fromJson(value);
case 'CreateProfileImageResponseDto': case 'CreateProfileImageResponseDto':
return CreateProfileImageResponseDto.fromJson(value); return CreateProfileImageResponseDto.fromJson(value);
case 'CreateTagDto': case 'CreateTagDto':
@ -263,8 +259,6 @@ class ApiClient {
return DeleteAssetStatusTypeTransformer().decode(value); return DeleteAssetStatusTypeTransformer().decode(value);
case 'DownloadFilesDto': case 'DownloadFilesDto':
return DownloadFilesDto.fromJson(value); return DownloadFilesDto.fromJson(value);
case 'EditSharedLinkDto':
return EditSharedLinkDto.fromJson(value);
case 'ExifResponseDto': case 'ExifResponseDto':
return ExifResponseDto.fromJson(value); return ExifResponseDto.fromJson(value);
case 'GetAssetByTimeBucketDto': case 'GetAssetByTimeBucketDto':
@ -333,6 +327,10 @@ class ApiClient {
return ServerStatsResponseDto.fromJson(value); return ServerStatsResponseDto.fromJson(value);
case 'ServerVersionReponseDto': case 'ServerVersionReponseDto':
return ServerVersionReponseDto.fromJson(value); return ServerVersionReponseDto.fromJson(value);
case 'SharedLinkCreateDto':
return SharedLinkCreateDto.fromJson(value);
case 'SharedLinkEditDto':
return SharedLinkEditDto.fromJson(value);
case 'SharedLinkResponseDto': case 'SharedLinkResponseDto':
return SharedLinkResponseDto.fromJson(value); return SharedLinkResponseDto.fromJson(value);
case 'SharedLinkType': case 'SharedLinkType':

View File

@ -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',
};
}

View File

@ -10,17 +10,21 @@
part of openapi.api; part of openapi.api;
class CreateAssetsShareLinkDto { class SharedLinkCreateDto {
/// Returns a new [CreateAssetsShareLinkDto] instance. /// Returns a new [SharedLinkCreateDto] instance.
CreateAssetsShareLinkDto({ SharedLinkCreateDto({
required this.type,
this.assetIds = const [], this.assetIds = const [],
this.expiresAt, this.albumId,
this.allowUpload,
this.allowDownload,
this.showExif,
this.description, this.description,
this.expiresAt,
this.allowUpload = false,
this.allowDownload = true,
this.showExif = true,
}); });
SharedLinkType type;
List<String> assetIds; List<String> assetIds;
/// ///
@ -29,31 +33,7 @@ class CreateAssetsShareLinkDto {
/// source code must fall back to having a nullable type. /// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note. /// Consider adding a "default:" property in the specification file to hide this note.
/// ///
DateTime? expiresAt; 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.
///
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 /// Please note: This property should have been non-nullable! Since the specification file
@ -63,63 +43,69 @@ class CreateAssetsShareLinkDto {
/// ///
String? description; String? description;
DateTime? expiresAt;
bool allowUpload;
bool allowDownload;
bool showExif;
@override @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.assetIds == assetIds &&
other.albumId == albumId &&
other.description == description &&
other.expiresAt == expiresAt && other.expiresAt == expiresAt &&
other.allowUpload == allowUpload && other.allowUpload == allowUpload &&
other.allowDownload == allowDownload && other.allowDownload == allowDownload &&
other.showExif == showExif && other.showExif == showExif;
other.description == description;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(type.hashCode) +
(assetIds.hashCode) + (assetIds.hashCode) +
(albumId == null ? 0 : albumId!.hashCode) +
(description == null ? 0 : description!.hashCode) +
(expiresAt == null ? 0 : expiresAt!.hashCode) + (expiresAt == null ? 0 : expiresAt!.hashCode) +
(allowUpload == null ? 0 : allowUpload!.hashCode) + (allowUpload.hashCode) +
(allowDownload == null ? 0 : allowDownload!.hashCode) + (allowDownload.hashCode) +
(showExif == null ? 0 : showExif!.hashCode) + (showExif.hashCode);
(description == null ? 0 : description!.hashCode);
@override @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() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
json[r'type'] = this.type;
json[r'assetIds'] = this.assetIds; json[r'assetIds'] = this.assetIds;
if (this.expiresAt != null) { if (this.albumId != null) {
json[r'expiresAt'] = this.expiresAt!.toUtc().toIso8601String(); json[r'albumId'] = this.albumId;
} else { } else {
// json[r'expiresAt'] = null; // json[r'albumId'] = 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) { if (this.description != null) {
json[r'description'] = this.description; json[r'description'] = this.description;
} else { } else {
// json[r'description'] = null; // 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; 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. /// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods // ignore: prefer_constructors_over_static_methods
static CreateAssetsShareLinkDto? fromJson(dynamic value) { static SharedLinkCreateDto? fromJson(dynamic value) {
if (value is Map) { if (value is Map) {
final json = value.cast<String, dynamic>(); final json = value.cast<String, dynamic>();
@ -128,31 +114,33 @@ class CreateAssetsShareLinkDto {
// Note 2: this code is stripped in release mode! // Note 2: this code is stripped in release mode!
assert(() { assert(() {
requiredKeys.forEach((key) { requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "CreateAssetsShareLinkDto[$key]" is missing from JSON.'); assert(json.containsKey(key), 'Required key "SharedLinkCreateDto[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "CreateAssetsShareLinkDto[$key]" has a null value in JSON.'); assert(json[key] != null, 'Required key "SharedLinkCreateDto[$key]" has a null value in JSON.');
}); });
return true; return true;
}()); }());
return CreateAssetsShareLinkDto( return SharedLinkCreateDto(
type: SharedLinkType.fromJson(json[r'type'])!,
assetIds: json[r'assetIds'] is Iterable assetIds: json[r'assetIds'] is Iterable
? (json[r'assetIds'] as Iterable).cast<String>().toList(growable: false) ? (json[r'assetIds'] as Iterable).cast<String>().toList(growable: false)
: const [], : const [],
expiresAt: mapDateTime(json, r'expiresAt', ''), albumId: mapValueOfType<String>(json, r'albumId'),
allowUpload: mapValueOfType<bool>(json, r'allowUpload'),
allowDownload: mapValueOfType<bool>(json, r'allowDownload'),
showExif: mapValueOfType<bool>(json, r'showExif'),
description: mapValueOfType<String>(json, r'description'), 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; return null;
} }
static List<CreateAssetsShareLinkDto> listFromJson(dynamic json, {bool growable = false,}) { static List<SharedLinkCreateDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <CreateAssetsShareLinkDto>[]; final result = <SharedLinkCreateDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
final value = CreateAssetsShareLinkDto.fromJson(row); final value = SharedLinkCreateDto.fromJson(row);
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@ -161,12 +149,12 @@ class CreateAssetsShareLinkDto {
return result.toList(growable: growable); return result.toList(growable: growable);
} }
static Map<String, CreateAssetsShareLinkDto> mapFromJson(dynamic json) { static Map<String, SharedLinkCreateDto> mapFromJson(dynamic json) {
final map = <String, CreateAssetsShareLinkDto>{}; final map = <String, SharedLinkCreateDto>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) { for (final entry in json.entries) {
final value = CreateAssetsShareLinkDto.fromJson(entry.value); final value = SharedLinkCreateDto.fromJson(entry.value);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@ -175,14 +163,14 @@ class CreateAssetsShareLinkDto {
return map; return map;
} }
// maps a json object with a list of CreateAssetsShareLinkDto-objects as value to a dart map // maps a json object with a list of SharedLinkCreateDto-objects as value to a dart map
static Map<String, List<CreateAssetsShareLinkDto>> mapListFromJson(dynamic json, {bool growable = false,}) { static Map<String, List<SharedLinkCreateDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<CreateAssetsShareLinkDto>>{}; final map = <String, List<SharedLinkCreateDto>>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments // ignore: parameter_assignments
json = json.cast<String, dynamic>(); json = json.cast<String, dynamic>();
for (final entry in json.entries) { 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; return map;
@ -190,7 +178,7 @@ class CreateAssetsShareLinkDto {
/// The list of required keys that must be present in a JSON. /// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{ static const requiredKeys = <String>{
'assetIds', 'type',
}; };
} }

View File

@ -10,9 +10,9 @@
part of openapi.api; part of openapi.api;
class EditSharedLinkDto { class SharedLinkEditDto {
/// Returns a new [EditSharedLinkDto] instance. /// Returns a new [SharedLinkEditDto] instance.
EditSharedLinkDto({ SharedLinkEditDto({
this.description, this.description,
this.expiresAt, this.expiresAt,
this.allowUpload, this.allowUpload,
@ -55,7 +55,7 @@ class EditSharedLinkDto {
bool? showExif; bool? showExif;
@override @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.description == description &&
other.expiresAt == expiresAt && other.expiresAt == expiresAt &&
other.allowUpload == allowUpload && other.allowUpload == allowUpload &&
@ -72,7 +72,7 @@ class EditSharedLinkDto {
(showExif == null ? 0 : showExif!.hashCode); (showExif == null ? 0 : showExif!.hashCode);
@override @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() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -104,10 +104,10 @@ class EditSharedLinkDto {
return json; 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. /// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods // ignore: prefer_constructors_over_static_methods
static EditSharedLinkDto? fromJson(dynamic value) { static SharedLinkEditDto? fromJson(dynamic value) {
if (value is Map) { if (value is Map) {
final json = value.cast<String, dynamic>(); final json = value.cast<String, dynamic>();
@ -116,13 +116,13 @@ class EditSharedLinkDto {
// Note 2: this code is stripped in release mode! // Note 2: this code is stripped in release mode!
assert(() { assert(() {
requiredKeys.forEach((key) { requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "EditSharedLinkDto[$key]" is missing from JSON.'); assert(json.containsKey(key), 'Required key "SharedLinkEditDto[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "EditSharedLinkDto[$key]" has a null value in JSON.'); assert(json[key] != null, 'Required key "SharedLinkEditDto[$key]" has a null value in JSON.');
}); });
return true; return true;
}()); }());
return EditSharedLinkDto( return SharedLinkEditDto(
description: mapValueOfType<String>(json, r'description'), description: mapValueOfType<String>(json, r'description'),
expiresAt: mapDateTime(json, r'expiresAt', ''), expiresAt: mapDateTime(json, r'expiresAt', ''),
allowUpload: mapValueOfType<bool>(json, r'allowUpload'), allowUpload: mapValueOfType<bool>(json, r'allowUpload'),
@ -133,11 +133,11 @@ class EditSharedLinkDto {
return null; return null;
} }
static List<EditSharedLinkDto> listFromJson(dynamic json, {bool growable = false,}) { static List<SharedLinkEditDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <EditSharedLinkDto>[]; final result = <SharedLinkEditDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
final value = EditSharedLinkDto.fromJson(row); final value = SharedLinkEditDto.fromJson(row);
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@ -146,12 +146,12 @@ class EditSharedLinkDto {
return result.toList(growable: growable); return result.toList(growable: growable);
} }
static Map<String, EditSharedLinkDto> mapFromJson(dynamic json) { static Map<String, SharedLinkEditDto> mapFromJson(dynamic json) {
final map = <String, EditSharedLinkDto>{}; final map = <String, SharedLinkEditDto>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) { for (final entry in json.entries) {
final value = EditSharedLinkDto.fromJson(entry.value); final value = SharedLinkEditDto.fromJson(entry.value);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@ -160,14 +160,14 @@ class EditSharedLinkDto {
return map; return map;
} }
// maps a json object with a list of EditSharedLinkDto-objects as value to a dart map // maps a json object with a list of SharedLinkEditDto-objects as value to a dart map
static Map<String, List<EditSharedLinkDto>> mapListFromJson(dynamic json, {bool growable = false,}) { static Map<String, List<SharedLinkEditDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<EditSharedLinkDto>>{}; final map = <String, List<SharedLinkEditDto>>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments // ignore: parameter_assignments
json = json.cast<String, dynamic>(); json = json.cast<String, dynamic>();
for (final entry in json.entries) { 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; return map;

View File

@ -15,7 +15,7 @@ class SharedLinkResponseDto {
SharedLinkResponseDto({ SharedLinkResponseDto({
required this.type, required this.type,
required this.id, required this.id,
this.description, required this.description,
required this.userId, required this.userId,
required this.key, required this.key,
required this.createdAt, required this.createdAt,
@ -31,12 +31,6 @@ class SharedLinkResponseDto {
String id; 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? description;
String userId; String userId;
@ -206,6 +200,7 @@ class SharedLinkResponseDto {
static const requiredKeys = <String>{ static const requiredKeys = <String>{
'type', 'type',
'id', 'id',
'description',
'userId', 'userId',
'key', 'key',
'createdAt', 'createdAt',

View File

@ -32,11 +32,6 @@ void main() {
// TODO // TODO
}); });
//Future<SharedLinkResponseDto> createAlbumSharedLink(CreateAlbumShareLinkDto createAlbumShareLinkDto) async
test('test createAlbumSharedLink', () async {
// TODO
});
//Future deleteAlbum(String id) async //Future deleteAlbum(String id) async
test('test deleteAlbum', () async { test('test deleteAlbum', () async {
// TODO // TODO

View File

@ -17,11 +17,6 @@ void main() {
// final instance = AssetApi(); // final instance = AssetApi();
group('tests for AssetApi', () { group('tests for AssetApi', () {
//Future<SharedLinkResponseDto> addAssetsToSharedLink(AddAssetsDto addAssetsDto, { String key }) async
test('test addAssetsToSharedLink', () async {
// TODO
});
// Checks if assets exist by checksums // Checks if assets exist by checksums
// //
//Future<AssetBulkUploadCheckResponseDto> bulkUploadCheck(AssetBulkUploadCheckDto assetBulkUploadCheckDto) async //Future<AssetBulkUploadCheckResponseDto> bulkUploadCheck(AssetBulkUploadCheckDto assetBulkUploadCheckDto) async
@ -43,11 +38,6 @@ void main() {
// TODO // TODO
}); });
//Future<SharedLinkResponseDto> createAssetsSharedLink(CreateAssetsShareLinkDto createAssetsShareLinkDto) async
test('test createAssetsSharedLink', () async {
// TODO
});
//Future<List<DeleteAssetResponseDto>> deleteAsset(DeleteAssetDto deleteAssetDto) async //Future<List<DeleteAssetResponseDto>> deleteAsset(DeleteAssetDto deleteAssetDto) async
test('test deleteAsset', () async { test('test deleteAsset', () async {
// TODO // TODO
@ -141,11 +131,6 @@ void main() {
// TODO // TODO
}); });
//Future<SharedLinkResponseDto> removeAssetsFromSharedLink(RemoveAssetsDto removeAssetsDto, { String key }) async
test('test removeAssetsFromSharedLink', () async {
// TODO
});
//Future<List<AssetResponseDto>> searchAsset(SearchAssetDto searchAssetDto) async //Future<List<AssetResponseDto>> searchAsset(SearchAssetDto searchAssetDto) async
test('test searchAsset', () async { test('test searchAsset', () async {
// TODO // TODO

View File

@ -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
});
});
}

View File

@ -12,11 +12,21 @@ import 'package:openapi/api.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
/// tests for ShareApi /// tests for SharedLinkApi
void main() { 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 //Future<List<SharedLinkResponseDto>> getAllSharedLinks() async
test('test getAllSharedLinks', () async { test('test getAllSharedLinks', () async {
// TODO // TODO
@ -37,7 +47,12 @@ void main() {
// TODO // 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 { test('test updateSharedLink', () async {
// TODO // TODO
}); });

View File

@ -11,33 +11,23 @@
import 'package:openapi/api.dart'; import 'package:openapi/api.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
// tests for CreateAssetsShareLinkDto // tests for SharedLinkCreateDto
void main() { 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 []) // List<String> assetIds (default value: const [])
test('to test the property `assetIds`', () async { test('to test the property `assetIds`', () async {
// TODO // TODO
}); });
// DateTime expiresAt // String albumId
test('to test the property `expiresAt`', () async { test('to test the property `albumId`', () 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 // TODO
}); });
@ -46,6 +36,26 @@ void main() {
// TODO // 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
});
}); });

View File

@ -11,11 +11,11 @@
import 'package:openapi/api.dart'; import 'package:openapi/api.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
// tests for EditSharedLinkDto // tests for SharedLinkEditDto
void main() { void main() {
// final instance = EditSharedLinkDto(); // final instance = SharedLinkEditDto();
group('test EditSharedLinkDto', () { group('test SharedLinkEditDto', () {
// String description // String description
test('to test the property `description`', () async { test('to test the property `description`', () async {
// TODO // TODO

View File

@ -1,7 +1,14 @@
import { AlbumResponseDto, AuthService, CreateAlbumDto, SharedLinkResponseDto, UserService } from '@app/domain'; import {
import { CreateAlbumShareLinkDto } from '@app/immich/api-v1/album/dto/create-album-shared-link.dto'; AlbumResponseDto,
AuthService,
CreateAlbumDto,
SharedLinkCreateDto,
SharedLinkResponseDto,
UserService,
} from '@app/domain';
import { AppModule } from '@app/immich/app.module'; import { AppModule } from '@app/immich/app.module';
import { AuthUserDto } from '@app/immich/decorators/auth-user.decorator'; import { AuthUserDto } from '@app/immich/decorators/auth-user.decorator';
import { SharedLinkType } from '@app/infra/entities';
import { INestApplication } from '@nestjs/common'; import { INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import request from 'supertest'; import request from 'supertest';
@ -14,8 +21,10 @@ async function _createAlbum(app: INestApplication, data: CreateAlbumDto) {
return res.body as AlbumResponseDto; return res.body as AlbumResponseDto;
} }
async function _createAlbumSharedLink(app: INestApplication, data: CreateAlbumShareLinkDto) { async function _createAlbumSharedLink(app: INestApplication, data: Omit<SharedLinkCreateDto, 'type'>) {
const res = await request(app.getHttpServer()).post('/album/create-shared-link').send(data); const res = await request(app.getHttpServer())
.post('/shared-link')
.send({ ...data, type: SharedLinkType.ALBUM });
expect(res.status).toEqual(201); expect(res.status).toEqual(201);
return res.body as SharedLinkResponseDto; return res.body as SharedLinkResponseDto;
} }

View File

@ -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}": { "/album/{id}": {
"patch": { "patch": {
"operationId": "updateAlbumInfo", "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": { "/asset/stat/archive": {
"get": { "get": {
"operationId": "getArchivedAssetCountByUserId", "operationId": "getArchivedAssetCountByUserId",
@ -3264,7 +3078,7 @@
] ]
} }
}, },
"/share": { "/shared-link": {
"get": { "get": {
"operationId": "getAllSharedLinks", "operationId": "getAllSharedLinks",
"parameters": [], "parameters": [],
@ -3284,7 +3098,47 @@
} }
}, },
"tags": [ "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": [ "security": [
{ {
@ -3299,7 +3153,7 @@
] ]
} }
}, },
"/share/me": { "/shared-link/me": {
"get": { "get": {
"operationId": "getMySharedLink", "operationId": "getMySharedLink",
"parameters": [ "parameters": [
@ -3325,7 +3179,7 @@
} }
}, },
"tags": [ "tags": [
"share" "Shared Link"
], ],
"security": [ "security": [
{ {
@ -3340,7 +3194,7 @@
] ]
} }
}, },
"/share/{id}": { "/shared-link/{id}": {
"get": { "get": {
"operationId": "getSharedLinkById", "operationId": "getSharedLinkById",
"parameters": [ "parameters": [
@ -3367,7 +3221,7 @@
} }
}, },
"tags": [ "tags": [
"share" "Shared Link"
], ],
"security": [ "security": [
{ {
@ -3399,7 +3253,7 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/EditSharedLinkDto" "$ref": "#/components/schemas/SharedLinkEditDto"
} }
} }
} }
@ -3417,7 +3271,7 @@
} }
}, },
"tags": [ "tags": [
"share" "Shared Link"
], ],
"security": [ "security": [
{ {
@ -3450,7 +3304,131 @@
} }
}, },
"tags": [ "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": [ "security": [
{ {
@ -5089,34 +5067,6 @@
"albumName" "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": { "CreateAssetDto": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -5176,42 +5126,6 @@
"fileExtension" "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": { "CreateProfileImageDto": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -5392,28 +5306,6 @@
"assetIds" "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": { "ExifResponseDto": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -6160,6 +6052,71 @@
"patch" "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": { "SharedLinkResponseDto": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -6170,7 +6127,8 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"type": "string" "type": "string",
"nullable": true
}, },
"userId": { "userId": {
"type": "string" "type": "string"
@ -6209,6 +6167,7 @@
"required": [ "required": [
"type", "type",
"id", "id",
"description",
"userId", "userId",
"key", "key",
"createdAt", "createdAt",

View File

@ -2,8 +2,11 @@ export const IAccessRepository = 'IAccessRepository';
export interface IAccessRepository { export interface IAccessRepository {
hasPartnerAccess(userId: string, partnerId: string): Promise<boolean>; hasPartnerAccess(userId: string, partnerId: string): Promise<boolean>;
hasAlbumAssetAccess(userId: string, assetId: string): Promise<boolean>; hasAlbumAssetAccess(userId: string, assetId: string): Promise<boolean>;
hasOwnerAssetAccess(userId: string, assetId: string): Promise<boolean>; hasOwnerAssetAccess(userId: string, assetId: string): Promise<boolean>;
hasPartnerAssetAccess(userId: string, assetId: string): Promise<boolean>; hasPartnerAssetAccess(userId: string, assetId: string): Promise<boolean>;
hasSharedLinkAssetAccess(userId: string, assetId: string): Promise<boolean>; hasSharedLinkAssetAccess(userId: string, assetId: string): Promise<boolean>;
hasAlbumOwnerAccess(userId: string, albumId: string): Promise<boolean>;
} }

View File

@ -13,7 +13,7 @@ import { IKeyRepository } from '../api-key';
import { APIKeyCore } from '../api-key/api-key.core'; import { APIKeyCore } from '../api-key/api-key.core';
import { ICryptoRepository } from '../crypto/crypto.repository'; import { ICryptoRepository } from '../crypto/crypto.repository';
import { OAuthCore } from '../oauth/oauth.core'; 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 { INITIAL_SYSTEM_CONFIG, ISystemConfigRepository } from '../system-config';
import { IUserRepository, UserCore } from '../user'; import { IUserRepository, UserCore } from '../user';
import { IUserTokenRepository, UserTokenCore } from '../user-token'; import { IUserTokenRepository, UserTokenCore } from '../user-token';
@ -35,7 +35,6 @@ export class AuthService {
private authCore: AuthCore; private authCore: AuthCore;
private oauthCore: OAuthCore; private oauthCore: OAuthCore;
private userCore: UserCore; private userCore: UserCore;
private shareCore: SharedLinkCore;
private keyCore: APIKeyCore; private keyCore: APIKeyCore;
private logger = new Logger(AuthService.name); private logger = new Logger(AuthService.name);
@ -45,7 +44,7 @@ export class AuthService {
@Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
@Inject(IUserRepository) userRepository: IUserRepository, @Inject(IUserRepository) userRepository: IUserRepository,
@Inject(IUserTokenRepository) userTokenRepository: IUserTokenRepository, @Inject(IUserTokenRepository) userTokenRepository: IUserTokenRepository,
@Inject(ISharedLinkRepository) shareRepository: ISharedLinkRepository, @Inject(ISharedLinkRepository) private sharedLinkRepository: ISharedLinkRepository,
@Inject(IKeyRepository) keyRepository: IKeyRepository, @Inject(IKeyRepository) keyRepository: IKeyRepository,
@Inject(INITIAL_SYSTEM_CONFIG) @Inject(INITIAL_SYSTEM_CONFIG)
initialConfig: SystemConfig, initialConfig: SystemConfig,
@ -54,7 +53,6 @@ export class AuthService {
this.authCore = new AuthCore(cryptoRepository, configRepository, userTokenRepository, initialConfig); this.authCore = new AuthCore(cryptoRepository, configRepository, userTokenRepository, initialConfig);
this.oauthCore = new OAuthCore(configRepository, initialConfig); this.oauthCore = new OAuthCore(configRepository, initialConfig);
this.userCore = new UserCore(userRepository, cryptoRepository); this.userCore = new UserCore(userRepository, cryptoRepository);
this.shareCore = new SharedLinkCore(shareRepository, cryptoRepository);
this.keyCore = new APIKeyCore(cryptoRepository, keyRepository); this.keyCore = new APIKeyCore(cryptoRepository, keyRepository);
} }
@ -147,7 +145,7 @@ export class AuthService {
const apiKey = (headers[IMMICH_API_KEY_HEADER] || params.apiKey) as string; const apiKey = (headers[IMMICH_API_KEY_HEADER] || params.apiKey) as string;
if (shareKey) { if (shareKey) {
return this.shareCore.validate(shareKey); return this.validateSharedLink(shareKey);
} }
if (userToken) { if (userToken) {
@ -193,4 +191,29 @@ export class AuthService {
const cookies = cookieParser.parse(headers.cookie || ''); const cookies = cookieParser.parse(headers.cookie || '');
return cookies[IMMICH_ACCESS_COOKIE] || null; 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');
}
} }

View File

@ -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;
}

View File

@ -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;
}

View File

@ -1,2 +0,0 @@
export * from './create-shared-link.dto';
export * from './edit-shared-link.dto';

View File

@ -1,5 +1,4 @@
export * from './dto'; export * from './shared-link-response.dto';
export * from './response-dto'; export * from './shared-link.dto';
export * from './shared-link.core';
export * from './shared-link.repository'; export * from './shared-link.repository';
export * from './shared-link.service'; export * from './shared-link.service';

View File

@ -1 +0,0 @@
export * from './shared-link-response.dto';

View File

@ -1,12 +1,12 @@
import { SharedLinkEntity, SharedLinkType } from '@app/infra/entities'; import { SharedLinkEntity, SharedLinkType } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import _ from 'lodash'; import _ from 'lodash';
import { AlbumResponseDto, mapAlbumExcludeAssetInfo } from '../../album'; import { AlbumResponseDto, mapAlbumExcludeAssetInfo } from '../album';
import { AssetResponseDto, mapAsset, mapAssetWithoutExif } from '../../asset'; import { AssetResponseDto, mapAsset, mapAssetWithoutExif } from '../asset';
export class SharedLinkResponseDto { export class SharedLinkResponseDto {
id!: string; id!: string;
description?: string; description!: string | null;
userId!: string; userId!: string;
key!: string; key!: string;

View File

@ -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');
}
}

View 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;
}

View File

@ -6,7 +6,7 @@ export interface ISharedLinkRepository {
getAll(userId: string): Promise<SharedLinkEntity[]>; getAll(userId: string): Promise<SharedLinkEntity[]>;
get(userId: string, id: string): Promise<SharedLinkEntity | null>; get(userId: string, id: string): Promise<SharedLinkEntity | null>;
getByKey(key: Buffer): 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>; update(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity>;
remove(entity: SharedLinkEntity): Promise<void>; remove(entity: SharedLinkEntity): Promise<void>;
} }

View File

@ -1,16 +1,33 @@
import { BadRequestException, ForbiddenException } from '@nestjs/common'; 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 { ISharedLinkRepository } from './shared-link.repository';
import { SharedLinkService } from './shared-link.service'; import { SharedLinkService } from './shared-link.service';
describe(SharedLinkService.name, () => { describe(SharedLinkService.name, () => {
let sut: SharedLinkService; let sut: SharedLinkService;
let accessMock: jest.Mocked<IAccessRepository>;
let cryptoMock: jest.Mocked<ICryptoRepository>;
let shareMock: jest.Mocked<ISharedLinkRepository>; let shareMock: jest.Mocked<ISharedLinkRepository>;
beforeEach(async () => { beforeEach(async () => {
accessMock = newAccessRepositoryMock();
cryptoMock = newCryptoRepositoryMock();
shareMock = newSharedLinkRepositoryMock(); shareMock = newSharedLinkRepositoryMock();
sut = new SharedLinkService(shareMock); sut = new SharedLinkService(accessMock, cryptoMock, shareMock);
}); });
it('should work', () => { 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', () => { describe('update', () => {
it('should throw an error for an invalid shared link', async () => { it('should throw an error for an invalid shared link', async () => {
shareMock.get.mockResolvedValue(null); shareMock.get.mockResolvedValue(null);
@ -100,4 +193,58 @@ describe(SharedLinkService.name, () => {
expect(shareMock.remove).toHaveBeenCalledWith(sharedLinkStub.valid); 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: [] });
});
});
}); });

View File

@ -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 { BadRequestException, ForbiddenException, Inject, Injectable } from '@nestjs/common';
import { IAccessRepository } from '../access';
import { AssetIdErrorReason, AssetIdsDto, AssetIdsResponseDto } from '../asset';
import { AuthUserDto } from '../auth'; import { AuthUserDto } from '../auth';
import { EditSharedLinkDto } from './dto'; import { ICryptoRepository } from '../crypto';
import { mapSharedLink, mapSharedLinkWithNoExif, SharedLinkResponseDto } from './response-dto'; import { mapSharedLink, mapSharedLinkWithNoExif, SharedLinkResponseDto } from './shared-link-response.dto';
import { SharedLinkCreateDto, SharedLinkEditDto } from './shared-link.dto';
import { ISharedLinkRepository } from './shared-link.repository'; import { ISharedLinkRepository } from './shared-link.repository';
@Injectable() @Injectable()
export class SharedLinkService { 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)); return this.repository.getAll(authUser.id).then((links) => links.map(mapSharedLink));
} }
@ -30,7 +37,52 @@ export class SharedLinkService {
return this.map(sharedLink, { withExif: true }); 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); await this.findOrFail(authUser, id);
const sharedLink = await this.repository.update({ const sharedLink = await this.repository.update({
id, id,
@ -57,6 +109,60 @@ export class SharedLinkService {
return sharedLink; 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 }) { private map(sharedLink: SharedLinkEntity, { withExif }: { withExif: boolean }) {
return withExif ? mapSharedLink(sharedLink) : mapSharedLinkWithNoExif(sharedLink); return withExif ? mapSharedLink(sharedLink) : mapSharedLinkWithNoExif(sharedLink);
} }

View File

@ -1,5 +1,5 @@
import { AlbumResponseDto } from '@app/domain'; 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 { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { Response as Res } from 'express'; import { Response as Res } from 'express';
import { handleDownload } from '../../app.utils'; 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 { DownloadDto } from '../asset/dto/download-library.dto';
import { AlbumService } from './album.service'; import { AlbumService } from './album.service';
import { AddAssetsDto } from './dto/add-assets.dto'; 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 { RemoveAssetsDto } from './dto/remove-assets.dto';
import { AddAssetsResponseDto } from './response-dto/add-assets-response.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)); 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);
}
} }

View File

@ -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 { AlbumEntity, UserEntity } from '@app/infra/entities';
import { ForbiddenException, NotFoundException } from '@nestjs/common'; import { ForbiddenException, NotFoundException } from '@nestjs/common';
import { newCryptoRepositoryMock, newSharedLinkRepositoryMock, userEntityStub } from '@test'; import { userEntityStub } from '@test';
import { AuthUserDto } from '../../decorators/auth-user.decorator'; import { AuthUserDto } from '../../decorators/auth-user.decorator';
import { DownloadService } from '../../modules/download/download.service'; import { DownloadService } from '../../modules/download/download.service';
import { IAlbumRepository } from './album-repository'; import { IAlbumRepository } from './album-repository';
@ -11,9 +11,7 @@ import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
describe('Album service', () => { describe('Album service', () => {
let sut: AlbumService; let sut: AlbumService;
let albumRepositoryMock: jest.Mocked<IAlbumRepository>; let albumRepositoryMock: jest.Mocked<IAlbumRepository>;
let sharedLinkRepositoryMock: jest.Mocked<ISharedLinkRepository>;
let downloadServiceMock: jest.Mocked<Partial<DownloadService>>; let downloadServiceMock: jest.Mocked<Partial<DownloadService>>;
let cryptoMock: jest.Mocked<ICryptoRepository>;
const authUser: AuthUserDto = Object.freeze({ const authUser: AuthUserDto = Object.freeze({
id: '1111', id: '1111',
@ -99,20 +97,11 @@ describe('Album service', () => {
updateThumbnails: jest.fn(), updateThumbnails: jest.fn(),
}; };
sharedLinkRepositoryMock = newSharedLinkRepositoryMock();
downloadServiceMock = { downloadServiceMock = {
downloadArchive: jest.fn(), downloadArchive: jest.fn(),
}; };
cryptoMock = newCryptoRepositoryMock(); sut = new AlbumService(albumRepositoryMock, downloadServiceMock as DownloadService);
sut = new AlbumService(
albumRepositoryMock,
sharedLinkRepositoryMock,
downloadServiceMock as DownloadService,
cryptoMock,
);
}); });
it('gets an owned album', async () => { it('gets an owned album', async () => {

View File

@ -1,36 +1,22 @@
import { import { AlbumResponseDto, mapAlbum } from '@app/domain';
AlbumResponseDto, import { AlbumEntity } from '@app/infra/entities';
ICryptoRepository,
ISharedLinkRepository,
mapAlbum,
mapSharedLink,
SharedLinkCore,
SharedLinkResponseDto,
} from '@app/domain';
import { AlbumEntity, SharedLinkType } from '@app/infra/entities';
import { BadRequestException, ForbiddenException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common'; import { BadRequestException, ForbiddenException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common';
import { AuthUserDto } from '../../decorators/auth-user.decorator'; import { AuthUserDto } from '../../decorators/auth-user.decorator';
import { DownloadService } from '../../modules/download/download.service'; import { DownloadService } from '../../modules/download/download.service';
import { DownloadDto } from '../asset/dto/download-library.dto'; import { DownloadDto } from '../asset/dto/download-library.dto';
import { IAlbumRepository } from './album-repository'; import { IAlbumRepository } from './album-repository';
import { AddAssetsDto } from './dto/add-assets.dto'; import { AddAssetsDto } from './dto/add-assets.dto';
import { CreateAlbumShareLinkDto } from './dto/create-album-shared-link.dto';
import { RemoveAssetsDto } from './dto/remove-assets.dto'; import { RemoveAssetsDto } from './dto/remove-assets.dto';
import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto'; import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
@Injectable() @Injectable()
export class AlbumService { export class AlbumService {
readonly logger = new Logger(AlbumService.name); private logger = new Logger(AlbumService.name);
private shareCore: SharedLinkCore;
constructor( constructor(
@Inject(IAlbumRepository) private albumRepository: IAlbumRepository, @Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
@Inject(ISharedLinkRepository) sharedLinkRepository: ISharedLinkRepository,
private downloadService: DownloadService, private downloadService: DownloadService,
@Inject(ICryptoRepository) cryptoRepository: ICryptoRepository, ) {}
) {
this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository);
}
private async _getAlbum({ private async _getAlbum({
authUser, authUser,
@ -91,7 +77,7 @@ export class AlbumService {
} }
async downloadArchive(authUser: AuthUserDto, albumId: string, dto: DownloadDto) { 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 album = await this._getAlbum({ authUser, albumId, validateIsOwner: false });
const assets = (album.assets || []).map((asset) => asset).slice(dto.skip || 0); 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); return this.downloadService.downloadArchive(album.albumName, assets);
} }
async createSharedLink(authUser: AuthUserDto, dto: CreateAlbumShareLinkDto): Promise<SharedLinkResponseDto> { private checkDownloadAccess(authUser: AuthUserDto) {
const album = await this._getAlbum({ authUser, albumId: dto.albumId }); if (authUser.isPublicUser && !authUser.isAllowDownload) {
throw new ForbiddenException();
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);
} }
} }

View File

@ -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;
}

View File

@ -1,4 +1,4 @@
import { AssetResponseDto, ImmichReadStream, SharedLinkResponseDto } from '@app/domain'; import { AssetResponseDto, ImmichReadStream } from '@app/domain';
import { import {
Body, Body,
Controller, Controller,
@ -10,7 +10,6 @@ import {
HttpStatus, HttpStatus,
Param, Param,
ParseFilePipe, ParseFilePipe,
Patch,
Post, Post,
Put, Put,
Query, Query,
@ -28,15 +27,12 @@ import { assetUploadOption, ImmichFile } from '../../config/asset-upload.config'
import { UUIDParamDto } from '../../controllers/dto/uuid-param.dto'; import { UUIDParamDto } from '../../controllers/dto/uuid-param.dto';
import { AuthUser, AuthUserDto } from '../../decorators/auth-user.decorator'; import { AuthUser, AuthUserDto } from '../../decorators/auth-user.decorator';
import { Authenticated, SharedLinkRoute } from '../../decorators/authenticated.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 FileNotEmptyValidator from '../validation/file-not-empty-validator';
import { AssetService } from './asset.service'; import { AssetService } from './asset.service';
import { AssetBulkUploadCheckDto } from './dto/asset-check.dto'; import { AssetBulkUploadCheckDto } from './dto/asset-check.dto';
import { AssetSearchDto } from './dto/asset-search.dto'; import { AssetSearchDto } from './dto/asset-search.dto';
import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto'; import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
import { CheckExistingAssetsDto } from './dto/check-existing-assets.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 { CreateAssetDto, mapToUploadFile } from './dto/create-asset.dto';
import { DeleteAssetDto } from './dto/delete-asset.dto'; import { DeleteAssetDto } from './dto/delete-asset.dto';
import { DeviceIdDto } from './dto/device-id.dto'; import { DeviceIdDto } from './dto/device-id.dto';
@ -319,30 +315,4 @@ export class AssetController {
): Promise<AssetBulkUploadCheckResponseDto> { ): Promise<AssetBulkUploadCheckResponseDto> {
return this.assetService.bulkUploadCheck(authUser, dto); 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);
}
} }

View File

@ -1,31 +1,19 @@
import { import { IAccessRepository, IJobRepository, IStorageRepository, JobName } from '@app/domain';
IAccessRepository,
ICryptoRepository,
IJobRepository,
ISharedLinkRepository,
IStorageRepository,
JobName,
} from '@app/domain';
import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities'; import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities';
import { BadRequestException, ForbiddenException } from '@nestjs/common'; import { ForbiddenException } from '@nestjs/common';
import { import {
assetEntityStub, assetEntityStub,
authStub, authStub,
fileStub, fileStub,
newAccessRepositoryMock, newAccessRepositoryMock,
newCryptoRepositoryMock,
newJobRepositoryMock, newJobRepositoryMock,
newSharedLinkRepositoryMock,
newStorageRepositoryMock, newStorageRepositoryMock,
sharedLinkResponseStub,
sharedLinkStub,
} from '@test'; } from '@test';
import { when } from 'jest-when'; import { when } from 'jest-when';
import { QueryFailedError, Repository } from 'typeorm'; import { QueryFailedError, Repository } from 'typeorm';
import { DownloadService } from '../../modules/download/download.service'; import { DownloadService } from '../../modules/download/download.service';
import { IAssetRepository } from './asset-repository'; import { IAssetRepository } from './asset-repository';
import { AssetService } from './asset.service'; import { AssetService } from './asset.service';
import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto';
import { CreateAssetDto } from './dto/create-asset.dto'; import { CreateAssetDto } from './dto/create-asset.dto';
import { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto'; import { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto';
import { AssetRejectReason, AssetUploadAction } from './response-dto/asset-check-response.dto'; import { AssetRejectReason, AssetUploadAction } from './response-dto/asset-check-response.dto';
@ -134,8 +122,6 @@ describe('AssetService', () => {
let accessMock: jest.Mocked<IAccessRepository>; let accessMock: jest.Mocked<IAccessRepository>;
let assetRepositoryMock: jest.Mocked<IAssetRepository>; let assetRepositoryMock: jest.Mocked<IAssetRepository>;
let downloadServiceMock: jest.Mocked<Partial<DownloadService>>; let downloadServiceMock: jest.Mocked<Partial<DownloadService>>;
let sharedLinkRepositoryMock: jest.Mocked<ISharedLinkRepository>;
let cryptoMock: jest.Mocked<ICryptoRepository>;
let jobMock: jest.Mocked<IJobRepository>; let jobMock: jest.Mocked<IJobRepository>;
let storageMock: jest.Mocked<IStorageRepository>; let storageMock: jest.Mocked<IStorageRepository>;
@ -165,9 +151,7 @@ describe('AssetService', () => {
}; };
accessMock = newAccessRepositoryMock(); accessMock = newAccessRepositoryMock();
sharedLinkRepositoryMock = newSharedLinkRepositoryMock();
jobMock = newJobRepositoryMock(); jobMock = newJobRepositoryMock();
cryptoMock = newCryptoRepositoryMock();
storageMock = newStorageRepositoryMock(); storageMock = newStorageRepositoryMock();
sut = new AssetService( sut = new AssetService(
@ -175,9 +159,7 @@ describe('AssetService', () => {
assetRepositoryMock, assetRepositoryMock,
a, a,
downloadServiceMock as DownloadService, downloadServiceMock as DownloadService,
sharedLinkRepositoryMock,
jobMock, jobMock,
cryptoMock,
storageMock, storageMock,
); );
@ -189,77 +171,6 @@ describe('AssetService', () => {
.mockResolvedValue(assetEntityStub.livePhotoMotionAsset); .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', () => { describe('uploadFile', () => {
it('should handle a file upload', async () => { it('should handle a file upload', async () => {
const assetEntity = _getAsset_1(); const assetEntity = _getAsset_1();

View File

@ -2,19 +2,14 @@ import {
AssetResponseDto, AssetResponseDto,
getLivePhotoMotionFilename, getLivePhotoMotionFilename,
IAccessRepository, IAccessRepository,
ICryptoRepository,
IJobRepository, IJobRepository,
ImmichReadStream, ImmichReadStream,
ISharedLinkRepository,
IStorageRepository, IStorageRepository,
JobName, JobName,
mapAsset, mapAsset,
mapAssetWithoutExif, mapAssetWithoutExif,
mapSharedLink,
SharedLinkCore,
SharedLinkResponseDto,
} from '@app/domain'; } from '@app/domain';
import { AssetEntity, AssetType, SharedLinkType } from '@app/infra/entities'; import { AssetEntity, AssetType } from '@app/infra/entities';
import { import {
BadRequestException, BadRequestException,
ForbiddenException, ForbiddenException,
@ -33,15 +28,12 @@ import { QueryFailedError, Repository } from 'typeorm';
import { promisify } from 'util'; import { promisify } from 'util';
import { AuthUserDto } from '../../decorators/auth-user.decorator'; import { AuthUserDto } from '../../decorators/auth-user.decorator';
import { DownloadService } from '../../modules/download/download.service'; 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 { IAssetRepository } from './asset-repository';
import { AssetCore } from './asset.core'; import { AssetCore } from './asset.core';
import { AssetBulkUploadCheckDto } from './dto/asset-check.dto'; import { AssetBulkUploadCheckDto } from './dto/asset-check.dto';
import { AssetSearchDto } from './dto/asset-search.dto'; import { AssetSearchDto } from './dto/asset-search.dto';
import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto'; import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
import { CheckExistingAssetsDto } from './dto/check-existing-assets.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 { CreateAssetDto, UploadFile } from './dto/create-asset.dto';
import { DeleteAssetDto } from './dto/delete-asset.dto'; import { DeleteAssetDto } from './dto/delete-asset.dto';
import { DownloadFilesDto } from './dto/download-files.dto'; import { DownloadFilesDto } from './dto/download-files.dto';
@ -80,22 +72,17 @@ interface ServableFile {
@Injectable() @Injectable()
export class AssetService { export class AssetService {
readonly logger = new Logger(AssetService.name); readonly logger = new Logger(AssetService.name);
private shareCore: SharedLinkCore;
private assetCore: AssetCore; private assetCore: AssetCore;
constructor( constructor(
@Inject(IAccessRepository) private accessRepository: IAccessRepository, @Inject(IAccessRepository) private accessRepository: IAccessRepository,
@Inject(IAssetRepository) private _assetRepository: IAssetRepository, @Inject(IAssetRepository) private _assetRepository: IAssetRepository,
@InjectRepository(AssetEntity) @InjectRepository(AssetEntity) private assetRepository: Repository<AssetEntity>,
private assetRepository: Repository<AssetEntity>,
private downloadService: DownloadService, private downloadService: DownloadService,
@Inject(ISharedLinkRepository) sharedLinkRepository: ISharedLinkRepository,
@Inject(IJobRepository) private jobRepository: IJobRepository, @Inject(IJobRepository) private jobRepository: IJobRepository,
@Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
@Inject(IStorageRepository) private storageRepository: IStorageRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository,
) { ) {
this.assetCore = new AssetCore(_assetRepository, jobRepository); this.assetCore = new AssetCore(_assetRepository, jobRepository);
this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository);
} }
public async uploadFile( public async uploadFile(
@ -608,61 +595,9 @@ export class AssetService {
} }
private checkDownloadAccess(authUser: AuthUserDto) { private checkDownloadAccess(authUser: AuthUserDto) {
this.shareCore.checkDownloadAccess(authUser); if (authUser.isPublicUser && !authUser.isAllowDownload) {
}
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) {
throw new ForbiddenException(); 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) { getExifPermission(authUser: AuthUserDto) {

View File

@ -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;
}

View File

@ -1,13 +1,21 @@
import { AuthUserDto, EditSharedLinkDto, SharedLinkResponseDto, SharedLinkService } from '@app/domain'; import {
import { Body, Controller, Delete, Get, Param, Patch } from '@nestjs/common'; 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 { ApiTags } from '@nestjs/swagger';
import { AuthUser } from '../decorators/auth-user.decorator'; import { AuthUser } from '../decorators/auth-user.decorator';
import { Authenticated, SharedLinkRoute } from '../decorators/authenticated.decorator'; import { Authenticated, SharedLinkRoute } from '../decorators/authenticated.decorator';
import { UseValidation } from '../decorators/use-validation.decorator'; import { UseValidation } from '../decorators/use-validation.decorator';
import { UUIDParamDto } from './dto/uuid-param.dto'; import { UUIDParamDto } from './dto/uuid-param.dto';
@ApiTags('share') @ApiTags('Shared Link')
@Controller('share') @Controller('shared-link')
@Authenticated() @Authenticated()
@UseValidation() @UseValidation()
export class SharedLinkController { export class SharedLinkController {
@ -29,11 +37,16 @@ export class SharedLinkController {
return this.service.get(authUser, id); return this.service.get(authUser, id);
} }
@Post()
createSharedLink(@AuthUser() authUser: AuthUserDto, @Body() dto: SharedLinkCreateDto) {
return this.service.create(authUser, dto);
}
@Patch(':id') @Patch(':id')
updateSharedLink( updateSharedLink(
@AuthUser() authUser: AuthUserDto, @AuthUser() authUser: AuthUserDto,
@Param() { id }: UUIDParamDto, @Param() { id }: UUIDParamDto,
@Body() dto: EditSharedLinkDto, @Body() dto: SharedLinkEditDto,
): Promise<SharedLinkResponseDto> { ): Promise<SharedLinkResponseDto> {
return this.service.update(authUser, id, dto); return this.service.update(authUser, id, dto);
} }
@ -42,4 +55,24 @@ export class SharedLinkController {
removeSharedLink(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> { removeSharedLink(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
return this.service.remove(authUser, id); 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);
}
} }

View File

@ -18,8 +18,8 @@ export class SharedLinkEntity {
@PrimaryGeneratedColumn('uuid') @PrimaryGeneratedColumn('uuid')
id!: string; id!: string;
@Column({ nullable: true }) @Column({ type: 'varchar', nullable: true })
description?: string; description!: string | null;
@Column() @Column()
userId!: string; userId!: string;
@ -55,6 +55,9 @@ export class SharedLinkEntity {
@Index('IDX_sharedlink_albumId') @Index('IDX_sharedlink_albumId')
@ManyToOne(() => AlbumEntity, (album) => album.sharedLinks, { onDelete: 'CASCADE', onUpdate: 'CASCADE' }) @ManyToOne(() => AlbumEntity, (album) => album.sharedLinks, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
album?: AlbumEntity; album?: AlbumEntity;
@Column({ type: 'varchar', nullable: true })
albumId!: string | null;
} }
export enum SharedLinkType { export enum SharedLinkType {

View File

@ -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,
},
});
}
} }

View File

@ -777,6 +777,21 @@ export const loginResponseStub = {
}; };
export const sharedLinkStub = { 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({ valid: Object.freeze({
id: '123', id: '123',
userId: authStub.admin.id, userId: authStub.admin.id,
@ -789,6 +804,8 @@ export const sharedLinkStub = {
allowDownload: true, allowDownload: true,
showExif: true, showExif: true,
album: undefined, album: undefined,
albumId: null,
description: null,
assets: [], assets: [],
} as SharedLinkEntity), } as SharedLinkEntity),
expired: Object.freeze({ expired: Object.freeze({
@ -802,6 +819,8 @@ export const sharedLinkStub = {
allowUpload: true, allowUpload: true,
allowDownload: true, allowDownload: true,
showExif: true, showExif: true,
description: null,
albumId: null,
assets: [], assets: [],
} as SharedLinkEntity), } as SharedLinkEntity),
readonlyNoExif: Object.freeze<SharedLinkEntity>({ readonlyNoExif: Object.freeze<SharedLinkEntity>({
@ -815,7 +834,9 @@ export const sharedLinkStub = {
allowUpload: false, allowUpload: false,
allowDownload: false, allowDownload: false,
showExif: false, showExif: false,
description: null,
assets: [], assets: [],
albumId: 'album-123',
album: { album: {
id: 'album-123', id: 'album-123',
ownerId: authStub.admin.id, ownerId: authStub.admin.id,
@ -903,7 +924,7 @@ export const sharedLinkResponseStub = {
allowUpload: true, allowUpload: true,
assets: [], assets: [],
createdAt: today, createdAt: today,
description: undefined, description: null,
expiresAt: tomorrow, expiresAt: tomorrow,
id: '123', id: '123',
key: sharedLinkBytes.toString('base64url'), key: sharedLinkBytes.toString('base64url'),
@ -917,7 +938,7 @@ export const sharedLinkResponseStub = {
allowUpload: true, allowUpload: true,
assets: [], assets: [],
createdAt: today, createdAt: today,
description: undefined, description: null,
expiresAt: yesterday, expiresAt: yesterday,
id: '123', id: '123',
key: sharedLinkBytes.toString('base64url'), key: sharedLinkBytes.toString('base64url'),
@ -932,7 +953,7 @@ export const sharedLinkResponseStub = {
type: SharedLinkType.ALBUM, type: SharedLinkType.ALBUM,
createdAt: today, createdAt: today,
expiresAt: tomorrow, expiresAt: tomorrow,
description: undefined, description: null,
allowUpload: false, allowUpload: false,
allowDownload: false, allowDownload: false,
showExif: true, showExif: true,
@ -946,7 +967,7 @@ export const sharedLinkResponseStub = {
type: SharedLinkType.ALBUM, type: SharedLinkType.ALBUM,
createdAt: today, createdAt: today,
expiresAt: tomorrow, expiresAt: tomorrow,
description: undefined, description: null,
allowUpload: false, allowUpload: false,
allowDownload: false, allowDownload: false,
showExif: false, showExif: false,

View File

@ -3,9 +3,12 @@ import { IAccessRepository } from '@app/domain';
export const newAccessRepositoryMock = (): jest.Mocked<IAccessRepository> => { export const newAccessRepositoryMock = (): jest.Mocked<IAccessRepository> => {
return { return {
hasPartnerAccess: jest.fn(), hasPartnerAccess: jest.fn(),
hasAlbumAssetAccess: jest.fn(), hasAlbumAssetAccess: jest.fn(),
hasOwnerAssetAccess: jest.fn(), hasOwnerAssetAccess: jest.fn(),
hasPartnerAssetAccess: jest.fn(), hasPartnerAssetAccess: jest.fn(),
hasSharedLinkAssetAccess: jest.fn(), hasSharedLinkAssetAccess: jest.fn(),
hasAlbumOwnerAccess: jest.fn(),
}; };
}; };

View File

@ -7,16 +7,16 @@ import {
Configuration, Configuration,
ConfigurationParameters, ConfigurationParameters,
JobApi, JobApi,
JobName,
OAuthApi, OAuthApi,
PersonApi,
PartnerApi, PartnerApi,
PersonApi,
SearchApi, SearchApi,
ServerInfoApi, ServerInfoApi,
ShareApi, SharedLinkApi,
SystemConfigApi, SystemConfigApi,
UserApi, UserApi,
UserApiFp, UserApiFp
JobName
} from './open-api'; } from './open-api';
import { BASE_PATH } from './open-api/base'; import { BASE_PATH } from './open-api/base';
import { DUMMY_BASE_URL, toPathString } from './open-api/common'; import { DUMMY_BASE_URL, toPathString } from './open-api/common';
@ -32,7 +32,7 @@ export class ImmichApi {
public partnerApi: PartnerApi; public partnerApi: PartnerApi;
public searchApi: SearchApi; public searchApi: SearchApi;
public serverInfoApi: ServerInfoApi; public serverInfoApi: ServerInfoApi;
public shareApi: ShareApi; public sharedLinkApi: SharedLinkApi;
public personApi: PersonApi; public personApi: PersonApi;
public systemConfigApi: SystemConfigApi; public systemConfigApi: SystemConfigApi;
public userApi: UserApi; public userApi: UserApi;
@ -51,7 +51,7 @@ export class ImmichApi {
this.partnerApi = new PartnerApi(this.config); this.partnerApi = new PartnerApi(this.config);
this.searchApi = new SearchApi(this.config); this.searchApi = new SearchApi(this.config);
this.serverInfoApi = new ServerInfoApi(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.personApi = new PersonApi(this.config);
this.systemConfigApi = new SystemConfigApi(this.config); this.systemConfigApi = new SystemConfigApi(this.config);
this.userApi = new UserApi(this.config); this.userApi = new UserApi(this.config);

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@
}); });
const getSharedLinks = async () => { const getSharedLinks = async () => {
const { data } = await api.shareApi.getAllSharedLinks(); const { data } = await api.sharedLinkApi.getAllSharedLinks();
sharedLinks = data.filter((link) => link.album?.id === album.id); sharedLinks = data.filter((link) => link.album?.id === album.id);
}; };

View File

@ -1,34 +1,65 @@
<script lang="ts"> <script lang="ts">
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; 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 DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
import ConfirmDialogue from '../../shared-components/confirm-dialogue.svelte';
import { getAssetControlContext } from '../asset-select-control-bar.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 sharedLink: SharedLinkResponseDto;
export let allAssets: AssetResponseDto[];
let removing = false;
const { getAssets, clearSelect } = getAssetControlContext(); const { getAssets, clearSelect } = getAssetControlContext();
const handleRemoveAssetsFromSharedLink = async () => { const handleRemove = async () => {
if (window.confirm('Do you want to remove selected assets from the shared link?')) { try {
// TODO: Rename API method or change functionality. The assetIds passed const { data: results } = await api.sharedLinkApi.removeSharedLinkAssets({
// in are kept instead of removed. id: sharedLink.id,
const assetsToKeep = allAssets.filter((a) => !getAssets().has(a)); assetIdsDto: {
await api.assetApi.removeAssetsFromSharedLink({ assetIds: Array.from(getAssets()).map((asset) => asset.id)
removeAssetsDto: {
assetIds: assetsToKeep.map((a) => a.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(); clearSelect();
} catch (error) {
handleError(error, 'Unable to remove assets from shared link');
} }
}; };
</script> </script>
<CircleIconButton <CircleIconButton
title="Remove from album" title="Remove from shared link"
on:click={handleRemoveAssetsFromSharedLink} on:click={() => (removing = true)}
logo={DeleteOutline} 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}

View File

@ -17,6 +17,7 @@
notificationController, notificationController,
NotificationType NotificationType
} from '../shared-components/notification/notification'; } from '../shared-components/notification/notification';
import { handleError } from '../../utils/handle-error';
export let sharedLink: SharedLinkResponseDto; export let sharedLink: SharedLinkResponseDto;
export let isOwned: boolean; export let isOwned: boolean;
@ -26,43 +27,40 @@
$: assets = sharedLink.assets; $: assets = sharedLink.assets;
$: isMultiSelectionMode = selectedAssets.size > 0; $: isMultiSelectionMode = selectedAssets.size > 0;
const clearMultiSelectAssetAssetHandler = () => {
selectedAssets = new Set();
};
const downloadAssets = async () => { const downloadAssets = async () => {
await bulkDownload('immich-shared', assets, undefined, sharedLink?.key); await bulkDownload('immich-shared', assets, undefined, sharedLink.key);
}; };
const handleUploadAssets = async () => { const handleUploadAssets = async () => {
try { try {
const results = await openFileUploadDialog(undefined, sharedLink?.key); const results = await openFileUploadDialog(undefined, sharedLink.key);
const assetIds = results.filter((id) => !!id) as string[]; const { data } = await api.sharedLinkApi.addSharedLinkAssets({
id: sharedLink.id,
await api.assetApi.addAssetsToSharedLink({ assetIdsDto: {
addAssetsDto: { assetIds: results.filter((id) => !!id) as string[]
assetIds
}, },
key: sharedLink?.key key: sharedLink.key
}); });
const added = data.filter((item) => item.success).length;
notificationController.show({ notificationController.show({
message: `Successfully add ${assetIds.length} to the shared link`, message: `Added ${added} assets`,
type: NotificationType.Info type: NotificationType.Info
}); });
} catch (e) { } catch (e) {
console.error('handleUploadAssets', e); handleError(e, 'Unable to add assets to shared link');
} }
}; };
</script> </script>
<section class="bg-immich-bg dark:bg-immich-dark-bg"> <section class="bg-immich-bg dark:bg-immich-dark-bg">
{#if isMultiSelectionMode} {#if isMultiSelectionMode}
<AssetSelectControlBar assets={selectedAssets} clearSelect={clearMultiSelectAssetAssetHandler}> <AssetSelectControlBar assets={selectedAssets} clearSelect={() => (selectedAssets = new Set())}>
<DownloadAction filename="immich-shared" sharedLinkKey={sharedLink.key} /> <DownloadAction filename="immich-shared" sharedLinkKey={sharedLink.key} />
{#if isOwned} {#if isOwned}
<RemoveFromSharedLink bind:sharedLink allAssets={assets} /> <RemoveFromSharedLink bind:sharedLink />
{/if} {/if}
</AssetSelectControlBar> </AssetSelectControlBar>
{:else} {:else}

View File

@ -7,31 +7,31 @@
import { handleError } from '$lib/utils/handle-error'; import { handleError } from '$lib/utils/handle-error';
import { import {
AlbumResponseDto, AlbumResponseDto,
api,
AssetResponseDto, AssetResponseDto,
SharedLinkResponseDto, SharedLinkResponseDto,
SharedLinkType, SharedLinkType
api
} from '@api'; } from '@api';
import { createEventDispatcher, onMount } from 'svelte'; import { createEventDispatcher, onMount } from 'svelte';
import Link from 'svelte-material-icons/Link.svelte'; import Link from 'svelte-material-icons/Link.svelte';
import BaseModal from '../base-modal.svelte'; import BaseModal from '../base-modal.svelte';
import type { ImmichDropDownOption } from '../dropdown-button.svelte'; import type { ImmichDropDownOption } from '../dropdown-button.svelte';
import DropdownButton 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 shareType: SharedLinkType;
export let sharedAssets: AssetResponseDto[] = []; export let sharedAssets: AssetResponseDto[] = [];
export let album: AlbumResponseDto | undefined = undefined; export let album: AlbumResponseDto | undefined = undefined;
export let editingLink: SharedLinkResponseDto | undefined = undefined; export let editingLink: SharedLinkResponseDto | undefined = undefined;
let isShowSharedLink = false; let sharedLink: string | null = null;
let expirationTime = '';
let isAllowUpload = false;
let sharedLink = '';
let description = ''; let description = '';
let allowDownload = true;
let allowUpload = false;
let showExif = true;
let expirationTime = '';
let shouldChangeExpirationTime = false; let shouldChangeExpirationTime = false;
let isAllowDownload = true;
let shouldShowExif = true;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const expiredDateOption: ImmichDropDownOption = { const expiredDateOption: ImmichDropDownOption = {
@ -44,9 +44,9 @@
if (editingLink.description) { if (editingLink.description) {
description = editingLink.description; description = editingLink.description;
} }
isAllowUpload = editingLink.allowUpload; allowUpload = editingLink.allowUpload;
isAllowDownload = editingLink.allowDownload; allowDownload = editingLink.allowDownload;
shouldShowExif = editingLink.showExif; showExif = editingLink.showExif;
} }
}); });
@ -58,49 +58,32 @@
: undefined; : undefined;
try { try {
if (shareType === SharedLinkType.Album && album) { const { data } = await api.sharedLinkApi.createSharedLink({
const { data } = await api.albumApi.createAlbumSharedLink({ sharedLinkCreateDto: {
createAlbumShareLinkDto: { type: shareType,
albumId: album.id, albumId: album ? album.id : undefined,
expiresAt: expirationDate, assetIds: sharedAssets.map((a) => a.id),
allowUpload: isAllowUpload, expiresAt: expirationDate,
description: description, allowUpload,
allowDownload: isAllowDownload, description,
showExif: shouldShowExif allowDownload,
} showExif
}); }
buildSharedLink(data); });
} else { sharedLink = `${window.location.origin}/share/${data.key}`;
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);
}
} catch (e) { } catch (e) {
handleError(e, 'Failed to create shared link'); handleError(e, 'Failed to create shared link');
} }
isShowSharedLink = true;
};
const buildSharedLink = (createdLink: SharedLinkResponseDto) => {
sharedLink = `${window.location.origin}/share/${createdLink.key}`;
}; };
const handleCopy = async () => { const handleCopy = async () => {
if (!sharedLink) {
return;
}
try { try {
await navigator.clipboard.writeText(sharedLink); await navigator.clipboard.writeText(sharedLink);
notificationController.show({ notificationController.show({ message: 'Copied to clipboard!', type: NotificationType.Info });
message: 'Copied to clipboard!',
type: NotificationType.Info
});
} catch (e) { } catch (e) {
handleError( handleError(
e, e,
@ -129,34 +112,36 @@
}; };
const handleEditLink = async () => { const handleEditLink = async () => {
if (editingLink) { if (!editingLink) {
try { return;
const expirationTime = getExpirationTimeInMillisecond(); }
const currentTime = new Date().getTime();
const expirationDate: string | null = expirationTime
? new Date(currentTime + expirationTime).toISOString()
: null;
await api.shareApi.updateSharedLink({ try {
id: editingLink.id, const expirationTime = getExpirationTimeInMillisecond();
editSharedLinkDto: { const currentTime = new Date().getTime();
description, const expirationDate: string | null = expirationTime
expiresAt: shouldChangeExpirationTime ? expirationDate : undefined, ? new Date(currentTime + expirationTime).toISOString()
allowUpload: isAllowUpload, : null;
allowDownload: isAllowDownload,
showExif: shouldShowExif
}
});
notificationController.show({ await api.sharedLinkApi.updateSharedLink({
type: NotificationType.Info, id: editingLink.id,
message: 'Edited' sharedLinkEditDto: {
}); description,
expiresAt: shouldChangeExpirationTime ? expirationDate : undefined,
allowUpload: allowUpload,
allowDownload: allowDownload,
showExif: showExif
}
});
dispatch('close'); notificationController.show({
} catch (e) { type: NotificationType.Info,
handleError(e, 'Failed to edit shared link'); message: 'Edited'
} });
dispatch('close');
} catch (e) {
handleError(e, 'Failed to edit shared link');
} }
}; };
</script> </script>
@ -212,15 +197,15 @@
</div> </div>
<div class="my-3"> <div class="my-3">
<SettingSwitch bind:checked={shouldShowExif} title={'Show metadata'} /> <SettingSwitch bind:checked={showExif} title={'Show metadata'} />
</div> </div>
<div class="my-3"> <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>
<div class="my-3"> <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>
<div class="text-sm"> <div class="text-sm">
@ -248,7 +233,7 @@
<hr /> <hr />
<section class="m-6"> <section class="m-6">
{#if !isShowSharedLink} {#if !sharedLink}
{#if editingLink} {#if editingLink}
<div class="flex justify-end"> <div class="flex justify-end">
<Button size="sm" rounded="lg" on:click={handleEditLink}>Confirm</Button> <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> <Button size="sm" rounded="lg" on:click={handleCreateSharedLink}>Create link</Button>
</div> </div>
{/if} {/if}
{/if} {:else}
{#if isShowSharedLink}
<div class="flex w-full gap-4"> <div class="flex w-full gap-4">
<input class="immich-form-input w-full" bind:value={sharedLink} disabled /> <input class="immich-form-input w-full" bind:value={sharedLink} disabled />

View File

@ -7,7 +7,7 @@ export const load = (async ({ params, locals: { api } }) => {
const { key } = params; const { key } = params;
try { try {
const { data: sharedLink } = await api.shareApi.getMySharedLink({ key }); const { data: sharedLink } = await api.sharedLinkApi.getMySharedLink({ key });
const assetCount = sharedLink.assets.length; const assetCount = sharedLink.assets.length;
const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id; const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id;

View File

@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte'; import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte'; import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
import { api, SharedLinkResponseDto } from '@api'; import { api, SharedLinkResponseDto } from '@api';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import SharedLinkCard from '$lib/components/sharedlinks-page/shared-link-card.svelte'; import SharedLinkCard from '$lib/components/sharedlinks-page/shared-link-card.svelte';
@ -11,53 +10,45 @@
} from '$lib/components/shared-components/notification/notification'; } from '$lib/components/shared-components/notification/notification';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.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 sharedLinks: SharedLinkResponseDto[] = [];
let showEditForm = false; let editSharedLink: SharedLinkResponseDto | null = null;
let editSharedLink: SharedLinkResponseDto;
onMount(async () => { let deleteLinkId: string | null = null;
sharedLinks = await getSharedLinks();
});
const getSharedLinks = async () => { const refresh = async () => {
const { data: sharedLinks } = await api.shareApi.getAllSharedLinks(); const { data } = await api.sharedLinkApi.getAllSharedLinks();
sharedLinks = data;
return sharedLinks;
}; };
const handleDeleteLink = async (linkId: string) => { onMount(async () => {
if (window.confirm('Do you want to delete the shared link? ')) { await refresh();
try { });
await api.shareApi.removeSharedLink({ id: linkId });
notificationController.show({
message: 'Shared link deleted',
type: NotificationType.Info
});
sharedLinks = await getSharedLinks(); const handleDeleteLink = async () => {
} catch (e) { if (!deleteLinkId) {
console.error(e); return;
notificationController.show({ }
message: 'Failed to delete shared link',
type: NotificationType.Error 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 () => { const handleEditDone = async () => {
sharedLinks = await getSharedLinks(); refresh();
showEditForm = false; editSharedLink = null;
}; };
const handleCopy = async (key: string) => { const handleCopyLink = async (key: string) => {
const link = `${window.location.origin}/share/${key}`; const link = `${window.location.origin}/share/${key}`;
await navigator.clipboard.writeText(link); await navigator.clipboard.writeText(link);
notificationController.show({ notificationController.show({
@ -67,7 +58,7 @@
}; };
</script> </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> <svelte:fragment slot="leading">Shared links</svelte:fragment>
</ControlAppBar> </ControlAppBar>
@ -86,16 +77,16 @@
{#each sharedLinks as link (link.id)} {#each sharedLinks as link (link.id)}
<SharedLinkCard <SharedLinkCard
{link} {link}
on:delete={() => handleDeleteLink(link.id)} on:delete={() => (deleteLinkId = link.id)}
on:edit={() => handleEditLink(link.id)} on:edit={() => (editSharedLink = link)}
on:copy={() => handleCopy(link.key)} on:copy={() => handleCopyLink(link.key)}
/> />
{/each} {/each}
</div> </div>
{/if} {/if}
</section> </section>
{#if showEditForm} {#if editSharedLink}
<CreateSharedLinkModal <CreateSharedLinkModal
editingLink={editSharedLink} editingLink={editSharedLink}
shareType={editSharedLink.type} shareType={editSharedLink.type}
@ -103,3 +94,13 @@
on:close={handleEditDone} on:close={handleEditDone}
/> />
{/if} {/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}