Remove retarded build time variables
This commit is contained in:
33
.env.example
33
.env.example
@@ -76,21 +76,28 @@ APP_ENV=prod
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Frontend (otel-bi-frontend) → frontend/
|
||||
# Baked into the image at build time via Docker build-args.
|
||||
# In Docker Compose these are passed as build args, not runtime env.
|
||||
# For local dev copy frontend/.env.example to frontend/.env.local instead.
|
||||
# -----------------------------------------------------------------------------
|
||||
VITE_API_BASE_URL=http://localhost:8000
|
||||
VITE_OTEL_COLLECTOR_ENDPOINT=http://alloy:4318
|
||||
VITE_OTEL_SERVICE_NAME=otel-bi-frontend
|
||||
VITE_OTEL_SERVICE_NAMESPACE=final-thesis
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# OpenTelemetry — shared collector endpoint
|
||||
# Same value goes to Go analytics, Python API, and frontend build arg above
|
||||
# Used by Go analytics + Python API for their own trace/metric export.
|
||||
# In-cluster K8s DNS for Alloy.
|
||||
# -----------------------------------------------------------------------------
|
||||
OTEL_COLLECTOR_ENDPOINT=http://alloy:4318
|
||||
OTEL_SERVICE_NAMESPACE=final-thesis
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Frontend telemetry — served to the SPA via GET /api/config.
|
||||
# These are the values the browser-side OTel SDK uses; the SPA reads them at
|
||||
# runtime so a single frontend image works across all environments.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Browser-reachable OTLP endpoint. Distinct from OTEL_COLLECTOR_ENDPOINT
|
||||
# because the browser can't reach in-cluster service DNS.
|
||||
# Default `/otel` assumes Ingress routes `/otel/v1/traces` → alloy:4318.
|
||||
# Set to an absolute URL (e.g. https://alloy.example.com) if exposing the
|
||||
# collector on its own host instead.
|
||||
FRONTEND_OTEL_COLLECTOR_ENDPOINT=/otel
|
||||
|
||||
# OTel resource attributes for the frontend service
|
||||
FRONTEND_OTEL_SERVICE_NAME=otel-bi-frontend
|
||||
FRONTEND_OTEL_SERVICE_NAMESPACE=final-thesis
|
||||
FRONTEND_DEPLOYMENT_ENVIRONMENT=production
|
||||
|
||||
@@ -148,9 +148,6 @@ jobs:
|
||||
push: true
|
||||
cache-from: type=registry,ref=${{ env.IMAGE_FRONTEND }}:latest
|
||||
cache-to: type=inline
|
||||
build-args: |
|
||||
VITE_API_BASE_URL=${{ vars.API_BASE_URL }}
|
||||
VITE_OTEL_COLLECTOR_ENDPOINT=${{ vars.OTEL_COLLECTOR_ENDPOINT }}
|
||||
tags: |
|
||||
${{ env.IMAGE_FRONTEND }}:${{ github.sha }}
|
||||
${{ env.IMAGE_FRONTEND }}:latest
|
||||
|
||||
@@ -53,6 +53,14 @@ class Settings(BaseSettings):
|
||||
otel_collector_endpoint: str = "http://localhost:4318"
|
||||
otel_export_timeout_ms: int = 10000
|
||||
|
||||
# Browser-reachable OTLP endpoint — served to the SPA via GET /api/config.
|
||||
# Distinct from otel_collector_endpoint, which is the backend's own
|
||||
# in-cluster collector address.
|
||||
frontend_otel_collector_endpoint: str = "/otel"
|
||||
frontend_otel_service_name: str = "otel-bi-frontend"
|
||||
frontend_otel_service_namespace: str = "final-thesis"
|
||||
frontend_deployment_environment: str = "production"
|
||||
|
||||
# Report output — points at the K8s CSI / SMB mountpoint in production
|
||||
report_output_dir: str = "/tmp/otel-bi-reports"
|
||||
|
||||
|
||||
@@ -36,6 +36,10 @@ def frontend_config() -> dict:
|
||||
"oidc_authority": settings.frontend_jwt_issuer_url,
|
||||
"oidc_client_id": settings.frontend_oidc_client_id,
|
||||
"oidc_scope": settings.frontend_oidc_scope,
|
||||
"otel_collector_endpoint": settings.frontend_otel_collector_endpoint,
|
||||
"otel_service_name": settings.frontend_otel_service_name,
|
||||
"otel_service_namespace": settings.frontend_otel_service_namespace,
|
||||
"deployment_environment": settings.frontend_deployment_environment,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,12 +2,6 @@ FROM rockylinux/rockylinux:10 AS build
|
||||
|
||||
RUN dnf install -y nodejs npm && dnf clean all
|
||||
|
||||
ARG VITE_API_BASE_URL=http://localhost:8000
|
||||
ARG VITE_OTEL_COLLECTOR_ENDPOINT=http://localhost:4318
|
||||
|
||||
ENV VITE_API_BASE_URL=$VITE_API_BASE_URL \
|
||||
VITE_OTEL_COLLECTOR_ENDPOINT=$VITE_OTEL_COLLECTOR_ENDPOINT
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
@@ -9,14 +9,13 @@ import type {
|
||||
AWAnomalyPoint,
|
||||
} from "./types";
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000";
|
||||
const tracer = trace.getTracer("aw-frontend-api");
|
||||
|
||||
async function get<T>(path: string, spanName: string): Promise<T> {
|
||||
return tracer.startActiveSpan(spanName, async (span) => {
|
||||
try {
|
||||
const token = currentAccessToken();
|
||||
const resp = await fetch(`${API_BASE}${path}`, {
|
||||
const resp = await fetch(path, {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
|
||||
@@ -3,14 +3,16 @@ export type AppConfig = {
|
||||
oidc_authority: string;
|
||||
oidc_client_id: string;
|
||||
oidc_scope: string;
|
||||
otel_collector_endpoint: string;
|
||||
otel_service_name: string;
|
||||
otel_service_namespace: string;
|
||||
deployment_environment: string;
|
||||
};
|
||||
|
||||
const API_BASE = (import.meta.env.VITE_API_BASE_URL as string | undefined) ?? "";
|
||||
|
||||
let _config: AppConfig | null = null;
|
||||
|
||||
export async function fetchAppConfig(): Promise<AppConfig> {
|
||||
const resp = await fetch(`${API_BASE}/api/config`);
|
||||
const resp = await fetch("/api/config");
|
||||
if (!resp.ok) throw new Error(`Failed to fetch app config: ${resp.status}`);
|
||||
_config = (await resp.json()) as AppConfig;
|
||||
return _config;
|
||||
|
||||
@@ -2,7 +2,6 @@ import { SpanStatusCode, trace } from "@opentelemetry/api";
|
||||
import { currentAccessToken } from "../auth/oidc";
|
||||
import type { JobExecution, AuditEntry, ExportRecord } from "./types";
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000";
|
||||
const tracer = trace.getTracer("gateway-frontend-api");
|
||||
|
||||
function authHeaders(): Record<string, string> {
|
||||
@@ -13,7 +12,7 @@ function authHeaders(): Record<string, string> {
|
||||
async function get<T>(path: string, spanName: string): Promise<T> {
|
||||
return tracer.startActiveSpan(spanName, async (span) => {
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE}${path}`, {
|
||||
const resp = await fetch(path, {
|
||||
headers: { Accept: "application/json", ...authHeaders() },
|
||||
});
|
||||
if (!resp.ok) {
|
||||
@@ -36,7 +35,7 @@ async function get<T>(path: string, spanName: string): Promise<T> {
|
||||
async function post<T>(path: string, spanName: string, body: unknown = {}): Promise<T> {
|
||||
return tracer.startActiveSpan(spanName, async (span) => {
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE}${path}`, {
|
||||
const resp = await fetch(path, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -9,7 +9,6 @@ import type {
|
||||
WWIScenario,
|
||||
} from "./types";
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000";
|
||||
const tracer = trace.getTracer("wwi-frontend-api");
|
||||
|
||||
function authHeaders(): Record<string, string> {
|
||||
@@ -20,7 +19,7 @@ function authHeaders(): Record<string, string> {
|
||||
async function get<T>(path: string, spanName: string): Promise<T> {
|
||||
return tracer.startActiveSpan(spanName, async (span) => {
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE}${path}`, {
|
||||
const resp = await fetch(path, {
|
||||
headers: { Accept: "application/json", ...authHeaders() },
|
||||
});
|
||||
if (!resp.ok) {
|
||||
@@ -43,7 +42,7 @@ async function get<T>(path: string, spanName: string): Promise<T> {
|
||||
async function post<T>(path: string, body: unknown, spanName: string): Promise<T> {
|
||||
return tracer.startActiveSpan(spanName, async (span) => {
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE}${path}`, {
|
||||
const resp = await fetch(path, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -11,8 +11,6 @@ import { fetchAppConfig } from "./api/config";
|
||||
import { AuthProvider } from "./auth/AuthContext";
|
||||
import { setupTelemetry } from "./telemetry";
|
||||
|
||||
setupTelemetry();
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
@@ -22,7 +20,13 @@ const queryClient = new QueryClient({
|
||||
},
|
||||
});
|
||||
|
||||
fetchAppConfig().then(() => {
|
||||
fetchAppConfig().then((config) => {
|
||||
setupTelemetry({
|
||||
collectorEndpoint: config.otel_collector_endpoint,
|
||||
serviceName: config.otel_service_name,
|
||||
serviceNamespace: config.otel_service_namespace,
|
||||
deploymentEnvironment: config.deployment_environment,
|
||||
});
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
|
||||
@@ -15,25 +15,19 @@ import { UserInteractionInstrumentation } from "@opentelemetry/instrumentation-u
|
||||
import { XMLHttpRequestInstrumentation } from "@opentelemetry/instrumentation-xml-http-request";
|
||||
import { ZoneContextManager } from "@opentelemetry/context-zone-peer-dep";
|
||||
|
||||
export type TelemetryConfig = {
|
||||
collectorEndpoint: string;
|
||||
serviceName: string;
|
||||
serviceNamespace: string;
|
||||
deploymentEnvironment: string;
|
||||
};
|
||||
|
||||
let initialized = false;
|
||||
|
||||
function escapeRegExp(value: string): string {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
export function setupTelemetry(): void {
|
||||
export function setupTelemetry(config: TelemetryConfig): void {
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
|
||||
const endpoint =
|
||||
import.meta.env.VITE_OTEL_COLLECTOR_ENDPOINT ?? "http://localhost:4318";
|
||||
const serviceName =
|
||||
import.meta.env.VITE_OTEL_SERVICE_NAME ?? "otel-bi-frontend";
|
||||
const serviceNamespace =
|
||||
import.meta.env.VITE_OTEL_SERVICE_NAMESPACE ?? "final-thesis";
|
||||
const apiBaseUrl =
|
||||
import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000";
|
||||
|
||||
propagation.setGlobalPropagator(
|
||||
new CompositePropagator({
|
||||
propagators: [
|
||||
@@ -45,14 +39,14 @@ export function setupTelemetry(): void {
|
||||
|
||||
const provider = new WebTracerProvider({
|
||||
resource: resourceFromAttributes({
|
||||
"service.name": serviceName,
|
||||
"service.namespace": serviceNamespace,
|
||||
"deployment.environment": import.meta.env.MODE,
|
||||
"service.name": config.serviceName,
|
||||
"service.namespace": config.serviceNamespace,
|
||||
"deployment.environment": config.deploymentEnvironment,
|
||||
}),
|
||||
spanProcessors: [
|
||||
new BatchSpanProcessor(
|
||||
new OTLPTraceExporter({
|
||||
url: `${endpoint}/v1/traces`,
|
||||
url: `${config.collectorEndpoint}/v1/traces`,
|
||||
}),
|
||||
),
|
||||
],
|
||||
@@ -65,11 +59,7 @@ export function setupTelemetry(): void {
|
||||
registerInstrumentations({
|
||||
instrumentations: [
|
||||
new DocumentLoadInstrumentation(),
|
||||
new FetchInstrumentation({
|
||||
propagateTraceHeaderCorsUrls: [
|
||||
new RegExp(`^${escapeRegExp(apiBaseUrl)}`),
|
||||
],
|
||||
}),
|
||||
new FetchInstrumentation(),
|
||||
new XMLHttpRequestInstrumentation(),
|
||||
new UserInteractionInstrumentation(),
|
||||
],
|
||||
|
||||
@@ -7,5 +7,8 @@ export default defineConfig({
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
port: 5173,
|
||||
proxy: {
|
||||
"/api": "http://localhost:8000",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user