fix(#338): Standardize API base URL and auth mechanism across components

- Create centralized config module (apps/web/src/lib/config.ts) exporting:
  - API_BASE_URL: Main API server URL from NEXT_PUBLIC_API_URL
  - ORCHESTRATOR_URL: Orchestrator service URL from NEXT_PUBLIC_ORCHESTRATOR_URL
  - Helper functions for building full URLs
- Update client.ts to import from central config
- Update LoginButton.tsx to use API_BASE_URL from config
- Update useWebSocket.ts to use API_BASE_URL from config
- Update AgentStatusWidget.tsx to use ORCHESTRATOR_URL from config
- Update TaskProgressWidget.tsx to use ORCHESTRATOR_URL from config
- Update useGraphData.ts to use API_BASE_URL from config
  - Fixed wrong default port (was 8000, now uses correct 3001)
- Add comprehensive tests for config module
- Update useWebSocket tests to properly mock config module

Refs #338

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-05 18:04:01 -06:00
parent 10d4de5d69
commit 203bd1e7f2
9 changed files with 204 additions and 26 deletions

View File

@@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:3001";
import { API_BASE_URL } from "../config";
/**
* In-memory CSRF token storage

View File

@@ -0,0 +1,91 @@
/**
* Tests for centralized API configuration
*/
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
// Store original env
const originalEnv = { ...process.env };
describe("API Configuration", () => {
beforeEach(() => {
// Reset modules to pick up new env values
vi.resetModules();
});
afterEach(() => {
// Restore original env
process.env = originalEnv;
});
describe("default values", () => {
it("should use default API URL when NEXT_PUBLIC_API_URL is not set", async () => {
delete process.env.NEXT_PUBLIC_API_URL;
delete process.env.NEXT_PUBLIC_ORCHESTRATOR_URL;
const { API_BASE_URL, ORCHESTRATOR_URL } = await import("./config");
expect(API_BASE_URL).toBe("http://localhost:3001");
expect(ORCHESTRATOR_URL).toBe("http://localhost:3001");
});
});
describe("custom values", () => {
it("should use NEXT_PUBLIC_API_URL when set", async () => {
process.env.NEXT_PUBLIC_API_URL = "https://api.example.com";
delete process.env.NEXT_PUBLIC_ORCHESTRATOR_URL;
const { API_BASE_URL, ORCHESTRATOR_URL } = await import("./config");
expect(API_BASE_URL).toBe("https://api.example.com");
// ORCHESTRATOR_URL should fall back to API_BASE_URL
expect(ORCHESTRATOR_URL).toBe("https://api.example.com");
});
it("should use separate NEXT_PUBLIC_ORCHESTRATOR_URL when set", async () => {
process.env.NEXT_PUBLIC_API_URL = "https://api.example.com";
process.env.NEXT_PUBLIC_ORCHESTRATOR_URL = "https://orchestrator.example.com";
const { API_BASE_URL, ORCHESTRATOR_URL } = await import("./config");
expect(API_BASE_URL).toBe("https://api.example.com");
expect(ORCHESTRATOR_URL).toBe("https://orchestrator.example.com");
});
});
describe("helper functions", () => {
it("should build API URLs correctly", async () => {
process.env.NEXT_PUBLIC_API_URL = "https://api.example.com";
delete process.env.NEXT_PUBLIC_ORCHESTRATOR_URL;
const { buildApiUrl } = await import("./config");
expect(buildApiUrl("/api/v1/tasks")).toBe("https://api.example.com/api/v1/tasks");
expect(buildApiUrl("/auth/signin")).toBe("https://api.example.com/auth/signin");
});
it("should build orchestrator URLs correctly", async () => {
process.env.NEXT_PUBLIC_API_URL = "https://api.example.com";
process.env.NEXT_PUBLIC_ORCHESTRATOR_URL = "https://orch.example.com";
const { buildOrchestratorUrl } = await import("./config");
expect(buildOrchestratorUrl("/agents")).toBe("https://orch.example.com/agents");
expect(buildOrchestratorUrl("/tasks/status")).toBe("https://orch.example.com/tasks/status");
});
});
describe("apiConfig object", () => {
it("should expose all configuration through apiConfig", async () => {
process.env.NEXT_PUBLIC_API_URL = "https://api.example.com";
process.env.NEXT_PUBLIC_ORCHESTRATOR_URL = "https://orch.example.com";
const { apiConfig } = await import("./config");
expect(apiConfig.baseUrl).toBe("https://api.example.com");
expect(apiConfig.orchestratorUrl).toBe("https://orch.example.com");
expect(apiConfig.buildUrl("/test")).toBe("https://api.example.com/test");
expect(apiConfig.buildOrchestratorUrl("/test")).toBe("https://orch.example.com/test");
});
});
});

View File

@@ -0,0 +1,60 @@
/**
* Centralized API Configuration
*
* This module provides a single source of truth for all API endpoints and URLs.
* All components should import from here instead of reading environment variables directly.
*
* Environment Variables:
* - NEXT_PUBLIC_API_URL: The main API server URL (default: http://localhost:3001)
* - NEXT_PUBLIC_ORCHESTRATOR_URL: The orchestrator service URL (default: same as API URL)
*/
/**
* Default API server URL for local development
*/
const DEFAULT_API_URL = "http://localhost:3001";
/**
* Main API server URL
* Used for authentication, tasks, events, knowledge, and all core API calls
*/
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL ?? DEFAULT_API_URL;
/**
* Orchestrator service URL
* Used for agent management, task progress, and orchestration features
* Falls back to main API URL if not specified (they may run on the same server)
*/
export const ORCHESTRATOR_URL = process.env.NEXT_PUBLIC_ORCHESTRATOR_URL ?? API_BASE_URL;
/**
* Build a full API endpoint URL
* @param endpoint - The API endpoint path (should start with /)
* @returns The full URL for the endpoint
*/
export function buildApiUrl(endpoint: string): string {
return `${API_BASE_URL}${endpoint}`;
}
/**
* Build a full orchestrator endpoint URL
* @param endpoint - The orchestrator endpoint path (should start with /)
* @returns The full URL for the endpoint
*/
export function buildOrchestratorUrl(endpoint: string): string {
return `${ORCHESTRATOR_URL}${endpoint}`;
}
/**
* Configuration object for convenient access to all URLs
*/
export const apiConfig = {
/** Main API base URL */
baseUrl: API_BASE_URL,
/** Orchestrator service URL */
orchestratorUrl: ORCHESTRATOR_URL,
/** Build full API URL for an endpoint */
buildUrl: buildApiUrl,
/** Build full orchestrator URL for an endpoint */
buildOrchestratorUrl,
} as const;