Merge branch 'main' of https://github.com/immich-app/immich into thumbhash_mobile
This commit is contained in:
commit
4fab2bcf63
130 changed files with 3823 additions and 2714 deletions
|
@ -84,6 +84,7 @@ Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
|||
| Global Map | No | Yes |
|
||||
| Partner Sharing | Yes | Yes |
|
||||
| Facial recognition and clustering | No | Yes |
|
||||
| Offline support | Yes | No |
|
||||
|
||||
# Support the project
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||
final showAppBar = useState<bool>(true);
|
||||
final isPlayingMotionVideo = useState(false);
|
||||
final isPlayingVideo = useState(false);
|
||||
late Offset localPosition;
|
||||
Offset? localPosition;
|
||||
final authToken = 'Bearer ${Store.get(StoreKey.accessToken)}';
|
||||
final currentIndex = useState(initialIndex);
|
||||
final currentAsset = loadAsset(currentIndex.value);
|
||||
|
@ -246,8 +246,13 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
// Guard [localPosition] null
|
||||
if (localPosition == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for delta from initial down point
|
||||
final d = details.localPosition - localPosition;
|
||||
final d = details.localPosition - localPosition!;
|
||||
// If the magnitude of the dx swipe is large, we probably didn't mean to go down
|
||||
if (d.dx.abs() > dxThreshold) {
|
||||
return;
|
||||
|
@ -367,6 +372,26 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
ImageProvider imageProvider(Asset asset) {
|
||||
if (asset.isLocal) {
|
||||
return localImageProvider(asset);
|
||||
} else {
|
||||
if (isLoadOriginal.value) {
|
||||
return originalImageProvider(asset);
|
||||
} else if (isLoadPreview.value) {
|
||||
return remoteThumbnailImageProvider(
|
||||
asset,
|
||||
api.ThumbnailFormat.JPEG,
|
||||
);
|
||||
} else {
|
||||
return remoteThumbnailImageProvider(
|
||||
asset,
|
||||
api.ThumbnailFormat.WEBP,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: WillPopScope(
|
||||
|
@ -460,26 +485,9 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||
: null,
|
||||
builder: (context, index) {
|
||||
final asset = loadAsset(index);
|
||||
final ImageProvider provider = imageProvider(asset);
|
||||
|
||||
if (asset.isImage && !isPlayingMotionVideo.value) {
|
||||
// Show photo
|
||||
final ImageProvider provider;
|
||||
if (asset.isLocal) {
|
||||
provider = localImageProvider(asset);
|
||||
} else {
|
||||
if (isLoadOriginal.value) {
|
||||
provider = originalImageProvider(asset);
|
||||
} else if (isLoadPreview.value) {
|
||||
provider = remoteThumbnailImageProvider(
|
||||
asset,
|
||||
api.ThumbnailFormat.JPEG,
|
||||
);
|
||||
} else {
|
||||
provider = remoteThumbnailImageProvider(
|
||||
asset,
|
||||
api.ThumbnailFormat.WEBP,
|
||||
);
|
||||
}
|
||||
}
|
||||
return PhotoViewGalleryPageOptions(
|
||||
onDragStart: (_, details, __) =>
|
||||
localPosition = details.localPosition,
|
||||
|
@ -512,18 +520,23 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||
maxScale: 1.0,
|
||||
minScale: 1.0,
|
||||
basePosition: Alignment.bottomCenter,
|
||||
child: SafeArea(
|
||||
child: VideoViewerPage(
|
||||
onPlaying: () => isPlayingVideo.value = true,
|
||||
onPaused: () => isPlayingVideo.value = false,
|
||||
asset: asset,
|
||||
isMotionVideo: isPlayingMotionVideo.value,
|
||||
onVideoEnded: () {
|
||||
if (isPlayingMotionVideo.value) {
|
||||
isPlayingMotionVideo.value = false;
|
||||
}
|
||||
},
|
||||
child: VideoViewerPage(
|
||||
onPlaying: () => isPlayingVideo.value = true,
|
||||
onPaused: () => isPlayingVideo.value = false,
|
||||
asset: asset,
|
||||
isMotionVideo: isPlayingMotionVideo.value,
|
||||
placeholder: Image(
|
||||
image: provider,
|
||||
fit: BoxFit.fitWidth,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
alignment: Alignment.center,
|
||||
),
|
||||
onVideoEnded: () {
|
||||
if (isPlayingMotionVideo.value) {
|
||||
isPlayingMotionVideo.value = false;
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import 'package:video_player/video_player.dart';
|
|||
class VideoViewerPage extends HookConsumerWidget {
|
||||
final Asset asset;
|
||||
final bool isMotionVideo;
|
||||
final Widget? placeholder;
|
||||
final VoidCallback onVideoEnded;
|
||||
final VoidCallback? onPlaying;
|
||||
final VoidCallback? onPaused;
|
||||
|
@ -26,6 +27,7 @@ class VideoViewerPage extends HookConsumerWidget {
|
|||
required this.onVideoEnded,
|
||||
this.onPlaying,
|
||||
this.onPaused,
|
||||
this.placeholder,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -66,6 +68,7 @@ class VideoViewerPage extends HookConsumerWidget {
|
|||
onVideoEnded: onVideoEnded,
|
||||
onPaused: onPaused,
|
||||
onPlaying: onPlaying,
|
||||
placeholder: placeholder,
|
||||
),
|
||||
if (downloadAssetStatus == DownloadAssetStatus.loading)
|
||||
const Center(
|
||||
|
@ -95,6 +98,10 @@ class VideoPlayer extends StatefulWidget {
|
|||
final Function()? onPlaying;
|
||||
final Function()? onPaused;
|
||||
|
||||
/// The placeholder to show while the video is loading
|
||||
/// usually, a thumbnail of the video
|
||||
final Widget? placeholder;
|
||||
|
||||
const VideoPlayer({
|
||||
Key? key,
|
||||
this.url,
|
||||
|
@ -104,6 +111,7 @@ class VideoPlayer extends StatefulWidget {
|
|||
required this.isMotionVideo,
|
||||
this.onPlaying,
|
||||
this.onPaused,
|
||||
this.placeholder,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -186,12 +194,18 @@ class _VideoPlayerState extends State<VideoPlayer> {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
return const Center(
|
||||
child: SizedBox(
|
||||
width: 75,
|
||||
height: 75,
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Center(
|
||||
child: Stack(
|
||||
children: [
|
||||
if (widget.placeholder != null)
|
||||
widget.placeholder!,
|
||||
const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
@ -51,6 +54,12 @@ class ImmichAssetGrid extends HookConsumerWidget {
|
|||
final enableHeroAnimations = useState(false);
|
||||
final transitionDuration = ModalRoute.of(context)?.transitionDuration;
|
||||
|
||||
final perRow = useState(
|
||||
assetsPerRow ?? settings.getSetting(AppSettingsEnum.tilesPerRow)!,
|
||||
);
|
||||
final scaleFactor = useState(7.0 - perRow.value);
|
||||
final baseScaleFactor = useState(7.0 - perRow.value);
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
// Wait for transition to complete, then re-enable
|
||||
|
@ -80,22 +89,43 @@ class ImmichAssetGrid extends HookConsumerWidget {
|
|||
onWillPop: onWillPop,
|
||||
child: HeroMode(
|
||||
enabled: enableHeroAnimations.value,
|
||||
child: ImmichAssetGridView(
|
||||
onRefresh: onRefresh,
|
||||
assetsPerRow: assetsPerRow ??
|
||||
settings.getSetting(AppSettingsEnum.tilesPerRow),
|
||||
listener: listener,
|
||||
showStorageIndicator: showStorageIndicator ??
|
||||
settings.getSetting(AppSettingsEnum.storageIndicator),
|
||||
renderList: renderList,
|
||||
margin: margin,
|
||||
selectionActive: selectionActive,
|
||||
preselectedAssets: preselectedAssets,
|
||||
canDeselect: canDeselect,
|
||||
dynamicLayout: dynamicLayout ??
|
||||
settings.getSetting(AppSettingsEnum.dynamicLayout),
|
||||
showMultiSelectIndicator: showMultiSelectIndicator,
|
||||
visibleItemsListener: visibleItemsListener,
|
||||
child: RawGestureDetector(
|
||||
gestures: {
|
||||
CustomScaleGestureRecognizer:
|
||||
GestureRecognizerFactoryWithHandlers<
|
||||
CustomScaleGestureRecognizer>(
|
||||
() => CustomScaleGestureRecognizer(),
|
||||
(CustomScaleGestureRecognizer scale) {
|
||||
scale.onStart = (details) {
|
||||
baseScaleFactor.value = scaleFactor.value;
|
||||
};
|
||||
|
||||
scale.onUpdate = (details) {
|
||||
scaleFactor.value =
|
||||
max(min(5.0, baseScaleFactor.value * details.scale), 1.0);
|
||||
if (7 - scaleFactor.value.toInt() != perRow.value) {
|
||||
perRow.value = 7 - scaleFactor.value.toInt();
|
||||
}
|
||||
};
|
||||
scale.onEnd = (details) {};
|
||||
})
|
||||
},
|
||||
child: ImmichAssetGridView(
|
||||
onRefresh: onRefresh,
|
||||
assetsPerRow: perRow.value,
|
||||
listener: listener,
|
||||
showStorageIndicator: showStorageIndicator ??
|
||||
settings.getSetting(AppSettingsEnum.storageIndicator),
|
||||
renderList: renderList,
|
||||
margin: margin,
|
||||
selectionActive: selectionActive,
|
||||
preselectedAssets: preselectedAssets,
|
||||
canDeselect: canDeselect,
|
||||
dynamicLayout: dynamicLayout ??
|
||||
settings.getSetting(AppSettingsEnum.dynamicLayout),
|
||||
showMultiSelectIndicator: showMultiSelectIndicator,
|
||||
visibleItemsListener: visibleItemsListener,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -113,3 +143,11 @@ class ImmichAssetGrid extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// accepts a gesture even though it should reject it (because child won)
|
||||
class CustomScaleGestureRecognizer extends ScaleGestureRecognizer {
|
||||
@override
|
||||
void rejectGesture(int pointer) {
|
||||
acceptGesture(pointer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,6 +134,15 @@ class FileHelper {
|
|||
case 'cin':
|
||||
return {"type": "image", "subType": "x-phantom-cin"};
|
||||
|
||||
case 'jxl':
|
||||
return {"type": "image", "subType": "jxl"};
|
||||
|
||||
case 'mts':
|
||||
return {"type": "video", "subType": "mp2t"};
|
||||
|
||||
case 'm2ts':
|
||||
return {"type": "video", "subType": "mp2t"};
|
||||
|
||||
default:
|
||||
return {"type": "unsupport", "subType": "unsupport"};
|
||||
}
|
||||
|
|
24
mobile/openapi/.openapi-generator/FILES
generated
24
mobile/openapi/.openapi-generator/FILES
generated
|
@ -37,8 +37,6 @@ doc/CheckDuplicateAssetResponseDto.md
|
|||
doc/CheckExistingAssetsDto.md
|
||||
doc/CheckExistingAssetsResponseDto.md
|
||||
doc/CreateAlbumDto.md
|
||||
doc/CreateAlbumShareLinkDto.md
|
||||
doc/CreateAssetsShareLinkDto.md
|
||||
doc/CreateProfileImageResponseDto.md
|
||||
doc/CreateTagDto.md
|
||||
doc/CreateUserDto.md
|
||||
|
@ -48,10 +46,10 @@ doc/DeleteAssetDto.md
|
|||
doc/DeleteAssetResponseDto.md
|
||||
doc/DeleteAssetStatus.md
|
||||
doc/DownloadFilesDto.md
|
||||
doc/EditSharedLinkDto.md
|
||||
doc/ExifResponseDto.md
|
||||
doc/GetAssetByTimeBucketDto.md
|
||||
doc/GetAssetCountByTimeBucketDto.md
|
||||
doc/ImportAssetDto.md
|
||||
doc/JobApi.md
|
||||
doc/JobCommand.md
|
||||
doc/JobCommandDto.md
|
||||
|
@ -89,7 +87,9 @@ doc/ServerInfoResponseDto.md
|
|||
doc/ServerPingResponse.md
|
||||
doc/ServerStatsResponseDto.md
|
||||
doc/ServerVersionReponseDto.md
|
||||
doc/ShareApi.md
|
||||
doc/SharedLinkApi.md
|
||||
doc/SharedLinkCreateDto.md
|
||||
doc/SharedLinkEditDto.md
|
||||
doc/SharedLinkResponseDto.md
|
||||
doc/SharedLinkType.md
|
||||
doc/SignUpDto.md
|
||||
|
@ -128,7 +128,7 @@ lib/api/partner_api.dart
|
|||
lib/api/person_api.dart
|
||||
lib/api/search_api.dart
|
||||
lib/api/server_info_api.dart
|
||||
lib/api/share_api.dart
|
||||
lib/api/shared_link_api.dart
|
||||
lib/api/system_config_api.dart
|
||||
lib/api/tag_api.dart
|
||||
lib/api/user_api.dart
|
||||
|
@ -170,8 +170,6 @@ lib/model/check_duplicate_asset_response_dto.dart
|
|||
lib/model/check_existing_assets_dto.dart
|
||||
lib/model/check_existing_assets_response_dto.dart
|
||||
lib/model/create_album_dto.dart
|
||||
lib/model/create_album_share_link_dto.dart
|
||||
lib/model/create_assets_share_link_dto.dart
|
||||
lib/model/create_profile_image_response_dto.dart
|
||||
lib/model/create_tag_dto.dart
|
||||
lib/model/create_user_dto.dart
|
||||
|
@ -181,10 +179,10 @@ lib/model/delete_asset_dto.dart
|
|||
lib/model/delete_asset_response_dto.dart
|
||||
lib/model/delete_asset_status.dart
|
||||
lib/model/download_files_dto.dart
|
||||
lib/model/edit_shared_link_dto.dart
|
||||
lib/model/exif_response_dto.dart
|
||||
lib/model/get_asset_by_time_bucket_dto.dart
|
||||
lib/model/get_asset_count_by_time_bucket_dto.dart
|
||||
lib/model/import_asset_dto.dart
|
||||
lib/model/job_command.dart
|
||||
lib/model/job_command_dto.dart
|
||||
lib/model/job_counts_dto.dart
|
||||
|
@ -216,6 +214,8 @@ lib/model/server_info_response_dto.dart
|
|||
lib/model/server_ping_response.dart
|
||||
lib/model/server_stats_response_dto.dart
|
||||
lib/model/server_version_reponse_dto.dart
|
||||
lib/model/shared_link_create_dto.dart
|
||||
lib/model/shared_link_edit_dto.dart
|
||||
lib/model/shared_link_response_dto.dart
|
||||
lib/model/shared_link_type.dart
|
||||
lib/model/sign_up_dto.dart
|
||||
|
@ -274,8 +274,6 @@ test/check_duplicate_asset_response_dto_test.dart
|
|||
test/check_existing_assets_dto_test.dart
|
||||
test/check_existing_assets_response_dto_test.dart
|
||||
test/create_album_dto_test.dart
|
||||
test/create_album_share_link_dto_test.dart
|
||||
test/create_assets_share_link_dto_test.dart
|
||||
test/create_profile_image_response_dto_test.dart
|
||||
test/create_tag_dto_test.dart
|
||||
test/create_user_dto_test.dart
|
||||
|
@ -285,10 +283,10 @@ test/delete_asset_dto_test.dart
|
|||
test/delete_asset_response_dto_test.dart
|
||||
test/delete_asset_status_test.dart
|
||||
test/download_files_dto_test.dart
|
||||
test/edit_shared_link_dto_test.dart
|
||||
test/exif_response_dto_test.dart
|
||||
test/get_asset_by_time_bucket_dto_test.dart
|
||||
test/get_asset_count_by_time_bucket_dto_test.dart
|
||||
test/import_asset_dto_test.dart
|
||||
test/job_api_test.dart
|
||||
test/job_command_dto_test.dart
|
||||
test/job_command_test.dart
|
||||
|
@ -326,7 +324,9 @@ test/server_info_response_dto_test.dart
|
|||
test/server_ping_response_test.dart
|
||||
test/server_stats_response_dto_test.dart
|
||||
test/server_version_reponse_dto_test.dart
|
||||
test/share_api_test.dart
|
||||
test/shared_link_api_test.dart
|
||||
test/shared_link_create_dto_test.dart
|
||||
test/shared_link_edit_dto_test.dart
|
||||
test/shared_link_response_dto_test.dart
|
||||
test/shared_link_type_test.dart
|
||||
test/sign_up_dto_test.dart
|
||||
|
|
24
mobile/openapi/README.md
generated
24
mobile/openapi/README.md
generated
|
@ -80,7 +80,6 @@ Class | Method | HTTP request | Description
|
|||
*AlbumApi* | [**addAssetsToAlbum**](doc//AlbumApi.md#addassetstoalbum) | **PUT** /album/{id}/assets |
|
||||
*AlbumApi* | [**addUsersToAlbum**](doc//AlbumApi.md#adduserstoalbum) | **PUT** /album/{id}/users |
|
||||
*AlbumApi* | [**createAlbum**](doc//AlbumApi.md#createalbum) | **POST** /album |
|
||||
*AlbumApi* | [**createAlbumSharedLink**](doc//AlbumApi.md#createalbumsharedlink) | **POST** /album/create-shared-link |
|
||||
*AlbumApi* | [**deleteAlbum**](doc//AlbumApi.md#deletealbum) | **DELETE** /album/{id} |
|
||||
*AlbumApi* | [**downloadArchive**](doc//AlbumApi.md#downloadarchive) | **GET** /album/{id}/download |
|
||||
*AlbumApi* | [**getAlbumCount**](doc//AlbumApi.md#getalbumcount) | **GET** /album/count |
|
||||
|
@ -89,11 +88,9 @@ Class | Method | HTTP request | Description
|
|||
*AlbumApi* | [**removeAssetFromAlbum**](doc//AlbumApi.md#removeassetfromalbum) | **DELETE** /album/{id}/assets |
|
||||
*AlbumApi* | [**removeUserFromAlbum**](doc//AlbumApi.md#removeuserfromalbum) | **DELETE** /album/{id}/user/{userId} |
|
||||
*AlbumApi* | [**updateAlbumInfo**](doc//AlbumApi.md#updatealbuminfo) | **PATCH** /album/{id} |
|
||||
*AssetApi* | [**addAssetsToSharedLink**](doc//AssetApi.md#addassetstosharedlink) | **PATCH** /asset/shared-link/add |
|
||||
*AssetApi* | [**bulkUploadCheck**](doc//AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |
|
||||
*AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
||||
*AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
||||
*AssetApi* | [**createAssetsSharedLink**](doc//AssetApi.md#createassetssharedlink) | **POST** /asset/shared-link |
|
||||
*AssetApi* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset |
|
||||
*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download/{id} |
|
||||
*AssetApi* | [**downloadFiles**](doc//AssetApi.md#downloadfiles) | **POST** /asset/download-files |
|
||||
|
@ -111,7 +108,7 @@ Class | Method | HTTP request | Description
|
|||
*AssetApi* | [**getMapMarkers**](doc//AssetApi.md#getmapmarkers) | **GET** /asset/map-marker |
|
||||
*AssetApi* | [**getMemoryLane**](doc//AssetApi.md#getmemorylane) | **GET** /asset/memory-lane |
|
||||
*AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
||||
*AssetApi* | [**removeAssetsFromSharedLink**](doc//AssetApi.md#removeassetsfromsharedlink) | **PATCH** /asset/shared-link/remove |
|
||||
*AssetApi* | [**importFile**](doc//AssetApi.md#importfile) | **POST** /asset/import |
|
||||
*AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search |
|
||||
*AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} |
|
||||
*AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{id} |
|
||||
|
@ -146,11 +143,14 @@ Class | Method | HTTP request | Description
|
|||
*ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
|
||||
*ServerInfoApi* | [**getStats**](doc//ServerInfoApi.md#getstats) | **GET** /server-info/stats |
|
||||
*ServerInfoApi* | [**pingServer**](doc//ServerInfoApi.md#pingserver) | **GET** /server-info/ping |
|
||||
*ShareApi* | [**getAllSharedLinks**](doc//ShareApi.md#getallsharedlinks) | **GET** /share |
|
||||
*ShareApi* | [**getMySharedLink**](doc//ShareApi.md#getmysharedlink) | **GET** /share/me |
|
||||
*ShareApi* | [**getSharedLinkById**](doc//ShareApi.md#getsharedlinkbyid) | **GET** /share/{id} |
|
||||
*ShareApi* | [**removeSharedLink**](doc//ShareApi.md#removesharedlink) | **DELETE** /share/{id} |
|
||||
*ShareApi* | [**updateSharedLink**](doc//ShareApi.md#updatesharedlink) | **PATCH** /share/{id} |
|
||||
*SharedLinkApi* | [**addSharedLinkAssets**](doc//SharedLinkApi.md#addsharedlinkassets) | **PUT** /shared-link/{id}/assets |
|
||||
*SharedLinkApi* | [**createSharedLink**](doc//SharedLinkApi.md#createsharedlink) | **POST** /shared-link |
|
||||
*SharedLinkApi* | [**getAllSharedLinks**](doc//SharedLinkApi.md#getallsharedlinks) | **GET** /shared-link |
|
||||
*SharedLinkApi* | [**getMySharedLink**](doc//SharedLinkApi.md#getmysharedlink) | **GET** /shared-link/me |
|
||||
*SharedLinkApi* | [**getSharedLinkById**](doc//SharedLinkApi.md#getsharedlinkbyid) | **GET** /shared-link/{id} |
|
||||
*SharedLinkApi* | [**removeSharedLink**](doc//SharedLinkApi.md#removesharedlink) | **DELETE** /shared-link/{id} |
|
||||
*SharedLinkApi* | [**removeSharedLinkAssets**](doc//SharedLinkApi.md#removesharedlinkassets) | **DELETE** /shared-link/{id}/assets |
|
||||
*SharedLinkApi* | [**updateSharedLink**](doc//SharedLinkApi.md#updatesharedlink) | **PATCH** /shared-link/{id} |
|
||||
*SystemConfigApi* | [**getConfig**](doc//SystemConfigApi.md#getconfig) | **GET** /system-config |
|
||||
*SystemConfigApi* | [**getDefaults**](doc//SystemConfigApi.md#getdefaults) | **GET** /system-config/defaults |
|
||||
*SystemConfigApi* | [**getStorageTemplateOptions**](doc//SystemConfigApi.md#getstoragetemplateoptions) | **GET** /system-config/storage-template-options |
|
||||
|
@ -207,8 +207,6 @@ Class | Method | HTTP request | Description
|
|||
- [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md)
|
||||
- [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md)
|
||||
- [CreateAlbumDto](doc//CreateAlbumDto.md)
|
||||
- [CreateAlbumShareLinkDto](doc//CreateAlbumShareLinkDto.md)
|
||||
- [CreateAssetsShareLinkDto](doc//CreateAssetsShareLinkDto.md)
|
||||
- [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md)
|
||||
- [CreateTagDto](doc//CreateTagDto.md)
|
||||
- [CreateUserDto](doc//CreateUserDto.md)
|
||||
|
@ -218,10 +216,10 @@ Class | Method | HTTP request | Description
|
|||
- [DeleteAssetResponseDto](doc//DeleteAssetResponseDto.md)
|
||||
- [DeleteAssetStatus](doc//DeleteAssetStatus.md)
|
||||
- [DownloadFilesDto](doc//DownloadFilesDto.md)
|
||||
- [EditSharedLinkDto](doc//EditSharedLinkDto.md)
|
||||
- [ExifResponseDto](doc//ExifResponseDto.md)
|
||||
- [GetAssetByTimeBucketDto](doc//GetAssetByTimeBucketDto.md)
|
||||
- [GetAssetCountByTimeBucketDto](doc//GetAssetCountByTimeBucketDto.md)
|
||||
- [ImportAssetDto](doc//ImportAssetDto.md)
|
||||
- [JobCommand](doc//JobCommand.md)
|
||||
- [JobCommandDto](doc//JobCommandDto.md)
|
||||
- [JobCountsDto](doc//JobCountsDto.md)
|
||||
|
@ -253,6 +251,8 @@ Class | Method | HTTP request | Description
|
|||
- [ServerPingResponse](doc//ServerPingResponse.md)
|
||||
- [ServerStatsResponseDto](doc//ServerStatsResponseDto.md)
|
||||
- [ServerVersionReponseDto](doc//ServerVersionReponseDto.md)
|
||||
- [SharedLinkCreateDto](doc//SharedLinkCreateDto.md)
|
||||
- [SharedLinkEditDto](doc//SharedLinkEditDto.md)
|
||||
- [SharedLinkResponseDto](doc//SharedLinkResponseDto.md)
|
||||
- [SharedLinkType](doc//SharedLinkType.md)
|
||||
- [SignUpDto](doc//SignUpDto.md)
|
||||
|
|
56
mobile/openapi/doc/AlbumApi.md
generated
56
mobile/openapi/doc/AlbumApi.md
generated
|
@ -12,7 +12,6 @@ Method | HTTP request | Description
|
|||
[**addAssetsToAlbum**](AlbumApi.md#addassetstoalbum) | **PUT** /album/{id}/assets |
|
||||
[**addUsersToAlbum**](AlbumApi.md#adduserstoalbum) | **PUT** /album/{id}/users |
|
||||
[**createAlbum**](AlbumApi.md#createalbum) | **POST** /album |
|
||||
[**createAlbumSharedLink**](AlbumApi.md#createalbumsharedlink) | **POST** /album/create-shared-link |
|
||||
[**deleteAlbum**](AlbumApi.md#deletealbum) | **DELETE** /album/{id} |
|
||||
[**downloadArchive**](AlbumApi.md#downloadarchive) | **GET** /album/{id}/download |
|
||||
[**getAlbumCount**](AlbumApi.md#getalbumcount) | **GET** /album/count |
|
||||
|
@ -194,61 +193,6 @@ Name | Type | Description | Notes
|
|||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **createAlbumSharedLink**
|
||||
> SharedLinkResponseDto createAlbumSharedLink(createAlbumShareLinkDto)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AlbumApi();
|
||||
final createAlbumShareLinkDto = CreateAlbumShareLinkDto(); // CreateAlbumShareLinkDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.createAlbumSharedLink(createAlbumShareLinkDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AlbumApi->createAlbumSharedLink: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**createAlbumShareLinkDto** | [**CreateAlbumShareLinkDto**](CreateAlbumShareLinkDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**SharedLinkResponseDto**](SharedLinkResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **deleteAlbum**
|
||||
> deleteAlbum(id)
|
||||
|
||||
|
|
1
mobile/openapi/doc/AlbumResponseDto.md
generated
1
mobile/openapi/doc/AlbumResponseDto.md
generated
|
@ -19,6 +19,7 @@ Name | Type | Description | Notes
|
|||
**sharedUsers** | [**List<UserResponseDto>**](UserResponseDto.md) | | [default to const []]
|
||||
**assets** | [**List<AssetResponseDto>**](AssetResponseDto.md) | | [default to const []]
|
||||
**owner** | [**UserResponseDto**](UserResponseDto.md) | |
|
||||
**lastModifiedAssetTimestamp** | [**DateTime**](DateTime.md) | | [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)
|
||||
|
||||
|
|
142
mobile/openapi/doc/AssetApi.md
generated
142
mobile/openapi/doc/AssetApi.md
generated
|
@ -9,11 +9,9 @@ All URIs are relative to */api*
|
|||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**addAssetsToSharedLink**](AssetApi.md#addassetstosharedlink) | **PATCH** /asset/shared-link/add |
|
||||
[**bulkUploadCheck**](AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |
|
||||
[**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
||||
[**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
||||
[**createAssetsSharedLink**](AssetApi.md#createassetssharedlink) | **POST** /asset/shared-link |
|
||||
[**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset |
|
||||
[**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download/{id} |
|
||||
[**downloadFiles**](AssetApi.md#downloadfiles) | **POST** /asset/download-files |
|
||||
|
@ -31,70 +29,13 @@ Method | HTTP request | Description
|
|||
[**getMapMarkers**](AssetApi.md#getmapmarkers) | **GET** /asset/map-marker |
|
||||
[**getMemoryLane**](AssetApi.md#getmemorylane) | **GET** /asset/memory-lane |
|
||||
[**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
||||
[**removeAssetsFromSharedLink**](AssetApi.md#removeassetsfromsharedlink) | **PATCH** /asset/shared-link/remove |
|
||||
[**importFile**](AssetApi.md#importfile) | **POST** /asset/import |
|
||||
[**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search |
|
||||
[**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{id} |
|
||||
[**updateAsset**](AssetApi.md#updateasset) | **PUT** /asset/{id} |
|
||||
[**uploadFile**](AssetApi.md#uploadfile) | **POST** /asset/upload |
|
||||
|
||||
|
||||
# **addAssetsToSharedLink**
|
||||
> SharedLinkResponseDto addAssetsToSharedLink(addAssetsDto, key)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AssetApi();
|
||||
final addAssetsDto = AddAssetsDto(); // AddAssetsDto |
|
||||
final key = key_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.addAssetsToSharedLink(addAssetsDto, key);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->addAssetsToSharedLink: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**addAssetsDto** | [**AddAssetsDto**](AddAssetsDto.md)| |
|
||||
**key** | **String**| | [optional]
|
||||
|
||||
### Return type
|
||||
|
||||
[**SharedLinkResponseDto**](SharedLinkResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **bulkUploadCheck**
|
||||
> AssetBulkUploadCheckResponseDto bulkUploadCheck(assetBulkUploadCheckDto)
|
||||
|
||||
|
@ -268,61 +209,6 @@ Name | Type | Description | Notes
|
|||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **createAssetsSharedLink**
|
||||
> SharedLinkResponseDto createAssetsSharedLink(createAssetsShareLinkDto)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AssetApi();
|
||||
final createAssetsShareLinkDto = CreateAssetsShareLinkDto(); // CreateAssetsShareLinkDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.createAssetsSharedLink(createAssetsShareLinkDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->createAssetsSharedLink: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**createAssetsShareLinkDto** | [**CreateAssetsShareLinkDto**](CreateAssetsShareLinkDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**SharedLinkResponseDto**](SharedLinkResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **deleteAsset**
|
||||
> List<DeleteAssetResponseDto> deleteAsset(deleteAssetDto)
|
||||
|
||||
|
@ -1274,8 +1160,8 @@ Name | Type | Description | Notes
|
|||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **removeAssetsFromSharedLink**
|
||||
> SharedLinkResponseDto removeAssetsFromSharedLink(removeAssetsDto, key)
|
||||
# **importFile**
|
||||
> AssetFileUploadResponseDto importFile(importAssetDto)
|
||||
|
||||
|
||||
|
||||
|
@ -1298,14 +1184,13 @@ import 'package:openapi/api.dart';
|
|||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AssetApi();
|
||||
final removeAssetsDto = RemoveAssetsDto(); // RemoveAssetsDto |
|
||||
final key = key_example; // String |
|
||||
final importAssetDto = ImportAssetDto(); // ImportAssetDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.removeAssetsFromSharedLink(removeAssetsDto, key);
|
||||
final result = api_instance.importFile(importAssetDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->removeAssetsFromSharedLink: $e\n');
|
||||
print('Exception when calling AssetApi->importFile: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1313,12 +1198,11 @@ try {
|
|||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**removeAssetsDto** | [**RemoveAssetsDto**](RemoveAssetsDto.md)| |
|
||||
**key** | **String**| | [optional]
|
||||
**importAssetDto** | [**ImportAssetDto**](ImportAssetDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**SharedLinkResponseDto**](SharedLinkResponseDto.md)
|
||||
[**AssetFileUploadResponseDto**](AssetFileUploadResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
|
@ -1507,7 +1391,7 @@ 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)
|
||||
|
||||
# **uploadFile**
|
||||
> AssetFileUploadResponseDto uploadFile(assetType, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, fileExtension, key, livePhotoData, sidecarData, isArchived, isVisible, duration)
|
||||
> AssetFileUploadResponseDto uploadFile(assetType, assetData, fileExtension, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, key, livePhotoData, sidecarData, isReadOnly, isArchived, isVisible, duration)
|
||||
|
||||
|
||||
|
||||
|
@ -1532,21 +1416,22 @@ import 'package:openapi/api.dart';
|
|||
final api_instance = AssetApi();
|
||||
final assetType = ; // AssetTypeEnum |
|
||||
final assetData = BINARY_DATA_HERE; // MultipartFile |
|
||||
final fileExtension = fileExtension_example; // String |
|
||||
final deviceAssetId = deviceAssetId_example; // String |
|
||||
final deviceId = deviceId_example; // String |
|
||||
final fileCreatedAt = 2013-10-20T19:20:30+01:00; // DateTime |
|
||||
final fileModifiedAt = 2013-10-20T19:20:30+01:00; // DateTime |
|
||||
final isFavorite = true; // bool |
|
||||
final fileExtension = fileExtension_example; // String |
|
||||
final key = key_example; // String |
|
||||
final livePhotoData = BINARY_DATA_HERE; // MultipartFile |
|
||||
final sidecarData = BINARY_DATA_HERE; // MultipartFile |
|
||||
final isReadOnly = true; // bool |
|
||||
final isArchived = true; // bool |
|
||||
final isVisible = true; // bool |
|
||||
final duration = duration_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.uploadFile(assetType, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, fileExtension, key, livePhotoData, sidecarData, isArchived, isVisible, duration);
|
||||
final result = api_instance.uploadFile(assetType, assetData, fileExtension, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, key, livePhotoData, sidecarData, isReadOnly, isArchived, isVisible, duration);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->uploadFile: $e\n');
|
||||
|
@ -1559,15 +1444,16 @@ Name | Type | Description | Notes
|
|||
------------- | ------------- | ------------- | -------------
|
||||
**assetType** | [**AssetTypeEnum**](AssetTypeEnum.md)| |
|
||||
**assetData** | **MultipartFile**| |
|
||||
**fileExtension** | **String**| |
|
||||
**deviceAssetId** | **String**| |
|
||||
**deviceId** | **String**| |
|
||||
**fileCreatedAt** | **DateTime**| |
|
||||
**fileModifiedAt** | **DateTime**| |
|
||||
**isFavorite** | **bool**| |
|
||||
**fileExtension** | **String**| |
|
||||
**key** | **String**| | [optional]
|
||||
**livePhotoData** | **MultipartFile**| | [optional]
|
||||
**sidecarData** | **MultipartFile**| | [optional]
|
||||
**isReadOnly** | **bool**| | [optional] [default to false]
|
||||
**isArchived** | **bool**| | [optional]
|
||||
**isVisible** | **bool**| | [optional]
|
||||
**duration** | **String**| | [optional]
|
||||
|
|
20
mobile/openapi/doc/CreateAssetsShareLinkDto.md
generated
20
mobile/openapi/doc/CreateAssetsShareLinkDto.md
generated
|
@ -1,20 +0,0 @@
|
|||
# openapi.model.CreateAssetsShareLinkDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**assetIds** | **List<String>** | | [default to const []]
|
||||
**expiresAt** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**allowUpload** | **bool** | | [optional]
|
||||
**allowDownload** | **bool** | | [optional]
|
||||
**showExif** | **bool** | | [optional]
|
||||
**description** | **String** | | [optional]
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
1
mobile/openapi/doc/CreateUserDto.md
generated
1
mobile/openapi/doc/CreateUserDto.md
generated
|
@ -13,6 +13,7 @@ Name | Type | Description | Notes
|
|||
**firstName** | **String** | |
|
||||
**lastName** | **String** | |
|
||||
**storageLabel** | **String** | | [optional]
|
||||
**externalPath** | **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)
|
||||
|
||||
|
|
26
mobile/openapi/doc/ImportAssetDto.md
generated
Normal file
26
mobile/openapi/doc/ImportAssetDto.md
generated
Normal file
|
@ -0,0 +1,26 @@
|
|||
# openapi.model.ImportAssetDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**assetType** | [**AssetTypeEnum**](AssetTypeEnum.md) | |
|
||||
**isReadOnly** | **bool** | | [optional] [default to true]
|
||||
**assetPath** | **String** | |
|
||||
**sidecarPath** | **String** | | [optional]
|
||||
**deviceAssetId** | **String** | |
|
||||
**deviceId** | **String** | |
|
||||
**fileCreatedAt** | [**DateTime**](DateTime.md) | |
|
||||
**fileModifiedAt** | [**DateTime**](DateTime.md) | |
|
||||
**isFavorite** | **bool** | |
|
||||
**isArchived** | **bool** | | [optional]
|
||||
**isVisible** | **bool** | | [optional]
|
||||
**duration** | **String** | | [optional]
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# openapi.api.ShareApi
|
||||
# openapi.api.SharedLinkApi
|
||||
|
||||
## Load the API package
|
||||
```dart
|
||||
|
@ -9,13 +9,130 @@ All URIs are relative to */api*
|
|||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**getAllSharedLinks**](ShareApi.md#getallsharedlinks) | **GET** /share |
|
||||
[**getMySharedLink**](ShareApi.md#getmysharedlink) | **GET** /share/me |
|
||||
[**getSharedLinkById**](ShareApi.md#getsharedlinkbyid) | **GET** /share/{id} |
|
||||
[**removeSharedLink**](ShareApi.md#removesharedlink) | **DELETE** /share/{id} |
|
||||
[**updateSharedLink**](ShareApi.md#updatesharedlink) | **PATCH** /share/{id} |
|
||||
[**addSharedLinkAssets**](SharedLinkApi.md#addsharedlinkassets) | **PUT** /shared-link/{id}/assets |
|
||||
[**createSharedLink**](SharedLinkApi.md#createsharedlink) | **POST** /shared-link |
|
||||
[**getAllSharedLinks**](SharedLinkApi.md#getallsharedlinks) | **GET** /shared-link |
|
||||
[**getMySharedLink**](SharedLinkApi.md#getmysharedlink) | **GET** /shared-link/me |
|
||||
[**getSharedLinkById**](SharedLinkApi.md#getsharedlinkbyid) | **GET** /shared-link/{id} |
|
||||
[**removeSharedLink**](SharedLinkApi.md#removesharedlink) | **DELETE** /shared-link/{id} |
|
||||
[**removeSharedLinkAssets**](SharedLinkApi.md#removesharedlinkassets) | **DELETE** /shared-link/{id}/assets |
|
||||
[**updateSharedLink**](SharedLinkApi.md#updatesharedlink) | **PATCH** /shared-link/{id} |
|
||||
|
||||
|
||||
# **addSharedLinkAssets**
|
||||
> List<AssetIdsResponseDto> addSharedLinkAssets(id, assetIdsDto, key)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = SharedLinkApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final assetIdsDto = AssetIdsDto(); // AssetIdsDto |
|
||||
final key = key_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.addSharedLinkAssets(id, assetIdsDto, key);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling SharedLinkApi->addSharedLinkAssets: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**id** | **String**| |
|
||||
**assetIdsDto** | [**AssetIdsDto**](AssetIdsDto.md)| |
|
||||
**key** | **String**| | [optional]
|
||||
|
||||
### Return type
|
||||
|
||||
[**List<AssetIdsResponseDto>**](AssetIdsResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **createSharedLink**
|
||||
> SharedLinkResponseDto createSharedLink(sharedLinkCreateDto)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = SharedLinkApi();
|
||||
final sharedLinkCreateDto = SharedLinkCreateDto(); // SharedLinkCreateDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.createSharedLink(sharedLinkCreateDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling SharedLinkApi->createSharedLink: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**sharedLinkCreateDto** | [**SharedLinkCreateDto**](SharedLinkCreateDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**SharedLinkResponseDto**](SharedLinkResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **getAllSharedLinks**
|
||||
> List<SharedLinkResponseDto> getAllSharedLinks()
|
||||
|
||||
|
@ -39,13 +156,13 @@ import 'package:openapi/api.dart';
|
|||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = ShareApi();
|
||||
final api_instance = SharedLinkApi();
|
||||
|
||||
try {
|
||||
final result = api_instance.getAllSharedLinks();
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling ShareApi->getAllSharedLinks: $e\n');
|
||||
print('Exception when calling SharedLinkApi->getAllSharedLinks: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -90,14 +207,14 @@ import 'package:openapi/api.dart';
|
|||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = ShareApi();
|
||||
final api_instance = SharedLinkApi();
|
||||
final key = key_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.getMySharedLink(key);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling ShareApi->getMySharedLink: $e\n');
|
||||
print('Exception when calling SharedLinkApi->getMySharedLink: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -145,14 +262,14 @@ import 'package:openapi/api.dart';
|
|||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = ShareApi();
|
||||
final api_instance = SharedLinkApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.getSharedLinkById(id);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling ShareApi->getSharedLinkById: $e\n');
|
||||
print('Exception when calling SharedLinkApi->getSharedLinkById: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -200,13 +317,13 @@ import 'package:openapi/api.dart';
|
|||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = ShareApi();
|
||||
final api_instance = SharedLinkApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
|
||||
try {
|
||||
api_instance.removeSharedLink(id);
|
||||
} catch (e) {
|
||||
print('Exception when calling ShareApi->removeSharedLink: $e\n');
|
||||
print('Exception when calling SharedLinkApi->removeSharedLink: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -231,8 +348,8 @@ void (empty response body)
|
|||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **updateSharedLink**
|
||||
> SharedLinkResponseDto updateSharedLink(id, editSharedLinkDto)
|
||||
# **removeSharedLinkAssets**
|
||||
> List<AssetIdsResponseDto> removeSharedLinkAssets(id, assetIdsDto, key)
|
||||
|
||||
|
||||
|
||||
|
@ -254,15 +371,16 @@ import 'package:openapi/api.dart';
|
|||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = ShareApi();
|
||||
final api_instance = SharedLinkApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final editSharedLinkDto = EditSharedLinkDto(); // EditSharedLinkDto |
|
||||
final assetIdsDto = AssetIdsDto(); // AssetIdsDto |
|
||||
final key = key_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.updateSharedLink(id, editSharedLinkDto);
|
||||
final result = api_instance.removeSharedLinkAssets(id, assetIdsDto, key);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling ShareApi->updateSharedLink: $e\n');
|
||||
print('Exception when calling SharedLinkApi->removeSharedLinkAssets: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -271,7 +389,65 @@ try {
|
|||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**id** | **String**| |
|
||||
**editSharedLinkDto** | [**EditSharedLinkDto**](EditSharedLinkDto.md)| |
|
||||
**assetIdsDto** | [**AssetIdsDto**](AssetIdsDto.md)| |
|
||||
**key** | **String**| | [optional]
|
||||
|
||||
### Return type
|
||||
|
||||
[**List<AssetIdsResponseDto>**](AssetIdsResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **updateSharedLink**
|
||||
> SharedLinkResponseDto updateSharedLink(id, sharedLinkEditDto)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = SharedLinkApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final sharedLinkEditDto = SharedLinkEditDto(); // SharedLinkEditDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.updateSharedLink(id, sharedLinkEditDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling SharedLinkApi->updateSharedLink: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**id** | **String**| |
|
||||
**sharedLinkEditDto** | [**SharedLinkEditDto**](SharedLinkEditDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# openapi.model.CreateAlbumShareLinkDto
|
||||
# openapi.model.SharedLinkCreateDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
|
@ -8,12 +8,14 @@ import 'package:openapi/api.dart';
|
|||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**albumId** | **String** | |
|
||||
**expiresAt** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**allowUpload** | **bool** | | [optional]
|
||||
**allowDownload** | **bool** | | [optional]
|
||||
**showExif** | **bool** | | [optional]
|
||||
**type** | [**SharedLinkType**](SharedLinkType.md) | |
|
||||
**assetIds** | **List<String>** | | [optional] [default to const []]
|
||||
**albumId** | **String** | | [optional]
|
||||
**description** | **String** | | [optional]
|
||||
**expiresAt** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**allowUpload** | **bool** | | [optional] [default to false]
|
||||
**allowDownload** | **bool** | | [optional] [default to true]
|
||||
**showExif** | **bool** | | [optional] [default to true]
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# openapi.model.EditSharedLinkDto
|
||||
# openapi.model.SharedLinkEditDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
2
mobile/openapi/doc/SharedLinkResponseDto.md
generated
2
mobile/openapi/doc/SharedLinkResponseDto.md
generated
|
@ -10,7 +10,7 @@ Name | Type | Description | Notes
|
|||
------------ | ------------- | ------------- | -------------
|
||||
**type** | [**SharedLinkType**](SharedLinkType.md) | |
|
||||
**id** | **String** | |
|
||||
**description** | **String** | | [optional]
|
||||
**description** | **String** | |
|
||||
**userId** | **String** | |
|
||||
**key** | **String** | |
|
||||
**createdAt** | [**DateTime**](DateTime.md) | |
|
||||
|
|
1
mobile/openapi/doc/UpdateUserDto.md
generated
1
mobile/openapi/doc/UpdateUserDto.md
generated
|
@ -14,6 +14,7 @@ Name | Type | Description | Notes
|
|||
**firstName** | **String** | | [optional]
|
||||
**lastName** | **String** | | [optional]
|
||||
**storageLabel** | **String** | | [optional]
|
||||
**externalPath** | **String** | | [optional]
|
||||
**isAdmin** | **bool** | | [optional]
|
||||
**shouldChangePassword** | **bool** | | [optional]
|
||||
|
||||
|
|
1
mobile/openapi/doc/UserResponseDto.md
generated
1
mobile/openapi/doc/UserResponseDto.md
generated
|
@ -13,6 +13,7 @@ Name | Type | Description | Notes
|
|||
**firstName** | **String** | |
|
||||
**lastName** | **String** | |
|
||||
**storageLabel** | **String** | |
|
||||
**externalPath** | **String** | |
|
||||
**profileImagePath** | **String** | |
|
||||
**shouldChangePassword** | **bool** | |
|
||||
**isAdmin** | **bool** | |
|
||||
|
|
8
mobile/openapi/lib/api.dart
generated
8
mobile/openapi/lib/api.dart
generated
|
@ -38,7 +38,7 @@ part 'api/partner_api.dart';
|
|||
part 'api/person_api.dart';
|
||||
part 'api/search_api.dart';
|
||||
part 'api/server_info_api.dart';
|
||||
part 'api/share_api.dart';
|
||||
part 'api/shared_link_api.dart';
|
||||
part 'api/system_config_api.dart';
|
||||
part 'api/tag_api.dart';
|
||||
part 'api/user_api.dart';
|
||||
|
@ -73,8 +73,6 @@ part 'model/check_duplicate_asset_response_dto.dart';
|
|||
part 'model/check_existing_assets_dto.dart';
|
||||
part 'model/check_existing_assets_response_dto.dart';
|
||||
part 'model/create_album_dto.dart';
|
||||
part 'model/create_album_share_link_dto.dart';
|
||||
part 'model/create_assets_share_link_dto.dart';
|
||||
part 'model/create_profile_image_response_dto.dart';
|
||||
part 'model/create_tag_dto.dart';
|
||||
part 'model/create_user_dto.dart';
|
||||
|
@ -84,10 +82,10 @@ part 'model/delete_asset_dto.dart';
|
|||
part 'model/delete_asset_response_dto.dart';
|
||||
part 'model/delete_asset_status.dart';
|
||||
part 'model/download_files_dto.dart';
|
||||
part 'model/edit_shared_link_dto.dart';
|
||||
part 'model/exif_response_dto.dart';
|
||||
part 'model/get_asset_by_time_bucket_dto.dart';
|
||||
part 'model/get_asset_count_by_time_bucket_dto.dart';
|
||||
part 'model/import_asset_dto.dart';
|
||||
part 'model/job_command.dart';
|
||||
part 'model/job_command_dto.dart';
|
||||
part 'model/job_counts_dto.dart';
|
||||
|
@ -119,6 +117,8 @@ part 'model/server_info_response_dto.dart';
|
|||
part 'model/server_ping_response.dart';
|
||||
part 'model/server_stats_response_dto.dart';
|
||||
part 'model/server_version_reponse_dto.dart';
|
||||
part 'model/shared_link_create_dto.dart';
|
||||
part 'model/shared_link_edit_dto.dart';
|
||||
part 'model/shared_link_response_dto.dart';
|
||||
part 'model/shared_link_type.dart';
|
||||
part 'model/sign_up_dto.dart';
|
||||
|
|
47
mobile/openapi/lib/api/album_api.dart
generated
47
mobile/openapi/lib/api/album_api.dart
generated
|
@ -175,53 +175,6 @@ class AlbumApi {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'POST /album/create-shared-link' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [CreateAlbumShareLinkDto] createAlbumShareLinkDto (required):
|
||||
Future<Response> createAlbumSharedLinkWithHttpInfo(CreateAlbumShareLinkDto createAlbumShareLinkDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/album/create-shared-link';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = createAlbumShareLinkDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'POST',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [CreateAlbumShareLinkDto] createAlbumShareLinkDto (required):
|
||||
Future<SharedLinkResponseDto?> createAlbumSharedLink(CreateAlbumShareLinkDto createAlbumShareLinkDto,) async {
|
||||
final response = await createAlbumSharedLinkWithHttpInfo(createAlbumShareLinkDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'DELETE /album/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
|
|
160
mobile/openapi/lib/api/asset_api.dart
generated
160
mobile/openapi/lib/api/asset_api.dart
generated
|
@ -16,61 +16,6 @@ class AssetApi {
|
|||
|
||||
final ApiClient apiClient;
|
||||
|
||||
/// Performs an HTTP 'PATCH /asset/shared-link/add' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [AddAssetsDto] addAssetsDto (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<Response> addAssetsToSharedLinkWithHttpInfo(AddAssetsDto addAssetsDto, { String? key, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/asset/shared-link/add';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = addAssetsDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
if (key != null) {
|
||||
queryParams.addAll(_queryParams('', 'key', key));
|
||||
}
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'PATCH',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [AddAssetsDto] addAssetsDto (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<SharedLinkResponseDto?> addAssetsToSharedLink(AddAssetsDto addAssetsDto, { String? key, }) async {
|
||||
final response = await addAssetsToSharedLinkWithHttpInfo(addAssetsDto, key: key, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Checks if assets exist by checksums
|
||||
///
|
||||
/// Note: This method returns the HTTP [Response].
|
||||
|
@ -235,53 +180,6 @@ class AssetApi {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'POST /asset/shared-link' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [CreateAssetsShareLinkDto] createAssetsShareLinkDto (required):
|
||||
Future<Response> createAssetsSharedLinkWithHttpInfo(CreateAssetsShareLinkDto createAssetsShareLinkDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/asset/shared-link';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = createAssetsShareLinkDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'POST',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [CreateAssetsShareLinkDto] createAssetsShareLinkDto (required):
|
||||
Future<SharedLinkResponseDto?> createAssetsSharedLink(CreateAssetsShareLinkDto createAssetsShareLinkDto,) async {
|
||||
final response = await createAssetsSharedLinkWithHttpInfo(createAssetsShareLinkDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'DELETE /asset' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
|
@ -1225,33 +1123,27 @@ class AssetApi {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'PATCH /asset/shared-link/remove' operation and returns the [Response].
|
||||
/// Performs an HTTP 'POST /asset/import' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [RemoveAssetsDto] removeAssetsDto (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<Response> removeAssetsFromSharedLinkWithHttpInfo(RemoveAssetsDto removeAssetsDto, { String? key, }) async {
|
||||
/// * [ImportAssetDto] importAssetDto (required):
|
||||
Future<Response> importFileWithHttpInfo(ImportAssetDto importAssetDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/asset/shared-link/remove';
|
||||
final path = r'/asset/import';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = removeAssetsDto;
|
||||
Object? postBody = importAssetDto;
|
||||
|
||||
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',
|
||||
'POST',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
|
@ -1262,11 +1154,9 @@ class AssetApi {
|
|||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [RemoveAssetsDto] removeAssetsDto (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<SharedLinkResponseDto?> removeAssetsFromSharedLink(RemoveAssetsDto removeAssetsDto, { String? key, }) async {
|
||||
final response = await removeAssetsFromSharedLinkWithHttpInfo(removeAssetsDto, key: key, );
|
||||
/// * [ImportAssetDto] importAssetDto (required):
|
||||
Future<AssetFileUploadResponseDto?> importFile(ImportAssetDto importAssetDto,) async {
|
||||
final response = await importFileWithHttpInfo(importAssetDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
|
@ -1274,7 +1164,7 @@ class AssetApi {
|
|||
// 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 await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AssetFileUploadResponseDto',) as AssetFileUploadResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
|
@ -1464,6 +1354,8 @@ class AssetApi {
|
|||
///
|
||||
/// * [MultipartFile] assetData (required):
|
||||
///
|
||||
/// * [String] fileExtension (required):
|
||||
///
|
||||
/// * [String] deviceAssetId (required):
|
||||
///
|
||||
/// * [String] deviceId (required):
|
||||
|
@ -1474,20 +1366,20 @@ class AssetApi {
|
|||
///
|
||||
/// * [bool] isFavorite (required):
|
||||
///
|
||||
/// * [String] fileExtension (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
///
|
||||
/// * [MultipartFile] livePhotoData:
|
||||
///
|
||||
/// * [MultipartFile] sidecarData:
|
||||
///
|
||||
/// * [bool] isReadOnly:
|
||||
///
|
||||
/// * [bool] isArchived:
|
||||
///
|
||||
/// * [bool] isVisible:
|
||||
///
|
||||
/// * [String] duration:
|
||||
Future<Response> uploadFileWithHttpInfo(AssetTypeEnum assetType, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, bool isFavorite, String fileExtension, { String? key, MultipartFile? livePhotoData, MultipartFile? sidecarData, bool? isArchived, bool? isVisible, String? duration, }) async {
|
||||
Future<Response> uploadFileWithHttpInfo(AssetTypeEnum assetType, MultipartFile assetData, String fileExtension, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, bool isFavorite, { String? key, MultipartFile? livePhotoData, MultipartFile? sidecarData, bool? isReadOnly, bool? isArchived, bool? isVisible, String? duration, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/asset/upload';
|
||||
|
||||
|
@ -1525,6 +1417,14 @@ class AssetApi {
|
|||
mp.fields[r'sidecarData'] = sidecarData.field;
|
||||
mp.files.add(sidecarData);
|
||||
}
|
||||
if (isReadOnly != null) {
|
||||
hasFields = true;
|
||||
mp.fields[r'isReadOnly'] = parameterToString(isReadOnly);
|
||||
}
|
||||
if (fileExtension != null) {
|
||||
hasFields = true;
|
||||
mp.fields[r'fileExtension'] = parameterToString(fileExtension);
|
||||
}
|
||||
if (deviceAssetId != null) {
|
||||
hasFields = true;
|
||||
mp.fields[r'deviceAssetId'] = parameterToString(deviceAssetId);
|
||||
|
@ -1553,10 +1453,6 @@ class AssetApi {
|
|||
hasFields = true;
|
||||
mp.fields[r'isVisible'] = parameterToString(isVisible);
|
||||
}
|
||||
if (fileExtension != null) {
|
||||
hasFields = true;
|
||||
mp.fields[r'fileExtension'] = parameterToString(fileExtension);
|
||||
}
|
||||
if (duration != null) {
|
||||
hasFields = true;
|
||||
mp.fields[r'duration'] = parameterToString(duration);
|
||||
|
@ -1582,6 +1478,8 @@ class AssetApi {
|
|||
///
|
||||
/// * [MultipartFile] assetData (required):
|
||||
///
|
||||
/// * [String] fileExtension (required):
|
||||
///
|
||||
/// * [String] deviceAssetId (required):
|
||||
///
|
||||
/// * [String] deviceId (required):
|
||||
|
@ -1592,21 +1490,21 @@ class AssetApi {
|
|||
///
|
||||
/// * [bool] isFavorite (required):
|
||||
///
|
||||
/// * [String] fileExtension (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
///
|
||||
/// * [MultipartFile] livePhotoData:
|
||||
///
|
||||
/// * [MultipartFile] sidecarData:
|
||||
///
|
||||
/// * [bool] isReadOnly:
|
||||
///
|
||||
/// * [bool] isArchived:
|
||||
///
|
||||
/// * [bool] isVisible:
|
||||
///
|
||||
/// * [String] duration:
|
||||
Future<AssetFileUploadResponseDto?> uploadFile(AssetTypeEnum assetType, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, bool isFavorite, String fileExtension, { String? key, MultipartFile? livePhotoData, MultipartFile? sidecarData, bool? isArchived, bool? isVisible, String? duration, }) async {
|
||||
final response = await uploadFileWithHttpInfo(assetType, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, fileExtension, key: key, livePhotoData: livePhotoData, sidecarData: sidecarData, isArchived: isArchived, isVisible: isVisible, duration: duration, );
|
||||
Future<AssetFileUploadResponseDto?> uploadFile(AssetTypeEnum assetType, MultipartFile assetData, String fileExtension, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, bool isFavorite, { String? key, MultipartFile? livePhotoData, MultipartFile? sidecarData, bool? isReadOnly, bool? isArchived, bool? isVisible, String? duration, }) async {
|
||||
final response = await uploadFileWithHttpInfo(assetType, assetData, fileExtension, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, key: key, livePhotoData: livePhotoData, sidecarData: sidecarData, isReadOnly: isReadOnly, isArchived: isArchived, isVisible: isVisible, duration: duration, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
|
|
253
mobile/openapi/lib/api/share_api.dart
generated
253
mobile/openapi/lib/api/share_api.dart
generated
|
@ -1,253 +0,0 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
|
||||
class ShareApi {
|
||||
ShareApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient;
|
||||
|
||||
final ApiClient apiClient;
|
||||
|
||||
/// Performs an HTTP 'GET /share' operation and returns the [Response].
|
||||
Future<Response> getAllSharedLinksWithHttpInfo() async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/share';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<SharedLinkResponseDto>?> getAllSharedLinks() async {
|
||||
final response = await getAllSharedLinksWithHttpInfo();
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
final responseBody = await _decodeBodyBytes(response);
|
||||
return (await apiClient.deserializeAsync(responseBody, 'List<SharedLinkResponseDto>') as List)
|
||||
.cast<SharedLinkResponseDto>()
|
||||
.toList();
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /share/me' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<Response> getMySharedLinkWithHttpInfo({ String? key, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/share/me';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
if (key != null) {
|
||||
queryParams.addAll(_queryParams('', 'key', key));
|
||||
}
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<SharedLinkResponseDto?> getMySharedLink({ String? key, }) async {
|
||||
final response = await getMySharedLinkWithHttpInfo( key: key, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /share/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<Response> getSharedLinkByIdWithHttpInfo(String id,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/share/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<SharedLinkResponseDto?> getSharedLinkById(String id,) async {
|
||||
final response = await getSharedLinkByIdWithHttpInfo(id,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'DELETE /share/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<Response> removeSharedLinkWithHttpInfo(String id,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/share/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'DELETE',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<void> removeSharedLink(String id,) async {
|
||||
final response = await removeSharedLinkWithHttpInfo(id,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'PATCH /share/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [EditSharedLinkDto] editSharedLinkDto (required):
|
||||
Future<Response> updateSharedLinkWithHttpInfo(String id, EditSharedLinkDto editSharedLinkDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/share/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = editSharedLinkDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'PATCH',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [EditSharedLinkDto] editSharedLinkDto (required):
|
||||
Future<SharedLinkResponseDto?> updateSharedLink(String id, EditSharedLinkDto editSharedLinkDto,) async {
|
||||
final response = await updateSharedLinkWithHttpInfo(id, editSharedLinkDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
426
mobile/openapi/lib/api/shared_link_api.dart
generated
Normal file
426
mobile/openapi/lib/api/shared_link_api.dart
generated
Normal file
|
@ -0,0 +1,426 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
|
||||
class SharedLinkApi {
|
||||
SharedLinkApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient;
|
||||
|
||||
final ApiClient apiClient;
|
||||
|
||||
/// Performs an HTTP 'PUT /shared-link/{id}/assets' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [AssetIdsDto] assetIdsDto (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<Response> addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/shared-link/{id}/assets'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = assetIdsDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
if (key != null) {
|
||||
queryParams.addAll(_queryParams('', 'key', key));
|
||||
}
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'PUT',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [AssetIdsDto] assetIdsDto (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<List<AssetIdsResponseDto>?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async {
|
||||
final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
final responseBody = await _decodeBodyBytes(response);
|
||||
return (await apiClient.deserializeAsync(responseBody, 'List<AssetIdsResponseDto>') as List)
|
||||
.cast<AssetIdsResponseDto>()
|
||||
.toList();
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'POST /shared-link' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [SharedLinkCreateDto] sharedLinkCreateDto (required):
|
||||
Future<Response> createSharedLinkWithHttpInfo(SharedLinkCreateDto sharedLinkCreateDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/shared-link';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = sharedLinkCreateDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'POST',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [SharedLinkCreateDto] sharedLinkCreateDto (required):
|
||||
Future<SharedLinkResponseDto?> createSharedLink(SharedLinkCreateDto sharedLinkCreateDto,) async {
|
||||
final response = await createSharedLinkWithHttpInfo(sharedLinkCreateDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /shared-link' operation and returns the [Response].
|
||||
Future<Response> getAllSharedLinksWithHttpInfo() async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/shared-link';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<SharedLinkResponseDto>?> getAllSharedLinks() async {
|
||||
final response = await getAllSharedLinksWithHttpInfo();
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
final responseBody = await _decodeBodyBytes(response);
|
||||
return (await apiClient.deserializeAsync(responseBody, 'List<SharedLinkResponseDto>') as List)
|
||||
.cast<SharedLinkResponseDto>()
|
||||
.toList();
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /shared-link/me' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<Response> getMySharedLinkWithHttpInfo({ String? key, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/shared-link/me';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
if (key != null) {
|
||||
queryParams.addAll(_queryParams('', 'key', key));
|
||||
}
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<SharedLinkResponseDto?> getMySharedLink({ String? key, }) async {
|
||||
final response = await getMySharedLinkWithHttpInfo( key: key, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /shared-link/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<Response> getSharedLinkByIdWithHttpInfo(String id,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/shared-link/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<SharedLinkResponseDto?> getSharedLinkById(String id,) async {
|
||||
final response = await getSharedLinkByIdWithHttpInfo(id,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'DELETE /shared-link/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<Response> removeSharedLinkWithHttpInfo(String id,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/shared-link/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'DELETE',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<void> removeSharedLink(String id,) async {
|
||||
final response = await removeSharedLinkWithHttpInfo(id,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'DELETE /shared-link/{id}/assets' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [AssetIdsDto] assetIdsDto (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<Response> removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/shared-link/{id}/assets'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = assetIdsDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
if (key != null) {
|
||||
queryParams.addAll(_queryParams('', 'key', key));
|
||||
}
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'DELETE',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [AssetIdsDto] assetIdsDto (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<List<AssetIdsResponseDto>?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async {
|
||||
final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
final responseBody = await _decodeBodyBytes(response);
|
||||
return (await apiClient.deserializeAsync(responseBody, 'List<AssetIdsResponseDto>') as List)
|
||||
.cast<AssetIdsResponseDto>()
|
||||
.toList();
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'PATCH /shared-link/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [SharedLinkEditDto] sharedLinkEditDto (required):
|
||||
Future<Response> updateSharedLinkWithHttpInfo(String id, SharedLinkEditDto sharedLinkEditDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/shared-link/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = sharedLinkEditDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'PATCH',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [SharedLinkEditDto] sharedLinkEditDto (required):
|
||||
Future<SharedLinkResponseDto?> updateSharedLink(String id, SharedLinkEditDto sharedLinkEditDto,) async {
|
||||
final response = await updateSharedLinkWithHttpInfo(id, sharedLinkEditDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
12
mobile/openapi/lib/api_client.dart
generated
12
mobile/openapi/lib/api_client.dart
generated
|
@ -241,10 +241,6 @@ class ApiClient {
|
|||
return CheckExistingAssetsResponseDto.fromJson(value);
|
||||
case 'CreateAlbumDto':
|
||||
return CreateAlbumDto.fromJson(value);
|
||||
case 'CreateAlbumShareLinkDto':
|
||||
return CreateAlbumShareLinkDto.fromJson(value);
|
||||
case 'CreateAssetsShareLinkDto':
|
||||
return CreateAssetsShareLinkDto.fromJson(value);
|
||||
case 'CreateProfileImageResponseDto':
|
||||
return CreateProfileImageResponseDto.fromJson(value);
|
||||
case 'CreateTagDto':
|
||||
|
@ -263,14 +259,14 @@ class ApiClient {
|
|||
return DeleteAssetStatusTypeTransformer().decode(value);
|
||||
case 'DownloadFilesDto':
|
||||
return DownloadFilesDto.fromJson(value);
|
||||
case 'EditSharedLinkDto':
|
||||
return EditSharedLinkDto.fromJson(value);
|
||||
case 'ExifResponseDto':
|
||||
return ExifResponseDto.fromJson(value);
|
||||
case 'GetAssetByTimeBucketDto':
|
||||
return GetAssetByTimeBucketDto.fromJson(value);
|
||||
case 'GetAssetCountByTimeBucketDto':
|
||||
return GetAssetCountByTimeBucketDto.fromJson(value);
|
||||
case 'ImportAssetDto':
|
||||
return ImportAssetDto.fromJson(value);
|
||||
case 'JobCommand':
|
||||
return JobCommandTypeTransformer().decode(value);
|
||||
case 'JobCommandDto':
|
||||
|
@ -333,6 +329,10 @@ class ApiClient {
|
|||
return ServerStatsResponseDto.fromJson(value);
|
||||
case 'ServerVersionReponseDto':
|
||||
return ServerVersionReponseDto.fromJson(value);
|
||||
case 'SharedLinkCreateDto':
|
||||
return SharedLinkCreateDto.fromJson(value);
|
||||
case 'SharedLinkEditDto':
|
||||
return SharedLinkEditDto.fromJson(value);
|
||||
case 'SharedLinkResponseDto':
|
||||
return SharedLinkResponseDto.fromJson(value);
|
||||
case 'SharedLinkType':
|
||||
|
|
23
mobile/openapi/lib/model/album_response_dto.dart
generated
23
mobile/openapi/lib/model/album_response_dto.dart
generated
|
@ -24,6 +24,7 @@ class AlbumResponseDto {
|
|||
this.sharedUsers = const [],
|
||||
this.assets = const [],
|
||||
required this.owner,
|
||||
this.lastModifiedAssetTimestamp,
|
||||
});
|
||||
|
||||
int assetCount;
|
||||
|
@ -48,6 +49,14 @@ class AlbumResponseDto {
|
|||
|
||||
UserResponseDto owner;
|
||||
|
||||
///
|
||||
/// 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? lastModifiedAssetTimestamp;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AlbumResponseDto &&
|
||||
other.assetCount == assetCount &&
|
||||
|
@ -60,7 +69,8 @@ class AlbumResponseDto {
|
|||
other.shared == shared &&
|
||||
other.sharedUsers == sharedUsers &&
|
||||
other.assets == assets &&
|
||||
other.owner == owner;
|
||||
other.owner == owner &&
|
||||
other.lastModifiedAssetTimestamp == lastModifiedAssetTimestamp;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
|
@ -75,10 +85,11 @@ class AlbumResponseDto {
|
|||
(shared.hashCode) +
|
||||
(sharedUsers.hashCode) +
|
||||
(assets.hashCode) +
|
||||
(owner.hashCode);
|
||||
(owner.hashCode) +
|
||||
(lastModifiedAssetTimestamp == null ? 0 : lastModifiedAssetTimestamp!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, updatedAt=$updatedAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets, owner=$owner]';
|
||||
String toString() => 'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, updatedAt=$updatedAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets, owner=$owner, lastModifiedAssetTimestamp=$lastModifiedAssetTimestamp]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -97,6 +108,11 @@ class AlbumResponseDto {
|
|||
json[r'sharedUsers'] = this.sharedUsers;
|
||||
json[r'assets'] = this.assets;
|
||||
json[r'owner'] = this.owner;
|
||||
if (this.lastModifiedAssetTimestamp != null) {
|
||||
json[r'lastModifiedAssetTimestamp'] = this.lastModifiedAssetTimestamp!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'lastModifiedAssetTimestamp'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
|
@ -130,6 +146,7 @@ class AlbumResponseDto {
|
|||
sharedUsers: UserResponseDto.listFromJson(json[r'sharedUsers']),
|
||||
assets: AssetResponseDto.listFromJson(json[r'assets']),
|
||||
owner: UserResponseDto.fromJson(json[r'owner'])!,
|
||||
lastModifiedAssetTimestamp: mapDateTime(json, r'lastModifiedAssetTimestamp', ''),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -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',
|
||||
};
|
||||
}
|
||||
|
17
mobile/openapi/lib/model/create_user_dto.dart
generated
17
mobile/openapi/lib/model/create_user_dto.dart
generated
|
@ -18,6 +18,7 @@ class CreateUserDto {
|
|||
required this.firstName,
|
||||
required this.lastName,
|
||||
this.storageLabel,
|
||||
this.externalPath,
|
||||
});
|
||||
|
||||
String email;
|
||||
|
@ -30,13 +31,16 @@ class CreateUserDto {
|
|||
|
||||
String? storageLabel;
|
||||
|
||||
String? externalPath;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is CreateUserDto &&
|
||||
other.email == email &&
|
||||
other.password == password &&
|
||||
other.firstName == firstName &&
|
||||
other.lastName == lastName &&
|
||||
other.storageLabel == storageLabel;
|
||||
other.storageLabel == storageLabel &&
|
||||
other.externalPath == externalPath;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
|
@ -45,10 +49,11 @@ class CreateUserDto {
|
|||
(password.hashCode) +
|
||||
(firstName.hashCode) +
|
||||
(lastName.hashCode) +
|
||||
(storageLabel == null ? 0 : storageLabel!.hashCode);
|
||||
(storageLabel == null ? 0 : storageLabel!.hashCode) +
|
||||
(externalPath == null ? 0 : externalPath!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'CreateUserDto[email=$email, password=$password, firstName=$firstName, lastName=$lastName, storageLabel=$storageLabel]';
|
||||
String toString() => 'CreateUserDto[email=$email, password=$password, firstName=$firstName, lastName=$lastName, storageLabel=$storageLabel, externalPath=$externalPath]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -61,6 +66,11 @@ class CreateUserDto {
|
|||
} else {
|
||||
// json[r'storageLabel'] = null;
|
||||
}
|
||||
if (this.externalPath != null) {
|
||||
json[r'externalPath'] = this.externalPath;
|
||||
} else {
|
||||
// json[r'externalPath'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
|
@ -88,6 +98,7 @@ class CreateUserDto {
|
|||
firstName: mapValueOfType<String>(json, r'firstName')!,
|
||||
lastName: mapValueOfType<String>(json, r'lastName')!,
|
||||
storageLabel: mapValueOfType<String>(json, r'storageLabel'),
|
||||
externalPath: mapValueOfType<String>(json, r'externalPath'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
|
232
mobile/openapi/lib/model/import_asset_dto.dart
generated
Normal file
232
mobile/openapi/lib/model/import_asset_dto.dart
generated
Normal file
|
@ -0,0 +1,232 @@
|
|||
//
|
||||
// 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 ImportAssetDto {
|
||||
/// Returns a new [ImportAssetDto] instance.
|
||||
ImportAssetDto({
|
||||
required this.assetType,
|
||||
this.isReadOnly = true,
|
||||
required this.assetPath,
|
||||
this.sidecarPath,
|
||||
required this.deviceAssetId,
|
||||
required this.deviceId,
|
||||
required this.fileCreatedAt,
|
||||
required this.fileModifiedAt,
|
||||
required this.isFavorite,
|
||||
this.isArchived,
|
||||
this.isVisible,
|
||||
this.duration,
|
||||
});
|
||||
|
||||
AssetTypeEnum assetType;
|
||||
|
||||
bool isReadOnly;
|
||||
|
||||
String assetPath;
|
||||
|
||||
///
|
||||
/// 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? sidecarPath;
|
||||
|
||||
String deviceAssetId;
|
||||
|
||||
String deviceId;
|
||||
|
||||
DateTime fileCreatedAt;
|
||||
|
||||
DateTime fileModifiedAt;
|
||||
|
||||
bool isFavorite;
|
||||
|
||||
///
|
||||
/// 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? isArchived;
|
||||
|
||||
///
|
||||
/// 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? isVisible;
|
||||
|
||||
///
|
||||
/// 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? duration;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is ImportAssetDto &&
|
||||
other.assetType == assetType &&
|
||||
other.isReadOnly == isReadOnly &&
|
||||
other.assetPath == assetPath &&
|
||||
other.sidecarPath == sidecarPath &&
|
||||
other.deviceAssetId == deviceAssetId &&
|
||||
other.deviceId == deviceId &&
|
||||
other.fileCreatedAt == fileCreatedAt &&
|
||||
other.fileModifiedAt == fileModifiedAt &&
|
||||
other.isFavorite == isFavorite &&
|
||||
other.isArchived == isArchived &&
|
||||
other.isVisible == isVisible &&
|
||||
other.duration == duration;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(assetType.hashCode) +
|
||||
(isReadOnly.hashCode) +
|
||||
(assetPath.hashCode) +
|
||||
(sidecarPath == null ? 0 : sidecarPath!.hashCode) +
|
||||
(deviceAssetId.hashCode) +
|
||||
(deviceId.hashCode) +
|
||||
(fileCreatedAt.hashCode) +
|
||||
(fileModifiedAt.hashCode) +
|
||||
(isFavorite.hashCode) +
|
||||
(isArchived == null ? 0 : isArchived!.hashCode) +
|
||||
(isVisible == null ? 0 : isVisible!.hashCode) +
|
||||
(duration == null ? 0 : duration!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'ImportAssetDto[assetType=$assetType, isReadOnly=$isReadOnly, assetPath=$assetPath, sidecarPath=$sidecarPath, deviceAssetId=$deviceAssetId, deviceId=$deviceId, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, isFavorite=$isFavorite, isArchived=$isArchived, isVisible=$isVisible, duration=$duration]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'assetType'] = this.assetType;
|
||||
json[r'isReadOnly'] = this.isReadOnly;
|
||||
json[r'assetPath'] = this.assetPath;
|
||||
if (this.sidecarPath != null) {
|
||||
json[r'sidecarPath'] = this.sidecarPath;
|
||||
} else {
|
||||
// json[r'sidecarPath'] = null;
|
||||
}
|
||||
json[r'deviceAssetId'] = this.deviceAssetId;
|
||||
json[r'deviceId'] = this.deviceId;
|
||||
json[r'fileCreatedAt'] = this.fileCreatedAt.toUtc().toIso8601String();
|
||||
json[r'fileModifiedAt'] = this.fileModifiedAt.toUtc().toIso8601String();
|
||||
json[r'isFavorite'] = this.isFavorite;
|
||||
if (this.isArchived != null) {
|
||||
json[r'isArchived'] = this.isArchived;
|
||||
} else {
|
||||
// json[r'isArchived'] = null;
|
||||
}
|
||||
if (this.isVisible != null) {
|
||||
json[r'isVisible'] = this.isVisible;
|
||||
} else {
|
||||
// json[r'isVisible'] = null;
|
||||
}
|
||||
if (this.duration != null) {
|
||||
json[r'duration'] = this.duration;
|
||||
} else {
|
||||
// json[r'duration'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [ImportAssetDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static ImportAssetDto? 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 "ImportAssetDto[$key]" is missing from JSON.');
|
||||
assert(json[key] != null, 'Required key "ImportAssetDto[$key]" has a null value in JSON.');
|
||||
});
|
||||
return true;
|
||||
}());
|
||||
|
||||
return ImportAssetDto(
|
||||
assetType: AssetTypeEnum.fromJson(json[r'assetType'])!,
|
||||
isReadOnly: mapValueOfType<bool>(json, r'isReadOnly') ?? true,
|
||||
assetPath: mapValueOfType<String>(json, r'assetPath')!,
|
||||
sidecarPath: mapValueOfType<String>(json, r'sidecarPath'),
|
||||
deviceAssetId: mapValueOfType<String>(json, r'deviceAssetId')!,
|
||||
deviceId: mapValueOfType<String>(json, r'deviceId')!,
|
||||
fileCreatedAt: mapDateTime(json, r'fileCreatedAt', '')!,
|
||||
fileModifiedAt: mapDateTime(json, r'fileModifiedAt', '')!,
|
||||
isFavorite: mapValueOfType<bool>(json, r'isFavorite')!,
|
||||
isArchived: mapValueOfType<bool>(json, r'isArchived'),
|
||||
isVisible: mapValueOfType<bool>(json, r'isVisible'),
|
||||
duration: mapValueOfType<String>(json, r'duration'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<ImportAssetDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <ImportAssetDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = ImportAssetDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, ImportAssetDto> mapFromJson(dynamic json) {
|
||||
final map = <String, ImportAssetDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = ImportAssetDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of ImportAssetDto-objects as value to a dart map
|
||||
static Map<String, List<ImportAssetDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<ImportAssetDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = ImportAssetDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'assetType',
|
||||
'assetPath',
|
||||
'deviceAssetId',
|
||||
'deviceId',
|
||||
'fileCreatedAt',
|
||||
'fileModifiedAt',
|
||||
'isFavorite',
|
||||
};
|
||||
}
|
||||
|
|
@ -10,17 +10,21 @@
|
|||
|
||||
part of openapi.api;
|
||||
|
||||
class CreateAssetsShareLinkDto {
|
||||
/// Returns a new [CreateAssetsShareLinkDto] instance.
|
||||
CreateAssetsShareLinkDto({
|
||||
class SharedLinkCreateDto {
|
||||
/// Returns a new [SharedLinkCreateDto] instance.
|
||||
SharedLinkCreateDto({
|
||||
required this.type,
|
||||
this.assetIds = const [],
|
||||
this.expiresAt,
|
||||
this.allowUpload,
|
||||
this.allowDownload,
|
||||
this.showExif,
|
||||
this.albumId,
|
||||
this.description,
|
||||
this.expiresAt,
|
||||
this.allowUpload = false,
|
||||
this.allowDownload = true,
|
||||
this.showExif = true,
|
||||
});
|
||||
|
||||
SharedLinkType type;
|
||||
|
||||
List<String> assetIds;
|
||||
|
||||
///
|
||||
|
@ -29,31 +33,7 @@ class CreateAssetsShareLinkDto {
|
|||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? expiresAt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? allowUpload;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? allowDownload;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? showExif;
|
||||
String? albumId;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
|
@ -63,63 +43,69 @@ class CreateAssetsShareLinkDto {
|
|||
///
|
||||
String? description;
|
||||
|
||||
DateTime? expiresAt;
|
||||
|
||||
bool allowUpload;
|
||||
|
||||
bool allowDownload;
|
||||
|
||||
bool showExif;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is CreateAssetsShareLinkDto &&
|
||||
bool operator ==(Object other) => identical(this, other) || other is SharedLinkCreateDto &&
|
||||
other.type == type &&
|
||||
other.assetIds == assetIds &&
|
||||
other.albumId == albumId &&
|
||||
other.description == description &&
|
||||
other.expiresAt == expiresAt &&
|
||||
other.allowUpload == allowUpload &&
|
||||
other.allowDownload == allowDownload &&
|
||||
other.showExif == showExif &&
|
||||
other.description == description;
|
||||
other.showExif == showExif;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(type.hashCode) +
|
||||
(assetIds.hashCode) +
|
||||
(albumId == null ? 0 : albumId!.hashCode) +
|
||||
(description == null ? 0 : description!.hashCode) +
|
||||
(expiresAt == null ? 0 : expiresAt!.hashCode) +
|
||||
(allowUpload == null ? 0 : allowUpload!.hashCode) +
|
||||
(allowDownload == null ? 0 : allowDownload!.hashCode) +
|
||||
(showExif == null ? 0 : showExif!.hashCode) +
|
||||
(description == null ? 0 : description!.hashCode);
|
||||
(allowUpload.hashCode) +
|
||||
(allowDownload.hashCode) +
|
||||
(showExif.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'CreateAssetsShareLinkDto[assetIds=$assetIds, expiresAt=$expiresAt, allowUpload=$allowUpload, allowDownload=$allowDownload, showExif=$showExif, description=$description]';
|
||||
String toString() => 'SharedLinkCreateDto[type=$type, assetIds=$assetIds, albumId=$albumId, description=$description, expiresAt=$expiresAt, allowUpload=$allowUpload, allowDownload=$allowDownload, showExif=$showExif]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'type'] = this.type;
|
||||
json[r'assetIds'] = this.assetIds;
|
||||
if (this.expiresAt != null) {
|
||||
json[r'expiresAt'] = this.expiresAt!.toUtc().toIso8601String();
|
||||
if (this.albumId != null) {
|
||||
json[r'albumId'] = this.albumId;
|
||||
} else {
|
||||
// json[r'expiresAt'] = null;
|
||||
}
|
||||
if (this.allowUpload != null) {
|
||||
json[r'allowUpload'] = this.allowUpload;
|
||||
} else {
|
||||
// json[r'allowUpload'] = null;
|
||||
}
|
||||
if (this.allowDownload != null) {
|
||||
json[r'allowDownload'] = this.allowDownload;
|
||||
} else {
|
||||
// json[r'allowDownload'] = null;
|
||||
}
|
||||
if (this.showExif != null) {
|
||||
json[r'showExif'] = this.showExif;
|
||||
} else {
|
||||
// json[r'showExif'] = null;
|
||||
// json[r'albumId'] = null;
|
||||
}
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
} else {
|
||||
// json[r'description'] = null;
|
||||
}
|
||||
if (this.expiresAt != null) {
|
||||
json[r'expiresAt'] = this.expiresAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'expiresAt'] = null;
|
||||
}
|
||||
json[r'allowUpload'] = this.allowUpload;
|
||||
json[r'allowDownload'] = this.allowDownload;
|
||||
json[r'showExif'] = this.showExif;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [CreateAssetsShareLinkDto] instance and imports its values from
|
||||
/// Returns a new [SharedLinkCreateDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static CreateAssetsShareLinkDto? fromJson(dynamic value) {
|
||||
static SharedLinkCreateDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
|
@ -128,31 +114,33 @@ class CreateAssetsShareLinkDto {
|
|||
// Note 2: this code is stripped in release mode!
|
||||
assert(() {
|
||||
requiredKeys.forEach((key) {
|
||||
assert(json.containsKey(key), 'Required key "CreateAssetsShareLinkDto[$key]" is missing from JSON.');
|
||||
assert(json[key] != null, 'Required key "CreateAssetsShareLinkDto[$key]" has a null value in JSON.');
|
||||
assert(json.containsKey(key), 'Required key "SharedLinkCreateDto[$key]" is missing from JSON.');
|
||||
assert(json[key] != null, 'Required key "SharedLinkCreateDto[$key]" has a null value in JSON.');
|
||||
});
|
||||
return true;
|
||||
}());
|
||||
|
||||
return CreateAssetsShareLinkDto(
|
||||
return SharedLinkCreateDto(
|
||||
type: SharedLinkType.fromJson(json[r'type'])!,
|
||||
assetIds: json[r'assetIds'] is Iterable
|
||||
? (json[r'assetIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
expiresAt: mapDateTime(json, r'expiresAt', ''),
|
||||
allowUpload: mapValueOfType<bool>(json, r'allowUpload'),
|
||||
allowDownload: mapValueOfType<bool>(json, r'allowDownload'),
|
||||
showExif: mapValueOfType<bool>(json, r'showExif'),
|
||||
albumId: mapValueOfType<String>(json, r'albumId'),
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
expiresAt: mapDateTime(json, r'expiresAt', ''),
|
||||
allowUpload: mapValueOfType<bool>(json, r'allowUpload') ?? false,
|
||||
allowDownload: mapValueOfType<bool>(json, r'allowDownload') ?? true,
|
||||
showExif: mapValueOfType<bool>(json, r'showExif') ?? true,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<CreateAssetsShareLinkDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <CreateAssetsShareLinkDto>[];
|
||||
static List<SharedLinkCreateDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SharedLinkCreateDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = CreateAssetsShareLinkDto.fromJson(row);
|
||||
final value = SharedLinkCreateDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
|
@ -161,12 +149,12 @@ class CreateAssetsShareLinkDto {
|
|||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, CreateAssetsShareLinkDto> mapFromJson(dynamic json) {
|
||||
final map = <String, CreateAssetsShareLinkDto>{};
|
||||
static Map<String, SharedLinkCreateDto> mapFromJson(dynamic json) {
|
||||
final map = <String, SharedLinkCreateDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = CreateAssetsShareLinkDto.fromJson(entry.value);
|
||||
final value = SharedLinkCreateDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
|
@ -175,14 +163,14 @@ class CreateAssetsShareLinkDto {
|
|||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of CreateAssetsShareLinkDto-objects as value to a dart map
|
||||
static Map<String, List<CreateAssetsShareLinkDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<CreateAssetsShareLinkDto>>{};
|
||||
// maps a json object with a list of SharedLinkCreateDto-objects as value to a dart map
|
||||
static Map<String, List<SharedLinkCreateDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SharedLinkCreateDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = CreateAssetsShareLinkDto.listFromJson(entry.value, growable: growable,);
|
||||
map[entry.key] = SharedLinkCreateDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
|
@ -190,7 +178,7 @@ class CreateAssetsShareLinkDto {
|
|||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'assetIds',
|
||||
'type',
|
||||
};
|
||||
}
|
||||
|
|
@ -10,9 +10,9 @@
|
|||
|
||||
part of openapi.api;
|
||||
|
||||
class EditSharedLinkDto {
|
||||
/// Returns a new [EditSharedLinkDto] instance.
|
||||
EditSharedLinkDto({
|
||||
class SharedLinkEditDto {
|
||||
/// Returns a new [SharedLinkEditDto] instance.
|
||||
SharedLinkEditDto({
|
||||
this.description,
|
||||
this.expiresAt,
|
||||
this.allowUpload,
|
||||
|
@ -55,7 +55,7 @@ class EditSharedLinkDto {
|
|||
bool? showExif;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is EditSharedLinkDto &&
|
||||
bool operator ==(Object other) => identical(this, other) || other is SharedLinkEditDto &&
|
||||
other.description == description &&
|
||||
other.expiresAt == expiresAt &&
|
||||
other.allowUpload == allowUpload &&
|
||||
|
@ -72,7 +72,7 @@ class EditSharedLinkDto {
|
|||
(showExif == null ? 0 : showExif!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'EditSharedLinkDto[description=$description, expiresAt=$expiresAt, allowUpload=$allowUpload, allowDownload=$allowDownload, showExif=$showExif]';
|
||||
String toString() => 'SharedLinkEditDto[description=$description, expiresAt=$expiresAt, allowUpload=$allowUpload, allowDownload=$allowDownload, showExif=$showExif]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -104,10 +104,10 @@ class EditSharedLinkDto {
|
|||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [EditSharedLinkDto] instance and imports its values from
|
||||
/// Returns a new [SharedLinkEditDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static EditSharedLinkDto? fromJson(dynamic value) {
|
||||
static SharedLinkEditDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
|
@ -116,13 +116,13 @@ class EditSharedLinkDto {
|
|||
// Note 2: this code is stripped in release mode!
|
||||
assert(() {
|
||||
requiredKeys.forEach((key) {
|
||||
assert(json.containsKey(key), 'Required key "EditSharedLinkDto[$key]" is missing from JSON.');
|
||||
assert(json[key] != null, 'Required key "EditSharedLinkDto[$key]" has a null value in JSON.');
|
||||
assert(json.containsKey(key), 'Required key "SharedLinkEditDto[$key]" is missing from JSON.');
|
||||
assert(json[key] != null, 'Required key "SharedLinkEditDto[$key]" has a null value in JSON.');
|
||||
});
|
||||
return true;
|
||||
}());
|
||||
|
||||
return EditSharedLinkDto(
|
||||
return SharedLinkEditDto(
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
expiresAt: mapDateTime(json, r'expiresAt', ''),
|
||||
allowUpload: mapValueOfType<bool>(json, r'allowUpload'),
|
||||
|
@ -133,11 +133,11 @@ class EditSharedLinkDto {
|
|||
return null;
|
||||
}
|
||||
|
||||
static List<EditSharedLinkDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <EditSharedLinkDto>[];
|
||||
static List<SharedLinkEditDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SharedLinkEditDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = EditSharedLinkDto.fromJson(row);
|
||||
final value = SharedLinkEditDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
|
@ -146,12 +146,12 @@ class EditSharedLinkDto {
|
|||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, EditSharedLinkDto> mapFromJson(dynamic json) {
|
||||
final map = <String, EditSharedLinkDto>{};
|
||||
static Map<String, SharedLinkEditDto> mapFromJson(dynamic json) {
|
||||
final map = <String, SharedLinkEditDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = EditSharedLinkDto.fromJson(entry.value);
|
||||
final value = SharedLinkEditDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
|
@ -160,14 +160,14 @@ class EditSharedLinkDto {
|
|||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of EditSharedLinkDto-objects as value to a dart map
|
||||
static Map<String, List<EditSharedLinkDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<EditSharedLinkDto>>{};
|
||||
// maps a json object with a list of SharedLinkEditDto-objects as value to a dart map
|
||||
static Map<String, List<SharedLinkEditDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SharedLinkEditDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = EditSharedLinkDto.listFromJson(entry.value, growable: growable,);
|
||||
map[entry.key] = SharedLinkEditDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
|
@ -15,7 +15,7 @@ class SharedLinkResponseDto {
|
|||
SharedLinkResponseDto({
|
||||
required this.type,
|
||||
required this.id,
|
||||
this.description,
|
||||
required this.description,
|
||||
required this.userId,
|
||||
required this.key,
|
||||
required this.createdAt,
|
||||
|
@ -31,12 +31,6 @@ class SharedLinkResponseDto {
|
|||
|
||||
String id;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? description;
|
||||
|
||||
String userId;
|
||||
|
@ -206,6 +200,7 @@ class SharedLinkResponseDto {
|
|||
static const requiredKeys = <String>{
|
||||
'type',
|
||||
'id',
|
||||
'description',
|
||||
'userId',
|
||||
'key',
|
||||
'createdAt',
|
||||
|
|
19
mobile/openapi/lib/model/update_user_dto.dart
generated
19
mobile/openapi/lib/model/update_user_dto.dart
generated
|
@ -19,6 +19,7 @@ class UpdateUserDto {
|
|||
this.firstName,
|
||||
this.lastName,
|
||||
this.storageLabel,
|
||||
this.externalPath,
|
||||
this.isAdmin,
|
||||
this.shouldChangePassword,
|
||||
});
|
||||
|
@ -65,6 +66,14 @@ class UpdateUserDto {
|
|||
///
|
||||
String? storageLabel;
|
||||
|
||||
///
|
||||
/// 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? externalPath;
|
||||
|
||||
///
|
||||
/// 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
|
||||
|
@ -89,6 +98,7 @@ class UpdateUserDto {
|
|||
other.firstName == firstName &&
|
||||
other.lastName == lastName &&
|
||||
other.storageLabel == storageLabel &&
|
||||
other.externalPath == externalPath &&
|
||||
other.isAdmin == isAdmin &&
|
||||
other.shouldChangePassword == shouldChangePassword;
|
||||
|
||||
|
@ -101,11 +111,12 @@ class UpdateUserDto {
|
|||
(firstName == null ? 0 : firstName!.hashCode) +
|
||||
(lastName == null ? 0 : lastName!.hashCode) +
|
||||
(storageLabel == null ? 0 : storageLabel!.hashCode) +
|
||||
(externalPath == null ? 0 : externalPath!.hashCode) +
|
||||
(isAdmin == null ? 0 : isAdmin!.hashCode) +
|
||||
(shouldChangePassword == null ? 0 : shouldChangePassword!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'UpdateUserDto[id=$id, email=$email, password=$password, firstName=$firstName, lastName=$lastName, storageLabel=$storageLabel, isAdmin=$isAdmin, shouldChangePassword=$shouldChangePassword]';
|
||||
String toString() => 'UpdateUserDto[id=$id, email=$email, password=$password, firstName=$firstName, lastName=$lastName, storageLabel=$storageLabel, externalPath=$externalPath, isAdmin=$isAdmin, shouldChangePassword=$shouldChangePassword]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -135,6 +146,11 @@ class UpdateUserDto {
|
|||
} else {
|
||||
// json[r'storageLabel'] = null;
|
||||
}
|
||||
if (this.externalPath != null) {
|
||||
json[r'externalPath'] = this.externalPath;
|
||||
} else {
|
||||
// json[r'externalPath'] = null;
|
||||
}
|
||||
if (this.isAdmin != null) {
|
||||
json[r'isAdmin'] = this.isAdmin;
|
||||
} else {
|
||||
|
@ -173,6 +189,7 @@ class UpdateUserDto {
|
|||
firstName: mapValueOfType<String>(json, r'firstName'),
|
||||
lastName: mapValueOfType<String>(json, r'lastName'),
|
||||
storageLabel: mapValueOfType<String>(json, r'storageLabel'),
|
||||
externalPath: mapValueOfType<String>(json, r'externalPath'),
|
||||
isAdmin: mapValueOfType<bool>(json, r'isAdmin'),
|
||||
shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword'),
|
||||
);
|
||||
|
|
5
mobile/openapi/test/album_api_test.dart
generated
5
mobile/openapi/test/album_api_test.dart
generated
|
@ -32,11 +32,6 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
//Future<SharedLinkResponseDto> createAlbumSharedLink(CreateAlbumShareLinkDto createAlbumShareLinkDto) async
|
||||
test('test createAlbumSharedLink', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future deleteAlbum(String id) async
|
||||
test('test deleteAlbum', () async {
|
||||
// TODO
|
||||
|
|
5
mobile/openapi/test/album_response_dto_test.dart
generated
5
mobile/openapi/test/album_response_dto_test.dart
generated
|
@ -71,6 +71,11 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// DateTime lastModifiedAssetTimestamp
|
||||
test('to test the property `lastModifiedAssetTimestamp`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
|
16
mobile/openapi/test/asset_api_test.dart
generated
16
mobile/openapi/test/asset_api_test.dart
generated
|
@ -17,11 +17,6 @@ void main() {
|
|||
// final instance = AssetApi();
|
||||
|
||||
group('tests for AssetApi', () {
|
||||
//Future<SharedLinkResponseDto> addAssetsToSharedLink(AddAssetsDto addAssetsDto, { String key }) async
|
||||
test('test addAssetsToSharedLink', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// Checks if assets exist by checksums
|
||||
//
|
||||
//Future<AssetBulkUploadCheckResponseDto> bulkUploadCheck(AssetBulkUploadCheckDto assetBulkUploadCheckDto) async
|
||||
|
@ -43,11 +38,6 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
//Future<SharedLinkResponseDto> createAssetsSharedLink(CreateAssetsShareLinkDto createAssetsShareLinkDto) async
|
||||
test('test createAssetsSharedLink', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<List<DeleteAssetResponseDto>> deleteAsset(DeleteAssetDto deleteAssetDto) async
|
||||
test('test deleteAsset', () async {
|
||||
// TODO
|
||||
|
@ -141,8 +131,8 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
//Future<SharedLinkResponseDto> removeAssetsFromSharedLink(RemoveAssetsDto removeAssetsDto, { String key }) async
|
||||
test('test removeAssetsFromSharedLink', () async {
|
||||
//Future<AssetFileUploadResponseDto> importFile(ImportAssetDto importAssetDto) async
|
||||
test('test importFile', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
@ -163,7 +153,7 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
//Future<AssetFileUploadResponseDto> uploadFile(AssetTypeEnum assetType, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, bool isFavorite, String fileExtension, { String key, MultipartFile livePhotoData, MultipartFile sidecarData, bool isArchived, bool isVisible, String duration }) async
|
||||
//Future<AssetFileUploadResponseDto> uploadFile(AssetTypeEnum assetType, MultipartFile assetData, String fileExtension, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, bool isFavorite, { String key, MultipartFile livePhotoData, MultipartFile sidecarData, bool isReadOnly, bool isArchived, bool isVisible, String duration }) async
|
||||
test('test uploadFile', () async {
|
||||
// TODO
|
||||
});
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// tests for CreateAlbumShareLinkDto
|
||||
void main() {
|
||||
// final instance = CreateAlbumShareLinkDto();
|
||||
|
||||
group('test CreateAlbumShareLinkDto', () {
|
||||
// String albumId
|
||||
test('to test the property `albumId`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// DateTime expiresAt
|
||||
test('to test the property `expiresAt`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool allowUpload
|
||||
test('to test the property `allowUpload`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool allowDownload
|
||||
test('to test the property `allowDownload`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool showExif
|
||||
test('to test the property `showExif`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String description
|
||||
test('to test the property `description`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
5
mobile/openapi/test/create_user_dto_test.dart
generated
5
mobile/openapi/test/create_user_dto_test.dart
generated
|
@ -41,6 +41,11 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// String externalPath
|
||||
test('to test the property `externalPath`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
|
82
mobile/openapi/test/import_asset_dto_test.dart
generated
Normal file
82
mobile/openapi/test/import_asset_dto_test.dart
generated
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// 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 ImportAssetDto
|
||||
void main() {
|
||||
// final instance = ImportAssetDto();
|
||||
|
||||
group('test ImportAssetDto', () {
|
||||
// AssetTypeEnum assetType
|
||||
test('to test the property `assetType`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool isReadOnly (default value: true)
|
||||
test('to test the property `isReadOnly`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String assetPath
|
||||
test('to test the property `assetPath`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String sidecarPath
|
||||
test('to test the property `sidecarPath`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String deviceAssetId
|
||||
test('to test the property `deviceAssetId`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String deviceId
|
||||
test('to test the property `deviceId`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// DateTime fileCreatedAt
|
||||
test('to test the property `fileCreatedAt`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// DateTime fileModifiedAt
|
||||
test('to test the property `fileModifiedAt`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool isFavorite
|
||||
test('to test the property `isFavorite`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool isArchived
|
||||
test('to test the property `isArchived`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool isVisible
|
||||
test('to test the property `isVisible`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String duration
|
||||
test('to test the property `duration`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
|
@ -12,11 +12,21 @@ import 'package:openapi/api.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
|
||||
/// tests for ShareApi
|
||||
/// tests for SharedLinkApi
|
||||
void main() {
|
||||
// final instance = ShareApi();
|
||||
// final instance = SharedLinkApi();
|
||||
|
||||
group('tests for SharedLinkApi', () {
|
||||
//Future<List<AssetIdsResponseDto>> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String key }) async
|
||||
test('test addSharedLinkAssets', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<SharedLinkResponseDto> createSharedLink(SharedLinkCreateDto sharedLinkCreateDto) async
|
||||
test('test createSharedLink', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
group('tests for ShareApi', () {
|
||||
//Future<List<SharedLinkResponseDto>> getAllSharedLinks() async
|
||||
test('test getAllSharedLinks', () async {
|
||||
// TODO
|
||||
|
@ -37,7 +47,12 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
//Future<SharedLinkResponseDto> updateSharedLink(String id, EditSharedLinkDto editSharedLinkDto) async
|
||||
//Future<List<AssetIdsResponseDto>> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String key }) async
|
||||
test('test removeSharedLinkAssets', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<SharedLinkResponseDto> updateSharedLink(String id, SharedLinkEditDto sharedLinkEditDto) async
|
||||
test('test updateSharedLink', () async {
|
||||
// TODO
|
||||
});
|
|
@ -11,33 +11,23 @@
|
|||
import 'package:openapi/api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// tests for CreateAssetsShareLinkDto
|
||||
// tests for SharedLinkCreateDto
|
||||
void main() {
|
||||
// final instance = CreateAssetsShareLinkDto();
|
||||
// final instance = SharedLinkCreateDto();
|
||||
|
||||
group('test SharedLinkCreateDto', () {
|
||||
// SharedLinkType type
|
||||
test('to test the property `type`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
group('test CreateAssetsShareLinkDto', () {
|
||||
// List<String> assetIds (default value: const [])
|
||||
test('to test the property `assetIds`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// DateTime expiresAt
|
||||
test('to test the property `expiresAt`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool allowUpload
|
||||
test('to test the property `allowUpload`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool allowDownload
|
||||
test('to test the property `allowDownload`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool showExif
|
||||
test('to test the property `showExif`', () async {
|
||||
// String albumId
|
||||
test('to test the property `albumId`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
@ -46,6 +36,26 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// DateTime expiresAt
|
||||
test('to test the property `expiresAt`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool allowUpload (default value: false)
|
||||
test('to test the property `allowUpload`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool allowDownload (default value: true)
|
||||
test('to test the property `allowDownload`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool showExif (default value: true)
|
||||
test('to test the property `showExif`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -11,11 +11,11 @@
|
|||
import 'package:openapi/api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// tests for EditSharedLinkDto
|
||||
// tests for SharedLinkEditDto
|
||||
void main() {
|
||||
// final instance = EditSharedLinkDto();
|
||||
// final instance = SharedLinkEditDto();
|
||||
|
||||
group('test EditSharedLinkDto', () {
|
||||
group('test SharedLinkEditDto', () {
|
||||
// String description
|
||||
test('to test the property `description`', () async {
|
||||
// TODO
|
5
mobile/openapi/test/update_user_dto_test.dart
generated
5
mobile/openapi/test/update_user_dto_test.dart
generated
|
@ -46,6 +46,11 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// String externalPath
|
||||
test('to test the property `externalPath`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool isAdmin
|
||||
test('to test the property `isAdmin`', () async {
|
||||
// TODO
|
||||
|
|
5
mobile/openapi/test/user_response_dto_test.dart
generated
5
mobile/openapi/test/user_response_dto_test.dart
generated
|
@ -41,6 +41,11 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// String externalPath
|
||||
test('to test the property `externalPath`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String profileImagePath
|
||||
test('to test the property `profileImagePath`', () async {
|
||||
// TODO
|
||||
|
|
|
@ -2,7 +2,7 @@ FROM node:18.16.0-alpine3.18@sha256:f41850f74ff16a33daff988e2ea06ef8f5daeb6fb849
|
|||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
RUN apk add --update-cache build-base imagemagick-dev python3 ffmpeg libraw-dev perl vips-dev vips-heif vips-magick
|
||||
RUN apk add --update-cache build-base imagemagick-dev python3 ffmpeg libraw-dev perl vips-dev vips-heif vips-jxl vips-magick
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
|
@ -23,7 +23,7 @@ ENV NODE_ENV=production
|
|||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
RUN apk add --no-cache ffmpeg imagemagick-dev libraw-dev perl vips-dev vips-heif vips-magick
|
||||
RUN apk add --no-cache ffmpeg imagemagick-dev libraw-dev perl vips-dev vips-heif vips-jxl vips-magick
|
||||
|
||||
COPY --from=prod /usr/src/app/node_modules ./node_modules
|
||||
COPY --from=prod /usr/src/app/dist ./dist
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import { AlbumResponseDto, AuthService, CreateAlbumDto, SharedLinkResponseDto, UserService } from '@app/domain';
|
||||
import { CreateAlbumShareLinkDto } from '@app/immich/api-v1/album/dto/create-album-shared-link.dto';
|
||||
import {
|
||||
AlbumResponseDto,
|
||||
AuthService,
|
||||
CreateAlbumDto,
|
||||
SharedLinkCreateDto,
|
||||
SharedLinkResponseDto,
|
||||
UserService,
|
||||
} from '@app/domain';
|
||||
import { AppModule } from '@app/immich/app.module';
|
||||
import { AuthUserDto } from '@app/immich/decorators/auth-user.decorator';
|
||||
import { SharedLinkType } from '@app/infra/entities';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import request from 'supertest';
|
||||
|
@ -14,8 +21,10 @@ async function _createAlbum(app: INestApplication, data: CreateAlbumDto) {
|
|||
return res.body as AlbumResponseDto;
|
||||
}
|
||||
|
||||
async function _createAlbumSharedLink(app: INestApplication, data: CreateAlbumShareLinkDto) {
|
||||
const res = await request(app.getHttpServer()).post('/album/create-shared-link').send(data);
|
||||
async function _createAlbumSharedLink(app: INestApplication, data: Omit<SharedLinkCreateDto, 'type'>) {
|
||||
const res = await request(app.getHttpServer())
|
||||
.post('/shared-link')
|
||||
.send({ ...data, type: SharedLinkType.ALBUM });
|
||||
expect(res.status).toEqual(201);
|
||||
return res.body as SharedLinkResponseDto;
|
||||
}
|
||||
|
|
|
@ -105,6 +105,7 @@ describe('User', () => {
|
|||
updatedAt: expect.anything(),
|
||||
oauthId: '',
|
||||
storageLabel: null,
|
||||
externalPath: null,
|
||||
},
|
||||
{
|
||||
email: userTwoEmail,
|
||||
|
@ -119,6 +120,7 @@ describe('User', () => {
|
|||
updatedAt: expect.anything(),
|
||||
oauthId: '',
|
||||
storageLabel: null,
|
||||
externalPath: null,
|
||||
},
|
||||
{
|
||||
email: authUserEmail,
|
||||
|
@ -133,6 +135,7 @@ describe('User', () => {
|
|||
updatedAt: expect.anything(),
|
||||
oauthId: '',
|
||||
storageLabel: 'admin',
|
||||
externalPath: null,
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
|
|
@ -127,48 +127,6 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/album/create-shared-link": {
|
||||
"post": {
|
||||
"operationId": "createAlbumSharedLink",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateAlbumShareLinkDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SharedLinkResponseDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Album"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/album/{id}": {
|
||||
"patch": {
|
||||
"operationId": "updateAlbumInfo",
|
||||
|
@ -1472,6 +1430,48 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/asset/import": {
|
||||
"post": {
|
||||
"operationId": "importFile",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ImportAssetDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AssetFileUploadResponseDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Asset"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/asset/map-marker": {
|
||||
"get": {
|
||||
"operationId": "getMapMarkers",
|
||||
|
@ -1660,150 +1660,6 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/asset/shared-link": {
|
||||
"post": {
|
||||
"operationId": "createAssetsSharedLink",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateAssetsShareLinkDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SharedLinkResponseDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Asset"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/asset/shared-link/add": {
|
||||
"patch": {
|
||||
"operationId": "addAssetsToSharedLink",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "key",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AddAssetsDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SharedLinkResponseDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Asset"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/asset/shared-link/remove": {
|
||||
"patch": {
|
||||
"operationId": "removeAssetsFromSharedLink",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "key",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RemoveAssetsDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SharedLinkResponseDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Asset"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/asset/stat/archive": {
|
||||
"get": {
|
||||
"operationId": "getArchivedAssetCountByUserId",
|
||||
|
@ -3264,7 +3120,7 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/share": {
|
||||
"/shared-link": {
|
||||
"get": {
|
||||
"operationId": "getAllSharedLinks",
|
||||
"parameters": [],
|
||||
|
@ -3284,7 +3140,47 @@
|
|||
}
|
||||
},
|
||||
"tags": [
|
||||
"share"
|
||||
"Shared Link"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"operationId": "createSharedLink",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SharedLinkCreateDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SharedLinkResponseDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Shared Link"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
|
@ -3299,7 +3195,7 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/share/me": {
|
||||
"/shared-link/me": {
|
||||
"get": {
|
||||
"operationId": "getMySharedLink",
|
||||
"parameters": [
|
||||
|
@ -3325,7 +3221,7 @@
|
|||
}
|
||||
},
|
||||
"tags": [
|
||||
"share"
|
||||
"Shared Link"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
|
@ -3340,7 +3236,7 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/share/{id}": {
|
||||
"/shared-link/{id}": {
|
||||
"get": {
|
||||
"operationId": "getSharedLinkById",
|
||||
"parameters": [
|
||||
|
@ -3367,7 +3263,7 @@
|
|||
}
|
||||
},
|
||||
"tags": [
|
||||
"share"
|
||||
"Shared Link"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
|
@ -3399,7 +3295,7 @@
|
|||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/EditSharedLinkDto"
|
||||
"$ref": "#/components/schemas/SharedLinkEditDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3417,7 +3313,7 @@
|
|||
}
|
||||
},
|
||||
"tags": [
|
||||
"share"
|
||||
"Shared Link"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
|
@ -3450,7 +3346,131 @@
|
|||
}
|
||||
},
|
||||
"tags": [
|
||||
"share"
|
||||
"Shared Link"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/shared-link/{id}/assets": {
|
||||
"put": {
|
||||
"operationId": "addSharedLinkAssets",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "key",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AssetIdsDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/AssetIdsResponseDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Shared Link"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"operationId": "removeSharedLinkAssets",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "key",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AssetIdsDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/AssetIdsResponseDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Shared Link"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
|
@ -4584,6 +4604,10 @@
|
|||
},
|
||||
"owner": {
|
||||
"$ref": "#/components/schemas/UserResponseDto"
|
||||
},
|
||||
"lastModifiedAssetTimestamp": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -5085,34 +5109,6 @@
|
|||
"albumName"
|
||||
]
|
||||
},
|
||||
"CreateAlbumShareLinkDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"albumId": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"expiresAt": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"allowUpload": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"allowDownload": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"showExif": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"albumId"
|
||||
]
|
||||
},
|
||||
"CreateAssetDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -5131,6 +5127,13 @@
|
|||
"type": "string",
|
||||
"format": "binary"
|
||||
},
|
||||
"isReadOnly": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"fileExtension": {
|
||||
"type": "string"
|
||||
},
|
||||
"deviceAssetId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -5154,9 +5157,6 @@
|
|||
"isVisible": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"fileExtension": {
|
||||
"type": "string"
|
||||
},
|
||||
"duration": {
|
||||
"type": "string"
|
||||
}
|
||||
|
@ -5164,48 +5164,12 @@
|
|||
"required": [
|
||||
"assetType",
|
||||
"assetData",
|
||||
"fileExtension",
|
||||
"deviceAssetId",
|
||||
"deviceId",
|
||||
"fileCreatedAt",
|
||||
"fileModifiedAt",
|
||||
"isFavorite",
|
||||
"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"
|
||||
"isFavorite"
|
||||
]
|
||||
},
|
||||
"CreateProfileImageDto": {
|
||||
|
@ -5268,6 +5232,10 @@
|
|||
"storageLabel": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"externalPath": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -5388,28 +5356,6 @@
|
|||
"assetIds"
|
||||
]
|
||||
},
|
||||
"EditSharedLinkDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"expiresAt": {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"allowUpload": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"allowDownload": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"showExif": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ExifResponseDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -5565,6 +5511,59 @@
|
|||
"timeGroup"
|
||||
]
|
||||
},
|
||||
"ImportAssetDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"assetType": {
|
||||
"$ref": "#/components/schemas/AssetTypeEnum"
|
||||
},
|
||||
"isReadOnly": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"assetPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"sidecarPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"deviceAssetId": {
|
||||
"type": "string"
|
||||
},
|
||||
"deviceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"fileCreatedAt": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"fileModifiedAt": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"isFavorite": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isArchived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isVisible": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"duration": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"assetType",
|
||||
"assetPath",
|
||||
"deviceAssetId",
|
||||
"deviceId",
|
||||
"fileCreatedAt",
|
||||
"fileModifiedAt",
|
||||
"isFavorite"
|
||||
]
|
||||
},
|
||||
"JobCommand": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
@ -6156,6 +6155,71 @@
|
|||
"patch"
|
||||
]
|
||||
},
|
||||
"SharedLinkCreateDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"$ref": "#/components/schemas/SharedLinkType"
|
||||
},
|
||||
"assetIds": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
"albumId": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"expiresAt": {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"default": null
|
||||
},
|
||||
"allowUpload": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"allowDownload": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"showExif": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"SharedLinkEditDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"expiresAt": {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"allowUpload": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"allowDownload": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"showExif": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SharedLinkResponseDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -6166,7 +6230,8 @@
|
|||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"userId": {
|
||||
"type": "string"
|
||||
|
@ -6205,6 +6270,7 @@
|
|||
"required": [
|
||||
"type",
|
||||
"id",
|
||||
"description",
|
||||
"userId",
|
||||
"key",
|
||||
"createdAt",
|
||||
|
@ -6629,6 +6695,9 @@
|
|||
"storageLabel": {
|
||||
"type": "string"
|
||||
},
|
||||
"externalPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"isAdmin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -6702,6 +6771,10 @@
|
|||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"externalPath": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"profileImagePath": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -6734,6 +6807,7 @@
|
|||
"firstName",
|
||||
"lastName",
|
||||
"storageLabel",
|
||||
"externalPath",
|
||||
"profileImagePath",
|
||||
"shouldChangePassword",
|
||||
"isAdmin",
|
||||
|
|
12
server/package-lock.json
generated
12
server/package-lock.json
generated
|
@ -21,6 +21,7 @@
|
|||
"@nestjs/typeorm": "^9.0.1",
|
||||
"@nestjs/websockets": "^9.2.1",
|
||||
"@socket.io/redis-adapter": "^8.0.1",
|
||||
"@types/mime-types": "^2.1.1",
|
||||
"archiver": "^5.3.1",
|
||||
"axios": "^0.26.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
|
@ -38,6 +39,7 @@
|
|||
"local-reverse-geocoder": "0.12.5",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.0.3",
|
||||
"mime-types": "^2.1.35",
|
||||
"mv": "^2.1.1",
|
||||
"nest-commander": "^3.3.0",
|
||||
"openid-client": "^5.2.1",
|
||||
|
@ -3018,6 +3020,11 @@
|
|||
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/mime-types": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz",
|
||||
"integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw=="
|
||||
},
|
||||
"node_modules/@types/multer": {
|
||||
"version": "1.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz",
|
||||
|
@ -14296,6 +14303,11 @@
|
|||
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/mime-types": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz",
|
||||
"integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw=="
|
||||
},
|
||||
"@types/multer": {
|
||||
"version": "1.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz",
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
"@nestjs/typeorm": "^9.0.1",
|
||||
"@nestjs/websockets": "^9.2.1",
|
||||
"@socket.io/redis-adapter": "^8.0.1",
|
||||
"@types/mime-types": "^2.1.1",
|
||||
"archiver": "^5.3.1",
|
||||
"axios": "^0.26.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
|
@ -67,6 +68,7 @@
|
|||
"local-reverse-geocoder": "0.12.5",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.0.3",
|
||||
"mime-types": "^2.1.35",
|
||||
"mv": "^2.1.1",
|
||||
"nest-commander": "^3.3.0",
|
||||
"openid-client": "^5.2.1",
|
||||
|
|
|
@ -2,8 +2,11 @@ export const IAccessRepository = 'IAccessRepository';
|
|||
|
||||
export interface IAccessRepository {
|
||||
hasPartnerAccess(userId: string, partnerId: string): Promise<boolean>;
|
||||
|
||||
hasAlbumAssetAccess(userId: string, assetId: string): Promise<boolean>;
|
||||
hasOwnerAssetAccess(userId: string, assetId: string): Promise<boolean>;
|
||||
hasPartnerAssetAccess(userId: string, assetId: string): Promise<boolean>;
|
||||
hasSharedLinkAssetAccess(userId: string, assetId: string): Promise<boolean>;
|
||||
|
||||
hasAlbumOwnerAccess(userId: string, albumId: string): Promise<boolean>;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ export class AlbumResponseDto {
|
|||
owner!: UserResponseDto;
|
||||
@ApiProperty({ type: 'integer' })
|
||||
assetCount!: number;
|
||||
lastModifiedAssetTimestamp?: Date;
|
||||
}
|
||||
|
||||
export function mapAlbum(entity: AlbumEntity): AlbumResponseDto {
|
||||
|
|
|
@ -169,6 +169,7 @@ describe(AlbumService.name, () => {
|
|||
createdAt: new Date('2021-01-01'),
|
||||
deletedAt: null,
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
externalPath: null,
|
||||
},
|
||||
ownerId: 'admin_id',
|
||||
shared: false,
|
||||
|
|
|
@ -53,15 +53,19 @@ export class AlbumService {
|
|||
return obj;
|
||||
}, {});
|
||||
|
||||
return albums.map((album) => {
|
||||
return {
|
||||
...album,
|
||||
assets: album?.assets?.map(mapAsset),
|
||||
sharedLinks: undefined, // Don't return shared links
|
||||
shared: album.sharedLinks?.length > 0 || album.sharedUsers?.length > 0,
|
||||
assetCount: albumsAssetCountObj[album.id],
|
||||
} as AlbumResponseDto;
|
||||
});
|
||||
return Promise.all(
|
||||
albums.map(async (album) => {
|
||||
const lastModifiedAsset = await this.assetRepository.getLastUpdatedAssetForAlbumId(album.id);
|
||||
return {
|
||||
...album,
|
||||
assets: album?.assets?.map(mapAsset),
|
||||
sharedLinks: undefined, // Don't return shared links
|
||||
shared: album.sharedLinks?.length > 0 || album.sharedUsers?.length > 0,
|
||||
assetCount: albumsAssetCountObj[album.id],
|
||||
lastModifiedAssetTimestamp: lastModifiedAsset?.fileModifiedAt,
|
||||
} as AlbumResponseDto;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private async updateInvalidThumbnails(): Promise<number> {
|
||||
|
|
|
@ -19,6 +19,7 @@ export class APIKeyCore {
|
|||
isAdmin: user.isAdmin,
|
||||
isPublicUser: false,
|
||||
isAllowUpload: true,
|
||||
externalPath: user.externalPath,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ export interface IAssetRepository {
|
|||
getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated<AssetEntity>;
|
||||
getWith(pagination: PaginationOptions, property: WithProperty): Paginated<AssetEntity>;
|
||||
getFirstAssetForAlbumId(albumId: string): Promise<AssetEntity | null>;
|
||||
getLastUpdatedAssetForAlbumId(albumId: string): Promise<AssetEntity | null>;
|
||||
deleteAll(ownerId: string): Promise<void>;
|
||||
getAll(pagination: PaginationOptions, options?: AssetSearchOptions): Paginated<AssetEntity>;
|
||||
save(asset: Partial<AssetEntity>): Promise<AssetEntity>;
|
||||
|
|
|
@ -13,7 +13,7 @@ import { IKeyRepository } from '../api-key';
|
|||
import { APIKeyCore } from '../api-key/api-key.core';
|
||||
import { ICryptoRepository } from '../crypto/crypto.repository';
|
||||
import { OAuthCore } from '../oauth/oauth.core';
|
||||
import { ISharedLinkRepository, SharedLinkCore } from '../shared-link';
|
||||
import { ISharedLinkRepository } from '../shared-link';
|
||||
import { INITIAL_SYSTEM_CONFIG, ISystemConfigRepository } from '../system-config';
|
||||
import { IUserRepository, UserCore } from '../user';
|
||||
import { IUserTokenRepository, UserTokenCore } from '../user-token';
|
||||
|
@ -35,7 +35,6 @@ export class AuthService {
|
|||
private authCore: AuthCore;
|
||||
private oauthCore: OAuthCore;
|
||||
private userCore: UserCore;
|
||||
private shareCore: SharedLinkCore;
|
||||
private keyCore: APIKeyCore;
|
||||
|
||||
private logger = new Logger(AuthService.name);
|
||||
|
@ -45,7 +44,7 @@ export class AuthService {
|
|||
@Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
|
||||
@Inject(IUserRepository) userRepository: IUserRepository,
|
||||
@Inject(IUserTokenRepository) userTokenRepository: IUserTokenRepository,
|
||||
@Inject(ISharedLinkRepository) shareRepository: ISharedLinkRepository,
|
||||
@Inject(ISharedLinkRepository) private sharedLinkRepository: ISharedLinkRepository,
|
||||
@Inject(IKeyRepository) keyRepository: IKeyRepository,
|
||||
@Inject(INITIAL_SYSTEM_CONFIG)
|
||||
initialConfig: SystemConfig,
|
||||
|
@ -54,7 +53,6 @@ export class AuthService {
|
|||
this.authCore = new AuthCore(cryptoRepository, configRepository, userTokenRepository, initialConfig);
|
||||
this.oauthCore = new OAuthCore(configRepository, initialConfig);
|
||||
this.userCore = new UserCore(userRepository, cryptoRepository);
|
||||
this.shareCore = new SharedLinkCore(shareRepository, cryptoRepository);
|
||||
this.keyCore = new APIKeyCore(cryptoRepository, keyRepository);
|
||||
}
|
||||
|
||||
|
@ -147,7 +145,7 @@ export class AuthService {
|
|||
const apiKey = (headers[IMMICH_API_KEY_HEADER] || params.apiKey) as string;
|
||||
|
||||
if (shareKey) {
|
||||
return this.shareCore.validate(shareKey);
|
||||
return this.validateSharedLink(shareKey);
|
||||
}
|
||||
|
||||
if (userToken) {
|
||||
|
@ -193,4 +191,29 @@ export class AuthService {
|
|||
const cookies = cookieParser.parse(headers.cookie || '');
|
||||
return cookies[IMMICH_ACCESS_COOKIE] || null;
|
||||
}
|
||||
|
||||
async validateSharedLink(key: string | string[]): Promise<AuthUserDto | null> {
|
||||
key = Array.isArray(key) ? key[0] : key;
|
||||
|
||||
const bytes = Buffer.from(key, key.length === 100 ? 'hex' : 'base64url');
|
||||
const link = await this.sharedLinkRepository.getByKey(bytes);
|
||||
if (link) {
|
||||
if (!link.expiresAt || new Date(link.expiresAt) > new Date()) {
|
||||
const user = link.user;
|
||||
if (user) {
|
||||
return {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
isAdmin: user.isAdmin,
|
||||
isPublicUser: true,
|
||||
sharedLinkId: link.id,
|
||||
isAllowUpload: link.allowUpload,
|
||||
isAllowDownload: link.allowDownload,
|
||||
isShowExif: link.showExif,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new UnauthorizedException('Invalid share key');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,4 +8,5 @@ export class AuthUserDto {
|
|||
isAllowDownload?: boolean;
|
||||
isShowExif?: boolean;
|
||||
accessTokenId?: string;
|
||||
externalPath?: string | null;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ export const ICryptoRepository = 'ICryptoRepository';
|
|||
|
||||
export interface ICryptoRepository {
|
||||
randomBytes(size: number): Buffer;
|
||||
hashFile(filePath: string): Promise<Buffer>;
|
||||
hashSha256(data: string): string;
|
||||
hashBcrypt(data: string | Buffer, saltOrRounds: string | number): Promise<string>;
|
||||
compareBcrypt(data: string | Buffer, encrypted: string): boolean;
|
||||
|
|
|
@ -27,3 +27,60 @@ export function assertMachineLearningEnabled() {
|
|||
throw new BadRequestException('Machine learning is not enabled.');
|
||||
}
|
||||
}
|
||||
|
||||
const validMimeTypes = [
|
||||
'image/avif',
|
||||
'image/gif',
|
||||
'image/heic',
|
||||
'image/heif',
|
||||
'image/jpeg',
|
||||
'image/jxl',
|
||||
'image/png',
|
||||
'image/tiff',
|
||||
'image/webp',
|
||||
'image/x-adobe-dng',
|
||||
'image/x-arriflex-ari',
|
||||
'image/x-canon-cr2',
|
||||
'image/x-canon-cr3',
|
||||
'image/x-canon-crw',
|
||||
'image/x-epson-erf',
|
||||
'image/x-fuji-raf',
|
||||
'image/x-hasselblad-3fr',
|
||||
'image/x-hasselblad-fff',
|
||||
'image/x-kodak-dcr',
|
||||
'image/x-kodak-k25',
|
||||
'image/x-kodak-kdc',
|
||||
'image/x-leica-rwl',
|
||||
'image/x-minolta-mrw',
|
||||
'image/x-nikon-nef',
|
||||
'image/x-olympus-orf',
|
||||
'image/x-olympus-ori',
|
||||
'image/x-panasonic-raw',
|
||||
'image/x-pentax-pef',
|
||||
'image/x-phantom-cin',
|
||||
'image/x-phaseone-cap',
|
||||
'image/x-phaseone-iiq',
|
||||
'image/x-samsung-srw',
|
||||
'image/x-sigma-x3f',
|
||||
'image/x-sony-arw',
|
||||
'image/x-sony-sr2',
|
||||
'image/x-sony-srf',
|
||||
'video/3gpp',
|
||||
'video/mp2t',
|
||||
'video/mp4',
|
||||
'video/mpeg',
|
||||
'video/quicktime',
|
||||
'video/webm',
|
||||
'video/x-flv',
|
||||
'video/x-matroska',
|
||||
'video/x-ms-wmv',
|
||||
'video/x-msvideo',
|
||||
];
|
||||
|
||||
export function isSupportedFileType(mimetype: string): boolean {
|
||||
return validMimeTypes.includes(mimetype);
|
||||
}
|
||||
|
||||
export function isSidecarFileType(mimeType: string): boolean {
|
||||
return ['application/xml', 'text/xml'].includes(mimeType);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ const responseDto = {
|
|||
createdAt: new Date('2021-01-01'),
|
||||
deletedAt: null,
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
externalPath: null,
|
||||
},
|
||||
user1: {
|
||||
email: 'immich@test.com',
|
||||
|
@ -31,6 +32,7 @@ const responseDto = {
|
|||
createdAt: new Date('2021-01-01'),
|
||||
deletedAt: null,
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
externalPath: null,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import { AlbumEntity, AssetEntity, SharedLinkType } from '@app/infra/entities';
|
||||
|
||||
export class CreateSharedLinkDto {
|
||||
description?: string;
|
||||
expiresAt?: Date;
|
||||
type!: SharedLinkType;
|
||||
assets!: AssetEntity[];
|
||||
album?: AlbumEntity;
|
||||
allowUpload?: boolean;
|
||||
allowDownload?: boolean;
|
||||
showExif?: boolean;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import { IsOptional } from 'class-validator';
|
||||
|
||||
export class EditSharedLinkDto {
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
|
||||
@IsOptional()
|
||||
expiresAt?: Date | null;
|
||||
|
||||
@IsOptional()
|
||||
allowUpload?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
allowDownload?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
showExif?: boolean;
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
export * from './create-shared-link.dto';
|
||||
export * from './edit-shared-link.dto';
|
|
@ -1,5 +1,4 @@
|
|||
export * from './dto';
|
||||
export * from './response-dto';
|
||||
export * from './shared-link.core';
|
||||
export * from './shared-link-response.dto';
|
||||
export * from './shared-link.dto';
|
||||
export * from './shared-link.repository';
|
||||
export * from './shared-link.service';
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from './shared-link-response.dto';
|
|
@ -1,12 +1,12 @@
|
|||
import { SharedLinkEntity, SharedLinkType } from '@app/infra/entities';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import _ from 'lodash';
|
||||
import { AlbumResponseDto, mapAlbumExcludeAssetInfo } from '../../album';
|
||||
import { AssetResponseDto, mapAsset, mapAssetWithoutExif } from '../../asset';
|
||||
import { AlbumResponseDto, mapAlbumExcludeAssetInfo } from '../album';
|
||||
import { AssetResponseDto, mapAsset, mapAssetWithoutExif } from '../asset';
|
||||
|
||||
export class SharedLinkResponseDto {
|
||||
id!: string;
|
||||
description?: string;
|
||||
description!: string | null;
|
||||
userId!: string;
|
||||
key!: string;
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
import { AssetEntity, SharedLinkEntity } from '@app/infra/entities';
|
||||
import { BadRequestException, ForbiddenException, Logger, UnauthorizedException } from '@nestjs/common';
|
||||
import { AuthUserDto } from '../auth';
|
||||
import { ICryptoRepository } from '../crypto';
|
||||
import { CreateSharedLinkDto } from './dto';
|
||||
import { ISharedLinkRepository } from './shared-link.repository';
|
||||
|
||||
export class SharedLinkCore {
|
||||
readonly logger = new Logger(SharedLinkCore.name);
|
||||
|
||||
constructor(private repository: ISharedLinkRepository, private cryptoRepository: ICryptoRepository) {}
|
||||
|
||||
// TODO: move to SharedLinkController/SharedLinkService
|
||||
create(userId: string, dto: CreateSharedLinkDto): Promise<SharedLinkEntity> {
|
||||
return this.repository.create({
|
||||
key: Buffer.from(this.cryptoRepository.randomBytes(50)),
|
||||
description: dto.description,
|
||||
userId,
|
||||
createdAt: new Date(),
|
||||
expiresAt: dto.expiresAt ?? null,
|
||||
type: dto.type,
|
||||
assets: dto.assets,
|
||||
album: dto.album,
|
||||
allowUpload: dto.allowUpload ?? false,
|
||||
allowDownload: dto.allowDownload ?? true,
|
||||
showExif: dto.showExif ?? true,
|
||||
});
|
||||
}
|
||||
|
||||
async addAssets(userId: string, id: string, assets: AssetEntity[]) {
|
||||
const link = await this.repository.get(userId, id);
|
||||
if (!link) {
|
||||
throw new BadRequestException('Shared link not found');
|
||||
}
|
||||
|
||||
return this.repository.update({ ...link, assets: [...link.assets, ...assets] });
|
||||
}
|
||||
|
||||
async removeAssets(userId: string, id: string, assets: AssetEntity[]) {
|
||||
const link = await this.repository.get(userId, id);
|
||||
if (!link) {
|
||||
throw new BadRequestException('Shared link not found');
|
||||
}
|
||||
|
||||
const newAssets = link.assets.filter((asset) => assets.find((a) => a.id === asset.id));
|
||||
|
||||
return this.repository.update({ ...link, assets: newAssets });
|
||||
}
|
||||
|
||||
checkDownloadAccess(user: AuthUserDto) {
|
||||
if (user.isPublicUser && !user.isAllowDownload) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
}
|
||||
|
||||
async validate(key: string | string[]): Promise<AuthUserDto | null> {
|
||||
key = Array.isArray(key) ? key[0] : key;
|
||||
|
||||
const bytes = Buffer.from(key, key.length === 100 ? 'hex' : 'base64url');
|
||||
const link = await this.repository.getByKey(bytes);
|
||||
if (link) {
|
||||
if (!link.expiresAt || new Date(link.expiresAt) > new Date()) {
|
||||
const user = link.user;
|
||||
if (user) {
|
||||
return {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
isAdmin: user.isAdmin,
|
||||
isPublicUser: true,
|
||||
sharedLinkId: link.id,
|
||||
isAllowUpload: link.allowUpload,
|
||||
isAllowDownload: link.allowDownload,
|
||||
isShowExif: link.showExif,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new UnauthorizedException('Invalid share key');
|
||||
}
|
||||
}
|
53
server/src/domain/shared-link/shared-link.dto.ts
Normal file
53
server/src/domain/shared-link/shared-link.dto.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { SharedLinkType } from '@app/infra/entities';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsDate, IsEnum, IsOptional, IsString } from 'class-validator';
|
||||
import { ValidateUUID } from '../../immich/decorators/validate-uuid.decorator';
|
||||
|
||||
export class SharedLinkCreateDto {
|
||||
@IsEnum(SharedLinkType)
|
||||
@ApiProperty({ enum: SharedLinkType, enumName: 'SharedLinkType' })
|
||||
type!: SharedLinkType;
|
||||
|
||||
@ValidateUUID({ each: true, optional: true })
|
||||
assetIds?: string[];
|
||||
|
||||
@ValidateUUID({ optional: true })
|
||||
albumId?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
|
||||
@IsDate()
|
||||
@IsOptional()
|
||||
expiresAt?: Date | null = null;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
allowUpload?: boolean = false;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
allowDownload?: boolean = true;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
showExif?: boolean = true;
|
||||
}
|
||||
|
||||
export class SharedLinkEditDto {
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
|
||||
@IsOptional()
|
||||
expiresAt?: Date | null;
|
||||
|
||||
@IsOptional()
|
||||
allowUpload?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
allowDownload?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
showExif?: boolean;
|
||||
}
|
|
@ -6,7 +6,7 @@ export interface ISharedLinkRepository {
|
|||
getAll(userId: string): Promise<SharedLinkEntity[]>;
|
||||
get(userId: string, id: string): Promise<SharedLinkEntity | null>;
|
||||
getByKey(key: Buffer): Promise<SharedLinkEntity | null>;
|
||||
create(entity: Omit<SharedLinkEntity, 'id' | 'user'>): Promise<SharedLinkEntity>;
|
||||
create(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity>;
|
||||
update(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity>;
|
||||
remove(entity: SharedLinkEntity): Promise<void>;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,33 @@
|
|||
import { BadRequestException, ForbiddenException } from '@nestjs/common';
|
||||
import { authStub, newSharedLinkRepositoryMock, sharedLinkResponseStub, sharedLinkStub } from '@test';
|
||||
import {
|
||||
albumStub,
|
||||
assetEntityStub,
|
||||
authStub,
|
||||
newAccessRepositoryMock,
|
||||
newCryptoRepositoryMock,
|
||||
newSharedLinkRepositoryMock,
|
||||
sharedLinkResponseStub,
|
||||
sharedLinkStub,
|
||||
} from '@test';
|
||||
import { when } from 'jest-when';
|
||||
import _ from 'lodash';
|
||||
import { SharedLinkType } from '../../infra/entities/shared-link.entity';
|
||||
import { AssetIdErrorReason, IAccessRepository, ICryptoRepository } from '../index';
|
||||
import { ISharedLinkRepository } from './shared-link.repository';
|
||||
import { SharedLinkService } from './shared-link.service';
|
||||
|
||||
describe(SharedLinkService.name, () => {
|
||||
let sut: SharedLinkService;
|
||||
let accessMock: jest.Mocked<IAccessRepository>;
|
||||
let cryptoMock: jest.Mocked<ICryptoRepository>;
|
||||
let shareMock: jest.Mocked<ISharedLinkRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
accessMock = newAccessRepositoryMock();
|
||||
cryptoMock = newCryptoRepositoryMock();
|
||||
shareMock = newSharedLinkRepositoryMock();
|
||||
|
||||
sut = new SharedLinkService(shareMock);
|
||||
sut = new SharedLinkService(accessMock, cryptoMock, shareMock);
|
||||
});
|
||||
|
||||
it('should work', () => {
|
||||
|
@ -64,6 +81,82 @@ describe(SharedLinkService.name, () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('should not allow an album shared link without an albumId', async () => {
|
||||
await expect(sut.create(authStub.admin, { type: SharedLinkType.ALBUM, assetIds: [] })).rejects.toBeInstanceOf(
|
||||
BadRequestException,
|
||||
);
|
||||
});
|
||||
|
||||
it('should not allow non-owners to create album shared links', async () => {
|
||||
accessMock.hasAlbumOwnerAccess.mockResolvedValue(false);
|
||||
await expect(
|
||||
sut.create(authStub.admin, { type: SharedLinkType.ALBUM, assetIds: [], albumId: 'album-1' }),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
});
|
||||
|
||||
it('should not allow individual shared links with no assets', async () => {
|
||||
await expect(
|
||||
sut.create(authStub.admin, { type: SharedLinkType.INDIVIDUAL, assetIds: [] }),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
});
|
||||
|
||||
it('should require asset ownership to make an individual shared link', async () => {
|
||||
accessMock.hasOwnerAssetAccess.mockResolvedValue(false);
|
||||
await expect(
|
||||
sut.create(authStub.admin, { type: SharedLinkType.INDIVIDUAL, assetIds: ['asset-1'] }),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
});
|
||||
|
||||
it('should create an album shared link', async () => {
|
||||
accessMock.hasAlbumOwnerAccess.mockResolvedValue(true);
|
||||
shareMock.create.mockResolvedValue(sharedLinkStub.valid);
|
||||
|
||||
await sut.create(authStub.admin, { type: SharedLinkType.ALBUM, albumId: albumStub.oneAsset.id });
|
||||
|
||||
expect(accessMock.hasAlbumOwnerAccess).toHaveBeenCalledWith(authStub.admin.id, albumStub.oneAsset.id);
|
||||
expect(shareMock.create).toHaveBeenCalledWith({
|
||||
type: SharedLinkType.ALBUM,
|
||||
userId: authStub.admin.id,
|
||||
albumId: albumStub.oneAsset.id,
|
||||
allowDownload: true,
|
||||
allowUpload: true,
|
||||
assets: [],
|
||||
description: null,
|
||||
expiresAt: null,
|
||||
showExif: true,
|
||||
key: Buffer.from('random-bytes', 'utf8'),
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an individual shared link', async () => {
|
||||
accessMock.hasOwnerAssetAccess.mockResolvedValue(true);
|
||||
shareMock.create.mockResolvedValue(sharedLinkStub.individual);
|
||||
|
||||
await sut.create(authStub.admin, {
|
||||
type: SharedLinkType.INDIVIDUAL,
|
||||
assetIds: [assetEntityStub.image.id],
|
||||
showExif: true,
|
||||
allowDownload: true,
|
||||
allowUpload: true,
|
||||
});
|
||||
|
||||
expect(accessMock.hasOwnerAssetAccess).toHaveBeenCalledWith(authStub.admin.id, assetEntityStub.image.id);
|
||||
expect(shareMock.create).toHaveBeenCalledWith({
|
||||
type: SharedLinkType.INDIVIDUAL,
|
||||
userId: authStub.admin.id,
|
||||
albumId: null,
|
||||
allowDownload: true,
|
||||
allowUpload: true,
|
||||
assets: [{ id: assetEntityStub.image.id }],
|
||||
description: null,
|
||||
expiresAt: null,
|
||||
showExif: true,
|
||||
key: Buffer.from('random-bytes', 'utf8'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('update', () => {
|
||||
it('should throw an error for an invalid shared link', async () => {
|
||||
shareMock.get.mockResolvedValue(null);
|
||||
|
@ -100,4 +193,58 @@ describe(SharedLinkService.name, () => {
|
|||
expect(shareMock.remove).toHaveBeenCalledWith(sharedLinkStub.valid);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addAssets', () => {
|
||||
it('should not work on album shared links', async () => {
|
||||
shareMock.get.mockResolvedValue(sharedLinkStub.valid);
|
||||
await expect(sut.addAssets(authStub.admin, 'link-1', { assetIds: ['asset-1'] })).rejects.toBeInstanceOf(
|
||||
BadRequestException,
|
||||
);
|
||||
});
|
||||
|
||||
it('should add assets to a shared link', async () => {
|
||||
shareMock.get.mockResolvedValue(_.cloneDeep(sharedLinkStub.individual));
|
||||
shareMock.create.mockResolvedValue(sharedLinkStub.individual);
|
||||
|
||||
when(accessMock.hasOwnerAssetAccess).calledWith(authStub.admin.id, 'asset-2').mockResolvedValue(false);
|
||||
when(accessMock.hasOwnerAssetAccess).calledWith(authStub.admin.id, 'asset-3').mockResolvedValue(true);
|
||||
|
||||
await expect(
|
||||
sut.addAssets(authStub.admin, 'link-1', { assetIds: [assetEntityStub.image.id, 'asset-2', 'asset-3'] }),
|
||||
).resolves.toEqual([
|
||||
{ assetId: assetEntityStub.image.id, success: false, error: AssetIdErrorReason.DUPLICATE },
|
||||
{ assetId: 'asset-2', success: false, error: AssetIdErrorReason.NO_PERMISSION },
|
||||
{ assetId: 'asset-3', success: true },
|
||||
]);
|
||||
|
||||
expect(accessMock.hasOwnerAssetAccess).toHaveBeenCalledTimes(2);
|
||||
expect(shareMock.update).toHaveBeenCalledWith({
|
||||
...sharedLinkStub.individual,
|
||||
assets: [assetEntityStub.image, { id: 'asset-3' }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeAssets', () => {
|
||||
it('should not work on album shared links', async () => {
|
||||
shareMock.get.mockResolvedValue(sharedLinkStub.valid);
|
||||
await expect(sut.removeAssets(authStub.admin, 'link-1', { assetIds: ['asset-1'] })).rejects.toBeInstanceOf(
|
||||
BadRequestException,
|
||||
);
|
||||
});
|
||||
|
||||
it('should remove assets from a shared link', async () => {
|
||||
shareMock.get.mockResolvedValue(_.cloneDeep(sharedLinkStub.individual));
|
||||
shareMock.create.mockResolvedValue(sharedLinkStub.individual);
|
||||
|
||||
await expect(
|
||||
sut.removeAssets(authStub.admin, 'link-1', { assetIds: [assetEntityStub.image.id, 'asset-2'] }),
|
||||
).resolves.toEqual([
|
||||
{ assetId: assetEntityStub.image.id, success: true },
|
||||
{ assetId: 'asset-2', success: false, error: AssetIdErrorReason.NOT_FOUND },
|
||||
]);
|
||||
|
||||
expect(shareMock.update).toHaveBeenCalledWith({ ...sharedLinkStub.individual, assets: [] });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
import { SharedLinkEntity } from '@app/infra/entities';
|
||||
import { AssetEntity, SharedLinkEntity, SharedLinkType } from '@app/infra/entities';
|
||||
import { BadRequestException, ForbiddenException, Inject, Injectable } from '@nestjs/common';
|
||||
import { IAccessRepository } from '../access';
|
||||
import { AssetIdErrorReason, AssetIdsDto, AssetIdsResponseDto } from '../asset';
|
||||
import { AuthUserDto } from '../auth';
|
||||
import { EditSharedLinkDto } from './dto';
|
||||
import { mapSharedLink, mapSharedLinkWithNoExif, SharedLinkResponseDto } from './response-dto';
|
||||
import { ICryptoRepository } from '../crypto';
|
||||
import { mapSharedLink, mapSharedLinkWithNoExif, SharedLinkResponseDto } from './shared-link-response.dto';
|
||||
import { SharedLinkCreateDto, SharedLinkEditDto } from './shared-link.dto';
|
||||
import { ISharedLinkRepository } from './shared-link.repository';
|
||||
|
||||
@Injectable()
|
||||
export class SharedLinkService {
|
||||
constructor(@Inject(ISharedLinkRepository) private repository: ISharedLinkRepository) {}
|
||||
constructor(
|
||||
@Inject(IAccessRepository) private accessRepository: IAccessRepository,
|
||||
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
|
||||
@Inject(ISharedLinkRepository) private repository: ISharedLinkRepository,
|
||||
) {}
|
||||
|
||||
async getAll(authUser: AuthUserDto): Promise<SharedLinkResponseDto[]> {
|
||||
getAll(authUser: AuthUserDto): Promise<SharedLinkResponseDto[]> {
|
||||
return this.repository.getAll(authUser.id).then((links) => links.map(mapSharedLink));
|
||||
}
|
||||
|
||||
|
@ -30,7 +37,52 @@ export class SharedLinkService {
|
|||
return this.map(sharedLink, { withExif: true });
|
||||
}
|
||||
|
||||
async update(authUser: AuthUserDto, id: string, dto: EditSharedLinkDto) {
|
||||
async create(authUser: AuthUserDto, dto: SharedLinkCreateDto): Promise<SharedLinkResponseDto> {
|
||||
switch (dto.type) {
|
||||
case SharedLinkType.ALBUM:
|
||||
if (!dto.albumId) {
|
||||
throw new BadRequestException('Invalid albumId');
|
||||
}
|
||||
|
||||
const isAlbumOwner = await this.accessRepository.hasAlbumOwnerAccess(authUser.id, dto.albumId);
|
||||
if (!isAlbumOwner) {
|
||||
throw new BadRequestException('Invalid albumId');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SharedLinkType.INDIVIDUAL:
|
||||
if (!dto.assetIds || dto.assetIds.length === 0) {
|
||||
throw new BadRequestException('Invalid assetIds');
|
||||
}
|
||||
|
||||
for (const assetId of dto.assetIds) {
|
||||
const hasAccess = await this.accessRepository.hasOwnerAssetAccess(authUser.id, assetId);
|
||||
if (!hasAccess) {
|
||||
throw new BadRequestException(`No access to assetId: ${assetId}`);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const sharedLink = await this.repository.create({
|
||||
key: this.cryptoRepository.randomBytes(50),
|
||||
userId: authUser.id,
|
||||
type: dto.type,
|
||||
albumId: dto.albumId || null,
|
||||
assets: (dto.assetIds || []).map((id) => ({ id } as AssetEntity)),
|
||||
description: dto.description || null,
|
||||
expiresAt: dto.expiresAt || null,
|
||||
allowUpload: dto.allowUpload ?? true,
|
||||
allowDownload: dto.allowDownload ?? true,
|
||||
showExif: dto.showExif ?? true,
|
||||
});
|
||||
|
||||
return this.map(sharedLink, { withExif: true });
|
||||
}
|
||||
|
||||
async update(authUser: AuthUserDto, id: string, dto: SharedLinkEditDto) {
|
||||
await this.findOrFail(authUser, id);
|
||||
const sharedLink = await this.repository.update({
|
||||
id,
|
||||
|
@ -57,6 +109,60 @@ export class SharedLinkService {
|
|||
return sharedLink;
|
||||
}
|
||||
|
||||
async addAssets(authUser: AuthUserDto, id: string, dto: AssetIdsDto): Promise<AssetIdsResponseDto[]> {
|
||||
const sharedLink = await this.findOrFail(authUser, id);
|
||||
|
||||
if (sharedLink.type !== SharedLinkType.INDIVIDUAL) {
|
||||
throw new BadRequestException('Invalid shared link type');
|
||||
}
|
||||
|
||||
const results: AssetIdsResponseDto[] = [];
|
||||
for (const assetId of dto.assetIds) {
|
||||
const hasAsset = sharedLink.assets.find((asset) => asset.id === assetId);
|
||||
if (hasAsset) {
|
||||
results.push({ assetId, success: false, error: AssetIdErrorReason.DUPLICATE });
|
||||
continue;
|
||||
}
|
||||
|
||||
const hasAccess = await this.accessRepository.hasOwnerAssetAccess(authUser.id, assetId);
|
||||
if (!hasAccess) {
|
||||
results.push({ assetId, success: false, error: AssetIdErrorReason.NO_PERMISSION });
|
||||
continue;
|
||||
}
|
||||
|
||||
results.push({ assetId, success: true });
|
||||
sharedLink.assets.push({ id: assetId } as AssetEntity);
|
||||
}
|
||||
|
||||
await this.repository.update(sharedLink);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
async removeAssets(authUser: AuthUserDto, id: string, dto: AssetIdsDto): Promise<AssetIdsResponseDto[]> {
|
||||
const sharedLink = await this.findOrFail(authUser, id);
|
||||
|
||||
if (sharedLink.type !== SharedLinkType.INDIVIDUAL) {
|
||||
throw new BadRequestException('Invalid shared link type');
|
||||
}
|
||||
|
||||
const results: AssetIdsResponseDto[] = [];
|
||||
for (const assetId of dto.assetIds) {
|
||||
const hasAsset = sharedLink.assets.find((asset) => asset.id === assetId);
|
||||
if (!hasAsset) {
|
||||
results.push({ assetId, success: false, error: AssetIdErrorReason.NOT_FOUND });
|
||||
continue;
|
||||
}
|
||||
|
||||
results.push({ assetId, success: true });
|
||||
sharedLink.assets = sharedLink.assets.filter((asset) => asset.id !== assetId);
|
||||
}
|
||||
|
||||
await this.repository.update(sharedLink);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private map(sharedLink: SharedLinkEntity, { withExif }: { withExif: boolean }) {
|
||||
return withExif ? mapSharedLink(sharedLink) : mapSharedLinkWithNoExif(sharedLink);
|
||||
}
|
||||
|
|
|
@ -194,5 +194,26 @@ describe(StorageTemplateService.name, () => {
|
|||
['upload/library/user-id/2023/2023-02-23/asset-id.ext', '/original/path.ext'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not move read-only asset', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
...assetEntityStub.image,
|
||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.ext',
|
||||
isReadOnly: true,
|
||||
},
|
||||
],
|
||||
hasNextPage: false,
|
||||
});
|
||||
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
||||
|
||||
await sut.handleMigration();
|
||||
|
||||
expect(assetMock.getAll).toHaveBeenCalled();
|
||||
expect(storageMock.moveFile).not.toHaveBeenCalled();
|
||||
expect(assetMock.save).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -76,6 +76,11 @@ export class StorageTemplateService {
|
|||
|
||||
// TODO: use asset core (once in domain)
|
||||
async moveAsset(asset: AssetEntity, metadata: MoveAssetMetadata) {
|
||||
if (asset.isReadOnly) {
|
||||
this.logger.verbose(`Not moving read-only asset: ${asset.originalPath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const destination = await this.core.getTemplatePath(asset, metadata);
|
||||
if (asset.originalPath !== destination) {
|
||||
const source = asset.originalPath;
|
||||
|
@ -96,7 +101,10 @@ export class StorageTemplateService {
|
|||
asset.originalPath = destination;
|
||||
asset.sidecarPath = sidecarDestination || null;
|
||||
} catch (error: any) {
|
||||
this.logger.warn('Unable to save new originalPath to database, undoing move', error?.stack);
|
||||
this.logger.warn(
|
||||
`Unable to save new originalPath to database, undoing move for path ${asset.originalPath} - filename ${asset.originalFileName} - id ${asset.id}`,
|
||||
error?.stack,
|
||||
);
|
||||
|
||||
// Either sidecar move failed or the save failed. Eithr way, move media back
|
||||
await this.storageRepository.moveFile(destination, source);
|
||||
|
|
|
@ -23,6 +23,10 @@ export class CreateUserDto {
|
|||
@IsString()
|
||||
@Transform(toSanitized)
|
||||
storageLabel?: string | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
externalPath?: string | null;
|
||||
}
|
||||
|
||||
export class CreateAdminDto {
|
||||
|
|
|
@ -29,6 +29,10 @@ export class UpdateUserDto {
|
|||
@Transform(toSanitized)
|
||||
storageLabel?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
externalPath?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsUUID('4')
|
||||
@ApiProperty({ format: 'uuid' })
|
||||
|
|
|
@ -6,6 +6,7 @@ export class UserResponseDto {
|
|||
firstName!: string;
|
||||
lastName!: string;
|
||||
storageLabel!: string | null;
|
||||
externalPath!: string | null;
|
||||
profileImagePath!: string;
|
||||
shouldChangePassword!: boolean;
|
||||
isAdmin!: boolean;
|
||||
|
@ -22,6 +23,7 @@ export function mapUser(entity: UserEntity): UserResponseDto {
|
|||
firstName: entity.firstName,
|
||||
lastName: entity.lastName,
|
||||
storageLabel: entity.storageLabel,
|
||||
externalPath: entity.externalPath,
|
||||
profileImagePath: entity.profileImagePath,
|
||||
shouldChangePassword: entity.shouldChangePassword,
|
||||
isAdmin: entity.isAdmin,
|
||||
|
|
|
@ -6,7 +6,6 @@ import {
|
|||
Logger,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { hash } from 'bcrypt';
|
||||
import { constants, createReadStream, ReadStream } from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import { AuthUserDto } from '../auth';
|
||||
|
@ -28,6 +27,7 @@ export class UserCore {
|
|||
// Users can never update the isAdmin property.
|
||||
delete dto.isAdmin;
|
||||
delete dto.storageLabel;
|
||||
delete dto.externalPath;
|
||||
} else if (dto.isAdmin && authUser.id !== id) {
|
||||
// Admin cannot create another admin.
|
||||
throw new BadRequestException('The server already has an admin');
|
||||
|
@ -56,6 +56,10 @@ export class UserCore {
|
|||
dto.storageLabel = null;
|
||||
}
|
||||
|
||||
if (dto.externalPath === '') {
|
||||
dto.externalPath = null;
|
||||
}
|
||||
|
||||
return this.userRepository.update(id, dto);
|
||||
} catch (e) {
|
||||
Logger.error(e, 'Failed to update user info');
|
||||
|
@ -79,7 +83,7 @@ export class UserCore {
|
|||
try {
|
||||
const payload: Partial<UserEntity> = { ...createUserDto };
|
||||
if (payload.password) {
|
||||
payload.password = await hash(payload.password, SALT_ROUNDS);
|
||||
payload.password = await this.cryptoRepository.hashBcrypt(payload.password, SALT_ROUNDS);
|
||||
}
|
||||
return this.userRepository.create(payload);
|
||||
} catch (e) {
|
||||
|
|
|
@ -53,6 +53,7 @@ const adminUser: UserEntity = Object.freeze({
|
|||
tags: [],
|
||||
assets: [],
|
||||
storageLabel: 'admin',
|
||||
externalPath: null,
|
||||
});
|
||||
|
||||
const immichUser: UserEntity = Object.freeze({
|
||||
|
@ -71,6 +72,7 @@ const immichUser: UserEntity = Object.freeze({
|
|||
tags: [],
|
||||
assets: [],
|
||||
storageLabel: null,
|
||||
externalPath: null,
|
||||
});
|
||||
|
||||
const updatedImmichUser: UserEntity = Object.freeze({
|
||||
|
@ -89,6 +91,7 @@ const updatedImmichUser: UserEntity = Object.freeze({
|
|||
tags: [],
|
||||
assets: [],
|
||||
storageLabel: null,
|
||||
externalPath: null,
|
||||
});
|
||||
|
||||
const adminUserResponse = Object.freeze({
|
||||
|
@ -104,6 +107,7 @@ const adminUserResponse = Object.freeze({
|
|||
deletedAt: null,
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
storageLabel: 'admin',
|
||||
externalPath: null,
|
||||
});
|
||||
|
||||
describe(UserService.name, () => {
|
||||
|
@ -153,6 +157,7 @@ describe(UserService.name, () => {
|
|||
deletedAt: null,
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
storageLabel: 'admin',
|
||||
externalPath: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { AlbumResponseDto } from '@app/domain';
|
||||
import { Body, Controller, Delete, Get, Param, Post, Put, Query, Response } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, Put, Query, Response } from '@nestjs/common';
|
||||
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { Response as Res } from 'express';
|
||||
import { handleDownload } from '../../app.utils';
|
||||
|
@ -10,7 +10,6 @@ import { UseValidation } from '../../decorators/use-validation.decorator';
|
|||
import { DownloadDto } from '../asset/dto/download-library.dto';
|
||||
import { AlbumService } from './album.service';
|
||||
import { AddAssetsDto } from './dto/add-assets.dto';
|
||||
import { CreateAlbumShareLinkDto as CreateAlbumSharedLinkDto } from './dto/create-album-shared-link.dto';
|
||||
import { RemoveAssetsDto } from './dto/remove-assets.dto';
|
||||
import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
|
||||
|
||||
|
@ -59,9 +58,4 @@ export class AlbumController {
|
|||
) {
|
||||
return this.service.downloadArchive(authUser, id, dto).then((download) => handleDownload(download, res));
|
||||
}
|
||||
|
||||
@Post('create-shared-link')
|
||||
createAlbumSharedLink(@AuthUser() authUser: AuthUserDto, @Body() dto: CreateAlbumSharedLinkDto) {
|
||||
return this.service.createSharedLink(authUser, dto);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { AlbumResponseDto, ICryptoRepository, ISharedLinkRepository, mapUser } from '@app/domain';
|
||||
import { AlbumResponseDto, mapUser } from '@app/domain';
|
||||
import { AlbumEntity, UserEntity } from '@app/infra/entities';
|
||||
import { ForbiddenException, NotFoundException } from '@nestjs/common';
|
||||
import { newCryptoRepositoryMock, newSharedLinkRepositoryMock, userEntityStub } from '@test';
|
||||
import { userEntityStub } from '@test';
|
||||
import { AuthUserDto } from '../../decorators/auth-user.decorator';
|
||||
import { DownloadService } from '../../modules/download/download.service';
|
||||
import { IAlbumRepository } from './album-repository';
|
||||
|
@ -11,9 +11,7 @@ import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
|
|||
describe('Album service', () => {
|
||||
let sut: AlbumService;
|
||||
let albumRepositoryMock: jest.Mocked<IAlbumRepository>;
|
||||
let sharedLinkRepositoryMock: jest.Mocked<ISharedLinkRepository>;
|
||||
let downloadServiceMock: jest.Mocked<Partial<DownloadService>>;
|
||||
let cryptoMock: jest.Mocked<ICryptoRepository>;
|
||||
|
||||
const authUser: AuthUserDto = Object.freeze({
|
||||
id: '1111',
|
||||
|
@ -34,6 +32,7 @@ describe('Album service', () => {
|
|||
tags: [],
|
||||
assets: [],
|
||||
storageLabel: null,
|
||||
externalPath: null,
|
||||
});
|
||||
const albumId = 'f19ab956-4761-41ea-a5d6-bae948308d58';
|
||||
const sharedAlbumOwnerId = '2222';
|
||||
|
@ -99,20 +98,11 @@ describe('Album service', () => {
|
|||
updateThumbnails: jest.fn(),
|
||||
};
|
||||
|
||||
sharedLinkRepositoryMock = newSharedLinkRepositoryMock();
|
||||
|
||||
downloadServiceMock = {
|
||||
downloadArchive: jest.fn(),
|
||||
};
|
||||
|
||||
cryptoMock = newCryptoRepositoryMock();
|
||||
|
||||
sut = new AlbumService(
|
||||
albumRepositoryMock,
|
||||
sharedLinkRepositoryMock,
|
||||
downloadServiceMock as DownloadService,
|
||||
cryptoMock,
|
||||
);
|
||||
sut = new AlbumService(albumRepositoryMock, downloadServiceMock as DownloadService);
|
||||
});
|
||||
|
||||
it('gets an owned album', async () => {
|
||||
|
|
|
@ -1,36 +1,22 @@
|
|||
import {
|
||||
AlbumResponseDto,
|
||||
ICryptoRepository,
|
||||
ISharedLinkRepository,
|
||||
mapAlbum,
|
||||
mapSharedLink,
|
||||
SharedLinkCore,
|
||||
SharedLinkResponseDto,
|
||||
} from '@app/domain';
|
||||
import { AlbumEntity, SharedLinkType } from '@app/infra/entities';
|
||||
import { AlbumResponseDto, mapAlbum } from '@app/domain';
|
||||
import { AlbumEntity } from '@app/infra/entities';
|
||||
import { BadRequestException, ForbiddenException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common';
|
||||
import { AuthUserDto } from '../../decorators/auth-user.decorator';
|
||||
import { DownloadService } from '../../modules/download/download.service';
|
||||
import { DownloadDto } from '../asset/dto/download-library.dto';
|
||||
import { IAlbumRepository } from './album-repository';
|
||||
import { AddAssetsDto } from './dto/add-assets.dto';
|
||||
import { CreateAlbumShareLinkDto } from './dto/create-album-shared-link.dto';
|
||||
import { RemoveAssetsDto } from './dto/remove-assets.dto';
|
||||
import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class AlbumService {
|
||||
readonly logger = new Logger(AlbumService.name);
|
||||
private shareCore: SharedLinkCore;
|
||||
private logger = new Logger(AlbumService.name);
|
||||
|
||||
constructor(
|
||||
@Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
|
||||
@Inject(ISharedLinkRepository) sharedLinkRepository: ISharedLinkRepository,
|
||||
private downloadService: DownloadService,
|
||||
@Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
|
||||
) {
|
||||
this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
private async _getAlbum({
|
||||
authUser,
|
||||
|
@ -91,7 +77,7 @@ export class AlbumService {
|
|||
}
|
||||
|
||||
async downloadArchive(authUser: AuthUserDto, albumId: string, dto: DownloadDto) {
|
||||
this.shareCore.checkDownloadAccess(authUser);
|
||||
this.checkDownloadAccess(authUser);
|
||||
|
||||
const album = await this._getAlbum({ authUser, albumId, validateIsOwner: false });
|
||||
const assets = (album.assets || []).map((asset) => asset).slice(dto.skip || 0);
|
||||
|
@ -99,20 +85,9 @@ export class AlbumService {
|
|||
return this.downloadService.downloadArchive(album.albumName, assets);
|
||||
}
|
||||
|
||||
async createSharedLink(authUser: AuthUserDto, dto: CreateAlbumShareLinkDto): Promise<SharedLinkResponseDto> {
|
||||
const album = await this._getAlbum({ authUser, albumId: dto.albumId });
|
||||
|
||||
const sharedLink = await this.shareCore.create(authUser.id, {
|
||||
type: SharedLinkType.ALBUM,
|
||||
expiresAt: dto.expiresAt,
|
||||
allowUpload: dto.allowUpload,
|
||||
album,
|
||||
assets: [],
|
||||
description: dto.description,
|
||||
allowDownload: dto.allowDownload,
|
||||
showExif: dto.showExif,
|
||||
});
|
||||
|
||||
return mapSharedLink(sharedLink);
|
||||
private checkDownloadAccess(authUser: AuthUserDto) {
|
||||
if (authUser.isPublicUser && !authUser.isAllowDownload) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
import { ValidateUUID } from '@app/immich/decorators/validate-uuid.decorator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsBoolean, IsDate, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateAlbumShareLinkDto {
|
||||
@ValidateUUID()
|
||||
albumId!: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
@ApiProperty()
|
||||
expiresAt?: Date;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@ApiProperty()
|
||||
allowUpload?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@ApiProperty()
|
||||
allowDownload?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@ApiProperty()
|
||||
showExif?: boolean;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiProperty()
|
||||
description?: string;
|
||||
}
|
|
@ -20,6 +20,10 @@ export interface AssetCheck {
|
|||
checksum: Buffer;
|
||||
}
|
||||
|
||||
export interface AssetOwnerCheck extends AssetCheck {
|
||||
ownerId: string;
|
||||
}
|
||||
|
||||
export interface IAssetRepository {
|
||||
get(id: string): Promise<AssetEntity | null>;
|
||||
create(
|
||||
|
@ -39,6 +43,7 @@ export interface IAssetRepository {
|
|||
getAssetByTimeBucket(userId: string, getAssetByTimeBucketDto: GetAssetByTimeBucketDto): Promise<AssetEntity[]>;
|
||||
getAssetsByChecksums(userId: string, checksums: Buffer[]): Promise<AssetCheck[]>;
|
||||
getExistingAssets(userId: string, checkDuplicateAssetDto: CheckExistingAssetsDto): Promise<string[]>;
|
||||
getByOriginalPath(originalPath: string): Promise<AssetOwnerCheck | null>;
|
||||
}
|
||||
|
||||
export const IAssetRepository = 'IAssetRepository';
|
||||
|
@ -350,4 +355,17 @@ export class AssetRepository implements IAssetRepository {
|
|||
|
||||
return assetCountByUserId;
|
||||
}
|
||||
|
||||
getByOriginalPath(originalPath: string): Promise<AssetOwnerCheck | null> {
|
||||
return this.assetRepository.findOne({
|
||||
select: {
|
||||
id: true,
|
||||
ownerId: true,
|
||||
checksum: true,
|
||||
},
|
||||
where: {
|
||||
originalPath,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { AssetResponseDto, ImmichReadStream, SharedLinkResponseDto } from '@app/domain';
|
||||
import { AssetResponseDto, ImmichReadStream } from '@app/domain';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
|
@ -10,7 +10,6 @@ import {
|
|||
HttpStatus,
|
||||
Param,
|
||||
ParseFilePipe,
|
||||
Patch,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
|
@ -28,16 +27,13 @@ import { assetUploadOption, ImmichFile } from '../../config/asset-upload.config'
|
|||
import { UUIDParamDto } from '../../controllers/dto/uuid-param.dto';
|
||||
import { AuthUser, AuthUserDto } from '../../decorators/auth-user.decorator';
|
||||
import { Authenticated, SharedLinkRoute } from '../../decorators/authenticated.decorator';
|
||||
import { AddAssetsDto } from '../album/dto/add-assets.dto';
|
||||
import { RemoveAssetsDto } from '../album/dto/remove-assets.dto';
|
||||
import FileNotEmptyValidator from '../validation/file-not-empty-validator';
|
||||
import { AssetService } from './asset.service';
|
||||
import { AssetBulkUploadCheckDto } from './dto/asset-check.dto';
|
||||
import { AssetSearchDto } from './dto/asset-search.dto';
|
||||
import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
|
||||
import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
|
||||
import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto';
|
||||
import { CreateAssetDto, mapToUploadFile } from './dto/create-asset.dto';
|
||||
import { CreateAssetDto, ImportAssetDto, mapToUploadFile } from './dto/create-asset.dto';
|
||||
import { DeleteAssetDto } from './dto/delete-asset.dto';
|
||||
import { DeviceIdDto } from './dto/device-id.dto';
|
||||
import { DownloadFilesDto } from './dto/download-files.dto';
|
||||
|
@ -118,6 +114,20 @@ export class AssetController {
|
|||
return responseDto;
|
||||
}
|
||||
|
||||
@Post('import')
|
||||
async importFile(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Body(new ValidationPipe()) dto: ImportAssetDto,
|
||||
@Response({ passthrough: true }) res: Res,
|
||||
): Promise<AssetFileUploadResponseDto> {
|
||||
const responseDto = await this.assetService.importFile(authUser, dto);
|
||||
if (responseDto.duplicate) {
|
||||
res.status(200);
|
||||
}
|
||||
|
||||
return responseDto;
|
||||
}
|
||||
|
||||
@SharedLinkRoute()
|
||||
@Get('/download/:id')
|
||||
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
|
||||
|
@ -319,30 +329,4 @@ export class AssetController {
|
|||
): Promise<AssetBulkUploadCheckResponseDto> {
|
||||
return this.assetService.bulkUploadCheck(authUser, dto);
|
||||
}
|
||||
|
||||
@Post('/shared-link')
|
||||
createAssetsSharedLink(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Body(ValidationPipe) dto: CreateAssetsShareLinkDto,
|
||||
): Promise<SharedLinkResponseDto> {
|
||||
return this.assetService.createAssetsSharedLink(authUser, dto);
|
||||
}
|
||||
|
||||
@SharedLinkRoute()
|
||||
@Patch('/shared-link/add')
|
||||
addAssetsToSharedLink(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Body(ValidationPipe) dto: AddAssetsDto,
|
||||
): Promise<SharedLinkResponseDto> {
|
||||
return this.assetService.addAssetsToSharedLink(authUser, dto);
|
||||
}
|
||||
|
||||
@SharedLinkRoute()
|
||||
@Patch('/shared-link/remove')
|
||||
removeAssetsFromSharedLink(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Body(ValidationPipe) dto: RemoveAssetsDto,
|
||||
): Promise<SharedLinkResponseDto> {
|
||||
return this.assetService.removeAssetsFromSharedLink(authUser, dto);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,17 +2,17 @@ import { AuthUserDto, IJobRepository, JobName } from '@app/domain';
|
|||
import { AssetEntity, AssetType, UserEntity } from '@app/infra/entities';
|
||||
import { parse } from 'node:path';
|
||||
import { IAssetRepository } from './asset-repository';
|
||||
import { CreateAssetDto, UploadFile } from './dto/create-asset.dto';
|
||||
import { CreateAssetDto, ImportAssetDto, UploadFile } from './dto/create-asset.dto';
|
||||
|
||||
export class AssetCore {
|
||||
constructor(private repository: IAssetRepository, private jobRepository: IJobRepository) {}
|
||||
|
||||
async create(
|
||||
authUser: AuthUserDto,
|
||||
dto: CreateAssetDto,
|
||||
dto: CreateAssetDto | ImportAssetDto,
|
||||
file: UploadFile,
|
||||
livePhotoAssetId?: string,
|
||||
sidecarFile?: UploadFile,
|
||||
sidecarPath?: string,
|
||||
): Promise<AssetEntity> {
|
||||
const asset = await this.repository.create({
|
||||
owner: { id: authUser.id } as UserEntity,
|
||||
|
@ -41,7 +41,8 @@ export class AssetCore {
|
|||
sharedLinks: [],
|
||||
originalFileName: parse(file.originalName).name,
|
||||
faces: [],
|
||||
sidecarPath: sidecarFile?.originalPath || null,
|
||||
sidecarPath: sidecarPath || null,
|
||||
isReadOnly: dto.isReadOnly ?? false,
|
||||
});
|
||||
|
||||
await this.jobRepository.queue({ name: JobName.METADATA_EXTRACTION, data: { id: asset.id, source: 'upload' } });
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
import {
|
||||
IAccessRepository,
|
||||
ICryptoRepository,
|
||||
IJobRepository,
|
||||
ISharedLinkRepository,
|
||||
IStorageRepository,
|
||||
JobName,
|
||||
} from '@app/domain';
|
||||
import { IAccessRepository, ICryptoRepository, IJobRepository, IStorageRepository, JobName } from '@app/domain';
|
||||
import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities';
|
||||
import { BadRequestException, ForbiddenException } from '@nestjs/common';
|
||||
import { ForbiddenException } from '@nestjs/common';
|
||||
import {
|
||||
assetEntityStub,
|
||||
authStub,
|
||||
|
@ -15,17 +8,13 @@ import {
|
|||
newAccessRepositoryMock,
|
||||
newCryptoRepositoryMock,
|
||||
newJobRepositoryMock,
|
||||
newSharedLinkRepositoryMock,
|
||||
newStorageRepositoryMock,
|
||||
sharedLinkResponseStub,
|
||||
sharedLinkStub,
|
||||
} from '@test';
|
||||
import { when } from 'jest-when';
|
||||
import { QueryFailedError, Repository } from 'typeorm';
|
||||
import { DownloadService } from '../../modules/download/download.service';
|
||||
import { IAssetRepository } from './asset-repository';
|
||||
import { AssetService } from './asset.service';
|
||||
import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto';
|
||||
import { CreateAssetDto } from './dto/create-asset.dto';
|
||||
import { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto';
|
||||
import { AssetRejectReason, AssetUploadAction } from './response-dto/asset-check-response.dto';
|
||||
|
@ -133,9 +122,8 @@ describe('AssetService', () => {
|
|||
let a: Repository<AssetEntity>; // TO BE DELETED AFTER FINISHED REFACTORING
|
||||
let accessMock: jest.Mocked<IAccessRepository>;
|
||||
let assetRepositoryMock: jest.Mocked<IAssetRepository>;
|
||||
let downloadServiceMock: jest.Mocked<Partial<DownloadService>>;
|
||||
let sharedLinkRepositoryMock: jest.Mocked<ISharedLinkRepository>;
|
||||
let cryptoMock: jest.Mocked<ICryptoRepository>;
|
||||
let downloadServiceMock: jest.Mocked<Partial<DownloadService>>;
|
||||
let jobMock: jest.Mocked<IJobRepository>;
|
||||
let storageMock: jest.Mocked<IStorageRepository>;
|
||||
|
||||
|
@ -158,26 +146,27 @@ describe('AssetService', () => {
|
|||
getAssetCountByUserId: jest.fn(),
|
||||
getArchivedAssetCountByUserId: jest.fn(),
|
||||
getExistingAssets: jest.fn(),
|
||||
getByOriginalPath: jest.fn(),
|
||||
};
|
||||
|
||||
cryptoMock = newCryptoRepositoryMock();
|
||||
|
||||
downloadServiceMock = {
|
||||
downloadArchive: jest.fn(),
|
||||
};
|
||||
|
||||
accessMock = newAccessRepositoryMock();
|
||||
sharedLinkRepositoryMock = newSharedLinkRepositoryMock();
|
||||
jobMock = newJobRepositoryMock();
|
||||
cryptoMock = newCryptoRepositoryMock();
|
||||
jobMock = newJobRepositoryMock();
|
||||
storageMock = newStorageRepositoryMock();
|
||||
|
||||
sut = new AssetService(
|
||||
accessMock,
|
||||
assetRepositoryMock,
|
||||
a,
|
||||
downloadServiceMock as DownloadService,
|
||||
sharedLinkRepositoryMock,
|
||||
jobMock,
|
||||
cryptoMock,
|
||||
downloadServiceMock as DownloadService,
|
||||
jobMock,
|
||||
storageMock,
|
||||
);
|
||||
|
||||
|
@ -189,77 +178,6 @@ describe('AssetService', () => {
|
|||
.mockResolvedValue(assetEntityStub.livePhotoMotionAsset);
|
||||
});
|
||||
|
||||
describe('createAssetsSharedLink', () => {
|
||||
it('should create an individual share link', async () => {
|
||||
const asset1 = _getAsset_1();
|
||||
const dto: CreateAssetsShareLinkDto = { assetIds: [asset1.id] };
|
||||
|
||||
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
||||
accessMock.hasOwnerAssetAccess.mockResolvedValue(true);
|
||||
sharedLinkRepositoryMock.create.mockResolvedValue(sharedLinkStub.valid);
|
||||
|
||||
await expect(sut.createAssetsSharedLink(authStub.user1, dto)).resolves.toEqual(sharedLinkResponseStub.valid);
|
||||
|
||||
expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id);
|
||||
expect(accessMock.hasOwnerAssetAccess).toHaveBeenCalledWith(authStub.user1.id, asset1.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateAssetsInSharedLink', () => {
|
||||
it('should require a valid shared link', async () => {
|
||||
const asset1 = _getAsset_1();
|
||||
|
||||
const authDto = authStub.adminSharedLink;
|
||||
const dto = { assetIds: [asset1.id] };
|
||||
|
||||
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
||||
sharedLinkRepositoryMock.get.mockResolvedValue(null);
|
||||
accessMock.hasSharedLinkAssetAccess.mockResolvedValue(true);
|
||||
|
||||
await expect(sut.addAssetsToSharedLink(authDto, dto)).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id);
|
||||
expect(sharedLinkRepositoryMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
|
||||
expect(sharedLinkRepositoryMock.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should add assets to a shared link', async () => {
|
||||
const asset1 = _getAsset_1();
|
||||
|
||||
const authDto = authStub.adminSharedLink;
|
||||
const dto = { assetIds: [asset1.id] };
|
||||
|
||||
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
||||
sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid);
|
||||
accessMock.hasSharedLinkAssetAccess.mockResolvedValue(true);
|
||||
sharedLinkRepositoryMock.update.mockResolvedValue(sharedLinkStub.valid);
|
||||
|
||||
await expect(sut.addAssetsToSharedLink(authDto, dto)).resolves.toEqual(sharedLinkResponseStub.valid);
|
||||
|
||||
expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id);
|
||||
expect(sharedLinkRepositoryMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
|
||||
expect(sharedLinkRepositoryMock.update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should remove assets from a shared link', async () => {
|
||||
const asset1 = _getAsset_1();
|
||||
|
||||
const authDto = authStub.adminSharedLink;
|
||||
const dto = { assetIds: [asset1.id] };
|
||||
|
||||
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
||||
sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid);
|
||||
accessMock.hasSharedLinkAssetAccess.mockResolvedValue(true);
|
||||
sharedLinkRepositoryMock.update.mockResolvedValue(sharedLinkStub.valid);
|
||||
|
||||
await expect(sut.removeAssetsFromSharedLink(authDto, dto)).resolves.toEqual(sharedLinkResponseStub.valid);
|
||||
|
||||
expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id);
|
||||
expect(sharedLinkRepositoryMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
|
||||
expect(sharedLinkRepositoryMock.update).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('uploadFile', () => {
|
||||
it('should handle a file upload', async () => {
|
||||
const assetEntity = _getAsset_1();
|
||||
|
@ -528,6 +446,43 @@ describe('AssetService', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('importFile', () => {
|
||||
it('should handle a file import', async () => {
|
||||
assetRepositoryMock.create.mockResolvedValue(assetEntityStub.image);
|
||||
storageMock.checkFileExists.mockResolvedValue(true);
|
||||
|
||||
await expect(
|
||||
sut.importFile(authStub.external1, {
|
||||
..._getCreateAssetDto(),
|
||||
assetPath: '/data/user1/fake_path/asset_1.jpeg',
|
||||
isReadOnly: true,
|
||||
}),
|
||||
).resolves.toEqual({ duplicate: false, id: 'asset-id' });
|
||||
|
||||
expect(assetRepositoryMock.create).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle a duplicate if originalPath already exists', async () => {
|
||||
const error = new QueryFailedError('', [], '');
|
||||
(error as any).constraint = 'UQ_userid_checksum';
|
||||
|
||||
assetRepositoryMock.create.mockRejectedValue(error);
|
||||
assetRepositoryMock.getAssetsByChecksums.mockResolvedValue([assetEntityStub.image]);
|
||||
storageMock.checkFileExists.mockResolvedValue(true);
|
||||
cryptoMock.hashFile.mockResolvedValue(Buffer.from('file hash', 'utf8'));
|
||||
|
||||
await expect(
|
||||
sut.importFile(authStub.external1, {
|
||||
..._getCreateAssetDto(),
|
||||
assetPath: '/data/user1/fake_path/asset_1.jpeg',
|
||||
isReadOnly: true,
|
||||
}),
|
||||
).resolves.toEqual({ duplicate: true, id: 'asset-id' });
|
||||
|
||||
expect(assetRepositoryMock.create).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAssetById', () => {
|
||||
it('should allow owner access', async () => {
|
||||
accessMock.hasOwnerAssetAccess.mockResolvedValue(true);
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
import {
|
||||
AssetResponseDto,
|
||||
AuthUserDto,
|
||||
getLivePhotoMotionFilename,
|
||||
IAccessRepository,
|
||||
ICryptoRepository,
|
||||
IJobRepository,
|
||||
ImmichReadStream,
|
||||
ISharedLinkRepository,
|
||||
isSidecarFileType,
|
||||
isSupportedFileType,
|
||||
IStorageRepository,
|
||||
JobName,
|
||||
mapAsset,
|
||||
mapAssetWithoutExif,
|
||||
mapSharedLink,
|
||||
SharedLinkCore,
|
||||
SharedLinkResponseDto,
|
||||
} from '@app/domain';
|
||||
import { AssetEntity, AssetType, SharedLinkType } from '@app/infra/entities';
|
||||
import { AssetEntity, AssetType } from '@app/infra/entities';
|
||||
import {
|
||||
BadRequestException,
|
||||
ForbiddenException,
|
||||
|
@ -26,23 +25,22 @@ import {
|
|||
StreamableFile,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { R_OK, W_OK } from 'constants';
|
||||
import { Response as Res } from 'express';
|
||||
import { constants, createReadStream, stat } from 'fs';
|
||||
import { createReadStream, stat } from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import mime from 'mime-types';
|
||||
import path from 'path';
|
||||
import { QueryFailedError, Repository } from 'typeorm';
|
||||
import { promisify } from 'util';
|
||||
import { AuthUserDto } from '../../decorators/auth-user.decorator';
|
||||
import { DownloadService } from '../../modules/download/download.service';
|
||||
import { AddAssetsDto } from '../album/dto/add-assets.dto';
|
||||
import { RemoveAssetsDto } from '../album/dto/remove-assets.dto';
|
||||
import { IAssetRepository } from './asset-repository';
|
||||
import { AssetCore } from './asset.core';
|
||||
import { AssetBulkUploadCheckDto } from './dto/asset-check.dto';
|
||||
import { AssetSearchDto } from './dto/asset-search.dto';
|
||||
import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
|
||||
import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
|
||||
import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto';
|
||||
import { CreateAssetDto, UploadFile } from './dto/create-asset.dto';
|
||||
import { CreateAssetDto, ImportAssetDto, UploadFile } from './dto/create-asset.dto';
|
||||
import { DeleteAssetDto } from './dto/delete-asset.dto';
|
||||
import { DownloadFilesDto } from './dto/download-files.dto';
|
||||
import { DownloadDto } from './dto/download-library.dto';
|
||||
|
@ -80,22 +78,18 @@ interface ServableFile {
|
|||
@Injectable()
|
||||
export class AssetService {
|
||||
readonly logger = new Logger(AssetService.name);
|
||||
private shareCore: SharedLinkCore;
|
||||
private assetCore: AssetCore;
|
||||
|
||||
constructor(
|
||||
@Inject(IAccessRepository) private accessRepository: IAccessRepository,
|
||||
@Inject(IAssetRepository) private _assetRepository: IAssetRepository,
|
||||
@InjectRepository(AssetEntity)
|
||||
private assetRepository: Repository<AssetEntity>,
|
||||
@InjectRepository(AssetEntity) private assetRepository: Repository<AssetEntity>,
|
||||
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
|
||||
private downloadService: DownloadService,
|
||||
@Inject(ISharedLinkRepository) sharedLinkRepository: ISharedLinkRepository,
|
||||
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
||||
@Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
|
||||
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
|
||||
) {
|
||||
this.assetCore = new AssetCore(_assetRepository, jobRepository);
|
||||
this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository);
|
||||
}
|
||||
|
||||
public async uploadFile(
|
||||
|
@ -120,7 +114,7 @@ export class AssetService {
|
|||
livePhotoAsset = await this.assetCore.create(authUser, livePhotoDto, livePhotoFile);
|
||||
}
|
||||
|
||||
const asset = await this.assetCore.create(authUser, dto, file, livePhotoAsset?.id, sidecarFile);
|
||||
const asset = await this.assetCore.create(authUser, dto, file, livePhotoAsset?.id, sidecarFile?.originalPath);
|
||||
|
||||
return { id: asset.id, duplicate: false };
|
||||
} catch (error: any) {
|
||||
|
@ -142,6 +136,73 @@ export class AssetService {
|
|||
}
|
||||
}
|
||||
|
||||
public async importFile(authUser: AuthUserDto, dto: ImportAssetDto): Promise<AssetFileUploadResponseDto> {
|
||||
dto = {
|
||||
...dto,
|
||||
assetPath: path.resolve(dto.assetPath),
|
||||
sidecarPath: dto.sidecarPath ? path.resolve(dto.sidecarPath) : undefined,
|
||||
};
|
||||
|
||||
const assetPathType = mime.lookup(dto.assetPath) as string;
|
||||
if (!isSupportedFileType(assetPathType)) {
|
||||
throw new BadRequestException(`Unsupported file type ${assetPathType}`);
|
||||
}
|
||||
|
||||
if (dto.sidecarPath) {
|
||||
const sidecarType = mime.lookup(dto.sidecarPath) as string;
|
||||
if (!isSidecarFileType(sidecarType)) {
|
||||
throw new BadRequestException(`Unsupported sidecar file type ${assetPathType}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const filepath of [dto.assetPath, dto.sidecarPath]) {
|
||||
if (!filepath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const exists = await this.storageRepository.checkFileExists(filepath, R_OK);
|
||||
if (!exists) {
|
||||
throw new BadRequestException('File does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
if (!authUser.externalPath || !dto.assetPath.match(new RegExp(`^${authUser.externalPath}`))) {
|
||||
throw new BadRequestException("File does not exist within user's external path");
|
||||
}
|
||||
|
||||
const assetFile: UploadFile = {
|
||||
checksum: await this.cryptoRepository.hashFile(dto.assetPath),
|
||||
mimeType: assetPathType,
|
||||
originalPath: dto.assetPath,
|
||||
originalName: path.parse(dto.assetPath).name,
|
||||
};
|
||||
|
||||
try {
|
||||
const asset = await this.assetCore.create(authUser, dto, assetFile, undefined, dto.sidecarPath);
|
||||
return { id: asset.id, duplicate: false };
|
||||
} catch (error: QueryFailedError | Error | any) {
|
||||
// handle duplicates with a success response
|
||||
if (error instanceof QueryFailedError && (error as any).constraint === 'UQ_userid_checksum') {
|
||||
const [duplicate] = await this._assetRepository.getAssetsByChecksums(authUser.id, [assetFile.checksum]);
|
||||
return { id: duplicate.id, duplicate: true };
|
||||
}
|
||||
|
||||
if (error instanceof QueryFailedError && (error as any).constraint === 'UQ_4ed4f8052685ff5b1e7ca1058ba') {
|
||||
const duplicate = await this._assetRepository.getByOriginalPath(dto.assetPath);
|
||||
if (duplicate) {
|
||||
if (duplicate.ownerId === authUser.id) {
|
||||
return { id: duplicate.id, duplicate: true };
|
||||
}
|
||||
|
||||
throw new BadRequestException('Path in use by another user');
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.error(`Error importing file ${error}`, error?.stack);
|
||||
throw new BadRequestException(`Error importing file`, `${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
public async getUserAssetsByDeviceId(authUser: AuthUserDto, deviceId: string) {
|
||||
return this._assetRepository.getAllByDeviceId(authUser.id, deviceId);
|
||||
}
|
||||
|
@ -304,7 +365,7 @@ export class AssetService {
|
|||
let videoPath = asset.originalPath;
|
||||
let mimeType = asset.mimeType;
|
||||
|
||||
await fs.access(videoPath, constants.R_OK | constants.W_OK);
|
||||
await fs.access(videoPath, R_OK | W_OK);
|
||||
|
||||
if (asset.encodedVideoPath) {
|
||||
videoPath = asset.encodedVideoPath == '' ? String(asset.originalPath) : String(asset.encodedVideoPath);
|
||||
|
@ -386,13 +447,16 @@ export class AssetService {
|
|||
await this.jobRepository.queue({ name: JobName.SEARCH_REMOVE_ASSET, data: { ids: [id] } });
|
||||
|
||||
result.push({ id, status: DeleteAssetStatusEnum.SUCCESS });
|
||||
deleteQueue.push(
|
||||
asset.originalPath,
|
||||
asset.webpPath,
|
||||
asset.resizePath,
|
||||
asset.encodedVideoPath,
|
||||
asset.sidecarPath,
|
||||
);
|
||||
|
||||
if (!asset.isReadOnly) {
|
||||
deleteQueue.push(
|
||||
asset.originalPath,
|
||||
asset.webpPath,
|
||||
asset.resizePath,
|
||||
asset.encodedVideoPath,
|
||||
asset.sidecarPath,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO refactor this to use cascades
|
||||
if (asset.livePhotoVideoId && !ids.includes(asset.livePhotoVideoId)) {
|
||||
|
@ -608,61 +672,9 @@ export class AssetService {
|
|||
}
|
||||
|
||||
private checkDownloadAccess(authUser: AuthUserDto) {
|
||||
this.shareCore.checkDownloadAccess(authUser);
|
||||
}
|
||||
|
||||
async createAssetsSharedLink(authUser: AuthUserDto, dto: CreateAssetsShareLinkDto): Promise<SharedLinkResponseDto> {
|
||||
const assets = [];
|
||||
|
||||
await this.checkAssetsAccess(authUser, dto.assetIds);
|
||||
for (const assetId of dto.assetIds) {
|
||||
const asset = await this._assetRepository.getById(assetId);
|
||||
assets.push(asset);
|
||||
}
|
||||
|
||||
const sharedLink = await this.shareCore.create(authUser.id, {
|
||||
type: SharedLinkType.INDIVIDUAL,
|
||||
expiresAt: dto.expiresAt,
|
||||
allowUpload: dto.allowUpload,
|
||||
assets,
|
||||
description: dto.description,
|
||||
allowDownload: dto.allowDownload,
|
||||
showExif: dto.showExif,
|
||||
});
|
||||
|
||||
return mapSharedLink(sharedLink);
|
||||
}
|
||||
|
||||
async addAssetsToSharedLink(authUser: AuthUserDto, dto: AddAssetsDto): Promise<SharedLinkResponseDto> {
|
||||
if (!authUser.sharedLinkId) {
|
||||
if (authUser.isPublicUser && !authUser.isAllowDownload) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
const assets = [];
|
||||
|
||||
for (const assetId of dto.assetIds) {
|
||||
const asset = await this._assetRepository.getById(assetId);
|
||||
assets.push(asset);
|
||||
}
|
||||
|
||||
const updatedLink = await this.shareCore.addAssets(authUser.id, authUser.sharedLinkId, assets);
|
||||
return mapSharedLink(updatedLink);
|
||||
}
|
||||
|
||||
async removeAssetsFromSharedLink(authUser: AuthUserDto, dto: RemoveAssetsDto): Promise<SharedLinkResponseDto> {
|
||||
if (!authUser.sharedLinkId) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
const assets = [];
|
||||
|
||||
for (const assetId of dto.assetIds) {
|
||||
const asset = await this._assetRepository.getById(assetId);
|
||||
assets.push(asset);
|
||||
}
|
||||
|
||||
const updatedLink = await this.shareCore.removeAssets(authUser.id, authUser.sharedLinkId, assets);
|
||||
return mapSharedLink(updatedLink);
|
||||
}
|
||||
|
||||
getExifPermission(authUser: AuthUserDto) {
|
||||
|
@ -730,7 +742,7 @@ export class AssetService {
|
|||
return;
|
||||
}
|
||||
|
||||
await fs.access(filepath, constants.R_OK);
|
||||
await fs.access(filepath, R_OK);
|
||||
|
||||
return new StreamableFile(createReadStream(filepath));
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsArray, IsBoolean, IsDate, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateAssetsShareLinkDto {
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@IsNotEmpty({ each: true })
|
||||
@ApiProperty({
|
||||
isArray: true,
|
||||
type: String,
|
||||
title: 'Array asset IDs to be shared',
|
||||
example: [
|
||||
'bf973405-3f2a-48d2-a687-2ed4167164be',
|
||||
'dd41870b-5d00-46d2-924e-1d8489a0aa0f',
|
||||
'fad77c3f-deef-4e7e-9608-14c1aa4e559a',
|
||||
],
|
||||
})
|
||||
assetIds!: string[];
|
||||
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
@IsOptional()
|
||||
expiresAt?: Date;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
allowUpload?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
allowDownload?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
showExif?: boolean;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import { AssetType } from '@app/infra/entities';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsEnum, IsNotEmpty, IsOptional } from 'class-validator';
|
||||
import { Transform } from 'class-transformer';
|
||||
import { IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
import { ImmichFile } from '../../../config/asset-upload.config';
|
||||
import { toBoolean, toSanitized } from '../../../utils/transform.util';
|
||||
|
||||
export class CreateAssetDto {
|
||||
export class CreateAssetBase {
|
||||
@IsNotEmpty()
|
||||
deviceAssetId!: string;
|
||||
|
||||
|
@ -32,11 +34,18 @@ export class CreateAssetDto {
|
|||
@IsBoolean()
|
||||
isVisible?: boolean;
|
||||
|
||||
@IsNotEmpty()
|
||||
fileExtension!: string;
|
||||
|
||||
@IsOptional()
|
||||
duration?: string;
|
||||
}
|
||||
|
||||
export class CreateAssetDto extends CreateAssetBase {
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Transform(toBoolean)
|
||||
isReadOnly?: boolean = false;
|
||||
|
||||
@IsNotEmpty()
|
||||
fileExtension!: string;
|
||||
|
||||
// The properties below are added to correctly generate the API docs
|
||||
// and client SDKs. Validation should be handled in the controller.
|
||||
|
@ -50,6 +59,23 @@ export class CreateAssetDto {
|
|||
sidecarData?: any;
|
||||
}
|
||||
|
||||
export class ImportAssetDto extends CreateAssetBase {
|
||||
@IsOptional()
|
||||
@Transform(toBoolean)
|
||||
isReadOnly?: boolean = true;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Transform(toSanitized)
|
||||
assetPath!: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@IsNotEmpty()
|
||||
@Transform(toSanitized)
|
||||
sidecarPath?: string;
|
||||
}
|
||||
|
||||
export interface UploadFile {
|
||||
mimeType: string;
|
||||
checksum: Buffer;
|
||||
|
|
|
@ -50,47 +50,49 @@ describe('assetUploadOption', () => {
|
|||
});
|
||||
|
||||
for (const { mimetype, extension } of [
|
||||
{ mimetype: 'image/dng', extension: 'dng' },
|
||||
{ mimetype: 'image/avif', extension: 'avif' },
|
||||
{ mimetype: 'image/gif', extension: 'gif' },
|
||||
{ mimetype: 'image/heic', extension: 'heic' },
|
||||
{ mimetype: 'image/heif', extension: 'heif' },
|
||||
{ mimetype: 'image/jpeg', extension: 'jpeg' },
|
||||
{ mimetype: 'image/jpeg', extension: 'jpg' },
|
||||
{ mimetype: 'image/jxl', extension: 'jxl' },
|
||||
{ mimetype: 'image/png', extension: 'png' },
|
||||
{ mimetype: 'image/tiff', extension: 'tiff' },
|
||||
{ mimetype: 'image/webp', extension: 'webp' },
|
||||
{ mimetype: 'image/avif', extension: 'avif' },
|
||||
{ mimetype: 'image/x-adobe-dng', extension: 'dng' },
|
||||
{ mimetype: 'image/x-fuji-raf', extension: 'raf' },
|
||||
{ mimetype: 'image/x-nikon-nef', extension: 'nef' },
|
||||
{ mimetype: 'image/x-samsung-srw', extension: 'srw' },
|
||||
{ mimetype: 'image/x-sony-arw', extension: 'arw' },
|
||||
{ mimetype: 'image/x-canon-crw', extension: 'crw' },
|
||||
{ mimetype: 'image/x-arriflex-ari', extension: 'ari' },
|
||||
{ mimetype: 'image/x-canon-cr2', extension: 'cr2' },
|
||||
{ mimetype: 'image/x-canon-cr3', extension: 'cr3' },
|
||||
{ mimetype: 'image/x-canon-crw', extension: 'crw' },
|
||||
{ mimetype: 'image/x-epson-erf', extension: 'erf' },
|
||||
{ mimetype: 'image/x-fuji-raf', extension: 'raf' },
|
||||
{ mimetype: 'image/x-hasselblad-3fr', extension: '3fr' },
|
||||
{ mimetype: 'image/x-hasselblad-fff', extension: 'fff' },
|
||||
{ mimetype: 'image/x-kodak-dcr', extension: 'dcr' },
|
||||
{ mimetype: 'image/x-kodak-k25', extension: 'k25' },
|
||||
{ mimetype: 'image/x-kodak-kdc', extension: 'kdc' },
|
||||
{ mimetype: 'image/x-leica-rwl', extension: 'rwl' },
|
||||
{ mimetype: 'image/x-minolta-mrw', extension: 'mrw' },
|
||||
{ mimetype: 'image/x-nikon-nef', extension: 'nef' },
|
||||
{ mimetype: 'image/x-olympus-orf', extension: 'orf' },
|
||||
{ mimetype: 'image/x-olympus-ori', extension: 'ori' },
|
||||
{ mimetype: 'image/x-panasonic-raw', extension: 'raw' },
|
||||
{ mimetype: 'image/x-pentax-pef', extension: 'pef' },
|
||||
{ mimetype: 'image/x-sigma-x3f', extension: 'x3f' },
|
||||
{ mimetype: 'image/x-sony-srf', extension: 'srf' },
|
||||
{ mimetype: 'image/x-sony-sr2', extension: 'sr2' },
|
||||
{ mimetype: 'image/x-hasselblad-3fr', extension: '3fr' },
|
||||
{ mimetype: 'image/x-hasselblad-fff', extension: 'fff' },
|
||||
{ mimetype: 'image/x-leica-rwl', extension: 'rwl' },
|
||||
{ mimetype: 'image/x-olympus-ori', extension: 'ori' },
|
||||
{ mimetype: 'image/x-phaseone-iiq', extension: 'iiq' },
|
||||
{ mimetype: 'image/x-arriflex-ari', extension: 'ari' },
|
||||
{ mimetype: 'image/x-phaseone-cap', extension: 'cap' },
|
||||
{ mimetype: 'image/x-phantom-cin', extension: 'cin' },
|
||||
{ mimetype: 'video/avi', extension: 'avi' },
|
||||
{ mimetype: 'video/mov', extension: 'mov' },
|
||||
{ mimetype: 'image/x-phaseone-cap', extension: 'cap' },
|
||||
{ mimetype: 'image/x-phaseone-iiq', extension: 'iiq' },
|
||||
{ mimetype: 'image/x-samsung-srw', extension: 'srw' },
|
||||
{ mimetype: 'image/x-sigma-x3f', extension: 'x3f' },
|
||||
{ mimetype: 'image/x-sony-arw', extension: 'arw' },
|
||||
{ mimetype: 'image/x-sony-sr2', extension: 'sr2' },
|
||||
{ mimetype: 'image/x-sony-srf', extension: 'srf' },
|
||||
{ mimetype: 'video/3gpp', extension: '3gp' },
|
||||
{ mimetype: 'video/mp2t', extension: 'm2ts' },
|
||||
{ mimetype: 'video/mp2t', extension: 'mts' },
|
||||
{ mimetype: 'video/mp4', extension: 'mp4' },
|
||||
{ mimetype: 'video/mpeg', extension: 'mpg' },
|
||||
{ mimetype: 'video/quicktime', extension: 'mov' },
|
||||
{ mimetype: 'video/webm', extension: 'webm' },
|
||||
{ mimetype: 'video/x-flv', extension: 'flv' },
|
||||
{ mimetype: 'video/x-matroska', extension: 'mkv' },
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { isSidecarFileType, isSupportedFileType } from '@app/domain';
|
||||
import { StorageCore, StorageFolder } from '@app/domain/storage';
|
||||
import { BadRequestException, Logger, UnauthorizedException } from '@nestjs/common';
|
||||
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
|
||||
|
@ -53,21 +54,19 @@ function fileFilter(req: AuthRequest, file: any, cb: any) {
|
|||
if (!req.user || (req.user.isPublicUser && !req.user.isAllowUpload)) {
|
||||
return cb(new UnauthorizedException());
|
||||
}
|
||||
if (
|
||||
file.mimetype.match(
|
||||
/\/(jpg|jpeg|png|gif|avi|mov|mp4|webm|x-msvideo|quicktime|heic|heif|avif|dng|x-adobe-dng|webp|tiff|3gpp|nef|x-nikon-nef|x-fuji-raf|x-samsung-srw|mpeg|x-flv|x-ms-wmv|x-matroska|x-sony-arw|arw|x-canon-crw|x-canon-cr2|x-canon-cr3|x-epson-erf|x-kodak-dcr|x-kodak-kdc|x-kodak-k25|x-minolta-mrw|x-olympus-orf|x-panasonic-raw|x-pentax-pef|x-sigma-x3f|x-sony-srf|x-sony-sr2|x-hasselblad-3fr|x-hasselblad-fff|x-leica-rwl|x-olympus-ori|x-phaseone-iiq|x-arriflex-ari|x-phaseone-cap|x-phantom-cin)$/,
|
||||
)
|
||||
) {
|
||||
cb(null, true);
|
||||
} else {
|
||||
// Additionally support XML but only for sidecar files
|
||||
if (file.fieldname == 'sidecarData' && file.mimetype.match(/\/xml$/)) {
|
||||
return cb(null, true);
|
||||
}
|
||||
|
||||
logger.error(`Unsupported file type ${extname(file.originalname)} file MIME type ${file.mimetype}`);
|
||||
cb(new BadRequestException(`Unsupported file type ${extname(file.originalname)}`), false);
|
||||
if (isSupportedFileType(file.mimetype)) {
|
||||
cb(null, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Additionally support XML but only for sidecar files.
|
||||
if (file.fieldname === 'sidecarData' && isSidecarFileType(file.mimetype)) {
|
||||
return cb(null, true);
|
||||
}
|
||||
|
||||
logger.error(`Unsupported file type ${extname(file.originalname)} file MIME type ${file.mimetype}`);
|
||||
cb(new BadRequestException(`Unsupported file type ${extname(file.originalname)}`), false);
|
||||
}
|
||||
|
||||
function destination(req: AuthRequest, file: Express.Multer.File, cb: any) {
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
import { AuthUserDto, EditSharedLinkDto, SharedLinkResponseDto, SharedLinkService } from '@app/domain';
|
||||
import { Body, Controller, Delete, Get, Param, Patch } from '@nestjs/common';
|
||||
import {
|
||||
AssetIdsDto,
|
||||
AssetIdsResponseDto,
|
||||
AuthUserDto,
|
||||
SharedLinkCreateDto,
|
||||
SharedLinkEditDto,
|
||||
SharedLinkResponseDto,
|
||||
SharedLinkService,
|
||||
} from '@app/domain';
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AuthUser } from '../decorators/auth-user.decorator';
|
||||
import { Authenticated, SharedLinkRoute } from '../decorators/authenticated.decorator';
|
||||
import { UseValidation } from '../decorators/use-validation.decorator';
|
||||
import { UUIDParamDto } from './dto/uuid-param.dto';
|
||||
|
||||
@ApiTags('share')
|
||||
@Controller('share')
|
||||
@ApiTags('Shared Link')
|
||||
@Controller('shared-link')
|
||||
@Authenticated()
|
||||
@UseValidation()
|
||||
export class SharedLinkController {
|
||||
|
@ -29,11 +37,16 @@ export class SharedLinkController {
|
|||
return this.service.get(authUser, id);
|
||||
}
|
||||
|
||||
@Post()
|
||||
createSharedLink(@AuthUser() authUser: AuthUserDto, @Body() dto: SharedLinkCreateDto) {
|
||||
return this.service.create(authUser, dto);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
updateSharedLink(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body() dto: EditSharedLinkDto,
|
||||
@Body() dto: SharedLinkEditDto,
|
||||
): Promise<SharedLinkResponseDto> {
|
||||
return this.service.update(authUser, id, dto);
|
||||
}
|
||||
|
@ -42,4 +55,24 @@ export class SharedLinkController {
|
|||
removeSharedLink(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||
return this.service.remove(authUser, id);
|
||||
}
|
||||
|
||||
@SharedLinkRoute()
|
||||
@Put(':id/assets')
|
||||
addSharedLinkAssets(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body() dto: AssetIdsDto,
|
||||
): Promise<AssetIdsResponseDto[]> {
|
||||
return this.service.addAssets(authUser, id, dto);
|
||||
}
|
||||
|
||||
@SharedLinkRoute()
|
||||
@Delete(':id/assets')
|
||||
removeSharedLinkAssets(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body() dto: AssetIdsDto,
|
||||
): Promise<AssetIdsResponseDto[]> {
|
||||
return this.service.removeAssets(authUser, id, dto);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ export class AssetEntity {
|
|||
@Column()
|
||||
type!: AssetType;
|
||||
|
||||
@Column()
|
||||
@Column({ unique: true })
|
||||
originalPath!: string;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
|
@ -75,6 +75,9 @@ export class AssetEntity {
|
|||
@Column({ type: 'boolean', default: false })
|
||||
isArchived!: boolean;
|
||||
|
||||
@Column({ type: 'boolean', default: false })
|
||||
isReadOnly!: boolean;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
mimeType!: string | null;
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ export class SharedLinkEntity {
|
|||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
description?: string;
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
description!: string | null;
|
||||
|
||||
@Column()
|
||||
userId!: string;
|
||||
|
@ -55,6 +55,9 @@ export class SharedLinkEntity {
|
|||
@Index('IDX_sharedlink_albumId')
|
||||
@ManyToOne(() => AlbumEntity, (album) => album.sharedLinks, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
||||
album?: AlbumEntity;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
albumId!: string | null;
|
||||
}
|
||||
|
||||
export enum SharedLinkType {
|
||||
|
|
|
@ -30,6 +30,9 @@ export class UserEntity {
|
|||
@Column({ type: 'varchar', unique: true, default: null })
|
||||
storageLabel!: string | null;
|
||||
|
||||
@Column({ type: 'varchar', default: null })
|
||||
externalPath!: string | null;
|
||||
|
||||
@Column({ default: '', select: false })
|
||||
password?: string;
|
||||
|
||||
|
|
18
server/src/infra/migrations/1686584273471-ImportAsset.ts
Normal file
18
server/src/infra/migrations/1686584273471-ImportAsset.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class ImportAsset1686584273471 implements MigrationInterface {
|
||||
name = 'ImportAsset1686584273471'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "assets" ADD "isReadOnly" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba" UNIQUE ("originalPath")`);
|
||||
await queryRunner.query(`ALTER TABLE "users" ADD "externalPath" character varying`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba"`);
|
||||
await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isReadOnly"`);
|
||||
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "externalPath"`);
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -248,6 +248,13 @@ export class AssetRepository implements IAssetRepository {
|
|||
});
|
||||
}
|
||||
|
||||
getLastUpdatedAssetForAlbumId(albumId: string): Promise<AssetEntity | null> {
|
||||
return this.repository.findOne({
|
||||
where: { albums: { id: albumId } },
|
||||
order: { updatedAt: 'DESC' },
|
||||
});
|
||||
}
|
||||
|
||||
async getMapMarkers(ownerId: string, options: MapMarkerSearchOptions = {}): Promise<MapMarker[]> {
|
||||
const { isFavorite, fileCreatedAfter, fileCreatedBefore } = options;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { ICryptoRepository } from '@app/domain';
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { compareSync, hash } from 'bcrypt';
|
||||
import { createHash, randomBytes } from 'crypto';
|
||||
import { createReadStream } from 'fs';
|
||||
|
||||
@Injectable()
|
||||
export class CryptoRepository implements ICryptoRepository {
|
||||
|
@ -13,4 +14,14 @@ export class CryptoRepository implements ICryptoRepository {
|
|||
hashSha256(value: string) {
|
||||
return createHash('sha256').update(value).digest('base64');
|
||||
}
|
||||
|
||||
hashFile(filepath: string): Promise<Buffer> {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
const hash = createHash('sha1');
|
||||
const stream = createReadStream(filepath);
|
||||
stream.on('error', (err) => reject(err));
|
||||
stream.on('data', (chunk) => hash.update(chunk));
|
||||
stream.on('end', () => resolve(hash.digest()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue