mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
Hotfix release closing #108 — the dashboard glucose tiles rendered the raw i18n key, and the bug-report screenshot upload promised a feature that never reached the published GitHub issue.
16 KiB
16 KiB
Changelog
[1.3.2] — 2026-04-28
Fixed
- Glucose tiles on the dashboard rendered the raw i18n key
targets.glucoseFastinginstead of the translated label (closes #108). Bothmessages/en.jsonandmessages/de.jsonhad two top-leveltargetsblocks;JSON.parsesilently keeps the last occurrence, and that block was missing the four glucose labels. The duplicate is now collapsed into a single block. A duplicatebugreport.bugTitlePlaceholdershadowed insidebugreportwas cleaned up too. Two further keys (dashboard.sleep,dashboard.steps) were missing from both locales and were falling back to hard-coded English; both are now translated. - New i18n locale-integrity test
(
src/lib/__tests__/i18n-locale-integrity.test.ts) fails the build on duplicate keys at any nesting depth and on key drift betweenenandde— closes the structural gap that let the duplicatetargetsblock ship in the first place.
Changed
- Screenshot upload removed from the bug-report form (also part
of #108). The form previously accepted an image attachment that
was stored in the local DB but never reached the published GitHub
issue — GitHub does not accept inline base64 data URIs in issue
bodies and offers no public API to attach images to an issue
programmatically. Rather than ship misleading
"a screenshot was attached" placeholder text in the resulting
issue, the upload UI is now gone and the placeholder note is no
longer added when promoting feedback. The
screenshotBase64column and the admin-side preview of previously-submitted screenshots are unchanged — existing reports keep their attachments locally. We plan to revisit a real screenshot pipeline in a future release.
[1.3.1] — 2026-04-27
Fixed
- Compose env-var validation no longer breaks Coolify-style deploys.
docker-compose.ymlpreviously used${VAR:?required}shell-parameter syntax for the four secrets andPOSTGRES_PASSWORD. Some hosting platforms (Coolify in particular) parse compose files eagerly and store the fallback error string ("POSTGRES_PASSWORD is required") as the literal env-var value whenPOSTGRES_PASSWORDwas unset, which then collided withDATABASE_URLand broke the running app withP1000: Authentication failed. Compose now uses plain${VAR}interpolation; validation moved intodocker-entrypoint.sh, which fails fast with a clear stderr message listing the unset variables.
Notes
If you upgraded an existing Compose stack from 1.2.x → 1.3.0 and hit
the POSTGRES_PASSWORD is required literal-as-value bug, set
POSTGRES_PASSWORD in your environment to whatever your existing
Postgres data volume was originally initialised with (likely
healthlog if you started from a pre-1.2.1 release), then redeploy.
Postgres only honours POSTGRES_PASSWORD on first volume init — the
existing user keeps the original password regardless of env changes.
[1.3.0] — 2026-04-27
Added — Body composition + targeted hardening
- Total body water and bone mass as measurement types (closes #89). New
enum values
TOTAL_BODY_WATERandBONE_MASS, both stored canonically in kilograms (matches Withings hydration/bone-mass measures and Health Connect'sTotalBodyWaterRecord/BoneMassRecord). Migration is purely additive (ALTER TYPE ... ADD VALUE) — safe to apply against any 1.2.x database without downtime. - Withings sync picks both up automatically. The Withings client now
maps measure type
77(hydration / water mass) and88(bone mass). Anyone with a Withings Body+ scale and an active connection will see the new metrics flowing in on the next sync without any extra config. - Doctor-report PDF includes both new types in the vital-signs table when data exists, with locale-aware labels in English and German.
- Dashboard widgets registered for both (default-invisible — opt in via Settings → Dashboard layout).
Security
- SSRF guard hardened (
isPublicUrl). The previous implementation usedparseIntwith permissive prefix checks likeh.startsWith("10.")which let010.0.0.1slip through — and worse, the WHATWG URL parser silently normalises010.0.0.1to8.0.0.1(octal interpretation), a real bypass on naive checks. The new guard adds a pre-URL leading-zero check on the raw input, a strict IPv4 parser, and proper IPv6 bracket / loopback / link-local handling. Now blocks127.0.0.0/8,0.0.0.0/8,10.0.0.0/8,169.254.0.0/16,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10(CGNAT),::1,fe80::/10,fc00::/7. Comprehensive regression tests included. - GitHub PAT redacted from logged error bodies. When a feedback
escalation to GitHub fails, the response body was being passed
verbatim into
getEvent()?.addWarning(...)— flowing to Loki. The body is now stripped of the configured token before logging. - Per-user threshold writes are rate-limited (30 writes / 5 min) to make audit-log enumeration unattractive. Audit-logging itself was already there.
Performance
- N+1 in
/api/insights/comprehensivefixed. The medication-compliance loop hit Postgres once per active medication. It now batches into a singlemedicationId IN (...)query and groups in memory. Latency improvement scales linearly with the user's active medication count.
Reliability
- pg-boss draining on SIGTERM/SIGINT.
docker stop, Coolify redeploys, and Kubernetes pod terminations now triggerboss.stop({ graceful: true, timeout: 30s })so in-flight handlers finish instead of being killed. Previously, pending handlers could be lost or replayed on restart. - CI now blocks on TypeScript strict checks.
continue-on-error: trueremoved for typecheck. Tests already were blocking. ESLint stays non-blocking for now — the existingsettings/page.tsxmonolith has long-standingreact-hooks/set-state-in-effectviolations whose proper fix lives in the 1.4.0 settings-split refactor; a blocking lint here would just gate every PR until that refactor lands. Required cleaning up twoanytypes inapi-handler.ts(justified inline — Next.js variadic handler signature constrained byPromise<Response>return).
Polish
- Bottom-nav touch targets sized to WCAG 2.5.5 minimum (44×44 CSS px). Visual icon stays at 20 px so the design doesn't shift.
- Phase-config dialog marks the decorative coloured dot
aria-hiddenbecause the redundant text label already conveys the phase to screen readers. No more meaninglessimagenode announcements. - Admin-status labels for "Web Push" and "Bug Report" now go through
t()(newadmin.integrationWebPush/admin.integrationBugReportkeys in en + de). They were the last hard-coded English strings on the admin page.
What's not in this release (tracked for later)
- Onboarding redesign (dashboard-first empty-state flow + persistent
Getting Started checklist) and the typed
apiClientwrapper that underpins it are tracked for a focused 1.4.0 cycle. The 1.2.1 patch already closed the acute symptoms of #87 (silent-failure toast + default schedule), so the redesign is now a proper UI investment rather than a bug-fix. - Withings sync is mapping both new measures, but a dedicated Bearer-auth ingest endpoint for external pipelines (n8n + Health Connect, requested in #89) ships in 1.4.0 alongside the API-token flow.
[1.2.1] — 2026-04-27
Fixed
- Onboarding: Medications added during onboarding are now actually persisted (closes #87). The wizard previously sent an empty
schedules: []array, the server-side validation rejected it with a 422, the client never checkedresponse.ok, and the user was redirected to the dashboard as if everything had worked. Onboarding now wraps each step intry/catch, surfaces failures via toast, and attaches a default reminder window (08:00–09:00 daily) so the medication actually persists. A hint under the medication list explains the default. - Docker setup (closes #88):
docker-compose.ymlnow usesports: "3000:3000"(wasexpose: "3000", which made the app unreachable from the host).POSTGRES_PASSWORDis a single env var that both the Postgres service andDATABASE_URLinterpolate, so they cannot drift apart..env.examplenow points at the in-container hostnamedb:5432(waslocalhost:5432, which never resolves inside the app container).
- Documentation:
package.jsonsynced to 1.2.0 (was lagging on 1.1.0).CLAUDE.mdandAGENTS.mdcorrected to 23 models (theFeedbackmodel added in v1.2 was missing from the count).README.mdQuick Start gives a realistic time estimate, generates the four secrets in one block straight into.env, and points reverse-proxy users at the docs.
Added — Tooling & Supply Chain
- Pre-built multi-arch images on GHCR:
.github/workflows/docker-publish.ymlnow buildslinux/amd64+linux/arm64images on every push tomainand on everyv*tag, publishing toghcr.io/mbombeck/healthlog. Self-hosters no longer need a build toolchain —docker compose pull && docker compose up -dis enough. The bundleddocker-compose.ymlreferences the published image with abuild:block as fallback for contributors. - Supply-chain attestations: each published image carries a SLSA build provenance statement and a Software Bill of Materials.
SECURITY.mddocuments how to verify them and how to pin a specific version. - Documentation single source of truth:
getting-started/installation.mdxis now the canonical setup guide (mirrors the bundleddocker-compose.yml);self-hosting/docker.mdxslimmed to image internals + ops notes only. The landing page's Quick Start terminal block now includes the secrets-generation step (was missing).
Notes
This is a patch release that closes the install/onboarding friction reported in #87 and #88. The bigger user-facing changes (additional measurement types like total body water and bone mass per #89, full onboarding redesign, typed API client) are tracked for 1.3.0.
[1.2.0] — 2026-04-18
Added — Personalization, Glucose & Multi-Provider AI
- Per-user custom thresholds: Override the computed default ranges (BP, BMI, glucose, pulse) with values from your clinician. Audit-logged with previous/new values and timestamps. Doctor Report PDF flags custom ranges and prints both your target and the standard guideline value.
- Blood glucose tracking: New metric with
fasting,postprandial,random, andbedtimecontexts. Display unit switch between mg/dL and mmol/L (lossless conversion). Context-aware classification per ADA 2024 / DGIM. Per-context charting on dashboard and Doctor Report PDF. - Dashboard customization: Show/hide and drag-to-reorder every dashboard widget. Per-user preference, reset-to-defaults button. Layout persists across the same user on the same device.
- Built-in feedback system: New in-app Send Feedback flow (Bug / Feature / Question / Other) with anonymized system info attachment. Stored in HealthLog's own database — no GitHub config required. Optional
Escalate to GitHubbutton for admins who configure a PAT. - Multi-provider AI insights: Provider abstraction extended with Anthropic Claude and local OpenAI-compatible endpoints (Ollama, LM Studio, vLLM, LiteLLM) alongside OpenAI. Per-user provider selection. Local endpoints keep all health data on your network.
- Locale-aware UI polish (English-first): Numbers, dates, glucose units, BP, weight, and BMI all formatted via
useFormatters()from the active locale. Doctor Report PDF and AI insight prompts now respect locale end-to-end (no hand-rolledIntl.*with fixed locales).
Changed
- Reference range computation extracted into a dedicated
src/lib/health/thresholds.tsmodule with computed defaults and override resolution. - AI provider routing reworked to dispatch by
providerfield on the user record; OpenAI remains the default for legacy users. - Dashboard route renders widgets from
UserDashboardLayoutmodel when present, otherwise falls back to the default order. - Doctor Report PDF: locale-aware headers, glucose section, custom-range badges.
Security
- GitHub PAT for feedback escalation stored AES-256-GCM encrypted in the database (never as env var).
- Local AI endpoint URLs validated against SSRF (no localhost/RFC1918 unless explicitly allowed by admin).
- Custom threshold writes rate-limited and audit-logged with IP.
[1.1.0] — 2026-04-06
Added — AI Insights Overhaul
- ChatGPT Proxy Integration: Insights now run through a local openai-oauth proxy using your ChatGPT subscription — no separate API billing required
- Admin AI Fallback: Admins can configure a global API key (OpenAI/OpenRouter) as fallback for users without their own connection
- Provider Abstraction: New
src/lib/ai/module with pluggable providers (Codex OAuth, Admin Key, None) and automatic failover - Medical Insight Prompts: 7 specialized prompts based on ESC/ESH 2023, WHO, DGE, and DEGAM guidelines
- Blood Pressure: ESC/ESH classification, morning risk ladder (J-HOP), pulse pressure, seasonal variation
- Weight: 5%/10% milestone recognition, plateau detection, body composition divergence
- Pulse: Fitness interpretation ladder, 80-100 bpm elevated-risk band, rate-pressure product
- BMI: Age-adjusted DEGAM classification for 65+
- Medication Compliance: Chronotherapy hints, mood-adherence risk prediction, 90-day tracking
- General Status: Cross-domain synthesis with cardiovascular risk stratification
- Mood: Bidirectional correlations with vitals and adherence
- Enriched Feature Extraction:
- Sleep duration and activity steps (previously ignored)
- Rate-Pressure Product (pulse × systolic BP, myocardial demand indicator)
- Body composition divergence flag (weight stable + body fat rising)
- Mood-adherence risk predictor
- Seasonal BP variation (winter vs summer, requires >180 days data)
- BP standard deviation (sdSys30/sdDia30) as variability risk marker
- Pulse pressure (arterial stiffness marker)
- 5 cross-metric Pearson correlations (weight↔BP, pulse↔BP, mood↔pulse, mood↔BP, mood↔weight)
- 90-day averages and all-time statistics for all metrics
- Historical comparison (current 7d vs previous 30d baseline)
- New UI Components:
InsightStatusCard: Compact per-metric status card with classification indicator and fade-in animationInsightAdvisorCard: Premium structured card with findings, correlations, recommendations (ready for integration)
- OAuth Routes:
/api/auth/codex/authorize,/callback,/disconnectfor ChatGPT connection - Admin AI Settings:
/api/admin/ai-settingsfor global API key management
Changed
- Insight prompts now use personal advisor tone ("dein Blutdruck") with positive-first pattern
- Reasoning scaffold in system prompt (What changed? → Why? → What to do?)
- Conditional correlation instructions (only mention when |r| > 0.4)
- InsightResult schema enriched with
insightType,primaryRecommendation,classificationLabel - BP target calculation now uses paired readings (both sys AND dia must be in range simultaneously)
- Medication streak tracking extended from 7-day to 30-day window
- CSP updated to allow
chatgpt.comfor OAuth flow
Security
- Rate limiting on all OAuth and admin endpoints
- PKCE (S256) + state parameter for OAuth CSRF protection
- Encrypted token storage at rest (AES-256-GCM)
- Error messages truncated to prevent upstream response body leaks
- Admin key preview shows last 4 chars of decrypted key (not ciphertext prefix)
prefers-reduced-motionsupport for insight card animations
Removed
openaiKeyEncryptedfield from User model (replaced by provider abstraction)- Direct OpenAI API calls from insight generators (now routed through provider)
- Legacy API key input in settings UI (replaced by ChatGPT connect button)