From e8a6525082b98baba352c2ce29017c4a7cbaa25e Mon Sep 17 00:00:00 2001 From: Daniel Heene Date: Fri, 9 Oct 2026 11:55:25 +0200 Subject: [PATCH] fix(runtimeConfig): filter localhost URLs from external service config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Localhost values in .env.test are format-valid placeholders — treating them as real URLs causes consumers (UmamiWidget, ServiceStatus) to attempt network connections to addresses that don't exist in test and E2E environments, filling logs with ECONNREFUSED noise. readExternalUrl() now returns undefined for any localhost/127.0.0.1/::1 URL, so those consumers skip their network calls without needing per-consumer guards. --- src/lib/runtimeConfig/index.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/lib/runtimeConfig/index.ts b/src/lib/runtimeConfig/index.ts index e95a9b44..0ae7200a 100644 --- a/src/lib/runtimeConfig/index.ts +++ b/src/lib/runtimeConfig/index.ts @@ -25,12 +25,26 @@ declare global { // Dynamic lookup on purpose: `process.env[name]` is never inlined at build time. const readEnv = (name: string): string | undefined => process.env[name] || undefined +// Localhost URLs in env files are format-valid placeholders (e.g. .env.test). +// Treat them as unset so consumers don't attempt real network calls to them. +const readExternalUrl = (name: string): string | undefined => { + const value = readEnv(name) + if (!value) return undefined + try { + const { hostname } = new URL(value) + if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1') return undefined + } catch { + return undefined + } + return value +} + export const readRuntimeConfigFromEnv = (): RuntimeConfig => ({ serverUrl: readEnv('SERVER_URL'), - statusPageUrl: readEnv('STATUS_PAGE_URL'), - umamiUrl: readEnv('NEXT_PUBLIC_UMAMI_URL'), + statusPageUrl: readExternalUrl('STATUS_PAGE_URL'), + umamiUrl: readExternalUrl('NEXT_PUBLIC_UMAMI_URL'), umamiSiteId: readEnv('NEXT_PUBLIC_UMAMI_SITE_ID'), - iconifyApi: readEnv('NEXT_PUBLIC_ICONIFY_API'), + iconifyApi: readExternalUrl('NEXT_PUBLIC_ICONIFY_API'), sentryDsn: readEnv('SENTRY_DSN'), sentryEnvironment: readEnv('SENTRY_ENVIRONMENT'), })