feat(portal-shell): wire environment.ts per ADR-0018 (#90)
CI / check (push) Successful in 2m46s
CI / commits (push) Has been skipped
CI / scan (push) Successful in 2m29s
CI / a11y (push) Successful in 1m14s
CI / perf (push) Successful in 3m21s

## Summary

First implementation step of ADR-0018. Create [`src/environments/environment.ts`](apps/portal-shell/src/environments/environment.ts) holding the two SPA per-environment values the ADR calls out — `bffApiBaseUrl` and `otlpEndpoint` — and replace the hard-coded URLs at the two SPA call sites that needed them.

## What changes

- **New `environment.ts`** with dev defaults (`http://localhost:3000/api` and `http://localhost:4318/v1/traces`). Header comment links to ADR-0018, documents the constraint that per-environment siblings must share the same shape, and notes that nothing here is a secret (the SPA bundle is public).
- **`observability/tracing.ts`** reads `environment.otlpEndpoint` for the exporter, and **derives** the `propagateTraceHeaderCorsUrls` regex from `environment.bffApiBaseUrl` — a future change to the BFF origin propagates `traceparent` to the right host automatically, no second edit needed.
- **`home-status.service.ts`** builds `/health` as `${environment.bffApiBaseUrl}/health`.

## What this PR explicitly does NOT do

- **No `environment.staging.ts` / `environment.prod.ts`** yet. The ADR says "ship later" for those, and the real prod / staging URLs are unknown until the infrastructure ADR lands. Dropping plausible-but-wrong URLs into the repo would be worse than waiting.
- **No `fileReplacements` configuration in `project.json`** — it depends on the per-environment files existing. Wired in the same PR that introduces them.
- **No BFF-side audit pool split** (`AUDIT_DATABASE_URL` validator, second Prisma client, boot-time UPDATE-rejection self-test). Also in the ADR's Confirmation list, but it touches `AuditModule` and deserves its own review. Separate PR.
- **No `SERVICE_VERSION` wiring** in `tracing.ts`. Still hard-coded to `'dev'`; the build-time version source (same one that will feed the footer's dev-only version badge) is its own small chantier.

## Test plan

- [x] `pnpm exec nx run-many -t lint test build --projects=portal-shell` — green (36 / 36 specs unchanged, no new tests needed).
- [x] Production build size unchanged (121 kB gzip initial — `environment.ts` is one literal object inlined by the bundler).
- [ ] Manual: `pnpm exec nx serve portal-shell` → home page loads, the health widget hits the BFF, Jaeger shows the SPA `document_load` + `fetch` + BFF child span trace.
- [ ] Manual: temporarily change `bffApiBaseUrl` to `http://localhost:9999/api` → the fetch fails (expected), and the `traceparent` propagation regex no longer matches `:3000` (verifiable in Network panel — header is absent on cross-origin requests).

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #90
This commit was merged in pull request #90.
This commit is contained in:
2026-05-11 12:48:55 +02:00
parent c5e91f240b
commit 8f125d2a90
3 changed files with 58 additions and 18 deletions
+20 -14
View File
@@ -30,12 +30,9 @@
*
* Endpoint configuration
* ──────────────────────
* The OTLP/HTTP endpoint is hard-coded for v1 because env vars do
* not flow into the browser bundle natively (Angular's
* `environment.ts` mechanism is the standard alternative; we'll
* adopt it when prod build needs it). The default targets the
* Collector that ships in `infra/local/dev.compose.yml`. CORS is
* enabled on that Collector receiver — see `otel-collector.yaml`.
* The OTLP/HTTP endpoint and the BFF origin both come from
* `src/environments/environment.ts` (per ADR-0018). Per-environment
* siblings of that file ship under `fileReplacements`.
*/
import { WebTracerProvider, BatchSpanProcessor } from '@opentelemetry/sdk-trace-web';
@@ -47,16 +44,28 @@ import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction';
import { environment } from '../environments/environment';
const SERVICE_NAME = 'portal-shell';
// Static for now; a follow-up wires this to the build-time version
// (same source as the footer's dev-only version badge).
const SERVICE_VERSION = 'dev';
const OTLP_ENDPOINT = 'http://localhost:4318/v1/traces';
// Derive the trace-header propagation pattern from the BFF base URL
// so a deploy-time change to `bffApiBaseUrl` automatically propagates
// `traceparent` to the right origin. RegExp special chars are escaped
// before going into the source.
const bffOrigin = new URL(environment.bffApiBaseUrl).origin;
const bffOriginRegex = new RegExp(`^${bffOrigin.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/.*`);
const provider = new WebTracerProvider({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: SERVICE_NAME,
[ATTR_SERVICE_VERSION]: SERVICE_VERSION,
}),
spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter({ url: OTLP_ENDPOINT }))],
spanProcessors: [
new BatchSpanProcessor(new OTLPTraceExporter({ url: environment.otlpEndpoint })),
],
});
provider.register();
@@ -65,13 +74,10 @@ registerInstrumentations({
instrumentations: [
// Times the initial page load — fires once.
new DocumentLoadInstrumentation(),
// Wraps every `fetch` call. Propagates `traceparent` to the
// listed origins; the BFF dev server is the one we care about
// today. Add prod origins once they exist.
// Wraps every `fetch` call. Propagates `traceparent` to the BFF
// origin derived from the environment.
new FetchInstrumentation({
propagateTraceHeaderCorsUrls: [
/^http:\/\/localhost:3000\/.*/, // BFF dev server
],
propagateTraceHeaderCorsUrls: [bffOriginRegex],
}),
// Auto-spans on click / keypress / submit (and a small allow-list
// beyond that). Quiet in dev, useful in staging perf