- express-tools: ExpressServer.serveStaticFrontend() sert le build du
frontend (assets + fallback SPA), monté après les routes API et avant
le 404 JSON. Opt-in via FRONTEND_DIST_DIR (uniquement défini dans
l'image Docker) — le dev natif (dev:api/dev:web) est inchangé.
- apps/api/Dockerfile: build aussi apps/web, embarque son dist dans le
runtime ; corrige au passage l'oubli de packages/date-tools. Supprime
apps/web/Dockerfile et nginx.conf (plus de conteneur nginx séparé).
- docker-compose.yml: un seul service "app" (postgres + app), un seul
port APP_PORT, plus de WEB_PORT/CORS_ORIGIN à coordonner entre deux
origines. Garde `build:` (pas de registre — Portainer build depuis le
repo Git).
- ci.yml: éclate le job unique lint-and-test+e2e en 4 jobs indépendants
(lint/test/build/e2e), sans chaînage, déclenchés sur chaque push
(toute branche) + PR vers main.
- release.yml (nouveau): sur tag vX.Y.Z, sanity-build de l'image Docker,
GitHub Release avec changelog auto-généré, puis notification best-effort
du webhook Portainer (secret PORTAINER_WEBHOOK_URL).
- README: documente le conteneur unique et le pipeline de release.
apps/api/Dockerfile: multi-stage build on node:22-slim (not alpine —
avoids musl-vs-glibc native binding surprises for argon2/Prisma's
engine binaries; same base image family for build and runtime stages
keeps "native" binaries compatible across stages). Runtime stage
copies the monorepo structure as-is rather than flattening to a single
package, so pnpm's symlinked node_modules stay valid. Container runs
`prisma migrate deploy` on startup before starting the server, so the
review environment's schema is always in sync automatically.
Installs openssl explicitly in the base image: without it, Prisma
can't detect the right engine binary and silently defaults to a guess
that may not match what's actually on the image — caught by checking
the build log, not just a successful build.
apps/web/Dockerfile: builds with Vite, serves the static output via
nginx (not a Node static server) — avoids the devDependency problem of
needing `vite preview` in a --prod-deployed image, and is the more
standard way to serve a built SPA. nginx.conf has an SPA fallback
(try_files ... /index.html) ready for when client-side routing lands.
docker-compose.yml: adds `api` and `web` services alongside the
existing `postgres`. api's DATABASE_URL targets the `postgres` service
name over the compose network (not localhost/POSTGRES_PORT, which is
only the host-side mapping). Both new services require JWT_SECRET/
ports via env vars with no defaults, consistent with the project's
existing no-hardcoded-credentials rule.
.dockerignore added — without it, the Windows-built node_modules
(with Windows-specific native binaries) would get copied into the
Linux build context.
Verified: full build (api + web images), `docker compose up -d`
brought up all three containers, curled /health and the web root,
ran a real signup through the containerized stack end-to-end.
* Scaffold generic pnpm monorepo (api + web + shared)
Sets up the initial project infrastructure only, no business modules yet:
- apps/api: Express/TypeScript backend skeleton (healthcheck route, zod-validated
env config, error handling, Prisma initialized with no models yet, Postgres
as the target DB)
- apps/web: React/Vite/TypeScript frontend skeleton, Capacitor-ready for the
future mobile app
- packages/shared: empty placeholder for types/schemas shared between api and
web once the data model is defined
- Tooling: Biome (lint/format), Mocha+Chai+Supertest (api tests), Cypress
(web e2e smoke test), GitHub Actions CI (lint + test + build + e2e)
- docker-compose.yml for local Postgres
- README documents setup steps, including the Cypress binary caveat (pnpm
install doesn't always fetch the native binary — needs `cypress install`
run locally per machine)
Fixes along the way:
- apps/web/cypress.config.ts: disable GPU on browser launch for
headless/sandboxed environments
- apps/web/tsconfig.*: split into solution/app/node tsconfig files (standard
Vite pattern) — the previous single-file setup caused `tsc -b` to emit
compiled .js/.d.ts next to vite.config.ts and cypress.config.ts
* Remove hardcoded credentials from committed env/compose files
.env.example and apps/api/.env.example had a real usable default
credential pair (batchcooking/batchcooking) baked in, and
docker-compose.yml fell back to the same values via ${VAR:-default}
if .env was missing. Neither should ship a working credential:
- .env.example / apps/api/.env.example now use "changeme" placeholders
that must be edited before use.
- docker-compose.yml uses ${VAR:?...} instead of ${VAR:-default} for
POSTGRES_USER/PASSWORD/DB, so compose fails loudly if .env isn't set
up rather than silently falling back to a guessable credential.
Healthcheck reads the container's own env var ($$POSTGRES_USER)
instead of duplicating the value in the compose file.
- README updated to say .env.example must be edited, not just copied.
Verified: `docker compose config` fails with a clear message when
.env is absent, and resolves correctly once .env is filled in.
* Fix CI: remove pnpm version conflict with packageManager field
pnpm/action-setup@v4 errored with "Multiple versions of pnpm
specified" because the workflow pinned version: 10 while
package.json's packageManager field pins pnpm@10.12.4. The action
already reads packageManager automatically, so drop the redundant
version input.
* Fix CI: install Cypress binary explicitly before running e2e
Same root cause as the README caveat: pnpm install doesn't reliably
trigger Cypress's postinstall binary download, so `cypress run` failed
in CI with "The cypress npm package is installed, but the Cypress
binary is missing." Add an explicit `cypress install` step, and cache
~/.cache/Cypress keyed on the lockfile so subsequent runs don't
re-download it.
* Fix Cypress config loading: give the solution tsconfig a module system
apps/web/tsconfig.json (the tsc -b "solution" file) had no
compilerOptions, only files/references. Cypress's bundled ts-node
picks the nearest tsconfig.json to transpile cypress.config.ts, and
with no "module" specified it defaulted to CommonJS while
package.json declares "type": "module" — causing:
ReferenceError: exports is not defined in ES module scope
Adding module/moduleResolution to the solution config (harmless for
tsc -b itself, since it only builds the referenced projects) fixes
the mismatch. This regressed after the earlier fix for the stray
vite.config.js emission and was never re-verified against Cypress
until CI caught it.