From 990aff441bb52642dd6c1e66d652d993c5f3845e Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 12 Mar 2026 16:10:55 -0400 Subject: [PATCH] fix: add to shared link (#26886) --- .../src/repositories/shared-link.repository.ts | 16 +++++++++++++++- server/src/services/asset-media.service.ts | 9 +++++++++ server/src/services/shared-link.service.ts | 6 ++++++ .../share-page/individual-shared-viewer.svelte | 16 +++------------- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/server/src/repositories/shared-link.repository.ts b/server/src/repositories/shared-link.repository.ts index 48afcf7d9..bc81e75c8 100644 --- a/server/src/repositories/shared-link.repository.ts +++ b/server/src/repositories/shared-link.repository.ts @@ -4,7 +4,7 @@ import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; import _ from 'lodash'; import { InjectKysely } from 'nestjs-kysely'; import { Album, columns } from 'src/database'; -import { DummyValue, GenerateSql } from 'src/decorators'; +import { ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; import { SharedLinkType } from 'src/enum'; import { DB } from 'src/schema'; import { AssetExifTable } from 'src/schema/tables/asset-exif.table'; @@ -249,6 +249,20 @@ export class SharedLinkRepository { await this.db.deleteFrom('shared_link').where('shared_link.id', '=', id).execute(); } + @ChunkedArray({ paramIndex: 1 }) + async addAssets(id: string, assetIds: string[]) { + if (assetIds.length === 0) { + return []; + } + + return await this.db + .insertInto('shared_link_asset') + .values(assetIds.map((assetId) => ({ assetId, sharedLinkId: id }))) + .onConflict((oc) => oc.doNothing()) + .returning(['shared_link_asset.assetId']) + .execute(); + } + @GenerateSql({ params: [DummyValue.UUID] }) private getSharedLinks(id: string) { return this.db diff --git a/server/src/services/asset-media.service.ts b/server/src/services/asset-media.service.ts index 020bda4df..3c981ea61 100644 --- a/server/src/services/asset-media.service.ts +++ b/server/src/services/asset-media.service.ts @@ -151,6 +151,10 @@ export class AssetMediaService extends BaseService { } const asset = await this.create(auth.user.id, dto, file, sidecarFile); + if (auth.sharedLink) { + await this.sharedLinkRepository.addAssets(auth.sharedLink.id, [asset.id]); + } + await this.userRepository.updateUsage(auth.user.id, file.size); return { id: asset.id, status: AssetMediaStatus.CREATED }; @@ -341,6 +345,11 @@ export class AssetMediaService extends BaseService { this.logger.error(`Error locating duplicate for checksum constraint`); throw new InternalServerErrorException(); } + + if (auth.sharedLink) { + await this.sharedLinkRepository.addAssets(auth.sharedLink.id, [duplicateId]); + } + return { status: AssetMediaStatus.DUPLICATE, id: duplicateId }; } diff --git a/server/src/services/shared-link.service.ts b/server/src/services/shared-link.service.ts index b942c3232..26b15031e 100644 --- a/server/src/services/shared-link.service.ts +++ b/server/src/services/shared-link.service.ts @@ -150,6 +150,12 @@ export class SharedLinkService extends BaseService { } async addAssets(auth: AuthDto, id: string, dto: AssetIdsDto): Promise { + if (auth.sharedLink) { + this.logger.deprecate( + 'Assets uploaded using shared link authentication are now automatically added to the shared link during upload and in the next major release this endpoint will no longer accept shared link authentication', + ); + } + const sharedLink = await this.findOrFail(auth.user.id, id); if (sharedLink.type !== SharedLinkType.Individual) { diff --git a/web/src/lib/components/share-page/individual-shared-viewer.svelte b/web/src/lib/components/share-page/individual-shared-viewer.svelte index 94e00500f..64eb98bec 100644 --- a/web/src/lib/components/share-page/individual-shared-viewer.svelte +++ b/web/src/lib/components/share-page/individual-shared-viewer.svelte @@ -16,7 +16,7 @@ import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader'; import { handleError } from '$lib/utils/handle-error'; import { toTimelineAsset } from '$lib/utils/timeline-util'; - import { addSharedLinkAssets, getAssetInfo, type SharedLinkResponseDto } from '@immich/sdk'; + import { getAssetInfo, type SharedLinkResponseDto } from '@immich/sdk'; import { IconButton, Logo, toastManager } from '@immich/ui'; import { mdiArrowLeft, mdiDownload, mdiFileImagePlusOutline, mdiSelectAll } from '@mdi/js'; import { t } from 'svelte-i18n'; @@ -48,21 +48,11 @@ const handleUploadAssets = async (files: File[] = []) => { try { - let results: (string | undefined)[] = []; - results = await (!files || files.length === 0 || !Array.isArray(files) + await (!files || files.length === 0 || !Array.isArray(files) ? openFileUploadDialog() : fileUploadHandler({ files })); - const data = await addSharedLinkAssets({ - ...authManager.params, - id: sharedLink.id, - assetIdsDto: { - assetIds: results.filter((id) => !!id) as string[], - }, - }); - const added = data.filter((item) => item.success).length; - - toastManager.success($t('assets_added_count', { values: { count: added } })); + toastManager.success(); } catch (error) { handleError(error, $t('errors.unable_to_add_assets_to_shared_link')); }