From d0383c06c35c0fa8c56f97ca310af04bef4cb295 Mon Sep 17 00:00:00 2001 From: jubnl Date: Sun, 19 Apr 2026 14:10:41 +0200 Subject: [PATCH 1/2] fix: skip FORCE_HTTPS redirect for /api/health endpoint Health probes (K8s, Docker, LB health checks) hit the endpoint over plain HTTP from inside the cluster/container. The catch-all HTTPS redirect was causing all probe types to fail whenever FORCE_HTTPS=true was set. Closes #735 --- server/src/app.ts | 1 + server/tests/integration/misc.test.ts | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/server/src/app.ts b/server/src/app.ts index c4788faa..24661ad7 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -112,6 +112,7 @@ export function createApp(): express.Application { if (shouldForceHttps) { app.use((req: Request, res: Response, next: NextFunction) => { + if (req.path === '/api/health') return next(); if (req.secure || req.headers['x-forwarded-proto'] === 'https') return next(); res.redirect(301, 'https://' + req.headers.host + req.url); }); diff --git a/server/tests/integration/misc.test.ts b/server/tests/integration/misc.test.ts index 3da8476b..fe0596a7 100644 --- a/server/tests/integration/misc.test.ts +++ b/server/tests/integration/misc.test.ts @@ -94,8 +94,22 @@ describe('Photo endpoint auth', () => { }); describe('Force HTTPS redirect', () => { - it('MISC-004 — FORCE_HTTPS redirect sends 301 for HTTP requests', async () => { + it('MISC-004 — FORCE_HTTPS redirect sends 301 for HTTP requests on non-health paths', async () => { // createApp() reads FORCE_HTTPS at call time, so we need a fresh app instance + process.env.FORCE_HTTPS = 'true'; + let httpsApp: Express; + try { + httpsApp = createApp(); + } finally { + delete process.env.FORCE_HTTPS; + } + const res = await request(httpsApp) + .get('/api/addons') + .set('X-Forwarded-Proto', 'http'); + expect(res.status).toBe(301); + }); + + it('MISC-008 — FORCE_HTTPS does not redirect /api/health (probes must reach it over HTTP)', async () => { process.env.FORCE_HTTPS = 'true'; let httpsApp: Express; try { @@ -106,7 +120,8 @@ describe('Force HTTPS redirect', () => { const res = await request(httpsApp) .get('/api/health') .set('X-Forwarded-Proto', 'http'); - expect(res.status).toBe(301); + expect(res.status).toBe(200); + expect(res.body.status).toBe('ok'); }); it('MISC-004 — no redirect when FORCE_HTTPS is not set', async () => { From e562d7a7ecdc0a0a2d1e69543d1b3bc5e592ffff Mon Sep 17 00:00:00 2001 From: jubnl Date: Sun, 19 Apr 2026 14:27:08 +0200 Subject: [PATCH 2/2] fix(test): initialize useCountUp to target immediately in jsdom to fix AdminPage stat test --- client/src/hooks/useCountUp.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/src/hooks/useCountUp.ts b/client/src/hooks/useCountUp.ts index 719502e3..43e3c324 100644 --- a/client/src/hooks/useCountUp.ts +++ b/client/src/hooks/useCountUp.ts @@ -1,15 +1,16 @@ import { useEffect, useRef, useState } from 'react' +const isTestEnv = typeof navigator !== 'undefined' && /jsdom/i.test(navigator.userAgent ?? '') + // Zählt beim Mount von 0 auf target hoch. Feste Dauer mit ease-out-quint. export function useCountUp(target: number, duration = 800): number { - const [value, setValue] = useState(0) + const [value, setValue] = useState(() => isTestEnv || target <= 0 ? target : 0) const startRef = useRef(null) const frameRef = useRef(null) useEffect(() => { const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches ?? false - const isJsdom = typeof navigator !== 'undefined' && /jsdom/i.test(navigator.userAgent ?? '') - if (reduced || isJsdom || target <= 0) { setValue(target); return } + if (reduced || isTestEnv || target <= 0) { setValue(target); return } startRef.current = null const step = (now: number) => {