From 78e9b2f3228370866827fa7b6c16806197d9cff8 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 17 Oct 2024 16:42:08 -0400 Subject: [PATCH] refactor(server): telemetry ports env --- server/src/interfaces/config.interface.ts | 15 +++++-- .../repositories/config.repository.spec.ts | 40 +++++++++++++++++++ server/src/repositories/config.repository.ts | 12 ++++++ server/src/workers/api.ts | 16 ++------ server/src/workers/microservices.ts | 6 +-- .../repositories/config.repository.mock.ts | 9 +++++ 6 files changed, 80 insertions(+), 18 deletions(-) diff --git a/server/src/interfaces/config.interface.ts b/server/src/interfaces/config.interface.ts index 10e9a86aefd25..9870b86d10d6c 100644 --- a/server/src/interfaces/config.interface.ts +++ b/server/src/interfaces/config.interface.ts @@ -29,6 +29,11 @@ export interface EnvData { thirdPartySupportUrl?: string; }; + bull: { + config: QueueOptions; + queues: RegisterQueueOptions[]; + }; + database: { url?: string; host: string; @@ -45,6 +50,10 @@ export interface EnvData { server: string; }; + network: { + trustedProxies: string[]; + }; + resourcePaths: { lockFile: string; geodata: { @@ -62,9 +71,9 @@ export interface EnvData { redis: RedisOptions; - bull: { - config: QueueOptions; - queues: RegisterQueueOptions[]; + telemetry: { + apiPort: number; + microservicesPort: number; }; storage: { diff --git a/server/src/repositories/config.repository.spec.ts b/server/src/repositories/config.repository.spec.ts index 78b512b2fd7b4..36b7b48062746 100644 --- a/server/src/repositories/config.repository.spec.ts +++ b/server/src/repositories/config.repository.spec.ts @@ -9,6 +9,9 @@ const resetEnv = () => { for (const env of [ 'IMMICH_WORKERS_INCLUDE', 'IMMICH_WORKERS_EXCLUDE', + 'IMMICH_TRUSTED_PROXIES', + 'IMMICH_API_METRICS_PORT', + 'IMMICH_MICROSERVICES_METRICS_PORT', 'DB_URL', 'DB_HOSTNAME', @@ -178,4 +181,41 @@ describe('getEnv', () => { expect(getEnv).toThrowError('Invalid worker(s) found: api,microservices,randomservice'); }); }); + + describe('network', () => { + it('should return default network options', () => { + const { network } = getEnv(); + expect(network).toEqual({ + trustedProxies: [], + }); + }); + + it('should parse trusted proxies', () => { + process.env.IMMICH_TRUSTED_PROXIES = '10.1.0.0,10.2.0.0, 169.254.0.0/16'; + const { network } = getEnv(); + expect(network).toEqual({ + trustedProxies: ['10.1.0.0', '10.2.0.0', '169.254.0.0/16'], + }); + }); + }); + + describe('telemetry', () => { + it('should return default ports', () => { + const { telemetry } = getEnv(); + expect(telemetry).toEqual({ + apiPort: 8081, + microservicesPort: 8082, + }); + }); + + it('should parse custom ports', () => { + process.env.IMMICH_API_METRICS_PORT = '2001'; + process.env.IMMICH_MICROSERVICES_METRICS_PORT = '2002'; + const { telemetry } = getEnv(); + expect(telemetry).toEqual({ + apiPort: 2001, + microservicesPort: 2002, + }); + }); + }); }); diff --git a/server/src/repositories/config.repository.ts b/server/src/repositories/config.repository.ts index 585e719e3af0a..8b511fba5ae2c 100644 --- a/server/src/repositories/config.repository.ts +++ b/server/src/repositories/config.repository.ts @@ -117,6 +117,13 @@ const getEnv = (): EnvData => { licensePublicKey: isProd ? productionKeys : stagingKeys, + network: { + trustedProxies: (process.env.IMMICH_TRUSTED_PROXIES ?? '') + .split(',') + .map((value) => value.trim()) + .filter(Boolean), + }, + redis: redisConfig, resourcePaths: { @@ -138,6 +145,11 @@ const getEnv = (): EnvData => { ignoreMountCheckErrors: process.env.IMMICH_IGNORE_MOUNT_CHECK_ERRORS === 'true', }, + telemetry: { + apiPort: Number(process.env.IMMICH_API_METRICS_PORT || '') || 8081, + microservicesPort: Number(process.env.IMMICH_MICROSERVICES_METRICS_PORT || '') || 8082, + }, + workers, noColor: !!process.env.NO_COLOR, diff --git a/server/src/workers/api.ts b/server/src/workers/api.ts index 4a7755fa1e860..b369b56953d2a 100644 --- a/server/src/workers/api.ts +++ b/server/src/workers/api.ts @@ -10,25 +10,17 @@ import { ImmichEnvironment } from 'src/enum'; import { IConfigRepository } from 'src/interfaces/config.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { WebSocketAdapter } from 'src/middleware/websocket.adapter'; +import { ConfigRepository } from 'src/repositories/config.repository'; import { ApiService } from 'src/services/api.service'; import { isStartUpError } from 'src/services/storage.service'; import { otelStart } from 'src/utils/instrumentation'; import { useSwagger } from 'src/utils/misc'; -function parseTrustedProxy(input?: string) { - if (!input) { - return []; - } - // Split on ',' char to allow multiple IPs - return input.split(','); -} - async function bootstrap() { process.title = 'immich-api'; - const otelPort = Number.parseInt(process.env.IMMICH_API_METRICS_PORT ?? '8081'); - const trustedProxies = parseTrustedProxy(process.env.IMMICH_TRUSTED_PROXIES ?? ''); - otelStart(otelPort); + const { telemetry, network } = new ConfigRepository().getEnv(); + otelStart(telemetry.apiPort); const app = await NestFactory.create(ApiModule, { bufferLogs: true }); const logger = await app.resolve(ILoggerRepository); @@ -39,7 +31,7 @@ async function bootstrap() { logger.setContext('Bootstrap'); app.useLogger(logger); - app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal', ...trustedProxies]); + app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal', ...network.trustedProxies]); app.set('etag', 'strong'); app.use(cookieParser()); app.use(json({ limit: '10mb' })); diff --git a/server/src/workers/microservices.ts b/server/src/workers/microservices.ts index 3cb478057ccd5..7b60fb8db600c 100644 --- a/server/src/workers/microservices.ts +++ b/server/src/workers/microservices.ts @@ -5,13 +5,13 @@ import { serverVersion } from 'src/constants'; import { IConfigRepository } from 'src/interfaces/config.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { WebSocketAdapter } from 'src/middleware/websocket.adapter'; +import { ConfigRepository } from 'src/repositories/config.repository'; import { isStartUpError } from 'src/services/storage.service'; import { otelStart } from 'src/utils/instrumentation'; export async function bootstrap() { - const otelPort = Number.parseInt(process.env.IMMICH_MICROSERVICES_METRICS_PORT ?? '8082'); - - otelStart(otelPort); + const { telemetry } = new ConfigRepository().getEnv(); + otelStart(telemetry.microservicesPort); const app = await NestFactory.create(MicroservicesModule, { bufferLogs: true }); const logger = await app.resolve(ILoggerRepository); diff --git a/server/test/repositories/config.repository.mock.ts b/server/test/repositories/config.repository.mock.ts index 65e419fe3620d..c7da917cc4565 100644 --- a/server/test/repositories/config.repository.mock.ts +++ b/server/test/repositories/config.repository.mock.ts @@ -31,6 +31,10 @@ const envData: EnvData = { server: 'server-public-key', }, + network: { + trustedProxies: [], + }, + redis: { host: 'redis', port: 6379, @@ -56,6 +60,11 @@ const envData: EnvData = { ignoreMountCheckErrors: false, }, + telemetry: { + apiPort: 8081, + microservicesPort: 8082, + }, + workers: [ImmichWorker.API, ImmichWorker.MICROSERVICES], noColor: false,