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
125 lines
3.2 KiB
JSON
125 lines
3.2 KiB
JSON
{
|
|
"name": "portal-admin",
|
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
"projectType": "application",
|
|
"prefix": "app",
|
|
"sourceRoot": "apps/portal-admin/src",
|
|
"tags": ["scope:portal-admin", "type:app"],
|
|
"i18n": {
|
|
"sourceLocale": {
|
|
"code": "en",
|
|
"baseHref": "/en/"
|
|
},
|
|
"locales": {
|
|
"fr": {
|
|
"translation": "apps/portal-admin/src/locale/messages.fr.xlf",
|
|
"baseHref": "/fr/"
|
|
}
|
|
}
|
|
},
|
|
"targets": {
|
|
"build": {
|
|
"executor": "@angular/build:application",
|
|
"outputs": ["{options.outputPath}"],
|
|
"options": {
|
|
"outputPath": "dist/apps/portal-admin",
|
|
"browser": "apps/portal-admin/src/main.ts",
|
|
"polyfills": ["@angular/localize/init"],
|
|
"tsConfig": "apps/portal-admin/tsconfig.app.json",
|
|
"inlineStyleLanguage": "scss",
|
|
"assets": [
|
|
{
|
|
"glob": "**/*",
|
|
"input": "apps/portal-admin/public"
|
|
}
|
|
],
|
|
"styles": ["apps/portal-admin/src/styles.css"]
|
|
},
|
|
"configurations": {
|
|
"production": {
|
|
"localize": ["en", "fr"],
|
|
"i18nMissingTranslation": "error",
|
|
"budgets": [
|
|
{
|
|
"type": "initial",
|
|
"maximumWarning": "1.5mb",
|
|
"maximumError": "1.5mb"
|
|
},
|
|
{
|
|
"type": "anyScript",
|
|
"maximumWarning": "500kb",
|
|
"maximumError": "500kb"
|
|
},
|
|
{
|
|
"type": "anyComponentStyle",
|
|
"maximumWarning": "6kb",
|
|
"maximumError": "8kb"
|
|
},
|
|
{
|
|
"type": "bundle",
|
|
"name": "styles",
|
|
"maximumWarning": "150kb",
|
|
"maximumError": "150kb"
|
|
}
|
|
],
|
|
"outputHashing": "all"
|
|
},
|
|
"development": {
|
|
"optimization": false,
|
|
"extractLicenses": false,
|
|
"sourceMap": true
|
|
}
|
|
},
|
|
"defaultConfiguration": "production"
|
|
},
|
|
"extract-i18n": {
|
|
"executor": "@angular/build:extract-i18n",
|
|
"options": {
|
|
"buildTarget": "portal-admin:build",
|
|
"outputPath": "apps/portal-admin/src/locale",
|
|
"outFile": "messages.xlf"
|
|
}
|
|
},
|
|
"serve": {
|
|
"continuous": true,
|
|
"executor": "@angular/build:dev-server",
|
|
"options": {
|
|
"port": 4300,
|
|
"proxyConfig": "apps/portal-admin/proxy.conf.js"
|
|
},
|
|
"configurations": {
|
|
"production": {
|
|
"buildTarget": "portal-admin:build:production"
|
|
},
|
|
"development": {
|
|
"buildTarget": "portal-admin:build:development"
|
|
}
|
|
},
|
|
"defaultConfiguration": "development"
|
|
},
|
|
"lint": {
|
|
"executor": "@nx/eslint:lint"
|
|
},
|
|
"test": {
|
|
"executor": "@angular/build:unit-test",
|
|
"options": {
|
|
"watch": false
|
|
},
|
|
"configurations": {
|
|
"watch": {
|
|
"watch": true
|
|
}
|
|
}
|
|
},
|
|
"serve-static": {
|
|
"continuous": true,
|
|
"executor": "@nx/web:file-server",
|
|
"options": {
|
|
"buildTarget": "portal-admin:build",
|
|
"port": 4300,
|
|
"staticFilePath": "dist/apps/portal-admin/browser"
|
|
}
|
|
}
|
|
}
|
|
}
|