a84ea2d116
## Summary Make the SPAs reach the BFF as a **same-origin** call via an Angular dev-server `/api` proxy. Solves the "Backend unreachable" error surfaced during the ADR-0030 dockerised dev mode VM validation when the SPA is accessed from a remote browser (e.g. `http://<vm-ip>:4200/`), and bypasses CORS in dev altogether. Follow-up to the just-merged ADR-0030 implementation. ## Root cause it fixes Before this PR, both SPAs hardcoded `bffApiBaseUrl: 'http://localhost:3000/api'` (per ADR-0018) and the BFF's `CORS_ALLOWED_ORIGINS` only allowed `http://localhost:4200,http://localhost:4300`. Both assumptions hold for native `nx serve` (developer on the same machine as the BFF) but break the moment the browser sits on a different host than the BFF — exactly the case for the ADR-0030 `apps` Compose profile: open `http://<vm-ip>:4200/` from your workstation and the SPA's `localhost:3000` call hits your **workstation's** loopback (nothing there), not the VM's BFF. Even if the URL were right, the origin `http://<vm-ip>:4200` is not in the CORS allowlist. ## Fix Switch to a same-origin dev pattern: the Angular dev-server proxies `/api/*` to the BFF, and `environment.ts` uses a relative `'/api'` URL. | File | Change | | --- | --- | | `apps/portal-shell/proxy.conf.js` | **New.** Maps `/api → ${BFF_TARGET:-http://localhost:3000}` (JS form so the env var can swap the target at startup). | | `apps/portal-admin/proxy.conf.js` | **New.** Same shape. | | `apps/portal-shell/project.json`, `apps/portal-admin/project.json` | `serve.options.proxyConfig` points at the new file. | | `apps/portal-shell/src/environments/environment.ts`, `apps/portal-admin/src/environments/environment.ts` | `bffApiBaseUrl: 'http://localhost:3000/api'` → `'/api'`. The comment block explains the rationale + how production siblings can still use an absolute origin if SPA + BFF live on different hosts. | | `apps/portal-shell/src/observability/tracing.ts` | `new URL(environment.bffApiBaseUrl)` would throw on a relative URL — resolved against `window.location.origin` so both relative (dev) and absolute (prod cross-origin) bases work. | | `infra/local/dev.compose.yml` | `BFF_TARGET=http://portal-bff:3000` added to `portal-shell` and `portal-admin` so the proxy hits the BFF **container** by name (Compose DNS) when the `apps` profile is up. Native `nx serve` leaves the var unset and falls back to `localhost:3000`. | ## Why a relative URL is safe - `${bffApiBaseUrl}/health` etc. compose to `/api/health` — relative paths work in `fetch` / `HttpClient`. - `tracing.ts` propagates `traceparent` on requests whose origin matches the BFF origin. Resolving the relative base against `window.location.origin` gives the current page's origin, which is exactly the origin the dev-server proxy serves from — so the regex still matches the right requests in dev. In a future cross-origin production deployment, `environment.prod.ts` can set an absolute `bffApiBaseUrl`; the URL constructor's second arg is ignored when the first is absolute, so the same code path keeps working. - Auth flow (`feature-auth` / `auth.config.ts`): consumes `bffApiBaseUrl` via DI as a string prefix — agnostic to absolute vs relative. ## Scope notes - The OTel HTTP exporter (`environment.otlpEndpoint = 'http://localhost:4318/v1/traces'`) and the cross-SPA links (`adminAppUrl`, `shellAppUrl`) **remain absolute**. They hit the same remote-browser problem on `apps`-profile access, but neither is blocking the user-visible "Backend unreachable" path this PR targets. Out of scope here; a follow-up could either proxy them too or surface them via runtime config. - This pattern is dev-server only — production builds do not use the proxy. Per-environment `bffApiBaseUrl` overrides remain the supported lever (ADR-0018), unchanged. ## Test plan - [x] `docker compose -f dev.compose.yml --profile apps config` still validates. - [ ] **On the VM**, `./infra/local/dev.sh up apps`, then in the workstation browser open `http://<vm-ip>:4200/` → SPA loads, the "Backend unreachable" message is gone, network tab shows `/api/...` calls succeeding (same-origin, no CORS preflight). - [ ] Native `nx serve portal-shell` still works (the proxy falls back to `localhost:3000`). - [ ] Trace headers (`traceparent`) appear on `/api/*` fetches in the browser network tab. - [ ] `pnpm exec nx affected -t test build` green on the two SPAs. ## Related - [ADR-0030](docs/decisions/0030-dockerised-dev-mode.md) — the dockerised dev mode this enables to actually work from a remote browser. - [ADR-0018](docs/decisions/0018-environment-configuration-strategy.md) — the `environment.ts` per-env strategy this complements (does not supersede — production behaviour unchanged). --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #259
66 lines
2.8 KiB
TypeScript
66 lines
2.8 KiB
TypeScript
/**
|
|
* Per-environment configuration for the SPA, per ADR-0018.
|
|
*
|
|
* This file holds the **dev defaults** and is the one checked in.
|
|
* Per-environment siblings (`environment.staging.ts`,
|
|
* `environment.prod.ts`) land alongside this file later; the
|
|
* production / staging build configurations in `project.json` declare
|
|
* a `fileReplacements` entry that swaps this file for the target one
|
|
* at build time.
|
|
*
|
|
* Constraint: every sibling must export an `environment` object of
|
|
* the same shape. TypeScript will catch missing keys at the
|
|
* consuming site once a sibling exists.
|
|
*
|
|
* Nothing here is a secret. The SPA is a static bundle; secrets that
|
|
* ever needed to live here would, by definition, be public. Per-user
|
|
* / per-tenant secrets live in the BFF session payload (ADR-0010).
|
|
*/
|
|
export const environment = {
|
|
/**
|
|
* Prefix of the BFF HTTP API. The SPA prepends this to every
|
|
* backend call (`${bffApiBaseUrl}/health`, etc.) and derives the
|
|
* OTel trace-header propagation pattern from its resolved origin
|
|
* (see `observability/tracing.ts`).
|
|
*
|
|
* Relative path: the Angular dev-server proxies `/api/*` to the
|
|
* BFF (see `proxy.conf.js`, BFF target overridable via the
|
|
* `BFF_TARGET` env var — set by the ADR-0030 `apps` Compose
|
|
* profile to `http://portal-bff:3000`). This keeps every BFF call
|
|
* same-origin in the browser, so the SPA works whether it is
|
|
* accessed via `localhost:4200`, the VM IP, or any other host —
|
|
* and the BFF's `CORS_ALLOWED_ORIGINS` no longer has to enumerate
|
|
* every developer-side hostname. Production siblings
|
|
* (`environment.prod.ts`, etc.) may set an absolute origin when
|
|
* the SPA and BFF live on different hosts; `tracing.ts` resolves
|
|
* either form against `window.location.origin`.
|
|
*/
|
|
bffApiBaseUrl: '/api',
|
|
|
|
/**
|
|
* Name of the BFF's CSRF cookie. Mirrors the BFF's
|
|
* `csrfCookieName()` convention: `__Host-portal_csrf` in
|
|
* production (Secure-required, hence excluded from HTTP dev),
|
|
* `portal_csrf` otherwise. Read by the `csrfInterceptor` to
|
|
* echo the value back in the `X-CSRF-Token` request header.
|
|
*/
|
|
bffCsrfCookieName: 'portal_csrf',
|
|
|
|
/**
|
|
* OTLP/HTTP traces endpoint. Targets the Collector in
|
|
* `infra/local/dev.compose.yml`. CORS is enabled there — see
|
|
* `infra/local/otel-collector.yaml`.
|
|
*/
|
|
otlpEndpoint: 'http://localhost:4318/v1/traces',
|
|
|
|
/**
|
|
* Origin of the sibling `portal-admin` SPA — driver of the
|
|
* cross-app "Open Portal Admin" entry in the user menu (gated on
|
|
* `CapabilitiesService.canAccessAdmin`). Per ADR-0020 the two
|
|
* SPAs live on distinct origins / cookies / sessions, so this is
|
|
* a raw cross-origin `<a href>` jump, not an Angular route.
|
|
* Per-env siblings override the host.
|
|
*/
|
|
adminAppUrl: 'http://localhost:4300',
|
|
};
|