README: document today's shipped work (story reactions/comments, per-pet
insights dashboard, DESIGN.md/PRODUCT.md design system, admin panel visual
pass) in "What's Shipped" and the admin panel section.
docker/rolling-deploy.sh and start.sh were tracked as 100644 (no executable
bit) since this repo is worked from Windows, which never records it — every
fresh git pull onto the Linux NAS made them non-executable again. Fixed via
git update-index --chmod=+x so this doesn't recur.
Adds a dependency-free liveness endpoint (GET /api/health), a Docker
healthcheck against it, and docker/rolling-deploy.sh which rebuilds
the shared image once and restarts pawfeed-1/2/3 sequentially, gated
on each becoming healthy before moving to the next. Verified
zero-downtime via continuous curl monitoring through a full run
against production.
Next.js standalone is single-process, so one container only used one
CPU core. A load test hit a hard ceiling at ~60-65 req/sec with one
core pegged at 133% while 3 cores sat idle. Now runs as pawfeed-1/2/3
behind an NPM least_conn upstream, verified via load test showing CPU
spread evenly across all three replicas.
Also caps the Prisma pg pool per-replica via DB_POOL_MAX so 3 replicas
stay well under the Supabase pooler's connection limit.
Confirmed via extensive isolated reproduction (docker run, plain BuildKit
RUN step, and Node child_process.spawn -- all three succeed) that TLS and
the sentry-cli invocation itself are fine; GlitchTip's releases API
rejects the authenticated request with "CSRF check Failed" (403) even
with clean token auth and no cookies. That's a server-side GlitchTip
issue (separate Docker stack), not something fixable from this repo.
Keeping ca-certificates/SSL_CERT_FILE since they're correct regardless.
Diagnostic only -- narrowing down whether the sourcemap upload's SSL
error is real or a red herring next to the CSRF 403 seen in manual
reproduction. Will be removed once the root cause is confirmed.
ca-certificates alone didn't fully resolve sentry-cli's TLS handshake
against GlitchTip in the Docker builder stage -- explicitly pointing its
vendored OpenSSL at the installed CA bundle location is the documented
workaround for this class of Rust-binary-on-Alpine cert issue.
sentry-cli (Rust binary) failed the sourcemap upload with "unable to get
local issuer certificate" when talking to GlitchTip over TLS -- the
node:22-alpine builder image ships without a CA bundle. Confirmed via
verbose build log after the SENTRY_RELEASE fix resolved the prior
--release undefined failure.
.git is excluded from the Docker build context (.dockerignore), so
@sentry/nextjs can't auto-detect a release via git and falls back to the
literal string "undefined" for --release, which made the sourcemaps
upload command fail with exit code 1 (confirmed via verbose build log).
start.sh now resolves the short SHA on the host and passes it through
as a SENTRY_RELEASE build arg, same pattern as the other Sentry vars.
- connect-src: allow the GlitchTip domain itself so the SDK can report events
- media-src: allow Mux's edge CDN (*.edgemv.mux.com) for HLS manifests
- img-src: allow blob: for client-side upload previews
- pass SENTRY_ORG/SENTRY_PROJECT/SENTRY_AUTH_TOKEN as Docker build args so
withSentryConfig can upload readable source maps during `npm run build`
Step 2/2 (Audit Gitea #17). NPM's proxy host now forwards to pawfeed:3000
over the shared nginx_default Docker network (verified working in the
previous deploy), so the app no longer needs a published host port at
all — it was reachable unencrypted on the LAN, bypassing Cloudflare's
TLS termination and protections entirely.
Step 1/2 of removing the direct, unencrypted LAN exposure on port 4563
(Audit Gitea #17). Attaches pawfeed to Nginx Proxy Manager's Docker
network so NPM can reach it via the container DNS name (pawfeed:3000)
instead of the published host port. Port mapping stays for now — removed
in a follow-up commit once NPM's proxy host forward target is switched
over, to avoid a downtime gap between the two changes.
Redis had no auth, defended only by not being port-mapped to the host.
Defense-in-depth per the audit — REDIS_PASSWORD is generated server-side
in docker/.env (not committed) and threaded through to both the redis
service's --requirepass and the app's REDIS_URL.
Server, edge, and client instrumentation wired up via SENTRY_DSN /
NEXT_PUBLIC_SENTRY_DSN. Fully inert without a DSN configured (verified
with a clean local build + full test suite) — safe to ship ahead of
actually having a Sentry project. Source-map upload is opt-in via
SENTRY_AUTH_TOKEN (kept out of the Docker build-arg chain since build
args land in image layer history; only the public DSN is a build arg).