refactor(server): external domain fallback (#13506)

This commit is contained in:
Jason Rasmussen 2024-10-16 18:13:12 -04:00 committed by GitHub
parent 51d4899cd1
commit 8ac40a933a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 21 additions and 18 deletions

View File

@ -20,8 +20,6 @@ export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 });
export const ONE_HOUR = Duration.fromObject({ hours: 1 }); export const ONE_HOUR = Duration.fromObject({ hours: 1 });
export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || './upload'; 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'; export const citiesFile = 'cities500.txt';

View File

@ -1,5 +1,4 @@
import { BadRequestException, Injectable } from '@nestjs/common'; import { BadRequestException, Injectable } from '@nestjs/common';
import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants';
import { OnEvent } from 'src/decorators'; import { OnEvent } from 'src/decorators';
import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto';
import { AlbumEntity } from 'src/entities/album.entity'; 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 { BaseService } from 'src/services/base.service';
import { getAssetFiles } from 'src/utils/asset.util'; import { getAssetFiles } from 'src/utils/asset.util';
import { getFilenameExtension } from 'src/utils/file'; import { getFilenameExtension } from 'src/utils/file';
import { getExternalDomain } from 'src/utils/misc';
import { isEqualObject } from 'src/utils/object'; import { isEqualObject } from 'src/utils/object';
import { getPreferences } from 'src/utils/preferences'; import { getPreferences } from 'src/utils/preferences';
@ -128,10 +128,11 @@ export class NotificationService extends BaseService {
} }
const { server } = await this.getConfig({ withCache: false }); const { server } = await this.getConfig({ withCache: false });
const { port } = this.configRepository.getEnv();
const { html, text } = await this.notificationRepository.renderEmail({ const { html, text } = await this.notificationRepository.renderEmail({
template: EmailTemplate.TEST_EMAIL, template: EmailTemplate.TEST_EMAIL,
data: { data: {
baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, baseUrl: getExternalDomain(server, port),
displayName: user.name, displayName: user.name,
}, },
}); });
@ -156,10 +157,11 @@ export class NotificationService extends BaseService {
} }
const { server } = await this.getConfig({ withCache: true }); const { server } = await this.getConfig({ withCache: true });
const { port } = this.configRepository.getEnv();
const { html, text } = await this.notificationRepository.renderEmail({ const { html, text } = await this.notificationRepository.renderEmail({
template: EmailTemplate.WELCOME, template: EmailTemplate.WELCOME,
data: { data: {
baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, baseUrl: getExternalDomain(server, port),
displayName: user.name, displayName: user.name,
username: user.email, username: user.email,
password: tempPassword, password: tempPassword,
@ -199,10 +201,11 @@ export class NotificationService extends BaseService {
const attachment = await this.getAlbumThumbnailAttachment(album); const attachment = await this.getAlbumThumbnailAttachment(album);
const { server } = await this.getConfig({ withCache: false }); const { server } = await this.getConfig({ withCache: false });
const { port } = this.configRepository.getEnv();
const { html, text } = await this.notificationRepository.renderEmail({ const { html, text } = await this.notificationRepository.renderEmail({
template: EmailTemplate.ALBUM_INVITE, template: EmailTemplate.ALBUM_INVITE,
data: { data: {
baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, baseUrl: getExternalDomain(server, port),
albumId: album.id, albumId: album.id,
albumName: album.albumName, albumName: album.albumName,
senderName: album.owner.name, senderName: album.owner.name,
@ -241,6 +244,7 @@ export class NotificationService extends BaseService {
const attachment = await this.getAlbumThumbnailAttachment(album); const attachment = await this.getAlbumThumbnailAttachment(album);
const { server } = await this.getConfig({ withCache: false }); const { server } = await this.getConfig({ withCache: false });
const { port } = this.configRepository.getEnv();
for (const recipient of recipients) { for (const recipient of recipients) {
const user = await this.userRepository.get(recipient.id, { withDeleted: false }); 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({ const { html, text } = await this.notificationRepository.renderEmail({
template: EmailTemplate.ALBUM_UPDATE, template: EmailTemplate.ALBUM_UPDATE,
data: { data: {
baseUrl: server.externalDomain || DEFAULT_EXTERNAL_DOMAIN, baseUrl: getExternalDomain(server, port),
albumId: album.id, albumId: album.id,
albumName: album.albumName, albumName: album.albumName,
recipientName: recipient.name, recipientName: recipient.name,

View File

@ -1,6 +1,5 @@
import { BadRequestException, ForbiddenException, UnauthorizedException } from '@nestjs/common'; import { BadRequestException, ForbiddenException, UnauthorizedException } from '@nestjs/common';
import _ from 'lodash'; import _ from 'lodash';
import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants';
import { AssetIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { AssetIdErrorReason } from 'src/dtos/asset-ids.response.dto';
import { SharedLinkType } from 'src/enum'; import { SharedLinkType } from 'src/enum';
import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface';
@ -304,7 +303,7 @@ describe(SharedLinkService.name, () => {
sharedLinkMock.get.mockResolvedValue(sharedLinkStub.individual); sharedLinkMock.get.mockResolvedValue(sharedLinkStub.individual);
await expect(sut.getMetadataTags(authStub.adminSharedLink)).resolves.toEqual({ await expect(sut.getMetadataTags(authStub.adminSharedLink)).resolves.toEqual({
description: '1 shared photos & videos', 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', title: 'Public Share',
}); });
expect(sharedLinkMock.get).toHaveBeenCalled(); expect(sharedLinkMock.get).toHaveBeenCalled();
@ -314,7 +313,7 @@ describe(SharedLinkService.name, () => {
sharedLinkMock.get.mockResolvedValue({ ...sharedLinkStub.individual, album: undefined, assets: [] }); sharedLinkMock.get.mockResolvedValue({ ...sharedLinkStub.individual, album: undefined, assets: [] });
await expect(sut.getMetadataTags(authStub.adminSharedLink)).resolves.toEqual({ await expect(sut.getMetadataTags(authStub.adminSharedLink)).resolves.toEqual({
description: '0 shared photos & videos', description: '0 shared photos & videos',
imageUrl: `${DEFAULT_EXTERNAL_DOMAIN}/feature-panel.png`, imageUrl: `http://localhost:2283/feature-panel.png`,
title: 'Public Share', title: 'Public Share',
}); });
expect(sharedLinkMock.get).toHaveBeenCalled(); expect(sharedLinkMock.get).toHaveBeenCalled();

View File

@ -1,21 +1,20 @@
import { BadRequestException, ForbiddenException, Injectable, UnauthorizedException } from '@nestjs/common'; 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 { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto';
import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { import {
mapSharedLink,
mapSharedLinkWithoutMetadata,
SharedLinkCreateDto, SharedLinkCreateDto,
SharedLinkEditDto, SharedLinkEditDto,
SharedLinkPasswordDto, SharedLinkPasswordDto,
SharedLinkResponseDto, SharedLinkResponseDto,
mapSharedLink,
mapSharedLinkWithoutMetadata,
} from 'src/dtos/shared-link.dto'; } from 'src/dtos/shared-link.dto';
import { AssetEntity } from 'src/entities/asset.entity'; import { AssetEntity } from 'src/entities/asset.entity';
import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { SharedLinkEntity } from 'src/entities/shared-link.entity';
import { Permission, SharedLinkType } from 'src/enum'; import { Permission, SharedLinkType } from 'src/enum';
import { BaseService } from 'src/services/base.service'; import { BaseService } from 'src/services/base.service';
import { OpenGraphTags } from 'src/utils/misc'; import { getExternalDomain, OpenGraphTags } from 'src/utils/misc';
@Injectable() @Injectable()
export class SharedLinkService extends BaseService { export class SharedLinkService extends BaseService {
@ -177,6 +176,7 @@ export class SharedLinkService extends BaseService {
} }
const config = await this.getConfig({ withCache: true }); const config = await this.getConfig({ withCache: true });
const { port } = this.configRepository.getEnv();
const sharedLink = await this.findOrFail(auth.sharedLink.userId, auth.sharedLink.id); const sharedLink = await this.findOrFail(auth.sharedLink.userId, auth.sharedLink.id);
const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id; const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id;
const assetCount = sharedLink.assets.length > 0 ? sharedLink.assets.length : sharedLink.album?.assets.length || 0; const assetCount = sharedLink.assets.length > 0 ? sharedLink.assets.length : sharedLink.album?.assets.length || 0;
@ -187,7 +187,7 @@ export class SharedLinkService extends BaseService {
return { return {
title: sharedLink.album ? sharedLink.album.albumName : 'Public Share', title: sharedLink.album ? sharedLink.album.albumName : 'Public Share',
description: sharedLink.description || `${assetCount} shared photos & videos`, 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,
}; };
} }

View File

@ -16,6 +16,9 @@ import { ImmichCookie, ImmichHeader } from 'src/dtos/auth.dto';
import { MetadataKey } from 'src/enum'; import { MetadataKey } from 'src/enum';
import { ILoggerRepository } from 'src/interfaces/logger.interface'; 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 * @returns a list of strings representing the keys of the object in dot notation
*/ */

View File

@ -49,10 +49,9 @@ const envData: EnvData = {
noColor: false, noColor: false,
}; };
export const mockEnvData = (config: Partial<EnvData>) => ({ ...envData, ...config });
export const newConfigRepositoryMock = (): Mocked<IConfigRepository> => { export const newConfigRepositoryMock = (): Mocked<IConfigRepository> => {
return { return {
getEnv: vitest.fn().mockReturnValue(envData), getEnv: vitest.fn().mockReturnValue(mockEnvData({})),
}; };
}; };
export const mockEnvData = (config: Partial<EnvData>) => ({ ...envData, ...config });