diff --git a/cli/src/api/open-api/api.ts b/cli/src/api/open-api/api.ts index 2301c02e1c..35a769d283 100644 --- a/cli/src/api/open-api/api.ts +++ b/cli/src/api/open-api/api.ts @@ -645,6 +645,12 @@ export interface AssetResponseDto { * @memberof AssetResponseDto */ 'originalPath': string; + /** + * + * @type {UserResponseDto} + * @memberof AssetResponseDto + */ + 'owner'?: UserResponseDto; /** * * @type {string} diff --git a/mobile/openapi/doc/AssetResponseDto.md b/mobile/openapi/doc/AssetResponseDto.md index b122147ff1..929031a3a3 100644 Binary files a/mobile/openapi/doc/AssetResponseDto.md and b/mobile/openapi/doc/AssetResponseDto.md differ diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart index 02bb376cc2..078389c549 100644 Binary files a/mobile/openapi/lib/model/asset_response_dto.dart and b/mobile/openapi/lib/model/asset_response_dto.dart differ diff --git a/mobile/openapi/test/asset_response_dto_test.dart b/mobile/openapi/test/asset_response_dto_test.dart index f89eacf986..d0985f429b 100644 Binary files a/mobile/openapi/test/asset_response_dto_test.dart and b/mobile/openapi/test/asset_response_dto_test.dart differ diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 2856819c58..0aa62f43c5 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -5208,6 +5208,9 @@ "originalPath": { "type": "string" }, + "owner": { + "$ref": "#/components/schemas/UserResponseDto" + }, "ownerId": { "type": "string" }, @@ -5246,8 +5249,8 @@ "type", "id", "deviceAssetId", - "ownerId", "deviceId", + "ownerId", "originalPath", "originalFileName", "resized", diff --git a/server/src/domain/asset/response-dto/asset-response.dto.ts b/server/src/domain/asset/response-dto/asset-response.dto.ts index 8079d9fd18..41e7f20522 100644 --- a/server/src/domain/asset/response-dto/asset-response.dto.ts +++ b/server/src/domain/asset/response-dto/asset-response.dto.ts @@ -2,14 +2,16 @@ import { AssetEntity, AssetType } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; import { PersonResponseDto, mapFace } from '../../person/person.dto'; import { TagResponseDto, mapTag } from '../../tag'; +import { UserResponseDto, mapUser } from '../../user/response-dto/user-response.dto'; import { ExifResponseDto, mapExif } from './exif-response.dto'; import { SmartInfoResponseDto, mapSmartInfo } from './smart-info-response.dto'; export class AssetResponseDto { id!: string; deviceAssetId!: string; - ownerId!: string; deviceId!: string; + ownerId!: string; + owner?: UserResponseDto; @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType }) type!: AssetType; @@ -33,11 +35,12 @@ export class AssetResponseDto { checksum!: string; } -export function mapAsset(entity: AssetEntity): AssetResponseDto { +function _map(entity: AssetEntity, withExif: boolean): AssetResponseDto { return { id: entity.id, deviceAssetId: entity.deviceAssetId, ownerId: entity.ownerId, + owner: entity.owner ? mapUser(entity.owner) : undefined, deviceId: entity.deviceId, type: entity.type, originalPath: entity.originalPath, @@ -50,7 +53,7 @@ export function mapAsset(entity: AssetEntity): AssetResponseDto { isFavorite: entity.isFavorite, isArchived: entity.isArchived, duration: entity.duration ?? '0:00:00.00000', - exifInfo: entity.exifInfo ? mapExif(entity.exifInfo) : undefined, + exifInfo: withExif ? (entity.exifInfo ? mapExif(entity.exifInfo) : undefined) : undefined, smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined, livePhotoVideoId: entity.livePhotoVideoId, tags: entity.tags?.map(mapTag), @@ -59,30 +62,12 @@ export function mapAsset(entity: AssetEntity): AssetResponseDto { }; } +export function mapAsset(entity: AssetEntity): AssetResponseDto { + return _map(entity, true); +} + export function mapAssetWithoutExif(entity: AssetEntity): AssetResponseDto { - return { - id: entity.id, - deviceAssetId: entity.deviceAssetId, - ownerId: entity.ownerId, - deviceId: entity.deviceId, - type: entity.type, - originalPath: entity.originalPath, - originalFileName: entity.originalFileName, - resized: !!entity.resizePath, - thumbhash: entity.thumbhash?.toString('base64') || null, - fileCreatedAt: entity.fileCreatedAt, - fileModifiedAt: entity.fileModifiedAt, - updatedAt: entity.updatedAt, - isFavorite: entity.isFavorite, - isArchived: entity.isArchived, - duration: entity.duration ?? '0:00:00.00000', - exifInfo: undefined, - smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined, - livePhotoVideoId: entity.livePhotoVideoId, - tags: entity.tags?.map(mapTag), - people: entity.faces?.map(mapFace), - checksum: entity.checksum.toString('base64'), - }; + return _map(entity, false); } export class MemoryLaneResponseDto { diff --git a/server/src/immich/api-v1/asset/asset-repository.ts b/server/src/immich/api-v1/asset/asset-repository.ts index 0466b3668c..c666e88bdf 100644 --- a/server/src/immich/api-v1/asset/asset-repository.ts +++ b/server/src/immich/api-v1/asset/asset-repository.ts @@ -107,6 +107,7 @@ export class AssetRepository implements IAssetRepository { tags: true, sharedLinks: true, smartInfo: true, + owner: true, faces: { person: true, }, diff --git a/server/src/immich/api-v1/asset/asset.service.ts b/server/src/immich/api-v1/asset/asset.service.ts index 77b3f73e09..c6013e2f7e 100644 --- a/server/src/immich/api-v1/asset/asset.service.ts +++ b/server/src/immich/api-v1/asset/asset.service.ts @@ -199,6 +199,10 @@ export class AssetService { data.people = []; } + if (authUser.isPublicUser) { + delete data.owner; + } + return data; } diff --git a/server/test/fixtures/shared-link.stub.ts b/server/test/fixtures/shared-link.stub.ts index e85b000ce1..31a9459850 100644 --- a/server/test/fixtures/shared-link.stub.ts +++ b/server/test/fixtures/shared-link.stub.ts @@ -1,5 +1,5 @@ import { AlbumResponseDto, AssetResponseDto, ExifResponseDto, mapUser, SharedLinkResponseDto } from '@app/domain'; -import { AssetType, SharedLinkEntity, SharedLinkType } from '@app/infra/entities'; +import { AssetType, SharedLinkEntity, SharedLinkType, UserEntity } from '@app/infra/entities'; import { assetStub } from './asset.stub'; import { authStub } from './auth.stub'; import { userStub } from './user.stub'; @@ -158,7 +158,7 @@ export const sharedLinkStub = { assets: [ { id: 'id_1', - owner: userStub.user1, + owner: undefined as unknown as UserEntity, ownerId: 'user_id_1', deviceAssetId: 'device_asset_id_1', deviceId: 'device_id_1', diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index 2301c02e1c..35a769d283 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -645,6 +645,12 @@ export interface AssetResponseDto { * @memberof AssetResponseDto */ 'originalPath': string; + /** + * + * @type {UserResponseDto} + * @memberof AssetResponseDto + */ + 'owner'?: UserResponseDto; /** * * @type {string} diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index fc4526a465..53bff125fb 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -13,6 +13,7 @@ import { asByteUnitString } from '../../utils/byte-units'; import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte'; import { getAssetFilename } from '$lib/utils/asset-utils'; + import UserAvatar from '../shared-components/user-avatar.svelte'; export let asset: AssetResponseDto; export let albums: AlbumResponseDto[] = []; @@ -20,6 +21,8 @@ let textarea: HTMLTextAreaElement; let description: string; + $: isOwner = $page?.data?.user?.id === asset.ownerId; + $: { // Get latest description from server if (asset.id && !api.isSharedLink) { @@ -93,20 +96,17 @@
Info
-APPEARS IN
- {/if} +{#if asset.owner && !isOwner} +SHARED BY
++ {asset.owner.firstName} + {asset.owner.lastName} +
+APPEARS IN
{#each albums as album} @@ -326,5 +342,5 @@