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:
Jonathan Jogenfors
2023-10-14 03:46:30 +02:00
committed by GitHub
parent 4a9f58bf9b
commit dadcf49eca
39 changed files with 332 additions and 150 deletions
+10 -2
View File
@@ -47,6 +47,7 @@ import {
BulkIdsDto,
MapMarkerResponseDto,
MemoryLaneResponseDto,
SanitizedAssetResponseDto,
TimeBucketResponseDto,
mapAsset,
} from './response-dto';
@@ -198,10 +199,17 @@ export class AssetService {
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);
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> {
@@ -6,43 +6,62 @@ import { UserResponseDto, mapUser } from '../../user/response-dto/user-response.
import { ExifResponseDto, mapExif } from './exif-response.dto';
import { SmartInfoResponseDto, mapSmartInfo } from './smart-info-response.dto';
export class AssetResponseDto {
export class SanitizedAssetResponseDto {
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;
deviceId!: string;
ownerId!: string;
owner?: UserResponseDto;
libraryId!: string;
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
type!: AssetType;
originalPath!: string;
originalFileName!: string;
resized!: boolean;
/**base64 encoded thumbhash */
thumbhash!: string | null;
fileCreatedAt!: Date;
fileModifiedAt!: Date;
updatedAt!: Date;
isFavorite!: boolean;
isArchived!: boolean;
isTrashed!: boolean;
localDateTime!: Date;
isOffline!: boolean;
isExternal!: boolean;
isReadOnly!: boolean;
duration!: string;
exifInfo?: ExifResponseDto;
smartInfo?: SmartInfoResponseDto;
livePhotoVideoId?: string | null;
tags?: TagResponseDto[];
people?: PersonResponseDto[];
/**base64 encoded sha1 hash */
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 {
...sanitizedAssetResponse,
id: entity.id,
deviceAssetId: entity.deviceAssetId,
ownerId: entity.ownerId,
@@ -62,7 +81,7 @@ function _map(entity: AssetEntity, withExif: boolean): AssetResponseDto {
isArchived: entity.isArchived,
isTrashed: !!entity.deletedAt,
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,
livePhotoVideoId: entity.livePhotoVideoId,
tags: entity.tags?.map(mapTag),
@@ -71,17 +90,10 @@ function _map(entity: AssetEntity, withExif: boolean): AssetResponseDto {
isExternal: entity.isExternal,
isOffline: entity.isOffline,
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 {
title!: string;
assets!: AssetResponseDto[];
@@ -52,3 +52,15 @@ export function mapExif(entity: ExifEntity): ExifResponseDto {
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,
};
}