feat(portal-shell): wire environment.ts per ADR-0018 #90
@@ -2,6 +2,7 @@ import { HttpClient } from '@angular/common/http';
|
|||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { toSignal } from '@angular/core/rxjs-interop';
|
import { toSignal } from '@angular/core/rxjs-interop';
|
||||||
import { catchError, map, of } from 'rxjs';
|
import { catchError, map, of } from 'rxjs';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
export interface BackendHealth {
|
export interface BackendHealth {
|
||||||
status: 'ok';
|
status: 'ok';
|
||||||
@@ -21,13 +22,12 @@ export type HomeStatus =
|
|||||||
* stack (CORS, OTel trace propagation, Pino log correlation —
|
* stack (CORS, OTel trace propagation, Pino log correlation —
|
||||||
* everything we wired in the observability foundation PRs).
|
* everything we wired in the observability foundation PRs).
|
||||||
*
|
*
|
||||||
* The base URL is hard-coded for now; it moves to Angular's
|
* The BFF base URL comes from `environment.ts` (per ADR-0018); the
|
||||||
* `environment.ts` mechanism with the env-config PR (same one that
|
* per-environment build target swaps the file via `fileReplacements`.
|
||||||
* will host the OTLP endpoint).
|
|
||||||
*/
|
*/
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class HomeStatusService {
|
export class HomeStatusService {
|
||||||
private static readonly HEALTH_URL = 'http://localhost:3000/api/health';
|
private static readonly HEALTH_URL = `${environment.bffApiBaseUrl}/health`;
|
||||||
private readonly http = inject(HttpClient);
|
private readonly http = inject(HttpClient);
|
||||||
|
|
||||||
readonly status = toSignal(
|
readonly status = toSignal(
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* 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 = {
|
||||||
|
/**
|
||||||
|
* Origin + 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 origin (see
|
||||||
|
* `observability/tracing.ts`).
|
||||||
|
*/
|
||||||
|
bffApiBaseUrl: 'http://localhost:3000/api',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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',
|
||||||
|
};
|
||||||
@@ -30,12 +30,9 @@
|
|||||||
*
|
*
|
||||||
* Endpoint configuration
|
* Endpoint configuration
|
||||||
* ──────────────────────
|
* ──────────────────────
|
||||||
* The OTLP/HTTP endpoint is hard-coded for v1 because env vars do
|
* The OTLP/HTTP endpoint and the BFF origin both come from
|
||||||
* not flow into the browser bundle natively (Angular's
|
* `src/environments/environment.ts` (per ADR-0018). Per-environment
|
||||||
* `environment.ts` mechanism is the standard alternative; we'll
|
* siblings of that file ship under `fileReplacements`.
|
||||||
* 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`.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { WebTracerProvider, BatchSpanProcessor } from '@opentelemetry/sdk-trace-web';
|
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 { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
|
||||||
import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction';
|
import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction';
|
||||||
|
|
||||||
|
import { environment } from '../environments/environment';
|
||||||
|
|
||||||
const SERVICE_NAME = 'portal-shell';
|
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 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({
|
const provider = new WebTracerProvider({
|
||||||
resource: resourceFromAttributes({
|
resource: resourceFromAttributes({
|
||||||
[ATTR_SERVICE_NAME]: SERVICE_NAME,
|
[ATTR_SERVICE_NAME]: SERVICE_NAME,
|
||||||
[ATTR_SERVICE_VERSION]: SERVICE_VERSION,
|
[ATTR_SERVICE_VERSION]: SERVICE_VERSION,
|
||||||
}),
|
}),
|
||||||
spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter({ url: OTLP_ENDPOINT }))],
|
spanProcessors: [
|
||||||
|
new BatchSpanProcessor(new OTLPTraceExporter({ url: environment.otlpEndpoint })),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
provider.register();
|
provider.register();
|
||||||
@@ -65,13 +74,10 @@ registerInstrumentations({
|
|||||||
instrumentations: [
|
instrumentations: [
|
||||||
// Times the initial page load — fires once.
|
// Times the initial page load — fires once.
|
||||||
new DocumentLoadInstrumentation(),
|
new DocumentLoadInstrumentation(),
|
||||||
// Wraps every `fetch` call. Propagates `traceparent` to the
|
// Wraps every `fetch` call. Propagates `traceparent` to the BFF
|
||||||
// listed origins; the BFF dev server is the one we care about
|
// origin derived from the environment.
|
||||||
// today. Add prod origins once they exist.
|
|
||||||
new FetchInstrumentation({
|
new FetchInstrumentation({
|
||||||
propagateTraceHeaderCorsUrls: [
|
propagateTraceHeaderCorsUrls: [bffOriginRegex],
|
||||||
/^http:\/\/localhost:3000\/.*/, // BFF dev server
|
|
||||||
],
|
|
||||||
}),
|
}),
|
||||||
// Auto-spans on click / keypress / submit (and a small allow-list
|
// Auto-spans on click / keypress / submit (and a small allow-list
|
||||||
// beyond that). Quiet in dev, useful in staging perf
|
// beyond that). Quiet in dev, useful in staging perf
|
||||||
|
|||||||
Reference in New Issue
Block a user