diff --git a/.env.example b/.env.example index d28d71b..51d9955 100644 --- a/.env.example +++ b/.env.example @@ -37,8 +37,8 @@ FESTIPOD_SHARED_WALLET_FILE_BASE64= FESTIPOD_AUTO_SEED= # ── Serveur ──────────────────────────────────────────────────────────────── -# Port HTTP du serveur (5000 : l'app est servie sur l'origine :5000 de la pile locale). -PORT=5000 +# Port HTTP du serveur (4000 : l'app est servie sur l'origine :4000 de la pile locale). +PORT=4000 # NODE_ENV=production bascule `bun run start` (pas de HMR). En dev, laisser vide. NODE_ENV= @@ -48,7 +48,7 @@ NODE_ENV= SEMAPPS_HOME_URL=http://localhost:3003 # URL publique du frontend (utilisée par le backend pour les OIDC redirects, etc.) -SEMAPPS_FRONT_URL=http://localhost:5000 +SEMAPPS_FRONT_URL=http://localhost:4000 # Endpoint SPARQL du Fuseki auquel se connecte le backend. SEMAPPS_SPARQL_ENDPOINT=http://localhost:3030/ diff --git a/.project/concepts/data-layer/cookbook_local-activitypods-platform.md b/.project/concepts/data-layer/cookbook_local-activitypods-platform.md index 8eaafac..697e922 100644 --- a/.project/concepts/data-layer/cookbook_local-activitypods-platform.md +++ b/.project/concepts/data-layer/cookbook_local-activitypods-platform.md @@ -9,7 +9,7 @@ Platform checkout: `/home/sylvain/projects/activitypods/activitypods` (never mod ## Ports -pod provider 3000 (OpenID + `/auth` signup/login), Festipod backend 3003, Festipod app 5000, fuseki 3030 (admin/admin), mailcatcher 1080, redis 6390, arena 4567, tripleadmin 3033. +pod provider 3000 (OpenID + `/auth` signup/login), Festipod backend 3003, Festipod app 4000, fuseki 3030 (admin/admin), mailcatcher 1080, redis 6390, arena 4567, tripleadmin 3033. The pod provider frontend owns :5000 — never run it while testing Festipod. ## Bring up @@ -17,7 +17,7 @@ pod provider 3000 (OpenID + `/auth` signup/login), Festipod backend 3003, Festip 2. Install once: `cd backend && COREPACK_ENABLE_AUTO_PIN=0 corepack yarn install --frozen-lockfile`, same in `frontend/`. Keep `COREPACK_ENABLE_AUTO_PIN=0`: corepack auto-pin rewrites `packageManager` in package.json. Note: the pod-provider README says `yarn start` for the frontend; only the `dev` script exists — use `BROWSER=none corepack yarn dev`. 3. Backend: `cd backend && corepack yarn start`. Wait for "ServiceBroker with ... service(s) started successfully" (~11 s). 4. Frontend: `cd frontend && BROWSER=none corepack yarn dev`. Verify: `GET http://localhost:5000/` → 200 and `GET http://localhost:3000/.well-known/config.js` → 200. -5. Festipod stack (this repo root): `cp backend/.env.example backend/.env` before `docker compose up`. The compose reads `backend/.env` as an optional `env_file`; on a fresh clone the file is gitignored and absent, so this copy is what feeds the backend its ActivityPods settings. With the coherent layout the app is served on :5000 and the Festipod backend on :3003. +5. Festipod stack (this repo root): `cp backend/.env.example backend/.env` before `docker compose up`. The compose reads `backend/.env` as an optional `env_file`; on a fresh clone the file is gitignored and absent, so this copy is what feeds the backend its ActivityPods settings. With the coherent layout the app is served on :4000 and the Festipod backend on :3003 — keep the pod provider frontend (which owns :5000) stopped while testing Festipod. ## Account and resource flow (all VERIFIED 2026-10-09) diff --git a/.project/handoff/refine-provider-abstraction.md b/.project/handoff/refine-provider-abstraction.md index 6695569..16293bd 100644 --- a/.project/handoff/refine-provider-abstraction.md +++ b/.project/handoff/refine-provider-abstraction.md @@ -113,7 +113,7 @@ So both platforms count deposits rather than people, and the gap sits in **neith Verified by the orchestrator's own runs after the round's fixes: `bunx tsc --noEmit` exit 0 ; `bun test` 96 pass / 0 fail ; the UI cucumber layer 125 scenarios / 0 failed (54 fully implemented + 71 `Scénario non implémenté` placeholders that carry a skipped step by design) ; smoke `@e2e-activitypods` 3/3 against the live local stack (pod provider :3000, festipod backend :3003, app served on an ephemeral port) ; `bun run build.ts` ok ; `contracts.py check --integrity-only` clean. -The round: three adversarial passes (auth & security, data seam & backend, gates & ops); every confirmed finding was fixed in the commit recording this state. Fix themes — auth: `redirect` param validated (same-origin leading-slash only), callback re-entry guarded against StrictMode double-exchange, stale `?code=` URLs degrade to the picker instead of the fatal screen, `register_app` without a session falls back to the picker (was a permanent blank screen), ActivityPods logout wired through the provider, deep links survive login, clientId trailing slash normalized. Data fidelity: the meeting point's host is read from the Pod-stamped `dc:creator` (the `organizer` term is absent from the platform context and never round-tripped, so the Join lost its addressee), `location`/`city` are written as inline named nodes (`as:location` is `@type: @id`; a free-text string resolved into a garbage IRI), record ids are URL-encoded (absolute IRIs broke the router's `/events/:id` parse), participant resolution is keyed by actor URI (profiles were stored under their document URI and never matched), one delayed re-invalidation reconciles the count after join/leave, a Join/Leave without addressee throws instead of posting to no one, anonymous placeholder rows render on the event detail screen. Backend & ops: the attendees collection gets a public `acl:Read` grant in `after.createAndAttach` (the count is readable by everyone; anonymity lives in the profiles' own WAC), Join/Leave handlers retry while the collection is still attaching, `tsx` promoted to dependencies so the `--prod` image starts, compose `env_file` made optional with fuseki/redis services added and the frontend published on :5000 (the SEMAPPS_FRONT_URL default — fixes the shipped redirect_uri mismatch), `test:data` removed (zero matching scenarios since the suspension — a vacuous green), `strict: true` on both cucumber configs. +The round: three adversarial passes (auth & security, data seam & backend, gates & ops); every confirmed finding was fixed in the commit recording this state. Fix themes — auth: `redirect` param validated (same-origin leading-slash only), callback re-entry guarded against StrictMode double-exchange, stale `?code=` URLs degrade to the picker instead of the fatal screen, `register_app` without a session falls back to the picker (was a permanent blank screen), ActivityPods logout wired through the provider, deep links survive login, clientId trailing slash normalized. Data fidelity: the meeting point's host is read from the Pod-stamped `dc:creator` (the `organizer` term is absent from the platform context and never round-tripped, so the Join lost its addressee), `location`/`city` are written as inline named nodes (`as:location` is `@type: @id`; a free-text string resolved into a garbage IRI), record ids are URL-encoded (absolute IRIs broke the router's `/events/:id` parse), participant resolution is keyed by actor URI (profiles were stored under their document URI and never matched), one delayed re-invalidation reconciles the count after join/leave, a Join/Leave without addressee throws instead of posting to no one, anonymous placeholder rows render on the event detail screen. Backend & ops: the attendees collection gets a public `acl:Read` grant in `after.createAndAttach` (the count is readable by everyone; anonymity lives in the profiles' own WAC), Join/Leave handlers retry while the collection is still attaching, `tsx` promoted to dependencies so the `--prod` image starts, compose `env_file` made optional with fuseki/redis services added and the frontend published on :4000 (the SEMAPPS_FRONT_URL default — fixes the shipped redirect_uri mismatch; :5000 belongs to the pod provider frontend and is deliberately avoided), `test:data` removed (zero matching scenarios since the suspension — a vacuous green), `strict: true` on both cucumber configs. `contracts.py check` also reports, pre-existing: `polyfill-surface: provider moved: a33fb8a214 → a8d53010c2` — the pulled copy is behind the pinned ref. diff --git a/AGENTS.md b/AGENTS.md index 4b42ef2..5ae7926 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,7 @@ Web app mobile-first où les utilisateurs créent des **points de rencontre** qu src/screens/index.ts # Registre d'écrans (utilisé par Storybook) ``` - **Un module n'importe QUE depuis `shared/` — jamais d'un autre module.** C'est l'invariant qui rend l'archi réelle. -- **Bun-first** : `bun` / `bun install` / `bun test` / `bun build`, jamais node/npm/vite/jest. `bun run dev` (portless, port dynamique ; défaut servi par `bun run start` : 5000). +- **Bun-first** : `bun` / `bun install` / `bun test` / `bun build`, jamais node/npm/vite/jest. `bun run dev` (portless, port dynamique ; défaut servi par `bun run start` : 4000). ## Frontière SDK diff --git a/backend/.env.example b/backend/.env.example index 28888b9..8011071 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -2,7 +2,7 @@ SEMAPPS_APP_NAME=Festipod SEMAPPS_APP_DESCRIPTION=Points de rencontre sur les événements publics SEMAPPS_APP_LANG=en,fr SEMAPPS_HOME_URL=http://localhost:3003 -SEMAPPS_FRONT_URL=http://localhost:5000 +SEMAPPS_FRONT_URL=http://localhost:4000 SEMAPPS_SHAPE_REPOSITORY_URL=https://shapes.activitypods.org/ SEMAPPS_PORT=3003 SEMAPPS_SPARQL_ENDPOINT=http://localhost:3030/ diff --git a/docker-compose.yml b/docker-compose.yml index 63bc42f..bda6c06 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: context: . dockerfile: Dockerfile ports: - - "5000:3000" + - "4000:3000" environment: - PORT=3000 - NODE_ENV=production @@ -26,4 +26,4 @@ services: required: false environment: - SEMAPPS_HOME_URL=${SEMAPPS_HOME_URL:-http://localhost:3003} - - SEMAPPS_FRONT_URL=${SEMAPPS_FRONT_URL:-http://localhost:5000} + - SEMAPPS_FRONT_URL=${SEMAPPS_FRONT_URL:-http://localhost:4000} diff --git a/src/index.ts b/src/index.ts index 142ac03..684f65d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import { serve } from "bun"; import index from "./index.html"; -const port = process.env.PORT ? parseInt(process.env.PORT) : 5000; +const port = process.env.PORT ? parseInt(process.env.PORT) : 4000; // Strict base64 check (not a mere `Buffer.from` attempt, which silently drops invalid // characters instead of failing): reject anything that is not a well-formed base64 body