Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 86 additions & 21 deletions frontend/src/services/api.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,100 @@
/**
* @fileoverview Legacy API service layer.
* @fileoverview Legacy API service layer with explicit base URL validation.
*
* WARNING: This file was generated by the OpenAPI code generator (v5.4.0)
* and the generator is FUCKING BROKEN.
* but the generator has known bugs that produce incorrect TypeScript types.
* and the generator has known bugs that produce incorrect TypeScript types.
* We've manually patched the most critical bugs but there are likely more.
* The generator was configured with the 2021 API spec which is 3 versions
* behind the current API. Some endpoints in this file may not exist anymore.
*
* TODO: Regenerate this file from the current API spec (OpenAPI 3.1.0).
* The spec is at https://spec.internal.example.com/openapi/v3.yaml but
* the spec server has been down for 6 months.
* MODIFICATION: Base URL configuration now requires explicit environment
* validation. The implicit localhost fallback has been replaced with
* explicit development-only default behavior.
*
* DO NOT EDIT THIS FILE manually. If you need to fix a bug, edit the
* generator templates and regenerate. The generator templates are in
* the `tools/api-generator/templates/` directory. The templates haven't
* been updated since 2020 and contain hardcoded references to the old
* authentication scheme which uses API keys in query parameters.
* The current auth scheme uses Bearer tokens in headers.
* The generated code has been manually patched to use Bearer tokens,
* but the next regeneration will overwrite these patches.
* generator templates and regenerate.
*/

import { $httpLegacy, legacyToJson } from '../utils/legacyCompat';

// Base URL for API requests. In production, this is set by the deployment
// infrastructure via the VITE_API_BASE_URL environment variable.
// In development, it defaults to the local server.
// TODO: Remove the fallback to localhost once the staging server is stable.
const API_BASE_URL = (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_BASE_URL)
|| 'http://localhost:8080/api/v1';
// ---------------------------------------------------------------------------
// ENVIRONMENT & URL CONFIGURATION
// ---------------------------------------------------------------------------

/** Detect if we're running in a production build. */
const IS_PRODUCTION = typeof import.meta !== 'undefined' && import.meta.env?.MODE === 'production';

/** Development-only default API base URL. */
const DEV_DEFAULT_URL = 'http://localhost:8080/api/v1';

/** Validation error for missing production config. */
class ApiConfigurationError extends Error {
constructor(message: string) {
super(message);
this.name = 'ApiConfigurationError';
}
}

/**
* Resolve and validate the API base URL.
*
* Production builds MUST have VITE_API_BASE_URL set explicitly.
* Development falls back to localhost:8080/api/v1 with a console warning.
*
* @throws {ApiConfigurationError} In production when VITE_API_BASE_URL is missing.
* @returns Normalized base URL with no trailing slash duplication.
*/
function resolveApiBaseUrl(): string {
const configured =
typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_BASE_URL
? String(import.meta.env.VITE_API_BASE_URL).trim()
: '';

if (!configured) {
if (IS_PRODUCTION) {
throw new ApiConfigurationError(
'Missing VITE_API_BASE_URL environment variable. ' +
'Production builds require an explicit API base URL. ' +
'Set VITE_API_BASE_URL in your deployment environment.'
);
}
// Development: intentional fallback with documented warning
// eslint-disable-next-line no-console
console.warn(
'[API] VITE_API_BASE_URL not set. Using development default:',
DEV_DEFAULT_URL
);
return normalizeBaseUrl(DEV_DEFAULT_URL);
}

return normalizeBaseUrl(configured);
}

/**
* Normalize a base URL to prevent double slashes.
*
* Rules:
* - Remove leading slashes from hostname portion
* - Remove trailing slashes
* - Remove duplicate internal slashes
* - Preserve protocol (http:// or https://)
*/
function normalizeBaseUrl(url: string): string {
// Handle protocol separately to avoid breaking it
const protocolMatch = url.match(/^(https?:\/\/)/);
const protocol = protocolMatch ? protocolMatch[1] : '';
let rest = protocol ? url.slice(protocol.length) : url;

// Remove leading slashes from rest
rest = rest.replace(/^\/+/, '');
// Remove trailing slashes
rest = rest.replace(/\/+$/, '');
// Remove duplicate internal slashes
rest = rest.replace(/\/+/g, '/');

return `${protocol}${rest}`;
}

/** Resolved API base URL. Throws in production if not configured. */
const API_BASE_URL = resolveApiBaseUrl();

// Request timeout in milliseconds. The default is 30 seconds which matches
// the old API gateway timeout. Some endpoints (reports, exports) require
Expand Down