Two production-blocker bugs from the first deploy:
1. Static SPA never served — Dockerfile copied apps/web/dist into
apps/api/public, but server.ts default static dir resolves to
apps/web/dist. Mismatch meant every route fell through to Express'
bare 404 ("Cannot GET /"). Aligning Dockerfile to the default path.
2. DNS for the Datum container name failed (getaddrinfo ENOTFOUND
datum_datum_1) — gashboard's Docker DNS doesn't reliably alias
external-network container names across compose stacks. Switch the
default DATUM_URL to the container's known IP on umbrel_main_network
(10.21.0.11, captured during earlier diagnostics). If the IP changes
the user can override DATUM_URL in env. If gashboard isn't actually
joined to umbrel_main_network, the next failure will be a much more
diagnostic ECONNREFUSED instead of an opaque ENOTFOUND.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User-facing port unified at 1337 — vibes-aligned and easier to remember
than 8080/8420. Updates: api default PORT, .env.example, docker-compose
mapping (1337:1337), healthcheck target, Dockerfile EXPOSE, Vite dev
proxy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The api and web tsconfig.json files both extend ../../tsconfig.base.json,
but the previous Dockerfile didn't COPY that file into /app. `tsc` then
fails to resolve the extends and exits with code 2 during the
`pnpm --filter @gashboard/api build` stage. Adding the file to the
COPY in the deps stage — both build stages inherit FROM deps, so this
fixes both api and web builds.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Node 22's bundled Corepack strict-validates pnpm package signatures, and
the Portainer build host couldn't complete `corepack prepare pnpm@9.12.3
--activate` (exit 1). Reproducible failure mode behind networks that
can't reach Corepack's signing-key host or against pinned pnpm versions
whose signatures aren't yet shipped in Corepack's known list.
Switch all three stages to install pnpm via plain `npm i -g pnpm@9.12.3`,
which has none of those constraints. Also copy pnpm-lock.yaml in so
`--frozen-lockfile` actually does what it says (was previously running
`--frozen-lockfile=false` because the lockfile wasn't being copied).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two-app pnpm workspace for the gashboard (mining dashboard) project:
@gashboard/api (Express 5 + TS) and @gashboard/web (Vue 3 + Vite + TS).
Shared tsconfig.base.json. Multi-stage Dockerfile (node:22.12-alpine,
non-root, healthchecked) and docker-compose.yml ready to deploy as a
Portainer Stack on Umbrel — joins umbrel_main_network so it can reach
the Datum container directly. .env.example documents every var; README
covers the Portainer deploy flow and the security posture.
Note: Dockerfile has a TODO marker to SHA256-pin the base image before
shipping to production.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>