Files
HealthLog/README.md
T
Marc-André Bombeck 38309932f3 Initial release v1.0.0
HealthLog — personal health-tracking PWA with weight, blood pressure,
pulse, mood tracking, medication compliance, Withings integration,
doctor report PDF export, and Dracula-themed UI.
2026-03-10 20:14:33 +01:00

336 lines
12 KiB
Markdown

<p align="center">
<img src="public/logo-readme.svg" alt="HealthLog Logo" width="120" height="120" />
</p>
<h1 align="center">HealthLog</h1>
<p align="center">
<strong>Your health data belongs to you. Track it on your own terms.</strong>
</p>
<p align="center">
<a href="LICENSE"><img src="https://img.shields.io/badge/License-AGPL--3.0-blue.svg" alt="License: AGPL-3.0" /></a>
<img src="https://img.shields.io/badge/TypeScript-strict-3178C6?logo=typescript&logoColor=white" alt="TypeScript" />
<img src="https://img.shields.io/badge/Next.js-16-black?logo=next.js" alt="Next.js 16" />
<img src="https://img.shields.io/badge/Self--Hosted-yes-success" alt="Self-Hosted" />
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome" />
</p>
<p align="center">
A self-hosted, privacy-first health tracking PWA.<br/>
Weight, blood pressure, mood, medications, AI insights -- all under your control.
</p>
<p align="center">
<a href="https://healthlog.dev">Website</a> &middot;
<a href="https://demo.healthlog.dev">Live Demo</a> &middot;
<a href="https://docs.healthlog.dev">Documentation</a>
</p>
---
## Why HealthLog?
Most 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.
---
## Key Features
**Health Metrics** -- Track weight, blood pressure, pulse, body fat, sleep, and steps with interactive trend charts, moving averages, and traffic-light ranges based on ESC/ESH 2018 guidelines.
**Mood Logging** -- 5-point scale with tags, notes, and trend analytics. Syncs automatically from moodLog.app via webhook.
**Medication Compliance** -- Flexible scheduling with time windows, recurrence patterns, intake logging (take/skip/snooze), and compliance heatmaps. External API for iOS Shortcuts integration.
**Withings Integration** -- OAuth2 device sync for scales, blood pressure monitors, and activity trackers with automatic deduplication.
**AI-Powered Insights** -- Bring your own OpenAI API key. Personalized analysis across blood pressure, weight, pulse, mood, BMI, and medication adherence. Cached daily to minimize API usage.
**Doctor Report PDF Export** -- Generate professional medical reports client-side. European format with vital sign summaries, BP/BMI classification, compliance rates, and optional AI analysis.
**PWA with Offline Support** -- Installable on iOS and Android. Service worker with intelligent caching strategies for reliable offline access.
**Multi-Channel Notifications** -- Telegram (with inline action buttons), ntfy (self-hostable), and Web Push. Medication reminders with late/missed escalation.
**Gamification** -- 30+ persistent achievements across intake streaks, compliance milestones, and healthy metric streaks.
**Internationalization** -- Full German and English UI with 1000+ translation keys. Browser-based locale detection with per-user override.
---
## Quick Start
```bash
git clone https://github.com/MBombeck/HealthLog.git
cd HealthLog
cp .env.example .env
```
Generate the required secrets:
```bash
openssl rand -hex 32 # SESSION_SECRET
openssl rand -hex 32 # ENCRYPTION_KEY
openssl rand -hex 32 # API_TOKEN_HMAC_KEY
```
Add them to `.env`, then:
```bash
docker compose up -d
```
Open **http://localhost:3000**. The first registered user becomes admin.
---
## Tech Stack
| Layer | Technology |
|-------|-----------|
| Framework | Next.js 16 (App Router, React Server Components) |
| Language | TypeScript (strict mode) |
| Database | PostgreSQL 16 + Prisma 7 (22 models) |
| Job Queue | pg-boss 12 (reminders, insights, backups) |
| UI | shadcn/ui, Tailwind CSS 4, Radix UI, Lucide Icons |
| Charts | Recharts 3 |
| Data Fetching | TanStack Query 5 |
| Forms | React Hook Form 7 + Zod 4 |
| Auth | SimpleWebAuthn 13, Argon2id |
| Notifications | Telegram Bot API, ntfy, Web Push (VAPID) |
| PDF | jsPDF (client-side generation) |
| Testing | Vitest 4 |
| Deployment | Docker (multi-stage Alpine) |
---
## Security and Privacy
HealthLog is designed for people who take data ownership seriously.
- **Self-hosted** -- Your data never leaves your server. No telemetry, no third-party tracking.
- **AES-256-GCM encryption** -- All stored secrets (OAuth tokens, API keys, VAPID keys) are encrypted at rest.
- **Passkey authentication** -- WebAuthn as primary auth with password fallback (Argon2id + zxcvbn strength validation).
- **Server-side sessions** -- PostgreSQL-backed with 30-day sliding expiry, HttpOnly/SameSite=Strict cookies.
- **Security headers** -- CSP with nonces, HSTS, X-Frame-Options DENY, Permissions-Policy, Referrer-Policy.
- **Rate limiting** -- Sliding window on auth and API endpoints.
- **HMAC-SHA256 API tokens** -- Bearer tokens are hashed before storage.
- **Audit logging** -- All sensitive operations tracked with IP addresses.
---
## Environment Variables
### Required
| Variable | Description |
|----------|-------------|
| `DATABASE_URL` | PostgreSQL connection string |
| `SESSION_SECRET` | 64-char hex string for session signing |
| `ENCRYPTION_KEY` | 64-char hex string for AES-256-GCM |
| `API_TOKEN_HMAC_KEY` | 64-char hex string for API token hashing |
### Optional
| Variable | Description |
|----------|-------------|
| `NEXT_PUBLIC_APP_URL` | Public-facing URL (default: `http://localhost:3000`) |
| `WITHINGS_CLIENT_ID` | Withings OAuth2 client ID |
| `WITHINGS_CLIENT_SECRET` | Withings OAuth2 client secret |
| `WITHINGS_REDIRECT_URI` | OAuth callback URL |
| `WITHINGS_WEBHOOK_SECRET` | Webhook URL hardening secret |
| `TELEGRAM_WEBHOOK_SECRET` | Telegram bot webhook secret |
Telegram bot token, ntfy settings, Web Push VAPID keys, Umami, and GlitchTip URLs are configured in the **Admin Panel** and stored encrypted in the database.
---
## Architecture
```
src/
├── app/ # Next.js App Router pages & API routes
│ ├── api/ # REST API endpoints (~60 route files)
│ ├── admin/ # Admin panel
│ ├── medications/ # Medication management
│ ├── measurements/ # Health metric entry
│ ├── insights/ # AI-powered analytics
│ ├── achievements/ # Gamification page
│ ├── onboarding/ # 4-step guided setup
│ └── settings/ # User preferences
├── components/
│ ├── ui/ # shadcn/ui primitives
│ ├── layout/ # Shell (sidebar, topbar, bottom nav)
│ ├── medications/ # Medication cards, forms, timeline
│ ├── measurements/ # Measurement form, list
│ ├── mood/ # Mood form, mood list
│ └── charts/ # Recharts wrappers
├── lib/
│ ├── auth/ # Session, audit, passkey logic
│ ├── notifications/ # Dispatcher + channel senders
│ ├── jobs/ # pg-boss worker
│ ├── analytics/ # Trend calculations, compliance
│ ├── withings/ # OAuth client, sync service
│ ├── i18n/ # Translations context & config
│ ├── validations/ # Shared Zod schemas
│ ├── crypto.ts # AES-256-GCM encrypt/decrypt
│ ├── db.ts # Prisma singleton
│ └── doctor-report-pdf.ts # Client-side PDF generation
├── hooks/ # React hooks
└── generated/prisma/ # Generated Prisma client
```
### Key Patterns
- **RSC by default** -- `"use client"` only for interactive components
- **API envelope** -- All responses follow `{ data, error, meta }` shape
- **Encrypted secrets** -- Withings tokens, API keys, VAPID keys, notification credentials
- **Timezone-aware** -- `Europe/Berlin` for display, UTC in database
- **Route protection** -- `proxy.ts` checks session cookie, redirects unauthenticated requests
- **Client-side PDF** -- Doctor reports generated in browser via jsPDF
---
## API Reference
All mutations require authentication via session cookie. External ingest uses Bearer tokens.
<details>
<summary><strong>Health Data</strong></summary>
| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/api/measurements` | List measurements (paginated, filterable) |
| `POST` | `/api/measurements` | Create measurement |
| `DELETE` | `/api/measurements/:id` | Delete measurement |
| `GET` | `/api/analytics` | Trend summaries (7d/30d) |
| `GET` | `/api/export` | Export as CSV or JSON |
| `POST` | `/api/import` | Import from JSON |
| `POST` | `/api/doctor-report` | Aggregated data for PDF |
</details>
<details>
<summary><strong>Mood</strong></summary>
| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/api/mood-entries` | List mood entries |
| `POST` | `/api/mood-entries` | Create mood entry |
| `DELETE` | `/api/mood-entries/:id` | Delete mood entry |
| `GET` | `/api/mood/analytics` | Mood trend analytics |
| `POST` | `/api/integrations/moodlog/webhook` | moodLog.app webhook |
</details>
<details>
<summary><strong>Medications</strong></summary>
| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/api/medications` | List all medications |
| `POST` | `/api/medications` | Create medication |
| `PUT` | `/api/medications/:id` | Update medication |
| `DELETE` | `/api/medications/:id` | Delete medication |
| `POST` | `/api/medications/:id/intake` | Log intake event |
| `GET` | `/api/medications/:id/compliance` | Compliance stats |
| `POST` | `/api/ingest/medication` | External intake (Bearer) |
</details>
<details>
<summary><strong>Auth and Integrations</strong></summary>
| Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/api/auth/register` | Create account |
| `POST` | `/api/auth/login` | Password login |
| `POST` | `/api/auth/logout` | Destroy session |
| `GET` | `/api/auth/me` | Current user profile |
| `POST` | `/api/auth/passkey/*` | WebAuthn flows |
| `GET` | `/api/withings/connect` | Initiate Withings OAuth |
| `POST` | `/api/insights/generate` | Regenerate AI insights |
| `GET` | `/api/gamification/achievements` | Achievement progress |
| `GET` | `/api/health` | Docker health check |
</details>
---
## Integrations
| Integration | Setup | Purpose |
|-------------|-------|---------|
| **Withings** | Env vars | Auto-sync weight, BP, and activity |
| **Telegram** | Admin Panel | Medication reminders with inline buttons |
| **ntfy** | User Settings | Self-hosted push notifications |
| **Web Push** | Admin Panel | Browser-native VAPID notifications |
| **OpenAI** | User Settings | AI health insights (BYOK) |
| **moodLog.app** | User Settings | Mood tracking sync |
| **Umami** | Admin Panel | Privacy-friendly analytics |
| **GlitchTip** | Admin Panel | Sentry-compatible error tracking |
---
## Local Development
```bash
# Prerequisites: Node.js 20+, pnpm, PostgreSQL
cp .env.example .env
pnpm install
pnpm db:generate
pnpm db:migrate
pnpm dev
```
### Scripts
```bash
pnpm dev # Development server
pnpm build # Production build
pnpm lint # ESLint
pnpm typecheck # TypeScript strict check
pnpm test # Vitest
pnpm format # Prettier
pnpm db:generate # Generate Prisma client
pnpm db:migrate # Create & apply migration
pnpm db:migrate:deploy # Apply migrations (production)
pnpm db:studio # Prisma Studio GUI
```
---
## Deployment
The included `docker-compose.yml` runs the app and PostgreSQL. The entrypoint automatically waits for the database, runs pending migrations, and starts the server.
The 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/).
---
## Contributing
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
- Code style: `pnpm format && pnpm lint`
- Type safety: `pnpm typecheck` must pass
- Tests: `pnpm test`
- UI language: German. Code, comments, and commits: English.
---
## License
HealthLog is licensed under the [GNU Affero General Public License v3.0](LICENSE).
---
<p align="center">
<a href="https://healthlog.dev">healthlog.dev</a> &middot;
<a href="https://demo.healthlog.dev">Live Demo</a> &middot;
<a href="https://docs.healthlog.dev">Docs</a>
</p>