From c000b3b42d6d3a19a55444a1ad7d51d51bd81073 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 16 Oct 2024 09:28:11 -0400 Subject: [PATCH] refactor(server): external domain fallback --- server/src/constants.ts | 2 -- server/src/services/notification.service.ts | 14 +++++++++----- server/src/services/shared-link.service.spec.ts | 5 ++--- server/src/services/shared-link.service.ts | 10 +++++----- server/src/utils/misc.ts | 3 +++ server/test/repositories/config.repository.mock.ts | 5 ++--- 6 files changed, 21 insertions(+), 18 deletions(-) diff --git a/server/src/constants.ts b/server/src/constants.ts index 5317d5e13c57a..eef9ffab0598c 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -20,8 +20,6 @@ export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 }); export const ONE_HOUR = Duration.fromObject({ hours: 1 }); export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || './upload'; -const HOST_SERVER_PORT = process.env.IMMICH_PORT || '2283'; -export const DEFAULT_EXTERNAL_DOMAIN = 'http://localhost:' + HOST_SERVER_PORT; export const citiesFile = 'cities500.txt'; diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index f6b338d79e716..122a09ee2ebd1 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -1,5 +1,4 @@ import { BadRequestException, Injectable } from '@nestjs/common'; -import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants'; import { OnEvent } from 'src/decorators'; import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; import { AlbumEntity } from 'src/entities/album.entity'; @@ -16,6 +15,7 @@ import { EmailImageAttachment, EmailTemplate } from 'src/interfaces/notification import { BaseService } from 'src/services/base.service'; import { getAssetFiles } from 'src/utils/asset.util'; import { getFilenameExtension } from 'src/utils/file'; +import { getExternalDomain } from 'src/utils/misc'; import { isEqualObject } from 'src/utils/object'; import { getPreferences } from 'src/utils/preferences'; @@ -128,10 +128,11 @@ export class NotificationService extends BaseService { } const { server } = await this.getConfig({ withCache: false }); + const { port } = this.configRepository.getEnv(); const { html, text } = await this.notificationRepository.renderEmail({ template: EmailTemplate.TEST_EMAIL, data: { - baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, + baseUrl: getExternalDomain(server, port), displayName: user.name, }, }); @@ -156,10 +157,11 @@ export class NotificationService extends BaseService { } const { server } = await this.getConfig({ withCache: true }); + const { port } = this.configRepository.getEnv(); const { html, text } = await this.notificationRepository.renderEmail({ template: EmailTemplate.WELCOME, data: { - baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, + baseUrl: getExternalDomain(server, port), displayName: user.name, username: user.email, password: tempPassword, @@ -199,10 +201,11 @@ export class NotificationService extends BaseService { const attachment = await this.getAlbumThumbnailAttachment(album); const { server } = await this.getConfig({ withCache: false }); + const { port } = this.configRepository.getEnv(); const { html, text } = await this.notificationRepository.renderEmail({ template: EmailTemplate.ALBUM_INVITE, data: { - baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, + baseUrl: getExternalDomain(server, port), albumId: album.id, albumName: album.albumName, senderName: album.owner.name, @@ -241,6 +244,7 @@ export class NotificationService extends BaseService { const attachment = await this.getAlbumThumbnailAttachment(album); const { server } = await this.getConfig({ withCache: false }); + const { port } = this.configRepository.getEnv(); for (const recipient of recipients) { const user = await this.userRepository.get(recipient.id, { withDeleted: false }); @@ -257,7 +261,7 @@ export class NotificationService extends BaseService { const { html, text } = await this.notificationRepository.renderEmail({ template: EmailTemplate.ALBUM_UPDATE, data: { - baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, + baseUrl: getExternalDomain(server, port), albumId: album.id, albumName: album.albumName, recipientName: recipient.name, diff --git a/server/src/services/shared-link.service.spec.ts b/server/src/services/shared-link.service.spec.ts index d0959f31b8f4a..6554421418dec 100644 --- a/server/src/services/shared-link.service.spec.ts +++ b/server/src/services/shared-link.service.spec.ts @@ -1,6 +1,5 @@ import { BadRequestException, ForbiddenException, UnauthorizedException } from '@nestjs/common'; import _ from 'lodash'; -import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants'; import { AssetIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { SharedLinkType } from 'src/enum'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface'; @@ -304,7 +303,7 @@ describe(SharedLinkService.name, () => { sharedLinkMock.get.mockResolvedValue(sharedLinkStub.individual); await expect(sut.getMetadataTags(authStub.adminSharedLink)).resolves.toEqual({ description: '1 shared photos & videos', - imageUrl: `${DEFAULT_EXTERNAL_DOMAIN}/api/assets/asset-id/thumbnail?key=LCtkaJX4R1O_9D-2lq0STzsPryoL1UdAbyb6Sna1xxmQCSuqU2J1ZUsqt6GR-yGm1s0`, + imageUrl: `http://localhost:2283/api/assets/asset-id/thumbnail?key=LCtkaJX4R1O_9D-2lq0STzsPryoL1UdAbyb6Sna1xxmQCSuqU2J1ZUsqt6GR-yGm1s0`, title: 'Public Share', }); expect(sharedLinkMock.get).toHaveBeenCalled(); @@ -314,7 +313,7 @@ describe(SharedLinkService.name, () => { sharedLinkMock.get.mockResolvedValue({ ...sharedLinkStub.individual, album: undefined, assets: [] }); await expect(sut.getMetadataTags(authStub.adminSharedLink)).resolves.toEqual({ description: '0 shared photos & videos', - imageUrl: `${DEFAULT_EXTERNAL_DOMAIN}/feature-panel.png`, + imageUrl: `http://localhost:2283/feature-panel.png`, title: 'Public Share', }); expect(sharedLinkMock.get).toHaveBeenCalled(); diff --git a/server/src/services/shared-link.service.ts b/server/src/services/shared-link.service.ts index a01a2f45a32c6..5ef140d26dbb5 100644 --- a/server/src/services/shared-link.service.ts +++ b/server/src/services/shared-link.service.ts @@ -1,21 +1,20 @@ import { BadRequestException, ForbiddenException, Injectable, UnauthorizedException } from '@nestjs/common'; -import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants'; import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { + mapSharedLink, + mapSharedLinkWithoutMetadata, SharedLinkCreateDto, SharedLinkEditDto, SharedLinkPasswordDto, SharedLinkResponseDto, - mapSharedLink, - mapSharedLinkWithoutMetadata, } from 'src/dtos/shared-link.dto'; import { AssetEntity } from 'src/entities/asset.entity'; import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { Permission, SharedLinkType } from 'src/enum'; import { BaseService } from 'src/services/base.service'; -import { OpenGraphTags } from 'src/utils/misc'; +import { getExternalDomain, OpenGraphTags } from 'src/utils/misc'; @Injectable() export class SharedLinkService extends BaseService { @@ -177,6 +176,7 @@ export class SharedLinkService extends BaseService { } const config = await this.getConfig({ withCache: true }); + const { port } = this.configRepository.getEnv(); const sharedLink = await this.findOrFail(auth.sharedLink.userId, auth.sharedLink.id); const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id; const assetCount = sharedLink.assets.length > 0 ? sharedLink.assets.length : sharedLink.album?.assets.length || 0; @@ -187,7 +187,7 @@ export class SharedLinkService extends BaseService { return { title: sharedLink.album ? sharedLink.album.albumName : 'Public Share', description: sharedLink.description || `${assetCount} shared photos & videos`, - imageUrl: new URL(imagePath, config.server.externalDomain || DEFAULT_EXTERNAL_DOMAIN).href, + imageUrl: new URL(imagePath, getExternalDomain(config.server, port)).href, }; } diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index 9bea4e9585c55..e191170e0dbe2 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -16,6 +16,9 @@ import { ImmichCookie, ImmichHeader } from 'src/dtos/auth.dto'; import { MetadataKey } from 'src/enum'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; +export const getExternalDomain = (server: SystemConfig['server'], port: number) => + server.externalDomain || `http://localhost:${port}`; + /** * @returns a list of strings representing the keys of the object in dot notation */ diff --git a/server/test/repositories/config.repository.mock.ts b/server/test/repositories/config.repository.mock.ts index 852868ee31554..8e71ba2dcaf54 100644 --- a/server/test/repositories/config.repository.mock.ts +++ b/server/test/repositories/config.repository.mock.ts @@ -49,10 +49,9 @@ const envData: EnvData = { noColor: false, }; +export const mockEnvData = (config: Partial) => ({ ...envData, ...config }); export const newConfigRepositoryMock = (): Mocked => { return { - getEnv: vitest.fn().mockReturnValue(envData), + getEnv: vitest.fn().mockReturnValue(mockEnvData({})), }; }; - -export const mockEnvData = (config: Partial) => ({ ...envData, ...config });