- inject the Umami script at runtime only when VITE_UMAMI_SRC + VITE_UMAMI_WEBSITE_ID
are set (so dev/local traffic isn't tracked); website-id is public, not a secret
- web Dockerfile passes them as build args; docker-compose.prod.yml + env examples
document them (optional, empty = disabled)
Verified: build bakes the values in when set; skipped when unset.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
postgres-js failed to parse DATABASE_URL when the password contained URL-special
characters (@ : / # ...). New createSqlClient(): uses DATABASE_URL if set, else
falls back to PG* env vars (PGHOST/PGUSER/PGPASSWORD/...) which postgres-js reads
directly — password passed verbatim, no URL encoding. docker-compose.prod.yml now
sets PG* for the server instead of an inline DSN.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Dockerfiles: server (Bun, runs migrations then starts) and web (Vite build
served by nginx with SPA fallback); .dockerignore
- docker-compose.prod.yml (postgres + server + web) ready for Dokploy, with
uploads/pgdata volumes and .env.production.example
- README: deployment (Dokploy) + gitflow/versioning sections, Images mode, bots,
i18n; CHANGELOG.md
- bump packages to 0.1.0
Docker images built and validated end-to-end: server migrates + serves
/health + /api/categories; web serves the SPA with VITE_SERVER_URL baked in.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>