mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
Five hotfix-ready findings from the v1.4.34.3 code-quality audit. SESSION_SECRET drop (F-3): The env var was required by `docker-entrypoint.sh`, listed in `.env.example`, `docker-compose.yml`, `CONTRIBUTING.md`, and the scaling guide — but `grep -rn SESSION_SECRET src/` returned nothing. Sessions are server-stored in `prisma.session` keyed by the cookie value; the secret signed nothing. Every self-hoster carried a ceremonial 64-char hex string that the app ignored. Removed everywhere except the README (separate batch). Each fresh install now generates 3 secrets, not 4. toJson helper (F-9): Seven sites cast Prisma JSON writes via `value as unknown as Prisma.InputJsonValue`. Each call re-invented the same escape hatch. Replaced with one helper in `src/lib/db.ts`: `export const toJson = <T>(v: T) => v as unknown as Prisma.InputJsonValue;` Migrated all 7 call sites: `auth/me/doctor-report-prefs`, `dashboard/chart-overlay-prefs`, `dashboard/widgets`, `integrations/healthkit`, and 4 sites in `apple-health-import-worker`. The helper has a one-line WHY comment + a unit test pinning the cast shape stays stable. db-compat docstring (F-10): `src/lib/db-compat.ts` looks like accumulated backwards-compat cruft on first read. It's actually the schema-bootstrap path that runs ALTER-TABLE-IF-NOT-EXISTS so a fresh container syncs without `prisma migrate deploy`. Added a header docstring naming the role and pointing at `prisma/migrations/` as the canonical history. Passkey types (F-11): `src/lib/auth/passkey.ts:103,169` used `response: any` with per-line `eslint-disable`. Imported `RegistrationResponseJSON` and `AuthenticationResponseJSON` from `@simplewebauthn/types` and dropped the disables. Zero-TODO CI gate (F-15): The audit confirmed product code has zero TODO / FIXME / XXX / HACK markers — genuinely rare and worth preserving. Added a tiny GitHub Actions workflow at `.github/workflows/no-todo-markers.yml` that fails the build on any future re-introduction. Test files are excluded; product code only.
53 lines
2.4 KiB
Markdown
53 lines
2.4 KiB
Markdown
# Scaling: web/worker container split (v1.4 G3)
|
|
|
|
The default `docker compose up` launches a single container that runs
|
|
both the Next.js HTTP server AND the pg-boss worker. That works fine
|
|
for a personal deployment but is hard to scale horizontally because
|
|
every replica also runs the full reminder/insight cron schedule.
|
|
|
|
v1.4 introduces an environment-variable gate so the same image can be
|
|
deployed in three modes:
|
|
|
|
| `HEALTHLOG_PROCESS_TYPE` | Web | Worker | Use when |
|
|
| ------------------------ | --- | ------ | ------------------------- |
|
|
| `all` (default) | ✅ | ✅ | single-host self-hosting |
|
|
| `web` | ✅ | ❌ | scaling HTTP horizontally |
|
|
| `worker` | ❌ | ✅ | dedicated job worker |
|
|
|
|
## How to split
|
|
|
|
1. Edit `docker-compose.yml`: set `HEALTHLOG_PROCESS_TYPE=web` on the
|
|
`app` service and uncomment the `app-worker` service block at the
|
|
bottom (it ships with `HEALTHLOG_PROCESS_TYPE=worker` baked in).
|
|
2. Make sure both containers see the same `DATABASE_URL`,
|
|
`ENCRYPTION_KEY` (or `ENCRYPTION_KEYS`), and `API_TOKEN_HMAC_KEY`.
|
|
3. `docker compose up -d --build`.
|
|
|
|
Both containers connect to the same Postgres. pg-boss claims jobs
|
|
atomically via row-level locking, so it is safe to run multiple worker
|
|
replicas — they will share the load.
|
|
|
|
## Healthchecks
|
|
|
|
- `app` (web) healthcheck: `wget /api/version` every 30s.
|
|
- `app-worker` does not expose a port; its liveness is reported into
|
|
the `worker_status` table on every reminder pass and surfaced through
|
|
`/api/admin/worker-status`.
|
|
|
|
The web container does NOT depend on the worker, and vice versa, so
|
|
neither container's startup can deadlock the other. The shared
|
|
dependency is Postgres; both wait on `db: condition: service_healthy`.
|
|
|
|
## Caveats
|
|
|
|
- Background tasks that touch user-scoped Wide Events (e.g. reminder
|
|
notifications) emit telemetry from the worker; configure `LOKI_*`
|
|
env vars on the worker if you want them.
|
|
- The off-host backup job runs in the worker only. The admin
|
|
`POST /api/admin/backup/test` endpoint runs in the web container and
|
|
exercises the same S3 credentials — set `BACKUP_*` env vars on BOTH.
|
|
- The startup gate (`assertSubsystemEnabled`) refuses to boot the
|
|
reminder worker when `HEALTHLOG_PROCESS_TYPE=web`, so an accidental
|
|
cross-mode invocation aborts immediately instead of silently doubling
|
|
cron load.
|