diff --git a/.changeset/silly-toes-agree.md b/.changeset/silly-toes-agree.md new file mode 100644 index 0000000000..de1482b26c --- /dev/null +++ b/.changeset/silly-toes-agree.md @@ -0,0 +1,5 @@ +--- +"@hyperdx/app": patch +--- + +fix: next-runtime-env runtime env var injection fixed for images diff --git a/docker/hyperdx/Dockerfile b/docker/hyperdx/Dockerfile index 24f52b1c5e..1bdf22380f 100644 --- a/docker/hyperdx/Dockerfile +++ b/docker/hyperdx/Dockerfile @@ -129,7 +129,8 @@ COPY --chown=node:node --from=builder /app/packages/app/.next/standalone ./packa COPY --chown=node:node --from=builder /app/packages/app/.next/static ./packages/app/packages/app/.next/static COPY --chown=node:node --from=builder /app/packages/app/public ./packages/app/packages/app/public -# Set up start script +# Set up start scripts +COPY --chown=node:node --from=hyperdx ./refresh-env.js /etc/local/refresh-env.js COPY --chown=node:node --from=hyperdx ./entry.prod.sh /etc/local/entry.sh HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ @@ -197,6 +198,7 @@ ENV NODE_ENV=production # Set up App COPY --from=prod /app /app +COPY --from=hyperdx ./refresh-env.js /etc/local/refresh-env.js COPY --from=hyperdx ./entry.local.base.sh /etc/local/entry.base.sh # Copy VEX exception files for Docker Scout vulnerability suppression diff --git a/docker/hyperdx/entry.local.base.sh b/docker/hyperdx/entry.local.base.sh index 865cbc5d53..3b612f9451 100644 --- a/docker/hyperdx/entry.local.base.sh +++ b/docker/hyperdx/entry.local.base.sh @@ -66,6 +66,8 @@ echo "ClickHouse is ready!" # Start Otel Collector with entrypoint script for template rendering and log rotation /otel-entrypoint.sh /usr/local/bin/opampsupervisor > /var/log/otel-collector.log 2>&1 & +node /etc/local/refresh-env.js + # Start HyperDX app ./node_modules/.bin/concurrently \ "--kill-others-on-fail" \ diff --git a/docker/hyperdx/entry.prod.sh b/docker/hyperdx/entry.prod.sh index bc9cd2130c..6f9888fdf8 100644 --- a/docker/hyperdx/entry.prod.sh +++ b/docker/hyperdx/entry.prod.sh @@ -12,6 +12,8 @@ echo "" echo "Visit the HyperDX UI at $FRONTEND_URL" echo "" +node /etc/local/refresh-env.js + # Optionally include the dashboard provisioner task EXTRA_NAMES="" EXTRA_CMDS="" diff --git a/docker/hyperdx/refresh-env.js b/docker/hyperdx/refresh-env.js new file mode 100644 index 0000000000..47b7593808 --- /dev/null +++ b/docker/hyperdx/refresh-env.js @@ -0,0 +1,20 @@ +// Merge runtime NEXT_PUBLIC_* env vars into the existing __ENV.js written at +// build time by configureRuntimeEnv, so build-time values are preserved and +// runtime values (e.g. docker run -e) take precedence. +const fs = require('fs'); + +const path = './packages/app/packages/app/public/__ENV.js'; + +let existing = {}; +try { + const content = fs.readFileSync(path, 'utf8'); + const json = content.replace(/^window\.__ENV\s*=\s*/, '').replace(/;\s*$/, ''); + existing = JSON.parse(json); +} catch {} + +const runtime = Object.fromEntries( + Object.entries(process.env).filter(([k]) => k.startsWith('NEXT_PUBLIC_')) +); + +const merged = { ...existing, ...runtime }; +fs.writeFileSync(path, 'window.__ENV = ' + JSON.stringify(merged) + ';'); diff --git a/packages/app/pages/_document.tsx b/packages/app/pages/_document.tsx index 387f7e252d..47a9651c45 100644 --- a/packages/app/pages/_document.tsx +++ b/packages/app/pages/_document.tsx @@ -2,17 +2,25 @@ import { Head, Html, Main, NextScript } from 'next/document'; import { IS_CLICKHOUSE_BUILD } from '@/config'; import { ibmPlexMono, inter, roboto, robotoMono } from '@/fonts'; +import { themes } from '@/theme'; -// Get theme class for SSR - must match ThemeProvider's resolution -// This ensures CSS variables are applied during server-side rendering -// to prevent hydration mismatch with button styling -function getThemeClass(): string { - const envTheme = process.env.NEXT_PUBLIC_THEME; - // Default to hyperdx if not set or invalid - const themeName = - envTheme === 'hyperdx' || envTheme === 'clickstack' ? envTheme : 'hyperdx'; - return `theme-${themeName}`; -} +// Applied before React hydrates to prevent a flash of the wrong theme. +// Reads the runtime value from window.__ENV (set by __ENV.js) and swaps +// the theme class on so CSS variables are correct on first paint. +const validThemes = Object.keys(themes); +const themeClasses = validThemes.map(t => `theme-${t}`); +const THEME_INIT_SCRIPT = ` +(function () { + var theme = window.__ENV && window.__ENV.NEXT_PUBLIC_THEME; + var valid = ${JSON.stringify(validThemes)}; + if (valid.indexOf(theme) !== -1) { + var html = document.documentElement; + var remove = ${JSON.stringify(themeClasses)}; + for (var i = 0; i < remove.length; i++) html.classList.remove(remove[i]); + html.classList.add('theme-' + theme); + } +})(); +`; export default function Document() { const fontClasses = [ @@ -22,13 +30,12 @@ export default function Document() { roboto.variable, ].join(' '); - const themeClass = getThemeClass(); - return ( - + {/* eslint-disable-next-line @next/next/no-sync-scripts */}