mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:27:09 -05:00 
			
		
		
		
	fix(server,web): correctly remove metadata from shared links (#4464)
* wip: strip metadata * fix: authenticate time buckets * hide detail panel * fix tests * fix lint * add e2e tests * chore: open api * fix web compilation error * feat: test with asset with gps position * fix: only import fs.promises.cp * fix: cleanup mapasset * fix: format --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
		
							parent
							
								
									4a9f58bf9b
								
							
						
					
					
						commit
						dadcf49eca
					
				
							
								
								
									
										14
									
								
								cli/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								cli/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							@ -640,6 +640,12 @@ export interface AssetResponseDto {
 | 
				
			|||||||
     * @memberof AssetResponseDto
 | 
					     * @memberof AssetResponseDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    'fileModifiedAt': string;
 | 
					    'fileModifiedAt': string;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @type {boolean}
 | 
				
			||||||
 | 
					     * @memberof AssetResponseDto
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    'hasMetadata': boolean;
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 
 | 
					     * 
 | 
				
			||||||
     * @type {string}
 | 
					     * @type {string}
 | 
				
			||||||
@ -749,7 +755,7 @@ export interface AssetResponseDto {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    'tags'?: Array<TagResponseDto>;
 | 
					    'tags'?: Array<TagResponseDto>;
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * base64 encoded thumbhash
 | 
					     * 
 | 
				
			||||||
     * @type {string}
 | 
					     * @type {string}
 | 
				
			||||||
     * @memberof AssetResponseDto
 | 
					     * @memberof AssetResponseDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@ -2882,7 +2888,7 @@ export interface SharedLinkCreateDto {
 | 
				
			|||||||
     * @type {boolean}
 | 
					     * @type {boolean}
 | 
				
			||||||
     * @memberof SharedLinkCreateDto
 | 
					     * @memberof SharedLinkCreateDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    'showExif'?: boolean;
 | 
					    'showMetadata'?: boolean;
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 
 | 
					     * 
 | 
				
			||||||
     * @type {SharedLinkType}
 | 
					     * @type {SharedLinkType}
 | 
				
			||||||
@ -2927,7 +2933,7 @@ export interface SharedLinkEditDto {
 | 
				
			|||||||
     * @type {boolean}
 | 
					     * @type {boolean}
 | 
				
			||||||
     * @memberof SharedLinkEditDto
 | 
					     * @memberof SharedLinkEditDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    'showExif'?: boolean;
 | 
					    'showMetadata'?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 
 | 
					 * 
 | 
				
			||||||
@ -2994,7 +3000,7 @@ export interface SharedLinkResponseDto {
 | 
				
			|||||||
     * @type {boolean}
 | 
					     * @type {boolean}
 | 
				
			||||||
     * @memberof SharedLinkResponseDto
 | 
					     * @memberof SharedLinkResponseDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    'showExif': boolean;
 | 
					    'showMetadata': boolean;
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 
 | 
					     * 
 | 
				
			||||||
     * @type {SharedLinkType}
 | 
					     * @type {SharedLinkType}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								mobile/openapi/doc/AssetResponseDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								mobile/openapi/doc/AssetResponseDto.md
									
									
									
										generated
									
									
									
								
							@ -15,6 +15,7 @@ Name | Type | Description | Notes
 | 
				
			|||||||
**exifInfo** | [**ExifResponseDto**](ExifResponseDto.md) |  | [optional] 
 | 
					**exifInfo** | [**ExifResponseDto**](ExifResponseDto.md) |  | [optional] 
 | 
				
			||||||
**fileCreatedAt** | [**DateTime**](DateTime.md) |  | 
 | 
					**fileCreatedAt** | [**DateTime**](DateTime.md) |  | 
 | 
				
			||||||
**fileModifiedAt** | [**DateTime**](DateTime.md) |  | 
 | 
					**fileModifiedAt** | [**DateTime**](DateTime.md) |  | 
 | 
				
			||||||
 | 
					**hasMetadata** | **bool** |  | 
 | 
				
			||||||
**id** | **String** |  | 
 | 
					**id** | **String** |  | 
 | 
				
			||||||
**isArchived** | **bool** |  | 
 | 
					**isArchived** | **bool** |  | 
 | 
				
			||||||
**isExternal** | **bool** |  | 
 | 
					**isExternal** | **bool** |  | 
 | 
				
			||||||
@ -33,7 +34,7 @@ Name | Type | Description | Notes
 | 
				
			|||||||
**resized** | **bool** |  | 
 | 
					**resized** | **bool** |  | 
 | 
				
			||||||
**smartInfo** | [**SmartInfoResponseDto**](SmartInfoResponseDto.md) |  | [optional] 
 | 
					**smartInfo** | [**SmartInfoResponseDto**](SmartInfoResponseDto.md) |  | [optional] 
 | 
				
			||||||
**tags** | [**List<TagResponseDto>**](TagResponseDto.md) |  | [optional] [default to const []]
 | 
					**tags** | [**List<TagResponseDto>**](TagResponseDto.md) |  | [optional] [default to const []]
 | 
				
			||||||
**thumbhash** | **String** | base64 encoded thumbhash | 
 | 
					**thumbhash** | **String** |  | 
 | 
				
			||||||
**type** | [**AssetTypeEnum**](AssetTypeEnum.md) |  | 
 | 
					**type** | [**AssetTypeEnum**](AssetTypeEnum.md) |  | 
 | 
				
			||||||
**updatedAt** | [**DateTime**](DateTime.md) |  | 
 | 
					**updatedAt** | [**DateTime**](DateTime.md) |  | 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								mobile/openapi/doc/SharedLinkCreateDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/doc/SharedLinkCreateDto.md
									
									
									
										generated
									
									
									
								
							@ -14,7 +14,7 @@ Name | Type | Description | Notes
 | 
				
			|||||||
**assetIds** | **List<String>** |  | [optional] [default to const []]
 | 
					**assetIds** | **List<String>** |  | [optional] [default to const []]
 | 
				
			||||||
**description** | **String** |  | [optional] 
 | 
					**description** | **String** |  | [optional] 
 | 
				
			||||||
**expiresAt** | [**DateTime**](DateTime.md) |  | [optional] 
 | 
					**expiresAt** | [**DateTime**](DateTime.md) |  | [optional] 
 | 
				
			||||||
**showExif** | **bool** |  | [optional] [default to true]
 | 
					**showMetadata** | **bool** |  | [optional] [default to true]
 | 
				
			||||||
**type** | [**SharedLinkType**](SharedLinkType.md) |  | 
 | 
					**type** | [**SharedLinkType**](SharedLinkType.md) |  | 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
 | 
					[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								mobile/openapi/doc/SharedLinkEditDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/doc/SharedLinkEditDto.md
									
									
									
										generated
									
									
									
								
							@ -12,7 +12,7 @@ Name | Type | Description | Notes
 | 
				
			|||||||
**allowUpload** | **bool** |  | [optional] 
 | 
					**allowUpload** | **bool** |  | [optional] 
 | 
				
			||||||
**description** | **String** |  | [optional] 
 | 
					**description** | **String** |  | [optional] 
 | 
				
			||||||
**expiresAt** | [**DateTime**](DateTime.md) |  | [optional] 
 | 
					**expiresAt** | [**DateTime**](DateTime.md) |  | [optional] 
 | 
				
			||||||
**showExif** | **bool** |  | [optional] 
 | 
					**showMetadata** | **bool** |  | [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)
 | 
					[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								mobile/openapi/doc/SharedLinkResponseDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/doc/SharedLinkResponseDto.md
									
									
									
										generated
									
									
									
								
							@ -17,7 +17,7 @@ Name | Type | Description | Notes
 | 
				
			|||||||
**expiresAt** | [**DateTime**](DateTime.md) |  | 
 | 
					**expiresAt** | [**DateTime**](DateTime.md) |  | 
 | 
				
			||||||
**id** | **String** |  | 
 | 
					**id** | **String** |  | 
 | 
				
			||||||
**key** | **String** |  | 
 | 
					**key** | **String** |  | 
 | 
				
			||||||
**showExif** | **bool** |  | 
 | 
					**showMetadata** | **bool** |  | 
 | 
				
			||||||
**type** | [**SharedLinkType**](SharedLinkType.md) |  | 
 | 
					**type** | [**SharedLinkType**](SharedLinkType.md) |  | 
 | 
				
			||||||
**userId** | **String** |  | 
 | 
					**userId** | **String** |  | 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										11
									
								
								mobile/openapi/lib/model/asset_response_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								mobile/openapi/lib/model/asset_response_dto.dart
									
									
									
										generated
									
									
									
								
							@ -20,6 +20,7 @@ class AssetResponseDto {
 | 
				
			|||||||
    this.exifInfo,
 | 
					    this.exifInfo,
 | 
				
			||||||
    required this.fileCreatedAt,
 | 
					    required this.fileCreatedAt,
 | 
				
			||||||
    required this.fileModifiedAt,
 | 
					    required this.fileModifiedAt,
 | 
				
			||||||
 | 
					    required this.hasMetadata,
 | 
				
			||||||
    required this.id,
 | 
					    required this.id,
 | 
				
			||||||
    required this.isArchived,
 | 
					    required this.isArchived,
 | 
				
			||||||
    required this.isExternal,
 | 
					    required this.isExternal,
 | 
				
			||||||
@ -64,6 +65,8 @@ class AssetResponseDto {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  DateTime fileModifiedAt;
 | 
					  DateTime fileModifiedAt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool hasMetadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  String id;
 | 
					  String id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool isArchived;
 | 
					  bool isArchived;
 | 
				
			||||||
@ -112,7 +115,6 @@ class AssetResponseDto {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  List<TagResponseDto> tags;
 | 
					  List<TagResponseDto> tags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// base64 encoded thumbhash
 | 
					 | 
				
			||||||
  String? thumbhash;
 | 
					  String? thumbhash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  AssetTypeEnum type;
 | 
					  AssetTypeEnum type;
 | 
				
			||||||
@ -128,6 +130,7 @@ class AssetResponseDto {
 | 
				
			|||||||
     other.exifInfo == exifInfo &&
 | 
					     other.exifInfo == exifInfo &&
 | 
				
			||||||
     other.fileCreatedAt == fileCreatedAt &&
 | 
					     other.fileCreatedAt == fileCreatedAt &&
 | 
				
			||||||
     other.fileModifiedAt == fileModifiedAt &&
 | 
					     other.fileModifiedAt == fileModifiedAt &&
 | 
				
			||||||
 | 
					     other.hasMetadata == hasMetadata &&
 | 
				
			||||||
     other.id == id &&
 | 
					     other.id == id &&
 | 
				
			||||||
     other.isArchived == isArchived &&
 | 
					     other.isArchived == isArchived &&
 | 
				
			||||||
     other.isExternal == isExternal &&
 | 
					     other.isExternal == isExternal &&
 | 
				
			||||||
@ -160,6 +163,7 @@ class AssetResponseDto {
 | 
				
			|||||||
    (exifInfo == null ? 0 : exifInfo!.hashCode) +
 | 
					    (exifInfo == null ? 0 : exifInfo!.hashCode) +
 | 
				
			||||||
    (fileCreatedAt.hashCode) +
 | 
					    (fileCreatedAt.hashCode) +
 | 
				
			||||||
    (fileModifiedAt.hashCode) +
 | 
					    (fileModifiedAt.hashCode) +
 | 
				
			||||||
 | 
					    (hasMetadata.hashCode) +
 | 
				
			||||||
    (id.hashCode) +
 | 
					    (id.hashCode) +
 | 
				
			||||||
    (isArchived.hashCode) +
 | 
					    (isArchived.hashCode) +
 | 
				
			||||||
    (isExternal.hashCode) +
 | 
					    (isExternal.hashCode) +
 | 
				
			||||||
@ -183,7 +187,7 @@ class AssetResponseDto {
 | 
				
			|||||||
    (updatedAt.hashCode);
 | 
					    (updatedAt.hashCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isArchived=$isArchived, isExternal=$isExternal, isFavorite=$isFavorite, isOffline=$isOffline, isReadOnly=$isReadOnly, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]';
 | 
					  String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isExternal=$isExternal, isFavorite=$isFavorite, isOffline=$isOffline, isReadOnly=$isReadOnly, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
    final json = <String, dynamic>{};
 | 
					    final json = <String, dynamic>{};
 | 
				
			||||||
@ -198,6 +202,7 @@ class AssetResponseDto {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
      json[r'fileCreatedAt'] = this.fileCreatedAt.toUtc().toIso8601String();
 | 
					      json[r'fileCreatedAt'] = this.fileCreatedAt.toUtc().toIso8601String();
 | 
				
			||||||
      json[r'fileModifiedAt'] = this.fileModifiedAt.toUtc().toIso8601String();
 | 
					      json[r'fileModifiedAt'] = this.fileModifiedAt.toUtc().toIso8601String();
 | 
				
			||||||
 | 
					      json[r'hasMetadata'] = this.hasMetadata;
 | 
				
			||||||
      json[r'id'] = this.id;
 | 
					      json[r'id'] = this.id;
 | 
				
			||||||
      json[r'isArchived'] = this.isArchived;
 | 
					      json[r'isArchived'] = this.isArchived;
 | 
				
			||||||
      json[r'isExternal'] = this.isExternal;
 | 
					      json[r'isExternal'] = this.isExternal;
 | 
				
			||||||
@ -253,6 +258,7 @@ class AssetResponseDto {
 | 
				
			|||||||
        exifInfo: ExifResponseDto.fromJson(json[r'exifInfo']),
 | 
					        exifInfo: ExifResponseDto.fromJson(json[r'exifInfo']),
 | 
				
			||||||
        fileCreatedAt: mapDateTime(json, r'fileCreatedAt', '')!,
 | 
					        fileCreatedAt: mapDateTime(json, r'fileCreatedAt', '')!,
 | 
				
			||||||
        fileModifiedAt: mapDateTime(json, r'fileModifiedAt', '')!,
 | 
					        fileModifiedAt: mapDateTime(json, r'fileModifiedAt', '')!,
 | 
				
			||||||
 | 
					        hasMetadata: mapValueOfType<bool>(json, r'hasMetadata')!,
 | 
				
			||||||
        id: mapValueOfType<String>(json, r'id')!,
 | 
					        id: mapValueOfType<String>(json, r'id')!,
 | 
				
			||||||
        isArchived: mapValueOfType<bool>(json, r'isArchived')!,
 | 
					        isArchived: mapValueOfType<bool>(json, r'isArchived')!,
 | 
				
			||||||
        isExternal: mapValueOfType<bool>(json, r'isExternal')!,
 | 
					        isExternal: mapValueOfType<bool>(json, r'isExternal')!,
 | 
				
			||||||
@ -327,6 +333,7 @@ class AssetResponseDto {
 | 
				
			|||||||
    'duration',
 | 
					    'duration',
 | 
				
			||||||
    'fileCreatedAt',
 | 
					    'fileCreatedAt',
 | 
				
			||||||
    'fileModifiedAt',
 | 
					    'fileModifiedAt',
 | 
				
			||||||
 | 
					    'hasMetadata',
 | 
				
			||||||
    'id',
 | 
					    'id',
 | 
				
			||||||
    'isArchived',
 | 
					    'isArchived',
 | 
				
			||||||
    'isExternal',
 | 
					    'isExternal',
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										14
									
								
								mobile/openapi/lib/model/shared_link_create_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								mobile/openapi/lib/model/shared_link_create_dto.dart
									
									
									
										generated
									
									
									
								
							@ -19,7 +19,7 @@ class SharedLinkCreateDto {
 | 
				
			|||||||
    this.assetIds = const [],
 | 
					    this.assetIds = const [],
 | 
				
			||||||
    this.description,
 | 
					    this.description,
 | 
				
			||||||
    this.expiresAt,
 | 
					    this.expiresAt,
 | 
				
			||||||
    this.showExif = true,
 | 
					    this.showMetadata = true,
 | 
				
			||||||
    required this.type,
 | 
					    required this.type,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -47,7 +47,7 @@ class SharedLinkCreateDto {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  DateTime? expiresAt;
 | 
					  DateTime? expiresAt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool showExif;
 | 
					  bool showMetadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  SharedLinkType type;
 | 
					  SharedLinkType type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -59,7 +59,7 @@ class SharedLinkCreateDto {
 | 
				
			|||||||
     other.assetIds == assetIds &&
 | 
					     other.assetIds == assetIds &&
 | 
				
			||||||
     other.description == description &&
 | 
					     other.description == description &&
 | 
				
			||||||
     other.expiresAt == expiresAt &&
 | 
					     other.expiresAt == expiresAt &&
 | 
				
			||||||
     other.showExif == showExif &&
 | 
					     other.showMetadata == showMetadata &&
 | 
				
			||||||
     other.type == type;
 | 
					     other.type == type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@ -71,11 +71,11 @@ class SharedLinkCreateDto {
 | 
				
			|||||||
    (assetIds.hashCode) +
 | 
					    (assetIds.hashCode) +
 | 
				
			||||||
    (description == null ? 0 : description!.hashCode) +
 | 
					    (description == null ? 0 : description!.hashCode) +
 | 
				
			||||||
    (expiresAt == null ? 0 : expiresAt!.hashCode) +
 | 
					    (expiresAt == null ? 0 : expiresAt!.hashCode) +
 | 
				
			||||||
    (showExif.hashCode) +
 | 
					    (showMetadata.hashCode) +
 | 
				
			||||||
    (type.hashCode);
 | 
					    (type.hashCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, showExif=$showExif, type=$type]';
 | 
					  String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, showMetadata=$showMetadata, type=$type]';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
    final json = <String, dynamic>{};
 | 
					    final json = <String, dynamic>{};
 | 
				
			||||||
@ -97,7 +97,7 @@ class SharedLinkCreateDto {
 | 
				
			|||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
    //  json[r'expiresAt'] = null;
 | 
					    //  json[r'expiresAt'] = null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
      json[r'showExif'] = this.showExif;
 | 
					      json[r'showMetadata'] = this.showMetadata;
 | 
				
			||||||
      json[r'type'] = this.type;
 | 
					      json[r'type'] = this.type;
 | 
				
			||||||
    return json;
 | 
					    return json;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -118,7 +118,7 @@ class SharedLinkCreateDto {
 | 
				
			|||||||
            : const [],
 | 
					            : const [],
 | 
				
			||||||
        description: mapValueOfType<String>(json, r'description'),
 | 
					        description: mapValueOfType<String>(json, r'description'),
 | 
				
			||||||
        expiresAt: mapDateTime(json, r'expiresAt', ''),
 | 
					        expiresAt: mapDateTime(json, r'expiresAt', ''),
 | 
				
			||||||
        showExif: mapValueOfType<bool>(json, r'showExif') ?? true,
 | 
					        showMetadata: mapValueOfType<bool>(json, r'showMetadata') ?? true,
 | 
				
			||||||
        type: SharedLinkType.fromJson(json[r'type'])!,
 | 
					        type: SharedLinkType.fromJson(json[r'type'])!,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										18
									
								
								mobile/openapi/lib/model/shared_link_edit_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								mobile/openapi/lib/model/shared_link_edit_dto.dart
									
									
									
										generated
									
									
									
								
							@ -17,7 +17,7 @@ class SharedLinkEditDto {
 | 
				
			|||||||
    this.allowUpload,
 | 
					    this.allowUpload,
 | 
				
			||||||
    this.description,
 | 
					    this.description,
 | 
				
			||||||
    this.expiresAt,
 | 
					    this.expiresAt,
 | 
				
			||||||
    this.showExif,
 | 
					    this.showMetadata,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
@ -52,7 +52,7 @@ class SharedLinkEditDto {
 | 
				
			|||||||
  /// source code must fall back to having a nullable type.
 | 
					  /// source code must fall back to having a nullable type.
 | 
				
			||||||
  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
					  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  bool? showExif;
 | 
					  bool? showMetadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  bool operator ==(Object other) => identical(this, other) || other is SharedLinkEditDto &&
 | 
					  bool operator ==(Object other) => identical(this, other) || other is SharedLinkEditDto &&
 | 
				
			||||||
@ -60,7 +60,7 @@ class SharedLinkEditDto {
 | 
				
			|||||||
     other.allowUpload == allowUpload &&
 | 
					     other.allowUpload == allowUpload &&
 | 
				
			||||||
     other.description == description &&
 | 
					     other.description == description &&
 | 
				
			||||||
     other.expiresAt == expiresAt &&
 | 
					     other.expiresAt == expiresAt &&
 | 
				
			||||||
     other.showExif == showExif;
 | 
					     other.showMetadata == showMetadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  int get hashCode =>
 | 
					  int get hashCode =>
 | 
				
			||||||
@ -69,10 +69,10 @@ class SharedLinkEditDto {
 | 
				
			|||||||
    (allowUpload == null ? 0 : allowUpload!.hashCode) +
 | 
					    (allowUpload == null ? 0 : allowUpload!.hashCode) +
 | 
				
			||||||
    (description == null ? 0 : description!.hashCode) +
 | 
					    (description == null ? 0 : description!.hashCode) +
 | 
				
			||||||
    (expiresAt == null ? 0 : expiresAt!.hashCode) +
 | 
					    (expiresAt == null ? 0 : expiresAt!.hashCode) +
 | 
				
			||||||
    (showExif == null ? 0 : showExif!.hashCode);
 | 
					    (showMetadata == null ? 0 : showMetadata!.hashCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, description=$description, expiresAt=$expiresAt, showExif=$showExif]';
 | 
					  String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, description=$description, expiresAt=$expiresAt, showMetadata=$showMetadata]';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
    final json = <String, dynamic>{};
 | 
					    final json = <String, dynamic>{};
 | 
				
			||||||
@ -96,10 +96,10 @@ class SharedLinkEditDto {
 | 
				
			|||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
    //  json[r'expiresAt'] = null;
 | 
					    //  json[r'expiresAt'] = null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this.showExif != null) {
 | 
					    if (this.showMetadata != null) {
 | 
				
			||||||
      json[r'showExif'] = this.showExif;
 | 
					      json[r'showMetadata'] = this.showMetadata;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
    //  json[r'showExif'] = null;
 | 
					    //  json[r'showMetadata'] = null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return json;
 | 
					    return json;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -116,7 +116,7 @@ class SharedLinkEditDto {
 | 
				
			|||||||
        allowUpload: mapValueOfType<bool>(json, r'allowUpload'),
 | 
					        allowUpload: mapValueOfType<bool>(json, r'allowUpload'),
 | 
				
			||||||
        description: mapValueOfType<String>(json, r'description'),
 | 
					        description: mapValueOfType<String>(json, r'description'),
 | 
				
			||||||
        expiresAt: mapDateTime(json, r'expiresAt', ''),
 | 
					        expiresAt: mapDateTime(json, r'expiresAt', ''),
 | 
				
			||||||
        showExif: mapValueOfType<bool>(json, r'showExif'),
 | 
					        showMetadata: mapValueOfType<bool>(json, r'showMetadata'),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
 | 
				
			|||||||
@ -22,7 +22,7 @@ class SharedLinkResponseDto {
 | 
				
			|||||||
    required this.expiresAt,
 | 
					    required this.expiresAt,
 | 
				
			||||||
    required this.id,
 | 
					    required this.id,
 | 
				
			||||||
    required this.key,
 | 
					    required this.key,
 | 
				
			||||||
    required this.showExif,
 | 
					    required this.showMetadata,
 | 
				
			||||||
    required this.type,
 | 
					    required this.type,
 | 
				
			||||||
    required this.userId,
 | 
					    required this.userId,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
@ -51,7 +51,7 @@ class SharedLinkResponseDto {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  String key;
 | 
					  String key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool showExif;
 | 
					  bool showMetadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  SharedLinkType type;
 | 
					  SharedLinkType type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -68,7 +68,7 @@ class SharedLinkResponseDto {
 | 
				
			|||||||
     other.expiresAt == expiresAt &&
 | 
					     other.expiresAt == expiresAt &&
 | 
				
			||||||
     other.id == id &&
 | 
					     other.id == id &&
 | 
				
			||||||
     other.key == key &&
 | 
					     other.key == key &&
 | 
				
			||||||
     other.showExif == showExif &&
 | 
					     other.showMetadata == showMetadata &&
 | 
				
			||||||
     other.type == type &&
 | 
					     other.type == type &&
 | 
				
			||||||
     other.userId == userId;
 | 
					     other.userId == userId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -84,12 +84,12 @@ class SharedLinkResponseDto {
 | 
				
			|||||||
    (expiresAt == null ? 0 : expiresAt!.hashCode) +
 | 
					    (expiresAt == null ? 0 : expiresAt!.hashCode) +
 | 
				
			||||||
    (id.hashCode) +
 | 
					    (id.hashCode) +
 | 
				
			||||||
    (key.hashCode) +
 | 
					    (key.hashCode) +
 | 
				
			||||||
    (showExif.hashCode) +
 | 
					    (showMetadata.hashCode) +
 | 
				
			||||||
    (type.hashCode) +
 | 
					    (type.hashCode) +
 | 
				
			||||||
    (userId.hashCode);
 | 
					    (userId.hashCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, showExif=$showExif, type=$type, userId=$userId]';
 | 
					  String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, showMetadata=$showMetadata, type=$type, userId=$userId]';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
    final json = <String, dynamic>{};
 | 
					    final json = <String, dynamic>{};
 | 
				
			||||||
@ -114,7 +114,7 @@ class SharedLinkResponseDto {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
      json[r'id'] = this.id;
 | 
					      json[r'id'] = this.id;
 | 
				
			||||||
      json[r'key'] = this.key;
 | 
					      json[r'key'] = this.key;
 | 
				
			||||||
      json[r'showExif'] = this.showExif;
 | 
					      json[r'showMetadata'] = this.showMetadata;
 | 
				
			||||||
      json[r'type'] = this.type;
 | 
					      json[r'type'] = this.type;
 | 
				
			||||||
      json[r'userId'] = this.userId;
 | 
					      json[r'userId'] = this.userId;
 | 
				
			||||||
    return json;
 | 
					    return json;
 | 
				
			||||||
@ -137,7 +137,7 @@ class SharedLinkResponseDto {
 | 
				
			|||||||
        expiresAt: mapDateTime(json, r'expiresAt', ''),
 | 
					        expiresAt: mapDateTime(json, r'expiresAt', ''),
 | 
				
			||||||
        id: mapValueOfType<String>(json, r'id')!,
 | 
					        id: mapValueOfType<String>(json, r'id')!,
 | 
				
			||||||
        key: mapValueOfType<String>(json, r'key')!,
 | 
					        key: mapValueOfType<String>(json, r'key')!,
 | 
				
			||||||
        showExif: mapValueOfType<bool>(json, r'showExif')!,
 | 
					        showMetadata: mapValueOfType<bool>(json, r'showMetadata')!,
 | 
				
			||||||
        type: SharedLinkType.fromJson(json[r'type'])!,
 | 
					        type: SharedLinkType.fromJson(json[r'type'])!,
 | 
				
			||||||
        userId: mapValueOfType<String>(json, r'userId')!,
 | 
					        userId: mapValueOfType<String>(json, r'userId')!,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
@ -195,7 +195,7 @@ class SharedLinkResponseDto {
 | 
				
			|||||||
    'expiresAt',
 | 
					    'expiresAt',
 | 
				
			||||||
    'id',
 | 
					    'id',
 | 
				
			||||||
    'key',
 | 
					    'key',
 | 
				
			||||||
    'showExif',
 | 
					    'showMetadata',
 | 
				
			||||||
    'type',
 | 
					    'type',
 | 
				
			||||||
    'userId',
 | 
					    'userId',
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								mobile/openapi/test/asset_response_dto_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								mobile/openapi/test/asset_response_dto_test.dart
									
									
									
										generated
									
									
									
								
							@ -52,6 +52,11 @@ void main() {
 | 
				
			|||||||
      // TODO
 | 
					      // TODO
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // bool hasMetadata
 | 
				
			||||||
 | 
					    test('to test the property `hasMetadata`', () async {
 | 
				
			||||||
 | 
					      // TODO
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // String id
 | 
					    // String id
 | 
				
			||||||
    test('to test the property `id`', () async {
 | 
					    test('to test the property `id`', () async {
 | 
				
			||||||
      // TODO
 | 
					      // TODO
 | 
				
			||||||
@ -142,7 +147,6 @@ void main() {
 | 
				
			|||||||
      // TODO
 | 
					      // TODO
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // base64 encoded thumbhash
 | 
					 | 
				
			||||||
    // String thumbhash
 | 
					    // String thumbhash
 | 
				
			||||||
    test('to test the property `thumbhash`', () async {
 | 
					    test('to test the property `thumbhash`', () async {
 | 
				
			||||||
      // TODO
 | 
					      // TODO
 | 
				
			||||||
 | 
				
			|||||||
@ -46,8 +46,8 @@ void main() {
 | 
				
			|||||||
      // TODO
 | 
					      // TODO
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // bool showExif (default value: true)
 | 
					    // bool showMetadata (default value: true)
 | 
				
			||||||
    test('to test the property `showExif`', () async {
 | 
					    test('to test the property `showMetadata`', () async {
 | 
				
			||||||
      // TODO
 | 
					      // TODO
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -36,8 +36,8 @@ void main() {
 | 
				
			|||||||
      // TODO
 | 
					      // TODO
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // bool showExif
 | 
					    // bool showMetadata
 | 
				
			||||||
    test('to test the property `showExif`', () async {
 | 
					    test('to test the property `showMetadata`', () async {
 | 
				
			||||||
      // TODO
 | 
					      // TODO
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -61,8 +61,8 @@ void main() {
 | 
				
			|||||||
      // TODO
 | 
					      // TODO
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // bool showExif
 | 
					    // bool showMetadata
 | 
				
			||||||
    test('to test the property `showExif`', () async {
 | 
					    test('to test the property `showMetadata`', () async {
 | 
				
			||||||
      // TODO
 | 
					      // TODO
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5770,6 +5770,9 @@
 | 
				
			|||||||
            "format": "date-time",
 | 
					            "format": "date-time",
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
 | 
					          "hasMetadata": {
 | 
				
			||||||
 | 
					            "type": "boolean"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
          "id": {
 | 
					          "id": {
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
@ -5833,7 +5836,6 @@
 | 
				
			|||||||
            "type": "array"
 | 
					            "type": "array"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "thumbhash": {
 | 
					          "thumbhash": {
 | 
				
			||||||
            "description": "base64 encoded thumbhash",
 | 
					 | 
				
			||||||
            "nullable": true,
 | 
					            "nullable": true,
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
@ -5847,7 +5849,6 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "required": [
 | 
					        "required": [
 | 
				
			||||||
          "type",
 | 
					          "type",
 | 
				
			||||||
          "id",
 | 
					 | 
				
			||||||
          "deviceAssetId",
 | 
					          "deviceAssetId",
 | 
				
			||||||
          "deviceId",
 | 
					          "deviceId",
 | 
				
			||||||
          "ownerId",
 | 
					          "ownerId",
 | 
				
			||||||
@ -5855,19 +5856,21 @@
 | 
				
			|||||||
          "originalPath",
 | 
					          "originalPath",
 | 
				
			||||||
          "originalFileName",
 | 
					          "originalFileName",
 | 
				
			||||||
          "resized",
 | 
					          "resized",
 | 
				
			||||||
          "thumbhash",
 | 
					 | 
				
			||||||
          "fileCreatedAt",
 | 
					          "fileCreatedAt",
 | 
				
			||||||
          "fileModifiedAt",
 | 
					          "fileModifiedAt",
 | 
				
			||||||
          "updatedAt",
 | 
					          "updatedAt",
 | 
				
			||||||
          "isFavorite",
 | 
					          "isFavorite",
 | 
				
			||||||
          "isArchived",
 | 
					          "isArchived",
 | 
				
			||||||
          "isTrashed",
 | 
					          "isTrashed",
 | 
				
			||||||
          "localDateTime",
 | 
					 | 
				
			||||||
          "isOffline",
 | 
					          "isOffline",
 | 
				
			||||||
          "isExternal",
 | 
					          "isExternal",
 | 
				
			||||||
          "isReadOnly",
 | 
					          "isReadOnly",
 | 
				
			||||||
 | 
					          "checksum",
 | 
				
			||||||
 | 
					          "id",
 | 
				
			||||||
 | 
					          "thumbhash",
 | 
				
			||||||
 | 
					          "localDateTime",
 | 
				
			||||||
          "duration",
 | 
					          "duration",
 | 
				
			||||||
          "checksum"
 | 
					          "hasMetadata"
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "type": "object"
 | 
					        "type": "object"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@ -7599,7 +7602,7 @@
 | 
				
			|||||||
            "nullable": true,
 | 
					            "nullable": true,
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "showExif": {
 | 
					          "showMetadata": {
 | 
				
			||||||
            "default": true,
 | 
					            "default": true,
 | 
				
			||||||
            "type": "boolean"
 | 
					            "type": "boolean"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
@ -7628,7 +7631,7 @@
 | 
				
			|||||||
            "nullable": true,
 | 
					            "nullable": true,
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "showExif": {
 | 
					          "showMetadata": {
 | 
				
			||||||
            "type": "boolean"
 | 
					            "type": "boolean"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -7670,7 +7673,7 @@
 | 
				
			|||||||
          "key": {
 | 
					          "key": {
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "showExif": {
 | 
					          "showMetadata": {
 | 
				
			||||||
            "type": "boolean"
 | 
					            "type": "boolean"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "type": {
 | 
					          "type": {
 | 
				
			||||||
@ -7691,7 +7694,7 @@
 | 
				
			|||||||
          "assets",
 | 
					          "assets",
 | 
				
			||||||
          "allowUpload",
 | 
					          "allowUpload",
 | 
				
			||||||
          "allowDownload",
 | 
					          "allowDownload",
 | 
				
			||||||
          "showExif"
 | 
					          "showMetadata"
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "type": "object"
 | 
					        "type": "object"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
 | 
				
			|||||||
@ -47,6 +47,7 @@ import {
 | 
				
			|||||||
  BulkIdsDto,
 | 
					  BulkIdsDto,
 | 
				
			||||||
  MapMarkerResponseDto,
 | 
					  MapMarkerResponseDto,
 | 
				
			||||||
  MemoryLaneResponseDto,
 | 
					  MemoryLaneResponseDto,
 | 
				
			||||||
 | 
					  SanitizedAssetResponseDto,
 | 
				
			||||||
  TimeBucketResponseDto,
 | 
					  TimeBucketResponseDto,
 | 
				
			||||||
  mapAsset,
 | 
					  mapAsset,
 | 
				
			||||||
} from './response-dto';
 | 
					} from './response-dto';
 | 
				
			||||||
@ -198,10 +199,17 @@ export class AssetService {
 | 
				
			|||||||
    return this.assetRepository.getTimeBuckets(dto);
 | 
					    return this.assetRepository.getTimeBuckets(dto);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async getByTimeBucket(authUser: AuthUserDto, dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> {
 | 
					  async getByTimeBucket(
 | 
				
			||||||
 | 
					    authUser: AuthUserDto,
 | 
				
			||||||
 | 
					    dto: TimeBucketAssetDto,
 | 
				
			||||||
 | 
					  ): Promise<AssetResponseDto[] | SanitizedAssetResponseDto[]> {
 | 
				
			||||||
    await this.timeBucketChecks(authUser, dto);
 | 
					    await this.timeBucketChecks(authUser, dto);
 | 
				
			||||||
    const assets = await this.assetRepository.getByTimeBucket(dto.timeBucket, dto);
 | 
					    const assets = await this.assetRepository.getByTimeBucket(dto.timeBucket, dto);
 | 
				
			||||||
    return assets.map(mapAsset);
 | 
					    if (authUser.isShowMetadata) {
 | 
				
			||||||
 | 
					      return assets.map((asset) => mapAsset(asset));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return assets.map((asset) => mapAsset(asset, true));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async downloadFile(authUser: AuthUserDto, id: string): Promise<ImmichReadStream> {
 | 
					  async downloadFile(authUser: AuthUserDto, id: string): Promise<ImmichReadStream> {
 | 
				
			||||||
 | 
				
			|||||||
@ -6,43 +6,62 @@ import { UserResponseDto, mapUser } from '../../user/response-dto/user-response.
 | 
				
			|||||||
import { ExifResponseDto, mapExif } from './exif-response.dto';
 | 
					import { ExifResponseDto, mapExif } from './exif-response.dto';
 | 
				
			||||||
import { SmartInfoResponseDto, mapSmartInfo } from './smart-info-response.dto';
 | 
					import { SmartInfoResponseDto, mapSmartInfo } from './smart-info-response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class AssetResponseDto {
 | 
					export class SanitizedAssetResponseDto {
 | 
				
			||||||
  id!: string;
 | 
					  id!: string;
 | 
				
			||||||
 | 
					  @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
 | 
				
			||||||
 | 
					  type!: AssetType;
 | 
				
			||||||
 | 
					  thumbhash!: string | null;
 | 
				
			||||||
 | 
					  resized!: boolean;
 | 
				
			||||||
 | 
					  localDateTime!: Date;
 | 
				
			||||||
 | 
					  duration!: string;
 | 
				
			||||||
 | 
					  livePhotoVideoId?: string | null;
 | 
				
			||||||
 | 
					  hasMetadata!: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AssetResponseDto extends SanitizedAssetResponseDto {
 | 
				
			||||||
  deviceAssetId!: string;
 | 
					  deviceAssetId!: string;
 | 
				
			||||||
  deviceId!: string;
 | 
					  deviceId!: string;
 | 
				
			||||||
  ownerId!: string;
 | 
					  ownerId!: string;
 | 
				
			||||||
  owner?: UserResponseDto;
 | 
					  owner?: UserResponseDto;
 | 
				
			||||||
  libraryId!: string;
 | 
					  libraryId!: string;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
 | 
					 | 
				
			||||||
  type!: AssetType;
 | 
					 | 
				
			||||||
  originalPath!: string;
 | 
					  originalPath!: string;
 | 
				
			||||||
  originalFileName!: string;
 | 
					  originalFileName!: string;
 | 
				
			||||||
  resized!: boolean;
 | 
					  resized!: boolean;
 | 
				
			||||||
  /**base64 encoded thumbhash */
 | 
					 | 
				
			||||||
  thumbhash!: string | null;
 | 
					 | 
				
			||||||
  fileCreatedAt!: Date;
 | 
					  fileCreatedAt!: Date;
 | 
				
			||||||
  fileModifiedAt!: Date;
 | 
					  fileModifiedAt!: Date;
 | 
				
			||||||
  updatedAt!: Date;
 | 
					  updatedAt!: Date;
 | 
				
			||||||
  isFavorite!: boolean;
 | 
					  isFavorite!: boolean;
 | 
				
			||||||
  isArchived!: boolean;
 | 
					  isArchived!: boolean;
 | 
				
			||||||
  isTrashed!: boolean;
 | 
					  isTrashed!: boolean;
 | 
				
			||||||
  localDateTime!: Date;
 | 
					 | 
				
			||||||
  isOffline!: boolean;
 | 
					  isOffline!: boolean;
 | 
				
			||||||
  isExternal!: boolean;
 | 
					  isExternal!: boolean;
 | 
				
			||||||
  isReadOnly!: boolean;
 | 
					  isReadOnly!: boolean;
 | 
				
			||||||
  duration!: string;
 | 
					 | 
				
			||||||
  exifInfo?: ExifResponseDto;
 | 
					  exifInfo?: ExifResponseDto;
 | 
				
			||||||
  smartInfo?: SmartInfoResponseDto;
 | 
					  smartInfo?: SmartInfoResponseDto;
 | 
				
			||||||
  livePhotoVideoId?: string | null;
 | 
					 | 
				
			||||||
  tags?: TagResponseDto[];
 | 
					  tags?: TagResponseDto[];
 | 
				
			||||||
  people?: PersonResponseDto[];
 | 
					  people?: PersonResponseDto[];
 | 
				
			||||||
  /**base64 encoded sha1 hash */
 | 
					  /**base64 encoded sha1 hash */
 | 
				
			||||||
  checksum!: string;
 | 
					  checksum!: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function _map(entity: AssetEntity, withExif: boolean): AssetResponseDto {
 | 
					export function mapAsset(entity: AssetEntity, stripMetadata = false): AssetResponseDto {
 | 
				
			||||||
 | 
					  const sanitizedAssetResponse: SanitizedAssetResponseDto = {
 | 
				
			||||||
 | 
					    id: entity.id,
 | 
				
			||||||
 | 
					    type: entity.type,
 | 
				
			||||||
 | 
					    thumbhash: entity.thumbhash?.toString('base64') ?? null,
 | 
				
			||||||
 | 
					    localDateTime: entity.localDateTime,
 | 
				
			||||||
 | 
					    resized: !!entity.resizePath,
 | 
				
			||||||
 | 
					    duration: entity.duration ?? '0:00:00.00000',
 | 
				
			||||||
 | 
					    livePhotoVideoId: entity.livePhotoVideoId,
 | 
				
			||||||
 | 
					    hasMetadata: false,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (stripMetadata) {
 | 
				
			||||||
 | 
					    return sanitizedAssetResponse as AssetResponseDto;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
 | 
					    ...sanitizedAssetResponse,
 | 
				
			||||||
    id: entity.id,
 | 
					    id: entity.id,
 | 
				
			||||||
    deviceAssetId: entity.deviceAssetId,
 | 
					    deviceAssetId: entity.deviceAssetId,
 | 
				
			||||||
    ownerId: entity.ownerId,
 | 
					    ownerId: entity.ownerId,
 | 
				
			||||||
@ -62,7 +81,7 @@ function _map(entity: AssetEntity, withExif: boolean): AssetResponseDto {
 | 
				
			|||||||
    isArchived: entity.isArchived,
 | 
					    isArchived: entity.isArchived,
 | 
				
			||||||
    isTrashed: !!entity.deletedAt,
 | 
					    isTrashed: !!entity.deletedAt,
 | 
				
			||||||
    duration: entity.duration ?? '0:00:00.00000',
 | 
					    duration: entity.duration ?? '0:00:00.00000',
 | 
				
			||||||
    exifInfo: withExif ? (entity.exifInfo ? mapExif(entity.exifInfo) : undefined) : undefined,
 | 
					    exifInfo: entity.exifInfo ? mapExif(entity.exifInfo) : undefined,
 | 
				
			||||||
    smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined,
 | 
					    smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined,
 | 
				
			||||||
    livePhotoVideoId: entity.livePhotoVideoId,
 | 
					    livePhotoVideoId: entity.livePhotoVideoId,
 | 
				
			||||||
    tags: entity.tags?.map(mapTag),
 | 
					    tags: entity.tags?.map(mapTag),
 | 
				
			||||||
@ -71,17 +90,10 @@ function _map(entity: AssetEntity, withExif: boolean): AssetResponseDto {
 | 
				
			|||||||
    isExternal: entity.isExternal,
 | 
					    isExternal: entity.isExternal,
 | 
				
			||||||
    isOffline: entity.isOffline,
 | 
					    isOffline: entity.isOffline,
 | 
				
			||||||
    isReadOnly: entity.isReadOnly,
 | 
					    isReadOnly: entity.isReadOnly,
 | 
				
			||||||
 | 
					    hasMetadata: true,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function mapAsset(entity: AssetEntity): AssetResponseDto {
 | 
					 | 
				
			||||||
  return _map(entity, true);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function mapAssetWithoutExif(entity: AssetEntity): AssetResponseDto {
 | 
					 | 
				
			||||||
  return _map(entity, false);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class MemoryLaneResponseDto {
 | 
					export class MemoryLaneResponseDto {
 | 
				
			||||||
  title!: string;
 | 
					  title!: string;
 | 
				
			||||||
  assets!: AssetResponseDto[];
 | 
					  assets!: AssetResponseDto[];
 | 
				
			||||||
 | 
				
			|||||||
@ -52,3 +52,15 @@ export function mapExif(entity: ExifEntity): ExifResponseDto {
 | 
				
			|||||||
    projectionType: entity.projectionType,
 | 
					    projectionType: entity.projectionType,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function mapSanitizedExif(entity: ExifEntity): ExifResponseDto {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    fileSizeInByte: entity.fileSizeInByte ? parseInt(entity.fileSizeInByte.toString()) : null,
 | 
				
			||||||
 | 
					    orientation: entity.orientation,
 | 
				
			||||||
 | 
					    dateTimeOriginal: entity.dateTimeOriginal,
 | 
				
			||||||
 | 
					    timeZone: entity.timeZone,
 | 
				
			||||||
 | 
					    projectionType: entity.projectionType,
 | 
				
			||||||
 | 
					    exifImageWidth: entity.exifImageWidth,
 | 
				
			||||||
 | 
					    exifImageHeight: entity.exifImageHeight,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -380,7 +380,7 @@ export class AuthService {
 | 
				
			|||||||
            sharedLinkId: link.id,
 | 
					            sharedLinkId: link.id,
 | 
				
			||||||
            isAllowUpload: link.allowUpload,
 | 
					            isAllowUpload: link.allowUpload,
 | 
				
			||||||
            isAllowDownload: link.allowDownload,
 | 
					            isAllowDownload: link.allowDownload,
 | 
				
			||||||
            isShowExif: link.showExif,
 | 
					            isShowMetadata: link.showExif,
 | 
				
			||||||
          };
 | 
					          };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -431,7 +431,7 @@ export class AuthService {
 | 
				
			|||||||
        isPublicUser: false,
 | 
					        isPublicUser: false,
 | 
				
			||||||
        isAllowUpload: true,
 | 
					        isAllowUpload: true,
 | 
				
			||||||
        isAllowDownload: true,
 | 
					        isAllowDownload: true,
 | 
				
			||||||
        isShowExif: true,
 | 
					        isShowMetadata: true,
 | 
				
			||||||
        accessTokenId: token.id,
 | 
					        accessTokenId: token.id,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ export class AuthUserDto {
 | 
				
			|||||||
  sharedLinkId?: string;
 | 
					  sharedLinkId?: string;
 | 
				
			||||||
  isAllowUpload?: boolean;
 | 
					  isAllowUpload?: boolean;
 | 
				
			||||||
  isAllowDownload?: boolean;
 | 
					  isAllowDownload?: boolean;
 | 
				
			||||||
  isShowExif?: boolean;
 | 
					  isShowMetadata?: boolean;
 | 
				
			||||||
  accessTokenId?: string;
 | 
					  accessTokenId?: string;
 | 
				
			||||||
  externalPath?: string | null;
 | 
					  externalPath?: string | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -97,7 +97,7 @@ export class PersonService {
 | 
				
			|||||||
  async getAssets(authUser: AuthUserDto, id: string): Promise<AssetResponseDto[]> {
 | 
					  async getAssets(authUser: AuthUserDto, id: string): Promise<AssetResponseDto[]> {
 | 
				
			||||||
    await this.access.requirePermission(authUser, Permission.PERSON_READ, id);
 | 
					    await this.access.requirePermission(authUser, Permission.PERSON_READ, id);
 | 
				
			||||||
    const assets = await this.repository.getAssets(id);
 | 
					    const assets = await this.repository.getAssets(id);
 | 
				
			||||||
    return assets.map(mapAsset);
 | 
					    return assets.map((asset) => mapAsset(asset));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async update(authUser: AuthUserDto, id: string, dto: PersonUpdateDto): Promise<PersonResponseDto> {
 | 
					  async update(authUser: AuthUserDto, id: string, dto: PersonUpdateDto): Promise<PersonResponseDto> {
 | 
				
			||||||
 | 
				
			|||||||
@ -154,7 +154,7 @@ export class SearchService {
 | 
				
			|||||||
        items: assets.items
 | 
					        items: assets.items
 | 
				
			||||||
          .map((item) => lookup[item.id])
 | 
					          .map((item) => lookup[item.id])
 | 
				
			||||||
          .filter((item) => !!item)
 | 
					          .filter((item) => !!item)
 | 
				
			||||||
          .map(mapAsset),
 | 
					          .map((asset) => mapAsset(asset)),
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@ import { SharedLinkEntity, SharedLinkType } from '@app/infra/entities';
 | 
				
			|||||||
import { ApiProperty } from '@nestjs/swagger';
 | 
					import { ApiProperty } from '@nestjs/swagger';
 | 
				
			||||||
import _ from 'lodash';
 | 
					import _ from 'lodash';
 | 
				
			||||||
import { AlbumResponseDto, mapAlbumWithoutAssets } from '../album';
 | 
					import { AlbumResponseDto, mapAlbumWithoutAssets } from '../album';
 | 
				
			||||||
import { AssetResponseDto, mapAsset, mapAssetWithoutExif } from '../asset';
 | 
					import { AssetResponseDto, mapAsset } from '../asset';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class SharedLinkResponseDto {
 | 
					export class SharedLinkResponseDto {
 | 
				
			||||||
  id!: string;
 | 
					  id!: string;
 | 
				
			||||||
@ -17,8 +17,9 @@ export class SharedLinkResponseDto {
 | 
				
			|||||||
  assets!: AssetResponseDto[];
 | 
					  assets!: AssetResponseDto[];
 | 
				
			||||||
  album?: AlbumResponseDto;
 | 
					  album?: AlbumResponseDto;
 | 
				
			||||||
  allowUpload!: boolean;
 | 
					  allowUpload!: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  allowDownload!: boolean;
 | 
					  allowDownload!: boolean;
 | 
				
			||||||
  showExif!: boolean;
 | 
					  showMetadata!: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function mapSharedLink(sharedLink: SharedLinkEntity): SharedLinkResponseDto {
 | 
					export function mapSharedLink(sharedLink: SharedLinkEntity): SharedLinkResponseDto {
 | 
				
			||||||
@ -35,15 +36,15 @@ export function mapSharedLink(sharedLink: SharedLinkEntity): SharedLinkResponseD
 | 
				
			|||||||
    type: sharedLink.type,
 | 
					    type: sharedLink.type,
 | 
				
			||||||
    createdAt: sharedLink.createdAt,
 | 
					    createdAt: sharedLink.createdAt,
 | 
				
			||||||
    expiresAt: sharedLink.expiresAt,
 | 
					    expiresAt: sharedLink.expiresAt,
 | 
				
			||||||
    assets: assets.map(mapAsset),
 | 
					    assets: assets.map((asset) => mapAsset(asset)),
 | 
				
			||||||
    album: sharedLink.album ? mapAlbumWithoutAssets(sharedLink.album) : undefined,
 | 
					    album: sharedLink.album ? mapAlbumWithoutAssets(sharedLink.album) : undefined,
 | 
				
			||||||
    allowUpload: sharedLink.allowUpload,
 | 
					    allowUpload: sharedLink.allowUpload,
 | 
				
			||||||
    allowDownload: sharedLink.allowDownload,
 | 
					    allowDownload: sharedLink.allowDownload,
 | 
				
			||||||
    showExif: sharedLink.showExif,
 | 
					    showMetadata: sharedLink.showExif,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function mapSharedLinkWithNoExif(sharedLink: SharedLinkEntity): SharedLinkResponseDto {
 | 
					export function mapSharedLinkWithoutMetadata(sharedLink: SharedLinkEntity): SharedLinkResponseDto {
 | 
				
			||||||
  const linkAssets = sharedLink.assets || [];
 | 
					  const linkAssets = sharedLink.assets || [];
 | 
				
			||||||
  const albumAssets = (sharedLink?.album?.assets || []).map((asset) => asset);
 | 
					  const albumAssets = (sharedLink?.album?.assets || []).map((asset) => asset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -57,10 +58,10 @@ export function mapSharedLinkWithNoExif(sharedLink: SharedLinkEntity): SharedLin
 | 
				
			|||||||
    type: sharedLink.type,
 | 
					    type: sharedLink.type,
 | 
				
			||||||
    createdAt: sharedLink.createdAt,
 | 
					    createdAt: sharedLink.createdAt,
 | 
				
			||||||
    expiresAt: sharedLink.expiresAt,
 | 
					    expiresAt: sharedLink.expiresAt,
 | 
				
			||||||
    assets: assets.map(mapAssetWithoutExif),
 | 
					    assets: assets.map((asset) => mapAsset(asset, true)) as AssetResponseDto[],
 | 
				
			||||||
    album: sharedLink.album ? mapAlbumWithoutAssets(sharedLink.album) : undefined,
 | 
					    album: sharedLink.album ? mapAlbumWithoutAssets(sharedLink.album) : undefined,
 | 
				
			||||||
    allowUpload: sharedLink.allowUpload,
 | 
					    allowUpload: sharedLink.allowUpload,
 | 
				
			||||||
    allowDownload: sharedLink.allowDownload,
 | 
					    allowDownload: sharedLink.allowDownload,
 | 
				
			||||||
    showExif: sharedLink.showExif,
 | 
					    showMetadata: sharedLink.showExif,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ export class SharedLinkCreateDto {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @Optional()
 | 
					  @Optional()
 | 
				
			||||||
  @IsBoolean()
 | 
					  @IsBoolean()
 | 
				
			||||||
  showExif?: boolean = true;
 | 
					  showMetadata?: boolean = true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class SharedLinkEditDto {
 | 
					export class SharedLinkEditDto {
 | 
				
			||||||
@ -51,5 +51,5 @@ export class SharedLinkEditDto {
 | 
				
			|||||||
  allowDownload?: boolean;
 | 
					  allowDownload?: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Optional()
 | 
					  @Optional()
 | 
				
			||||||
  showExif?: boolean;
 | 
					  showMetadata?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -59,10 +59,10 @@ describe(SharedLinkService.name, () => {
 | 
				
			|||||||
      expect(shareMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
 | 
					      expect(shareMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should return not return exif', async () => {
 | 
					    it('should not return metadata', async () => {
 | 
				
			||||||
      const authDto = authStub.adminSharedLinkNoExif;
 | 
					      const authDto = authStub.adminSharedLinkNoExif;
 | 
				
			||||||
      shareMock.get.mockResolvedValue(sharedLinkStub.readonlyNoExif);
 | 
					      shareMock.get.mockResolvedValue(sharedLinkStub.readonlyNoExif);
 | 
				
			||||||
      await expect(sut.getMine(authDto)).resolves.toEqual(sharedLinkResponseStub.readonlyNoExif);
 | 
					      await expect(sut.getMine(authDto)).resolves.toEqual(sharedLinkResponseStub.readonlyNoMetadata);
 | 
				
			||||||
      expect(shareMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
 | 
					      expect(shareMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
@ -137,7 +137,7 @@ describe(SharedLinkService.name, () => {
 | 
				
			|||||||
      await sut.create(authStub.admin, {
 | 
					      await sut.create(authStub.admin, {
 | 
				
			||||||
        type: SharedLinkType.INDIVIDUAL,
 | 
					        type: SharedLinkType.INDIVIDUAL,
 | 
				
			||||||
        assetIds: [assetStub.image.id],
 | 
					        assetIds: [assetStub.image.id],
 | 
				
			||||||
        showExif: true,
 | 
					        showMetadata: true,
 | 
				
			||||||
        allowDownload: true,
 | 
					        allowDownload: true,
 | 
				
			||||||
        allowUpload: true,
 | 
					        allowUpload: true,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import { AccessCore, Permission } from '../access';
 | 
				
			|||||||
import { AssetIdErrorReason, AssetIdsDto, AssetIdsResponseDto } from '../asset';
 | 
					import { AssetIdErrorReason, AssetIdsDto, AssetIdsResponseDto } from '../asset';
 | 
				
			||||||
import { AuthUserDto } from '../auth';
 | 
					import { AuthUserDto } from '../auth';
 | 
				
			||||||
import { IAccessRepository, ICryptoRepository, ISharedLinkRepository } from '../repositories';
 | 
					import { IAccessRepository, ICryptoRepository, ISharedLinkRepository } from '../repositories';
 | 
				
			||||||
import { SharedLinkResponseDto, mapSharedLink, mapSharedLinkWithNoExif } from './shared-link-response.dto';
 | 
					import { SharedLinkResponseDto, mapSharedLink, mapSharedLinkWithoutMetadata } from './shared-link-response.dto';
 | 
				
			||||||
import { SharedLinkCreateDto, SharedLinkEditDto } from './shared-link.dto';
 | 
					import { SharedLinkCreateDto, SharedLinkEditDto } from './shared-link.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
@ -24,7 +24,7 @@ export class SharedLinkService {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async getMine(authUser: AuthUserDto): Promise<SharedLinkResponseDto> {
 | 
					  async getMine(authUser: AuthUserDto): Promise<SharedLinkResponseDto> {
 | 
				
			||||||
    const { sharedLinkId: id, isPublicUser, isShowExif } = authUser;
 | 
					    const { sharedLinkId: id, isPublicUser, isShowMetadata: isShowExif } = authUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!isPublicUser || !id) {
 | 
					    if (!isPublicUser || !id) {
 | 
				
			||||||
      throw new ForbiddenException();
 | 
					      throw new ForbiddenException();
 | 
				
			||||||
@ -69,7 +69,7 @@ export class SharedLinkService {
 | 
				
			|||||||
      expiresAt: dto.expiresAt || null,
 | 
					      expiresAt: dto.expiresAt || null,
 | 
				
			||||||
      allowUpload: dto.allowUpload ?? true,
 | 
					      allowUpload: dto.allowUpload ?? true,
 | 
				
			||||||
      allowDownload: dto.allowDownload ?? true,
 | 
					      allowDownload: dto.allowDownload ?? true,
 | 
				
			||||||
      showExif: dto.showExif ?? true,
 | 
					      showExif: dto.showMetadata ?? true,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return this.map(sharedLink, { withExif: true });
 | 
					    return this.map(sharedLink, { withExif: true });
 | 
				
			||||||
@ -84,7 +84,7 @@ export class SharedLinkService {
 | 
				
			|||||||
      expiresAt: dto.expiresAt,
 | 
					      expiresAt: dto.expiresAt,
 | 
				
			||||||
      allowUpload: dto.allowUpload,
 | 
					      allowUpload: dto.allowUpload,
 | 
				
			||||||
      allowDownload: dto.allowDownload,
 | 
					      allowDownload: dto.allowDownload,
 | 
				
			||||||
      showExif: dto.showExif,
 | 
					      showExif: dto.showMetadata,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    return this.map(sharedLink, { withExif: true });
 | 
					    return this.map(sharedLink, { withExif: true });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -157,6 +157,6 @@ export class SharedLinkService {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private map(sharedLink: SharedLinkEntity, { withExif }: { withExif: boolean }) {
 | 
					  private map(sharedLink: SharedLinkEntity, { withExif }: { withExif: boolean }) {
 | 
				
			||||||
    return withExif ? mapSharedLink(sharedLink) : mapSharedLinkWithNoExif(sharedLink);
 | 
					    return withExif ? mapSharedLink(sharedLink) : mapSharedLinkWithoutMetadata(sharedLink);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -47,7 +47,7 @@ export class TagService {
 | 
				
			|||||||
  async getAssets(authUser: AuthUserDto, id: string): Promise<AssetResponseDto[]> {
 | 
					  async getAssets(authUser: AuthUserDto, id: string): Promise<AssetResponseDto[]> {
 | 
				
			||||||
    await this.findOrFail(authUser, id);
 | 
					    await this.findOrFail(authUser, id);
 | 
				
			||||||
    const assets = await this.repository.getAssets(authUser.id, id);
 | 
					    const assets = await this.repository.getAssets(authUser.id, id);
 | 
				
			||||||
    return assets.map(mapAsset);
 | 
					    return assets.map((asset) => mapAsset(asset));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async addAssets(authUser: AuthUserDto, id: string, dto: AssetIdsDto): Promise<AssetIdsResponseDto[]> {
 | 
					  async addAssets(authUser: AuthUserDto, id: string, dto: AssetIdsDto): Promise<AssetIdsResponseDto[]> {
 | 
				
			||||||
 | 
				
			|||||||
@ -186,7 +186,7 @@ export class AssetController {
 | 
				
			|||||||
  @SharedLinkRoute()
 | 
					  @SharedLinkRoute()
 | 
				
			||||||
  @Get('/assetById/:id')
 | 
					  @Get('/assetById/:id')
 | 
				
			||||||
  getAssetById(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto> {
 | 
					  getAssetById(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto> {
 | 
				
			||||||
    return this.assetService.getAssetById(authUser, id);
 | 
					    return this.assetService.getAssetById(authUser, id) as Promise<AssetResponseDto>;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
 | 
				
			|||||||
@ -10,9 +10,9 @@ import {
 | 
				
			|||||||
  IStorageRepository,
 | 
					  IStorageRepository,
 | 
				
			||||||
  JobName,
 | 
					  JobName,
 | 
				
			||||||
  mapAsset,
 | 
					  mapAsset,
 | 
				
			||||||
  mapAssetWithoutExif,
 | 
					 | 
				
			||||||
  mimeTypes,
 | 
					  mimeTypes,
 | 
				
			||||||
  Permission,
 | 
					  Permission,
 | 
				
			||||||
 | 
					  SanitizedAssetResponseDto,
 | 
				
			||||||
  UploadFile,
 | 
					  UploadFile,
 | 
				
			||||||
} from '@app/domain';
 | 
					} from '@app/domain';
 | 
				
			||||||
import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity, AssetType, LibraryType } from '@app/infra/entities';
 | 
					import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity, AssetType, LibraryType } from '@app/infra/entities';
 | 
				
			||||||
@ -187,22 +187,29 @@ export class AssetService {
 | 
				
			|||||||
    return assets.map((asset) => mapAsset(asset));
 | 
					    return assets.map((asset) => mapAsset(asset));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async getAssetById(authUser: AuthUserDto, assetId: string): Promise<AssetResponseDto> {
 | 
					  public async getAssetById(
 | 
				
			||||||
 | 
					    authUser: AuthUserDto,
 | 
				
			||||||
 | 
					    assetId: string,
 | 
				
			||||||
 | 
					  ): Promise<AssetResponseDto | SanitizedAssetResponseDto> {
 | 
				
			||||||
    await this.access.requirePermission(authUser, Permission.ASSET_READ, assetId);
 | 
					    await this.access.requirePermission(authUser, Permission.ASSET_READ, assetId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const allowExif = this.getExifPermission(authUser);
 | 
					    const includeMetadata = this.getExifPermission(authUser);
 | 
				
			||||||
    const asset = await this._assetRepository.getById(assetId);
 | 
					    const asset = await this._assetRepository.getById(assetId);
 | 
				
			||||||
    const data = allowExif ? mapAsset(asset) : mapAssetWithoutExif(asset);
 | 
					    if (includeMetadata) {
 | 
				
			||||||
 | 
					      const data = mapAsset(asset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (data.ownerId !== authUser.id) {
 | 
					      if (data.ownerId !== authUser.id) {
 | 
				
			||||||
      data.people = [];
 | 
					        data.people = [];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (authUser.isPublicUser) {
 | 
				
			||||||
 | 
					        delete data.owner;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return data;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return mapAsset(asset, true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (authUser.isPublicUser) {
 | 
					 | 
				
			||||||
      delete data.owner;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return data;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async serveThumbnail(authUser: AuthUserDto, assetId: string, query: GetAssetThumbnailDto, res: Res) {
 | 
					  async serveThumbnail(authUser: AuthUserDto, assetId: string, query: GetAssetThumbnailDto, res: Res) {
 | 
				
			||||||
@ -374,7 +381,7 @@ export class AssetService {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getExifPermission(authUser: AuthUserDto) {
 | 
					  getExifPermission(authUser: AuthUserDto) {
 | 
				
			||||||
    return !authUser.isPublicUser || authUser.isShowExif;
 | 
					    return !authUser.isPublicUser || authUser.isShowMetadata;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private getThumbnailPath(asset: AssetEntity, format: GetAssetThumbnailFormatEnum) {
 | 
					  private getThumbnailPath(asset: AssetEntity, format: GetAssetThumbnailFormatEnum) {
 | 
				
			||||||
 | 
				
			|||||||
@ -98,7 +98,7 @@ export class AssetController {
 | 
				
			|||||||
  @Authenticated({ isShared: true })
 | 
					  @Authenticated({ isShared: true })
 | 
				
			||||||
  @Get('time-bucket')
 | 
					  @Get('time-bucket')
 | 
				
			||||||
  getByTimeBucket(@AuthUser() authUser: AuthUserDto, @Query() dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> {
 | 
					  getByTimeBucket(@AuthUser() authUser: AuthUserDto, @Query() dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> {
 | 
				
			||||||
    return this.service.getByTimeBucket(authUser, dto);
 | 
					    return this.service.getByTimeBucket(authUser, dto) as Promise<AssetResponseDto[]>;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Post('jobs')
 | 
					  @Post('jobs')
 | 
				
			||||||
 | 
				
			|||||||
@ -10,4 +10,11 @@ export const sharedLinkApi = {
 | 
				
			|||||||
    expect(status).toBe(201);
 | 
					    expect(status).toBe(201);
 | 
				
			||||||
    return body as SharedLinkResponseDto;
 | 
					    return body as SharedLinkResponseDto;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getMySharedLink: async (server: any, key: string) => {
 | 
				
			||||||
 | 
					    const { status, body } = await request(server).get('/shared-link/me').query({ key });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expect(status).toBe(200);
 | 
				
			||||||
 | 
					    return body as SharedLinkResponseDto;
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1 +1 @@
 | 
				
			|||||||
Subproject commit 9e6e1bcc245e0ae0285bb596faf310ead851fac6
 | 
					Subproject commit 948f353e3c9b66156c86c86cf078e0746ec1598e
 | 
				
			||||||
@ -1,11 +1,17 @@
 | 
				
			|||||||
import { AlbumResponseDto, LoginResponseDto, SharedLinkResponseDto } from '@app/domain';
 | 
					import { AlbumResponseDto, LoginResponseDto, SharedLinkResponseDto } from '@app/domain';
 | 
				
			||||||
import { PartnerController } from '@app/immich';
 | 
					import { PartnerController } from '@app/immich';
 | 
				
			||||||
import { SharedLinkType } from '@app/infra/entities';
 | 
					import { LibraryType, SharedLinkType } from '@app/infra/entities';
 | 
				
			||||||
import { INestApplication } from '@nestjs/common';
 | 
					import { INestApplication } from '@nestjs/common';
 | 
				
			||||||
import { api } from '@test/api';
 | 
					import { api } from '@test/api';
 | 
				
			||||||
import { db } from '@test/db';
 | 
					import { db } from '@test/db';
 | 
				
			||||||
import { errorStub, uuidStub } from '@test/fixtures';
 | 
					import { errorStub, uuidStub } from '@test/fixtures';
 | 
				
			||||||
import { createTestApp } from '@test/test-utils';
 | 
					import {
 | 
				
			||||||
 | 
					  IMMICH_TEST_ASSET_PATH,
 | 
				
			||||||
 | 
					  IMMICH_TEST_ASSET_TEMP_PATH,
 | 
				
			||||||
 | 
					  createTestApp,
 | 
				
			||||||
 | 
					  restoreTempFolder,
 | 
				
			||||||
 | 
					} from '@test/test-utils';
 | 
				
			||||||
 | 
					import { cp } from 'fs/promises';
 | 
				
			||||||
import request from 'supertest';
 | 
					import request from 'supertest';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const user1Dto = {
 | 
					const user1Dto = {
 | 
				
			||||||
@ -18,24 +24,22 @@ const user1Dto = {
 | 
				
			|||||||
describe(`${PartnerController.name} (e2e)`, () => {
 | 
					describe(`${PartnerController.name} (e2e)`, () => {
 | 
				
			||||||
  let app: INestApplication;
 | 
					  let app: INestApplication;
 | 
				
			||||||
  let server: any;
 | 
					  let server: any;
 | 
				
			||||||
  let loginResponse: LoginResponseDto;
 | 
					  let admin: LoginResponseDto;
 | 
				
			||||||
  let accessToken: string;
 | 
					 | 
				
			||||||
  let user1: LoginResponseDto;
 | 
					  let user1: LoginResponseDto;
 | 
				
			||||||
  let album: AlbumResponseDto;
 | 
					  let album: AlbumResponseDto;
 | 
				
			||||||
  let sharedLink: SharedLinkResponseDto;
 | 
					  let sharedLink: SharedLinkResponseDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeAll(async () => {
 | 
					  beforeAll(async () => {
 | 
				
			||||||
    app = await createTestApp();
 | 
					    app = await createTestApp(true);
 | 
				
			||||||
    server = app.getHttpServer();
 | 
					    server = app.getHttpServer();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await db.reset();
 | 
					    await db.reset();
 | 
				
			||||||
    await api.authApi.adminSignUp(server);
 | 
					    await api.authApi.adminSignUp(server);
 | 
				
			||||||
    loginResponse = await api.authApi.adminLogin(server);
 | 
					    admin = await api.authApi.adminLogin(server);
 | 
				
			||||||
    accessToken = loginResponse.accessToken;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await api.userApi.create(server, accessToken, user1Dto);
 | 
					    await api.userApi.create(server, admin.accessToken, user1Dto);
 | 
				
			||||||
    user1 = await api.authApi.login(server, { email: user1Dto.email, password: user1Dto.password });
 | 
					    user1 = await api.authApi.login(server, { email: user1Dto.email, password: user1Dto.password });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    album = await api.albumApi.create(server, user1.accessToken, { albumName: 'shared with link' });
 | 
					    album = await api.albumApi.create(server, user1.accessToken, { albumName: 'shared with link' });
 | 
				
			||||||
@ -48,6 +52,7 @@ describe(`${PartnerController.name} (e2e)`, () => {
 | 
				
			|||||||
  afterAll(async () => {
 | 
					  afterAll(async () => {
 | 
				
			||||||
    await db.disconnect();
 | 
					    await db.disconnect();
 | 
				
			||||||
    await app.close();
 | 
					    await app.close();
 | 
				
			||||||
 | 
					    await restoreTempFolder();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('GET /shared-link', () => {
 | 
					  describe('GET /shared-link', () => {
 | 
				
			||||||
@ -68,7 +73,9 @@ describe(`${PartnerController.name} (e2e)`, () => {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should not get shared links created by other users', async () => {
 | 
					    it('should not get shared links created by other users', async () => {
 | 
				
			||||||
      const { status, body } = await request(server).get('/shared-link').set('Authorization', `Bearer ${accessToken}`);
 | 
					      const { status, body } = await request(server)
 | 
				
			||||||
 | 
					        .get('/shared-link')
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(status).toBe(200);
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
      expect(body).toEqual([]);
 | 
					      expect(body).toEqual([]);
 | 
				
			||||||
@ -77,7 +84,9 @@ describe(`${PartnerController.name} (e2e)`, () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  describe('GET /shared-link/me', () => {
 | 
					  describe('GET /shared-link/me', () => {
 | 
				
			||||||
    it('should not require admin authentication', async () => {
 | 
					    it('should not require admin authentication', async () => {
 | 
				
			||||||
      const { status } = await request(server).get('/shared-link/me').set('Authorization', `Bearer ${accessToken}`);
 | 
					      const { status } = await request(server)
 | 
				
			||||||
 | 
					        .get('/shared-link/me')
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(status).toBe(403);
 | 
					      expect(status).toBe(403);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@ -104,7 +113,7 @@ describe(`${PartnerController.name} (e2e)`, () => {
 | 
				
			|||||||
        type: SharedLinkType.ALBUM,
 | 
					        type: SharedLinkType.ALBUM,
 | 
				
			||||||
        albumId: softDeletedAlbum.id,
 | 
					        albumId: softDeletedAlbum.id,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      await api.userApi.delete(server, accessToken, user1.userId);
 | 
					      await api.userApi.delete(server, admin.accessToken, user1.userId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const { status, body } = await request(server).get('/shared-link/me').query({ key: softDeletedAlbumLink.key });
 | 
					      const { status, body } = await request(server).get('/shared-link/me').query({ key: softDeletedAlbumLink.key });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -133,7 +142,7 @@ describe(`${PartnerController.name} (e2e)`, () => {
 | 
				
			|||||||
    it('should not get shared link by id if user has not created the link or it does not exist', async () => {
 | 
					    it('should not get shared link by id if user has not created the link or it does not exist', async () => {
 | 
				
			||||||
      const { status, body } = await request(server)
 | 
					      const { status, body } = await request(server)
 | 
				
			||||||
        .get(`/shared-link/${sharedLink.id}`)
 | 
					        .get(`/shared-link/${sharedLink.id}`)
 | 
				
			||||||
        .set('Authorization', `Bearer ${accessToken}`);
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(status).toBe(400);
 | 
					      expect(status).toBe(400);
 | 
				
			||||||
      expect(body).toEqual(expect.objectContaining({ message: 'Shared link not found' }));
 | 
					      expect(body).toEqual(expect.objectContaining({ message: 'Shared link not found' }));
 | 
				
			||||||
@ -248,4 +257,81 @@ describe(`${PartnerController.name} (e2e)`, () => {
 | 
				
			|||||||
      expect(status).toBe(200);
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('Shared link metadata', () => {
 | 
				
			||||||
 | 
					    beforeEach(async () => {
 | 
				
			||||||
 | 
					      await restoreTempFolder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await cp(
 | 
				
			||||||
 | 
					        `${IMMICH_TEST_ASSET_PATH}/metadata/gps-position/thompson-springs.jpg`,
 | 
				
			||||||
 | 
					        `${IMMICH_TEST_ASSET_TEMP_PATH}/thompson-springs.jpg`,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const library = await api.libraryApi.create(server, admin.accessToken, {
 | 
				
			||||||
 | 
					        type: LibraryType.EXTERNAL,
 | 
				
			||||||
 | 
					        importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`],
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await api.libraryApi.scanLibrary(server, admin.accessToken, library.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const assets = await api.assetApi.getAllAssets(server, admin.accessToken);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(assets).toHaveLength(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      album = await api.albumApi.create(server, admin.accessToken, { albumName: 'New album' });
 | 
				
			||||||
 | 
					      await api.albumApi.addAssets(server, admin.accessToken, album.id, { ids: [assets[0].id] });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should return metadata for album shared link', async () => {
 | 
				
			||||||
 | 
					      const sharedLink = await api.sharedLinkApi.create(server, admin.accessToken, {
 | 
				
			||||||
 | 
					        type: SharedLinkType.ALBUM,
 | 
				
			||||||
 | 
					        albumId: album.id,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const returnedLink = await api.sharedLinkApi.getMySharedLink(server, sharedLink.key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(returnedLink.assets).toHaveLength(1);
 | 
				
			||||||
 | 
					      expect(returnedLink.album).toBeDefined();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const returnedAsset = returnedLink.assets[0];
 | 
				
			||||||
 | 
					      expect(returnedAsset).toEqual(
 | 
				
			||||||
 | 
					        expect.objectContaining({
 | 
				
			||||||
 | 
					          originalFileName: 'thompson-springs',
 | 
				
			||||||
 | 
					          resized: true,
 | 
				
			||||||
 | 
					          localDateTime: '2022-01-10T15:15:44.310Z',
 | 
				
			||||||
 | 
					          fileCreatedAt: '2022-01-10T19:15:44.310Z',
 | 
				
			||||||
 | 
					          exifInfo: expect.objectContaining({
 | 
				
			||||||
 | 
					            longitude: -108.400968333333,
 | 
				
			||||||
 | 
					            latitude: 39.115,
 | 
				
			||||||
 | 
					            orientation: '1',
 | 
				
			||||||
 | 
					            dateTimeOriginal: '2022-01-10T19:15:44.310Z',
 | 
				
			||||||
 | 
					            timeZone: 'UTC-4',
 | 
				
			||||||
 | 
					            state: 'Mesa County, Colorado',
 | 
				
			||||||
 | 
					            country: 'United States of America',
 | 
				
			||||||
 | 
					          }),
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should not return metadata for album shared link without metadata', async () => {
 | 
				
			||||||
 | 
					      const sharedLink = await api.sharedLinkApi.create(server, admin.accessToken, {
 | 
				
			||||||
 | 
					        type: SharedLinkType.ALBUM,
 | 
				
			||||||
 | 
					        albumId: album.id,
 | 
				
			||||||
 | 
					        showMetadata: false,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const returnedLink = await api.sharedLinkApi.getMySharedLink(server, sharedLink.key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(returnedLink.assets).toHaveLength(1);
 | 
				
			||||||
 | 
					      expect(returnedLink.album).toBeDefined();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const returnedAsset = returnedLink.assets[0];
 | 
				
			||||||
 | 
					      expect(returnedAsset).not.toHaveProperty('exifInfo');
 | 
				
			||||||
 | 
					      expect(returnedAsset).not.toHaveProperty('fileCreatedAt');
 | 
				
			||||||
 | 
					      expect(returnedAsset).not.toHaveProperty('originalFilename');
 | 
				
			||||||
 | 
					      expect(returnedAsset).not.toHaveProperty('originalPath');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										12
									
								
								server/test/fixtures/auth.stub.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								server/test/fixtures/auth.stub.ts
									
									
									
									
										vendored
									
									
								
							@ -48,7 +48,7 @@ export const authStub = {
 | 
				
			|||||||
    isPublicUser: false,
 | 
					    isPublicUser: false,
 | 
				
			||||||
    isAllowUpload: true,
 | 
					    isAllowUpload: true,
 | 
				
			||||||
    isAllowDownload: true,
 | 
					    isAllowDownload: true,
 | 
				
			||||||
    isShowExif: true,
 | 
					    isShowMetadata: true,
 | 
				
			||||||
    accessTokenId: 'token-id',
 | 
					    accessTokenId: 'token-id',
 | 
				
			||||||
    externalPath: null,
 | 
					    externalPath: null,
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
@ -59,7 +59,7 @@ export const authStub = {
 | 
				
			|||||||
    isPublicUser: false,
 | 
					    isPublicUser: false,
 | 
				
			||||||
    isAllowUpload: true,
 | 
					    isAllowUpload: true,
 | 
				
			||||||
    isAllowDownload: true,
 | 
					    isAllowDownload: true,
 | 
				
			||||||
    isShowExif: true,
 | 
					    isShowMetadata: true,
 | 
				
			||||||
    accessTokenId: 'token-id',
 | 
					    accessTokenId: 'token-id',
 | 
				
			||||||
    externalPath: null,
 | 
					    externalPath: null,
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
@ -70,7 +70,7 @@ export const authStub = {
 | 
				
			|||||||
    isPublicUser: false,
 | 
					    isPublicUser: false,
 | 
				
			||||||
    isAllowUpload: true,
 | 
					    isAllowUpload: true,
 | 
				
			||||||
    isAllowDownload: true,
 | 
					    isAllowDownload: true,
 | 
				
			||||||
    isShowExif: true,
 | 
					    isShowMetadata: true,
 | 
				
			||||||
    accessTokenId: 'token-id',
 | 
					    accessTokenId: 'token-id',
 | 
				
			||||||
    externalPath: '/data/user1',
 | 
					    externalPath: '/data/user1',
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
@ -81,7 +81,7 @@ export const authStub = {
 | 
				
			|||||||
    isAllowUpload: true,
 | 
					    isAllowUpload: true,
 | 
				
			||||||
    isAllowDownload: true,
 | 
					    isAllowDownload: true,
 | 
				
			||||||
    isPublicUser: true,
 | 
					    isPublicUser: true,
 | 
				
			||||||
    isShowExif: true,
 | 
					    isShowMetadata: true,
 | 
				
			||||||
    sharedLinkId: '123',
 | 
					    sharedLinkId: '123',
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
  adminSharedLinkNoExif: Object.freeze<AuthUserDto>({
 | 
					  adminSharedLinkNoExif: Object.freeze<AuthUserDto>({
 | 
				
			||||||
@ -91,7 +91,7 @@ export const authStub = {
 | 
				
			|||||||
    isAllowUpload: true,
 | 
					    isAllowUpload: true,
 | 
				
			||||||
    isAllowDownload: true,
 | 
					    isAllowDownload: true,
 | 
				
			||||||
    isPublicUser: true,
 | 
					    isPublicUser: true,
 | 
				
			||||||
    isShowExif: false,
 | 
					    isShowMetadata: false,
 | 
				
			||||||
    sharedLinkId: '123',
 | 
					    sharedLinkId: '123',
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
  readonlySharedLink: Object.freeze<AuthUserDto>({
 | 
					  readonlySharedLink: Object.freeze<AuthUserDto>({
 | 
				
			||||||
@ -101,7 +101,7 @@ export const authStub = {
 | 
				
			|||||||
    isAllowUpload: false,
 | 
					    isAllowUpload: false,
 | 
				
			||||||
    isAllowDownload: false,
 | 
					    isAllowDownload: false,
 | 
				
			||||||
    isPublicUser: true,
 | 
					    isPublicUser: true,
 | 
				
			||||||
    isShowExif: true,
 | 
					    isShowMetadata: true,
 | 
				
			||||||
    sharedLinkId: '123',
 | 
					    sharedLinkId: '123',
 | 
				
			||||||
    accessTokenId: 'token-id',
 | 
					    accessTokenId: 'token-id',
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										24
									
								
								server/test/fixtures/shared-link.stub.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								server/test/fixtures/shared-link.stub.ts
									
									
									
									
										vendored
									
									
								
							@ -71,8 +71,20 @@ const assetResponse: AssetResponseDto = {
 | 
				
			|||||||
  checksum: 'ZmlsZSBoYXNo',
 | 
					  checksum: 'ZmlsZSBoYXNo',
 | 
				
			||||||
  isTrashed: false,
 | 
					  isTrashed: false,
 | 
				
			||||||
  libraryId: 'library-id',
 | 
					  libraryId: 'library-id',
 | 
				
			||||||
 | 
					  hasMetadata: true,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const assetResponseWithoutMetadata = {
 | 
				
			||||||
 | 
					  id: 'id_1',
 | 
				
			||||||
 | 
					  type: AssetType.VIDEO,
 | 
				
			||||||
 | 
					  resized: false,
 | 
				
			||||||
 | 
					  thumbhash: null,
 | 
				
			||||||
 | 
					  localDateTime: today,
 | 
				
			||||||
 | 
					  duration: '0:00:00.00000',
 | 
				
			||||||
 | 
					  livePhotoVideoId: null,
 | 
				
			||||||
 | 
					  hasMetadata: false,
 | 
				
			||||||
 | 
					} as AssetResponseDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const albumResponse: AlbumResponseDto = {
 | 
					const albumResponse: AlbumResponseDto = {
 | 
				
			||||||
  albumName: 'Test Album',
 | 
					  albumName: 'Test Album',
 | 
				
			||||||
  description: '',
 | 
					  description: '',
 | 
				
			||||||
@ -253,7 +265,7 @@ export const sharedLinkResponseStub = {
 | 
				
			|||||||
    expiresAt: tomorrow,
 | 
					    expiresAt: tomorrow,
 | 
				
			||||||
    id: '123',
 | 
					    id: '123',
 | 
				
			||||||
    key: sharedLinkBytes.toString('base64url'),
 | 
					    key: sharedLinkBytes.toString('base64url'),
 | 
				
			||||||
    showExif: true,
 | 
					    showMetadata: true,
 | 
				
			||||||
    type: SharedLinkType.ALBUM,
 | 
					    type: SharedLinkType.ALBUM,
 | 
				
			||||||
    userId: 'admin_id',
 | 
					    userId: 'admin_id',
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
@ -267,7 +279,7 @@ export const sharedLinkResponseStub = {
 | 
				
			|||||||
    expiresAt: yesterday,
 | 
					    expiresAt: yesterday,
 | 
				
			||||||
    id: '123',
 | 
					    id: '123',
 | 
				
			||||||
    key: sharedLinkBytes.toString('base64url'),
 | 
					    key: sharedLinkBytes.toString('base64url'),
 | 
				
			||||||
    showExif: true,
 | 
					    showMetadata: true,
 | 
				
			||||||
    type: SharedLinkType.ALBUM,
 | 
					    type: SharedLinkType.ALBUM,
 | 
				
			||||||
    userId: 'admin_id',
 | 
					    userId: 'admin_id',
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
@ -281,11 +293,11 @@ export const sharedLinkResponseStub = {
 | 
				
			|||||||
    description: null,
 | 
					    description: null,
 | 
				
			||||||
    allowUpload: false,
 | 
					    allowUpload: false,
 | 
				
			||||||
    allowDownload: false,
 | 
					    allowDownload: false,
 | 
				
			||||||
    showExif: true,
 | 
					    showMetadata: true,
 | 
				
			||||||
    album: albumResponse,
 | 
					    album: albumResponse,
 | 
				
			||||||
    assets: [assetResponse],
 | 
					    assets: [assetResponse],
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
  readonlyNoExif: Object.freeze<SharedLinkResponseDto>({
 | 
					  readonlyNoMetadata: Object.freeze<SharedLinkResponseDto>({
 | 
				
			||||||
    id: '123',
 | 
					    id: '123',
 | 
				
			||||||
    userId: 'admin_id',
 | 
					    userId: 'admin_id',
 | 
				
			||||||
    key: sharedLinkBytes.toString('base64url'),
 | 
					    key: sharedLinkBytes.toString('base64url'),
 | 
				
			||||||
@ -295,8 +307,8 @@ export const sharedLinkResponseStub = {
 | 
				
			|||||||
    description: null,
 | 
					    description: null,
 | 
				
			||||||
    allowUpload: false,
 | 
					    allowUpload: false,
 | 
				
			||||||
    allowDownload: false,
 | 
					    allowDownload: false,
 | 
				
			||||||
    showExif: false,
 | 
					    showMetadata: false,
 | 
				
			||||||
    album: { ...albumResponse, startDate: assetResponse.fileCreatedAt, endDate: assetResponse.fileCreatedAt },
 | 
					    album: { ...albumResponse, startDate: assetResponse.fileCreatedAt, endDate: assetResponse.fileCreatedAt },
 | 
				
			||||||
    assets: [{ ...assetResponse, exifInfo: undefined }],
 | 
					    assets: [{ ...assetResponseWithoutMetadata, exifInfo: undefined }],
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										14
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							@ -640,6 +640,12 @@ export interface AssetResponseDto {
 | 
				
			|||||||
     * @memberof AssetResponseDto
 | 
					     * @memberof AssetResponseDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    'fileModifiedAt': string;
 | 
					    'fileModifiedAt': string;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @type {boolean}
 | 
				
			||||||
 | 
					     * @memberof AssetResponseDto
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    'hasMetadata': boolean;
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 
 | 
					     * 
 | 
				
			||||||
     * @type {string}
 | 
					     * @type {string}
 | 
				
			||||||
@ -749,7 +755,7 @@ export interface AssetResponseDto {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    'tags'?: Array<TagResponseDto>;
 | 
					    'tags'?: Array<TagResponseDto>;
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * base64 encoded thumbhash
 | 
					     * 
 | 
				
			||||||
     * @type {string}
 | 
					     * @type {string}
 | 
				
			||||||
     * @memberof AssetResponseDto
 | 
					     * @memberof AssetResponseDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@ -2882,7 +2888,7 @@ export interface SharedLinkCreateDto {
 | 
				
			|||||||
     * @type {boolean}
 | 
					     * @type {boolean}
 | 
				
			||||||
     * @memberof SharedLinkCreateDto
 | 
					     * @memberof SharedLinkCreateDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    'showExif'?: boolean;
 | 
					    'showMetadata'?: boolean;
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 
 | 
					     * 
 | 
				
			||||||
     * @type {SharedLinkType}
 | 
					     * @type {SharedLinkType}
 | 
				
			||||||
@ -2927,7 +2933,7 @@ export interface SharedLinkEditDto {
 | 
				
			|||||||
     * @type {boolean}
 | 
					     * @type {boolean}
 | 
				
			||||||
     * @memberof SharedLinkEditDto
 | 
					     * @memberof SharedLinkEditDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    'showExif'?: boolean;
 | 
					    'showMetadata'?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 
 | 
					 * 
 | 
				
			||||||
@ -2994,7 +3000,7 @@ export interface SharedLinkResponseDto {
 | 
				
			|||||||
     * @type {boolean}
 | 
					     * @type {boolean}
 | 
				
			||||||
     * @memberof SharedLinkResponseDto
 | 
					     * @memberof SharedLinkResponseDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    'showExif': boolean;
 | 
					    'showMetadata': boolean;
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 
 | 
					     * 
 | 
				
			||||||
     * @type {SharedLinkType}
 | 
					     * @type {SharedLinkType}
 | 
				
			||||||
 | 
				
			|||||||
@ -28,6 +28,7 @@
 | 
				
			|||||||
  export let showMotionPlayButton: boolean;
 | 
					  export let showMotionPlayButton: boolean;
 | 
				
			||||||
  export let isMotionPhotoPlaying = false;
 | 
					  export let isMotionPhotoPlaying = false;
 | 
				
			||||||
  export let showDownloadButton: boolean;
 | 
					  export let showDownloadButton: boolean;
 | 
				
			||||||
 | 
					  export let showDetailButton: boolean;
 | 
				
			||||||
  export let showSlideshow = false;
 | 
					  export let showSlideshow = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const isOwner = asset.ownerId === $page.data.user?.id;
 | 
					  const isOwner = asset.ownerId === $page.data.user?.id;
 | 
				
			||||||
@ -133,7 +134,14 @@
 | 
				
			|||||||
        title="Download"
 | 
					        title="Download"
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
    {/if}
 | 
					    {/if}
 | 
				
			||||||
    <CircleIconButton isOpacity={true} logo={InformationOutline} on:click={() => dispatch('showDetail')} title="Info" />
 | 
					    {#if showDetailButton}
 | 
				
			||||||
 | 
					      <CircleIconButton
 | 
				
			||||||
 | 
					        isOpacity={true}
 | 
				
			||||||
 | 
					        logo={InformationOutline}
 | 
				
			||||||
 | 
					        on:click={() => dispatch('showDetail')}
 | 
				
			||||||
 | 
					        title="Info"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    {/if}
 | 
				
			||||||
    {#if isOwner}
 | 
					    {#if isOwner}
 | 
				
			||||||
      <CircleIconButton
 | 
					      <CircleIconButton
 | 
				
			||||||
        isOpacity={true}
 | 
					        isOpacity={true}
 | 
				
			||||||
 | 
				
			|||||||
@ -55,6 +55,7 @@
 | 
				
			|||||||
  let shouldPlayMotionPhoto = false;
 | 
					  let shouldPlayMotionPhoto = false;
 | 
				
			||||||
  let isShowProfileImageCrop = false;
 | 
					  let isShowProfileImageCrop = false;
 | 
				
			||||||
  let shouldShowDownloadButton = sharedLink ? sharedLink.allowDownload : !asset.isOffline;
 | 
					  let shouldShowDownloadButton = sharedLink ? sharedLink.allowDownload : !asset.isOffline;
 | 
				
			||||||
 | 
					  let shouldShowDetailButton = asset.hasMetadata;
 | 
				
			||||||
  let canCopyImagesToClipboard: boolean;
 | 
					  let canCopyImagesToClipboard: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onKeyboardPress = (keyInfo: KeyboardEvent) => handleKeyboardPress(keyInfo);
 | 
					  const onKeyboardPress = (keyInfo: KeyboardEvent) => handleKeyboardPress(keyInfo);
 | 
				
			||||||
@ -392,6 +393,7 @@
 | 
				
			|||||||
        showZoomButton={asset.type === AssetTypeEnum.Image}
 | 
					        showZoomButton={asset.type === AssetTypeEnum.Image}
 | 
				
			||||||
        showMotionPlayButton={!!asset.livePhotoVideoId}
 | 
					        showMotionPlayButton={!!asset.livePhotoVideoId}
 | 
				
			||||||
        showDownloadButton={shouldShowDownloadButton}
 | 
					        showDownloadButton={shouldShowDownloadButton}
 | 
				
			||||||
 | 
					        showDetailButton={shouldShowDetailButton}
 | 
				
			||||||
        showSlideshow={!!assetStore}
 | 
					        showSlideshow={!!assetStore}
 | 
				
			||||||
        on:goBack={closeViewer}
 | 
					        on:goBack={closeViewer}
 | 
				
			||||||
        on:showDetail={showDetailInfoHandler}
 | 
					        on:showDetail={showDetailInfoHandler}
 | 
				
			||||||
@ -433,9 +435,9 @@
 | 
				
			|||||||
            on:close={closeViewer}
 | 
					            on:close={closeViewer}
 | 
				
			||||||
            on:onVideoEnded={() => (shouldPlayMotionPhoto = false)}
 | 
					            on:onVideoEnded={() => (shouldPlayMotionPhoto = false)}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        {:else if asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR || asset.originalPath
 | 
					        {:else if asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR || (asset.originalPath && asset.originalPath
 | 
				
			||||||
            .toLowerCase()
 | 
					              .toLowerCase()
 | 
				
			||||||
            .endsWith('.insp')}
 | 
					              .endsWith('.insp'))}
 | 
				
			||||||
          <PanoramaViewer {asset} />
 | 
					          <PanoramaViewer {asset} />
 | 
				
			||||||
        {:else}
 | 
					        {:else}
 | 
				
			||||||
          <PhotoViewer {asset} on:close={closeViewer} />
 | 
					          <PhotoViewer {asset} on:close={closeViewer} />
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,7 @@
 | 
				
			|||||||
  let description = '';
 | 
					  let description = '';
 | 
				
			||||||
  let allowDownload = true;
 | 
					  let allowDownload = true;
 | 
				
			||||||
  let allowUpload = false;
 | 
					  let allowUpload = false;
 | 
				
			||||||
  let showExif = true;
 | 
					  let showMetadata = true;
 | 
				
			||||||
  let expirationTime = '';
 | 
					  let expirationTime = '';
 | 
				
			||||||
  let shouldChangeExpirationTime = false;
 | 
					  let shouldChangeExpirationTime = false;
 | 
				
			||||||
  let canCopyImagesToClipboard = true;
 | 
					  let canCopyImagesToClipboard = true;
 | 
				
			||||||
@ -41,7 +41,7 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      allowUpload = editingLink.allowUpload;
 | 
					      allowUpload = editingLink.allowUpload;
 | 
				
			||||||
      allowDownload = editingLink.allowDownload;
 | 
					      allowDownload = editingLink.allowDownload;
 | 
				
			||||||
      showExif = editingLink.showExif;
 | 
					      showMetadata = editingLink.showMetadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      albumId = editingLink.album?.id;
 | 
					      albumId = editingLink.album?.id;
 | 
				
			||||||
      assetIds = editingLink.assets.map(({ id }) => id);
 | 
					      assetIds = editingLink.assets.map(({ id }) => id);
 | 
				
			||||||
@ -66,7 +66,7 @@
 | 
				
			|||||||
          allowUpload,
 | 
					          allowUpload,
 | 
				
			||||||
          description,
 | 
					          description,
 | 
				
			||||||
          allowDownload,
 | 
					          allowDownload,
 | 
				
			||||||
          showExif,
 | 
					          showMetadata,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      sharedLink = `${window.location.origin}/share/${data.key}`;
 | 
					      sharedLink = `${window.location.origin}/share/${data.key}`;
 | 
				
			||||||
@ -119,9 +119,9 @@
 | 
				
			|||||||
        sharedLinkEditDto: {
 | 
					        sharedLinkEditDto: {
 | 
				
			||||||
          description,
 | 
					          description,
 | 
				
			||||||
          expiresAt: shouldChangeExpirationTime ? expirationDate : undefined,
 | 
					          expiresAt: shouldChangeExpirationTime ? expirationDate : undefined,
 | 
				
			||||||
          allowUpload: allowUpload,
 | 
					          allowUpload,
 | 
				
			||||||
          allowDownload: allowDownload,
 | 
					          allowDownload,
 | 
				
			||||||
          showExif: showExif,
 | 
					          showMetadata,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -184,7 +184,7 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="my-3">
 | 
					        <div class="my-3">
 | 
				
			||||||
          <SettingSwitch bind:checked={showExif} title={'Show metadata'} />
 | 
					          <SettingSwitch bind:checked={showMetadata} title={'Show metadata'} />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="my-3">
 | 
					        <div class="my-3">
 | 
				
			||||||
 | 
				
			|||||||
@ -136,7 +136,7 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      {/if}
 | 
					      {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      {#if link.showExif}
 | 
					      {#if link.showMetadata}
 | 
				
			||||||
        <div
 | 
					        <div
 | 
				
			||||||
          class="flex w-[60px] place-content-center place-items-center rounded-full bg-immich-primary px-2 py-1 text-xs text-white dark:bg-immich-dark-primary dark:text-immich-dark-gray"
 | 
					          class="flex w-[60px] place-content-center place-items-center rounded-full bg-immich-primary px-2 py-1 text-xs text-white dark:bg-immich-dark-primary dark:text-immich-dark-gray"
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user