Commit graph

5 commits

Author SHA1 Message Date
c4ef989e1a ci: drop pull_request trigger, push alone covers same-repo PRs
push (all branches) already fires for a PR branch on this repo; keeping
pull_request too just ran every job twice on the same commit. GitHub
still attaches the push-triggered run to the PR by SHA regardless of
which event fired it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 23:14:46 +02:00
c2cc773c7b Fusionner web+api en une image, séparer la CI, ajouter la release
- 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.
2026-08-17 23:11:26 +02:00
kyuno053
42d094764f
API: signup/login (profile creation + JWT auth) (#4)
* Add signup/login (profile creation + JWT auth)

API:
- POST /auth/signup — creates a house + user_profile (transactional),
  hashes the password with argon2, sets a JWT in an httpOnly cookie
- POST /auth/login — verifies credentials (generic 401 for both wrong
  email and wrong password, doesn't leak which), sets the cookie
- POST /auth/logout — clears the cookie
- GET /auth/me — current profile, behind requireAuth middleware
- requireAuth verifies the JWT and re-checks tokenVersion against the
  DB, so a stateless JWT can still be invalidated (password change /
  logout-everywhere, not built yet but the field is in place)

Schema: user_profiles gets password_hash + token_version (not in the
original spec doc — required for auth). New migration, with
COMMENT ON for the new columns per the established pattern.

Decisions from the auth planning discussion: JWT in httpOnly cookie
(not server-side sessions), first profile created also creates its
house, argon2 for hashing.

argon2 pinned to 0.31.2 (not ^, deliberately): 0.45.1 segfaults at
runtime on this Windows machine — reproduced consistently across bash
(sandboxed and unsandboxed) and PowerShell, while 0.31.2 works fine
with the same API. Documented in the README as a trap for future
upgrades, since `tsc`/`prisma generate` succeeding doesn't catch a
runtime native-binding crash.

Tests: Mocha (unit-style, apps/api/test/auth.test.ts) and a Cucumber
feature (apps/api/features/auth.feature) covering the full signup →
authenticated flow, duplicate email, wrong password. Both share
test-support/reset-db.ts (TRUNCATE ... CASCADE) to start each
test/scenario from a clean slate. Test-only argon2 cost parameters
(NODE_ENV=test) keep the suite fast — argon2's real cost is
deliberately expensive, which made hashing dozens of times per run
slow and occasionally timeout-flaky at default cost.

CI: added a Postgres service container to lint-and-test (previously
none — tests didn't touch a real DB), runs `prisma migrate deploy`
before the test steps.

Verified end-to-end manually against the dev server (curl): signup,
duplicate email (409), wrong password (401), valid login (200),
validation errors (400), /me with and without cookie, logout (204) —
all behave as intended. Full suite (lint, mocha, cucumber, build) run
multiple times locally with no flakiness after the timeout/cost fixes.

* Fix CI: generate Prisma Client via postinstall

CI failed with "@prisma/client did not initialize yet" — pnpm install
never ran `prisma generate`, and `prisma migrate deploy` (unlike
`migrate dev`) doesn't do it either. Worked locally only because prior
`prisma migrate dev` runs had already generated the client as a side
effect.

Adding a postinstall script fixes it for CI and for anyone cloning the
repo fresh and running plain `pnpm install`.
2026-08-16 13:45:23 +02:00
kyuno053
c53803d708
Add Cucumber for readable BDD-style integration tests (apps/api) (#2)
Coexists with Mocha (kept for unit-style tests) and Cypress (unchanged,
web e2e). Adds:

- apps/api/features/*.feature — Gherkin scenarios
- apps/api/features/step-definitions/*.steps.ts — step implementations
- apps/api/features/support/world.ts — per-scenario World, spins up the
  Express app in-process via createApp() + supertest (no real server
  needed, same approach as the existing Mocha health test)
- apps/api/cucumber.cjs — config, deliberately .cjs (not .js) to avoid
  the same ESM/CJS config-loading mismatch that broke
  apps/web/cypress.config.ts earlier
- `test:bdd` script (cross-env + tsx via NODE_OPTIONS=--import=tsx, for
  cross-platform ESM+TS loading)
- health.feature/steps as a working example, mirroring the existing
  Mocha health test so both suites cover the same behavior in their
  respective styles

CI: runs `pnpm --filter api test:bdd` alongside the existing test step.
README: documents the new test layer and the TS/ESM config-loading
caveat for future tool configs.

Verified locally: lint, mocha, cucumber, and full build all pass.
2026-08-16 11:11:38 +02:00
kyuno053
746100e257
Scaffold generic pnpm monorepo (api + web + shared) (#1)
* 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.
2026-08-16 10:55:13 +02:00