The development stack was running permanently on a server: polling file
watchers, a healthcheck that rendered a full page every five seconds and no
memory limit grew next dev to 10 GB and pushed the host into swap.
- add compose.prod.yaml running compiled output in separate api/web services
- make the Dockerfile multi-stage with dev and prod targets, prune
devDependencies and run the runtime image as node instead of root
- bake API_INTERNAL_URL at build time; next start ignores it at runtime
because rewrite destinations are resolved into routes-manifest.json
- drop CHOKIDAR_USEPOLLING and WATCHPACK_POLLING
- probe /health instead of /, which redirects to /projects and made every
healthcheck render the project list
- give every service a memory limit and forbid swap in production
- rename the development compose project to leistungsbilanz-dev so its
down command cannot target the production stack
- bind development ports to localhost
- close the http server and the SQLite handle on SIGTERM/SIGINT
- match probe user agents in the navigation log filter; Node's fetch sends
one, so the previous check never matched
- exit docker-start.sh when either supervised process dies
- remove drizzle.config.js, a compiled copy drizzle-kit never reads, and the
pre-Next index.html/styles.css leftovers
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- circuit-list-editor-api.md: next-identifier moved under
/projects/:projectId and now documents its 404 scoping behavior.
- current-architecture.md: clarify that the BMK trigger register's
DB-level normalization is ASCII-only and describe the added
application-level Unicode-aware uniqueness check that closes the
umlaut gap; note the additive migration 0006 indexes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Review of the logging work from the previous commit turned up a real
correctness issue: the uncaughtException/unhandledRejection handlers
logged the error but let the process keep running, which silently
disabled Node's default crash-on-fatal-error behavior and could leave
a zombie process serving broken requests instead of restarting. Both
processes now log and then exit(1); restart: unless-stopped is added
to both services so Docker actually brings them back up.
Also: harden the logger against JSON.stringify throwing on
non-serializable meta, log aborted (closed-before-finished) requests
in the API access log, and stop the Next.js proxy from logging the
Docker healthcheck's request to "/" every few seconds so real
navigation events aren't drowned out.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a leveled JSON logger (error/warn/info/verbose/debug, controlled via
LOG_LEVEL) wired into the Express API (access log, error middleware,
crash handlers, memory heartbeat) and the Next.js server (page-request
proxy, instrumentation crash handlers, heartbeat). LOG_LEVEL is exposed
through compose.yaml, and both services now rotate their Docker logs
(json-file, 20m x 10 files) instead of growing unbounded. Intended to
capture long-running diagnostic data for the intermittent 502s seen on
the Docker host.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>