{"id":49333316,"url":"https://github.com/mbombeck/healthlog","last_synced_at":"2026-06-07T00:01:57.173Z","repository":{"id":344628174,"uuid":"1162142501","full_name":"MBombeck/HealthLog","owner":"MBombeck","description":"Personal health-tracking PWA — weight, blood pressure, pulse, mood, medication compliance, Withings sync, doctor report PDF export. Dracula-themed, mobile-first, offline-capable.","archived":false,"fork":false,"pushed_at":"2026-04-26T16:02:59.000Z","size":2357,"stargazers_count":5,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-26T18:04:23.815Z","etag":null,"topics":["dracula-theme","health-tracking","nextjs","passkeys","prisma","pwa","self-hosted","shadcn-ui","typescript","webauthn"],"latest_commit_sha":null,"homepage":"https://healthlog.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MBombeck.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-19T23:09:12.000Z","updated_at":"2026-04-26T16:02:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"a7731071-fab5-4590-be6d-89c42194b674","html_url":"https://github.com/MBombeck/HealthLog","commit_stats":null,"previous_names":["mbombeck/healthlog"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/MBombeck/HealthLog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MBombeck%2FHealthLog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MBombeck%2FHealthLog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MBombeck%2FHealthLog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MBombeck%2FHealthLog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MBombeck","download_url":"https://codeload.github.com/MBombeck/HealthLog/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MBombeck%2FHealthLog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32317168,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"ssl_error","status_checked_at":"2026-04-26T23:26:25.802Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dracula-theme","health-tracking","nextjs","passkeys","prisma","pwa","self-hosted","shadcn-ui","typescript","webauthn"],"created_at":"2026-04-27T00:00:19.185Z","updated_at":"2026-05-16T03:23:19.145Z","avatar_url":"https://github.com/MBombeck.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"public/logo-readme.svg\" alt=\"HealthLog Logo\" width=\"120\" height=\"120\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eHealthLog\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eYour health data belongs to you. Track it on your own terms.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-AGPL--3.0-blue.svg\" alt=\"License: AGPL-3.0\" /\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/TypeScript-strict-3178C6?logo=typescript\u0026logoColor=white\" alt=\"TypeScript\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Next.js-16-black?logo=next.js\" alt=\"Next.js 16\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Self--Hosted-yes-success\" alt=\"Self-Hosted\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg\" alt=\"PRs Welcome\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  A self-hosted, privacy-first health tracking PWA.\u003cbr/\u003e\n  Weight, blood pressure, mood, medications, AI insights -- all under your control.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://healthlog.dev\"\u003eWebsite\u003c/a\u003e \u0026middot;\n  \u003ca href=\"https://demo.healthlog.dev\"\u003eLive Demo\u003c/a\u003e \u0026middot;\n  \u003ca href=\"https://docs.healthlog.dev\"\u003eDocumentation\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## What it is\n\nHealthLog is a self-hosted personal health tracker that runs from a single `docker compose up`. It covers the metrics most people actually log -- weight, blood pressure, pulse, body composition, blood glucose, sleep, mood, and medication compliance -- and brings them together in one dashboard with reference ranges from ESC/ESH 2018, ADA 2024, and NICE NG115. Withings devices sync automatically; multi-provider AI Insights (BYOK or local) explain what the numbers mean; a doctor-report PDF generates client-side. EN/DE end-to-end. AGPL-3.0.\n\nBuilt for people who want their health data on their own server -- whether that's a NAS, a homelab, or a small VPS -- and who don't want to hand it to a US cloud to read a 7-day weight trend. **Try the [live demo](https://demo.healthlog.dev)** to see what a working install looks like, or skip to [Quick Start](#quick-start) below.\n\n---\n\n## Why HealthLog?\n\nMost health apps lock your data behind proprietary clouds, push subscriptions, and sell your metrics to advertisers. HealthLog takes a different approach: your data stays on your server, encrypted at rest, accessible only to you.\n\n---\n\n## Key Features\n\n**Health Metrics** -- Track weight, blood pressure, pulse, body fat, sleep, steps, blood glucose (fasting/postprandial/random/bedtime, mg/dL ↔ mmol/L), total body water, bone mass, and pulse oximetry (SpO₂) with interactive trend charts, moving averages, and traffic-light ranges based on ESC/ESH 2018, ADA 2024, and consensus pulse-oximeter guidance (NICE NG115). Body-composition + SpO₂ metrics sync automatically from Withings Body+ scales and ScanWatch devices.\n\n**Custom Thresholds** -- Override the computed default ranges per metric with the targets your clinician set. Audit-logged. Doctor Report PDF prints both your target and the standard reference.\n\n**Customizable Dashboard** -- Show, hide, and drag-to-reorder every widget. Per-user layout with reset-to-defaults.\n\n**Mood Logging** -- 5-point scale with tags, notes, and trend analytics. Syncs automatically from moodLog.app via webhook.\n\n**Medication Compliance** -- Flexible scheduling with time windows, recurrence patterns, intake logging (take/skip/snooze), and compliance heatmaps. External API for iOS Shortcuts integration.\n\n**Withings Integration** -- OAuth2 device sync for scales, blood pressure monitors, and activity trackers with automatic deduplication.\n\n**Multi-Provider AI Insights** -- Pick OpenAI, Anthropic Claude, or any local OpenAI-compatible endpoint (Ollama, LM Studio, vLLM). BYOK or admin-shared key. Cached daily. Local endpoints keep all data on your network.\n\n**Doctor Report PDF Export** -- Generate professional medical reports client-side. Locale-aware (English/German), with vital sign summaries, BP/BMI/glucose classification, compliance rates, custom-threshold badges, and optional AI analysis.\n\n**Built-in Feedback** -- Send bug reports and feature requests from inside the app. Stored in your HealthLog database — no GitHub config required. Optional GitHub escalation for admins.\n\n**PWA with Offline Support** -- Installable on iOS and Android. Service worker with intelligent caching strategies for reliable offline access.\n\n**Multi-Channel Notifications** -- Telegram (with inline action buttons), ntfy (self-hostable), and Web Push. Medication reminders with late/missed escalation.\n\n**Gamification** -- 38+ persistent achievements across intake streaks, compliance milestones, and healthy metric streaks.\n\n**Internationalization** -- English (default) and German UI with 1500+ translation keys, guarded by a CI integrity test that fails the build on duplicate keys or drift between locales. Numbers, dates, units, and AI prompts all locale-aware via `useFormatters()`. Browser-based detection with per-user override.\n\n**Multi-tenant ready** _(v1.4)_ — Off-host AES-GCM-encrypted weekly backups to any S3-compatible bucket (R2, B2, MinIO, AWS), encryption-key versioning + zero-downtime rotation CLI, optional `HEALTHLOG_PROCESS_TYPE=web|worker|all` so HTTP and pg-boss can scale independently, and short-lived 24h access tokens with refresh-token rotation for native API clients. The browser cookie session is unchanged.\n\n**Test connection buttons** _(v1.4)_ — One-click probes for Withings, moodLog.app, Web Push, Glitchtip, and Umami in addition to the existing AI / Telegram / ntfy tests. Each one rate-limited, sanitised against SSRF redirects, and surfaces a localisable `errorCode` so the UI can render the failure in the user's language.\n\n---\n\n## Quick Start\n\n**3 minutes from `git clone` to a working install.** The bundled `docker-compose.yml` pulls a pre-built multi-arch image (`linux/amd64` + `linux/arm64`) from [GitHub Container Registry](https://github.com/MBombeck/HealthLog/pkgs/container/healthlog); no build step required for self-hosters. Contributors who want to test local changes can `docker compose up --build`.\n\n```bash\ngit clone https://github.com/MBombeck/HealthLog.git\ncd HealthLog\ncp .env.example .env\n```\n\nGenerate the four required secrets and paste them into `.env`:\n\n```bash\necho \"POSTGRES_PASSWORD=$(openssl rand -base64 24)\" \u003e\u003e .env\necho \"SESSION_SECRET=$(openssl rand -hex 32)\"       \u003e\u003e .env\necho \"ENCRYPTION_KEY=$(openssl rand -hex 32)\"       \u003e\u003e .env\necho \"API_TOKEN_HMAC_KEY=$(openssl rand -hex 32)\"   \u003e\u003e .env\n```\n\nThen bring the stack up:\n\n```bash\ndocker compose up -d\n```\n\nOpen **http://localhost:3000**. The first registered user becomes admin.\n\n\u003e Behind a reverse proxy (Caddy / Traefik / Nginx) for TLS, set `NEXT_PUBLIC_APP_URL` and `APP_URL` to your public URL in `.env` before starting. See [Self-Hosting → Reverse Proxy](https://docs.healthlog.dev/self-hosting/reverse-proxy/) for examples.\n\n---\n\n## Tech Stack\n\n| Layer         | Technology                                        |\n| ------------- | ------------------------------------------------- |\n| Framework     | Next.js 16 (App Router, React Server Components)  |\n| Language      | TypeScript (strict mode)                          |\n| Database      | PostgreSQL 16 + Prisma 7 (26 models)              |\n| Job Queue     | pg-boss 12 (reminders, insights, backups)         |\n| UI            | shadcn/ui, Tailwind CSS 4, Radix UI, Lucide Icons |\n| Charts        | Recharts 3                                        |\n| Data Fetching | TanStack Query 5                                  |\n| Forms         | React Hook Form 7 + Zod 4                         |\n| Auth          | SimpleWebAuthn 13, Argon2id                       |\n| Notifications | Telegram Bot API, ntfy, Web Push (VAPID)          |\n| PDF           | jsPDF (client-side generation)                    |\n| Testing       | Vitest 4                                          |\n| Deployment    | Docker (multi-stage Alpine)                       |\n\n---\n\n## Security and Privacy\n\nHealthLog is designed for people who take data ownership seriously.\n\n- **Self-hosted** -- Your data never leaves your server. No telemetry, no third-party tracking.\n- **AES-256-GCM encryption** -- All stored secrets (OAuth tokens, API keys, VAPID keys) are encrypted at rest.\n- **Passkey authentication** -- WebAuthn as primary auth with password fallback (Argon2id + zxcvbn strength validation).\n- **Server-side sessions** -- PostgreSQL-backed with 30-day sliding expiry, HttpOnly/SameSite=Strict cookies.\n- **Security headers** -- CSP with nonces, HSTS, X-Frame-Options DENY, Permissions-Policy, Referrer-Policy.\n- **Rate limiting** -- Sliding window on auth and API endpoints.\n- **HMAC-SHA256 API tokens** -- Bearer tokens are hashed before storage.\n- **Audit logging** -- All sensitive operations tracked with IP addresses.\n\n---\n\n## Environment Variables\n\n### Required\n\n| Variable             | Description                                                   |\n| -------------------- | ------------------------------------------------------------- |\n| `POSTGRES_PASSWORD`  | Password for the bundled Postgres service (Docker Compose)    |\n| `DATABASE_URL`       | PostgreSQL connection string (uses `POSTGRES_PASSWORD` above) |\n| `SESSION_SECRET`     | 64-char hex string for session signing                        |\n| `ENCRYPTION_KEY`     | 64-char hex string for AES-256-GCM                            |\n| `API_TOKEN_HMAC_KEY` | 64-char hex string for API token hashing                      |\n\n### Optional\n\n| Variable                  | Description                                          |\n| ------------------------- | ---------------------------------------------------- |\n| `NEXT_PUBLIC_APP_URL`     | Public-facing URL (default: `http://localhost:3000`) |\n| `WITHINGS_CLIENT_ID`      | Withings OAuth2 client ID                            |\n| `WITHINGS_CLIENT_SECRET`  | Withings OAuth2 client secret                        |\n| `WITHINGS_REDIRECT_URI`   | OAuth callback URL                                   |\n| `WITHINGS_WEBHOOK_SECRET` | Webhook URL hardening secret                         |\n| `TELEGRAM_WEBHOOK_SECRET` | Telegram bot webhook secret                          |\n\nTelegram bot token, ntfy settings, Web Push VAPID keys, Umami, and GlitchTip URLs are configured in the **Admin Panel** and stored encrypted in the database.\n\n---\n\n## Architecture\n\n```\nsrc/\n├── app/                    # Next.js App Router pages \u0026 API routes\n│   ├── api/                # REST API endpoints (100+ route files)\n│   ├── admin/              # Admin panel\n│   ├── auth/               # Login, register, passkey enrolment\n│   ├── medications/        # Medication management\n│   ├── measurements/       # Health metric entry\n│   ├── mood/               # Mood log\n│   ├── insights/           # AI-powered analytics\n│   ├── charts/             # Long-form charts\n│   ├── achievements/       # Gamification page\n│   ├── targets/            # Custom thresholds dashboard\n│   ├── notifications/      # Notification preferences matrix\n│   ├── bugreport/          # Built-in feedback / bug report\n│   ├── onboarding/         # 4-step guided setup\n│   └── settings/           # User preferences (8 top-level sections)\n├── components/\n│   ├── ui/                 # shadcn/ui primitives\n│   ├── layout/             # Shell (sidebar, topbar, bottom nav)\n│   ├── medications/        # Medication cards, forms, timeline\n│   ├── measurements/       # Measurement form, list\n│   ├── mood/               # Mood form, mood list\n│   ├── charts/             # Recharts wrappers\n│   ├── insights/           # AI insight status / advisor cards\n│   ├── gamification/       # Achievement cards, progress\n│   ├── monitoring/         # Umami / GlitchTip bootstrap\n│   └── settings/           # Settings-page section components\n├── lib/\n│   ├── auth/               # Session, audit, passkey logic\n│   ├── notifications/      # Dispatcher + channel senders\n│   ├── jobs/               # pg-boss worker (reminders, insights, backups)\n│   ├── analytics/          # Trend calculations, compliance, correlations\n│   ├── ai/                 # Multi-provider client (OpenAI, Anthropic, local)\n│   ├── insights/           # Insight pipeline + medical prompts\n│   ├── gamification/       # Achievement definitions\n│   ├── feedback/           # Built-in feedback + GitHub escalation\n│   ├── moodlog/            # moodLog.app webhook + sync\n│   ├── monitoring/         # Umami / GlitchTip server-side hooks\n│   ├── withings/           # OAuth client, sync service\n│   ├── logging/            # Wide Events: builder, context, transports\n│   ├── i18n/               # Translations context \u0026 config\n│   ├── validations/        # Shared Zod schemas\n│   ├── api-handler.ts      # apiHandler wrapper, requireAuth/requireAdmin\n│   ├── api-response.ts     # { data, error } envelope helpers\n│   ├── crypto.ts           # AES-256-GCM encrypt/decrypt\n│   ├── rate-limit.ts       # Sliding-window rate limiter\n│   ├── db.ts               # Prisma singleton\n│   └── doctor-report-pdf.ts # Client-side PDF generation\n├── hooks/                  # React hooks\n└── generated/prisma/       # Generated Prisma client\n```\n\n### Key Patterns\n\n- **RSC by default** -- `\"use client\"` only for interactive components\n- **API envelope** -- All responses follow `{ data, error }` shape via `apiSuccess()` / `apiError()` in `src/lib/api-response.ts`\n- **apiHandler wrapper** -- Every API route wraps its handler in `apiHandler()` (`src/lib/api-handler.ts`) for consistent error handling, Wide-Event structured logging, and `x-request-id` propagation\n- **Encrypted secrets** -- Withings tokens, API keys, VAPID keys, notification credentials\n- **Timezone-aware** -- `Europe/Berlin` for display, UTC in database\n- **Route protection** -- `proxy.ts` (Next.js 16's renamed middleware) checks session cookie, redirects unauthenticated requests\n- **Client-side PDF** -- Doctor reports generated in browser via jsPDF\n\n---\n\n## API Reference\n\nAll mutations require authentication via session cookie. External ingest uses Bearer tokens.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eHealth Data\u003c/strong\u003e\u003c/summary\u003e\n\n| Method   | Endpoint                | Description                               |\n| -------- | ----------------------- | ----------------------------------------- |\n| `GET`    | `/api/measurements`     | List measurements (paginated, filterable) |\n| `POST`   | `/api/measurements`     | Create measurement                        |\n| `DELETE` | `/api/measurements/:id` | Delete measurement                        |\n| `GET`    | `/api/analytics`        | Trend summaries (7d/30d)                  |\n| `GET`    | `/api/export`           | Export as CSV or JSON                     |\n| `POST`   | `/api/import`           | Import from JSON                          |\n| `POST`   | `/api/doctor-report`    | Aggregated data for PDF                   |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eMood\u003c/strong\u003e\u003c/summary\u003e\n\n| Method   | Endpoint                            | Description          |\n| -------- | ----------------------------------- | -------------------- |\n| `GET`    | `/api/mood-entries`                 | List mood entries    |\n| `POST`   | `/api/mood-entries`                 | Create mood entry    |\n| `DELETE` | `/api/mood-entries/:id`             | Delete mood entry    |\n| `GET`    | `/api/mood/analytics`               | Mood trend analytics |\n| `POST`   | `/api/integrations/moodlog/webhook` | moodLog.app webhook  |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eMedications\u003c/strong\u003e\u003c/summary\u003e\n\n| Method   | Endpoint                          | Description              |\n| -------- | --------------------------------- | ------------------------ |\n| `GET`    | `/api/medications`                | List all medications     |\n| `POST`   | `/api/medications`                | Create medication        |\n| `PUT`    | `/api/medications/:id`            | Update medication        |\n| `DELETE` | `/api/medications/:id`            | Delete medication        |\n| `POST`   | `/api/medications/:id/intake`     | Log intake event         |\n| `GET`    | `/api/medications/:id/compliance` | Compliance stats         |\n| `POST`   | `/api/ingest/medication`          | External intake (Bearer) |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eAuth and Integrations\u003c/strong\u003e\u003c/summary\u003e\n\n| Method  | Endpoint                         | Description                         |\n| ------- | -------------------------------- | ----------------------------------- |\n| `POST`  | `/api/auth/register`             | Create account                      |\n| `POST`  | `/api/auth/login`                | Password login                      |\n| `POST`  | `/api/auth/logout`               | Destroy session                     |\n| `GET`   | `/api/auth/me`                   | Current user profile + Gravatar URL |\n| `POST`  | `/api/auth/password`             | Change password                     |\n| `PATCH` | `/api/auth/profile`              | Update profile fields               |\n| `POST`  | `/api/auth/passkey/*`            | WebAuthn flows (4 sub-routes)       |\n| `GET`   | `/api/auth/passkeys`             | List enrolled passkeys              |\n| `GET`   | `/api/auth/codex/authorize`      | ChatGPT (codex) OAuth start         |\n| `GET`   | `/api/withings/connect`          | Initiate Withings OAuth             |\n| `POST`  | `/api/withings/sync`             | Trigger manual Withings sync        |\n| `POST`  | `/api/withings/webhook`          | Withings notification webhook       |\n| `POST`  | `/api/insights/generate`         | Regenerate AI insights              |\n| `GET`   | `/api/insights/comprehensive`    | Aggregated insight payload          |\n| `GET`   | `/api/gamification/achievements` | Achievement progress                |\n| `GET`   | `/api/health`                    | Docker health check                 |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003ePersonalization (Thresholds + Dashboard)\u003c/strong\u003e\u003c/summary\u003e\n\n| Method | Endpoint                   | Description                                    |\n| ------ | -------------------------- | ---------------------------------------------- |\n| `GET`  | `/api/user/thresholds`     | Read per-user threshold overrides              |\n| `PUT`  | `/api/user/thresholds`     | Upsert thresholds (rate-limited, audit-logged) |\n| `GET`  | `/api/insights/targets`    | Effective ranges (defaults + overrides merged) |\n| `GET`  | `/api/dashboard/widgets`   | Read dashboard layout                          |\n| `PUT`  | `/api/dashboard/widgets`   | Persist dashboard layout (show/hide/reorder)   |\n| `POST` | `/api/onboarding/complete` | Mark onboarding finished                       |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eFeedback + API Tokens\u003c/strong\u003e\u003c/summary\u003e\n\n| Method   | Endpoint                | Description                         |\n| -------- | ----------------------- | ----------------------------------- |\n| `POST`   | `/api/feedback`         | Submit in-app feedback              |\n| `GET`    | `/api/bugreport/status` | Check published GitHub issue state  |\n| `GET`    | `/api/tokens`           | List own API tokens                 |\n| `POST`   | `/api/tokens`           | Mint new API token (Bearer, hashed) |\n| `DELETE` | `/api/tokens/:id`       | Revoke API token                    |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eNotifications\u003c/strong\u003e\u003c/summary\u003e\n\n| Method | Endpoint                         | Description                         |\n| ------ | -------------------------------- | ----------------------------------- |\n| `GET`  | `/api/notifications/preferences` | Read per-channel × per-event matrix |\n| `PUT`  | `/api/notifications/preferences` | Update preferences                  |\n| `GET`  | `/api/notifications/vapid`       | VAPID public key for Web Push       |\n| `POST` | `/api/notifications/web-push`    | Register Web Push subscription      |\n| `POST` | `/api/telegram/webhook`          | Telegram bot inline-button callback |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eAdmin (admin role required)\u003c/strong\u003e\u003c/summary\u003e\n\n| Method | Endpoint                              | Description                           |\n| ------ | ------------------------------------- | ------------------------------------- |\n| `GET`  | `/api/admin/status`                   | System + integration status           |\n| `GET`  | `/api/admin/users`                    | List users                            |\n| `POST` | `/api/admin/users/:id/reset-password` | Force password reset                  |\n| `GET`  | `/api/admin/feedback`                 | All feedback / bug reports            |\n| `POST` | `/api/admin/feedback/:id/github`      | Escalate feedback to GitHub issue     |\n| `GET`  | `/api/admin/audit-log`                | Audit-log viewer                      |\n| `GET`  | `/api/admin/ai-settings`              | Read shared AI provider config        |\n| `PUT`  | `/api/admin/ai-settings`              | Update shared AI provider config      |\n| `GET`  | `/api/admin/tokens`                   | All issued API tokens                 |\n| `POST` | `/api/admin/notifications/test`       | Send test notification                |\n| `GET`  | `/api/admin/data`                     | Data backups + counts                 |\n| `GET`  | `/api/admin/status-overview`          | Aggregated status for the 6-card grid |\n| `POST` | `/api/admin/backup/test`              | Probe S3-compatible backup target     |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003ePublic + v1.4 additions\u003c/strong\u003e\u003c/summary\u003e\n\n| Method | Endpoint                           | Description                                     |\n| ------ | ---------------------------------- | ----------------------------------------------- |\n| `GET`  | `/api/version`                     | Public — version + build SHA + license, no auth |\n| `POST` | `/api/integrations/withings/test`  | Probe a saved Withings connection               |\n| `POST` | `/api/integrations/moodlog/test`   | Probe moodLog.app webhook reachability          |\n| `POST` | `/api/notifications/web-push/test` | Send a test Web Push to the current user        |\n| `POST` | `/api/monitoring/glitchtip/test`   | Trigger a Glitchtip ingest probe                |\n| `POST` | `/api/monitoring/umami/test`       | Verify Umami script + website ID resolve        |\n| `POST` | `/api/auth/refresh`                | Native client refresh-token rotation            |\n| `POST` | `/api/auth/refresh/revoke`         | Revoke an issued refresh token                  |\n\n\u003c/details\u003e\n\n---\n\n## Integrations\n\n| Integration     | Setup         | Purpose                                  |\n| --------------- | ------------- | ---------------------------------------- |\n| **Withings**    | Env vars      | Auto-sync weight, BP, and activity       |\n| **Telegram**    | Admin Panel   | Medication reminders with inline buttons |\n| **ntfy**        | User Settings | Self-hosted push notifications           |\n| **Web Push**    | Admin Panel   | Browser-native VAPID notifications       |\n| **OpenAI**      | User Settings | AI health insights (BYOK)                |\n| **moodLog.app** | User Settings | Mood tracking sync                       |\n| **Umami**       | Admin Panel   | Privacy-friendly analytics               |\n| **GlitchTip**   | Admin Panel   | Sentry-compatible error tracking         |\n\n---\n\n## Local Development\n\n```bash\n# Prerequisites: Node.js 20+, pnpm, PostgreSQL\n\ncp .env.example .env\npnpm install\npnpm db:generate\npnpm db:migrate\npnpm dev\n```\n\n### Scripts\n\n```bash\npnpm dev              # Development server\npnpm build            # Production build\npnpm lint             # ESLint\npnpm typecheck        # TypeScript strict check\npnpm test             # Vitest\npnpm format           # Prettier\n\npnpm db:generate      # Generate Prisma client\npnpm db:migrate       # Create \u0026 apply migration\npnpm db:migrate:deploy # Apply migrations (production)\npnpm db:studio        # Prisma Studio GUI\n```\n\n---\n\n## Deployment\n\nThe included `docker-compose.yml` runs the app and PostgreSQL. The entrypoint automatically waits for the database, runs pending migrations, and starts the server.\n\nThe app listens on port **3000**. Place it behind Nginx, Caddy, or Traefik for TLS termination. Works out of the box with [Coolify](https://coolify.io/).\n\n---\n\n## Contributing\n\nContributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n- Code style: `pnpm format \u0026\u0026 pnpm lint`\n- Type safety: `pnpm typecheck` must pass\n- Tests: `pnpm test`\n- UI language: English by default, German selectable per user. Code, comments, and commits: English.\n\n---\n\n## License\n\nHealthLog is licensed under the [GNU Affero General Public License v3.0](LICENSE).\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://healthlog.dev\"\u003ehealthlog.dev\u003c/a\u003e \u0026middot;\n  \u003ca href=\"https://demo.healthlog.dev\"\u003eLive Demo\u003c/a\u003e \u0026middot;\n  \u003ca href=\"https://docs.healthlog.dev\"\u003eDocs\u003c/a\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbombeck%2Fhealthlog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmbombeck%2Fhealthlog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbombeck%2Fhealthlog/lists"}