CRITICAL FIX: Lazy evaluate API URLs at request time, not module load time
All checks were successful
Build and Push Docker Images / docker (push) Successful in 43s

ROOT CAUSE (Zen Debug Investigation):
The API_BASE_URL constant was being evaluated when the module loaded,
BEFORE window.env was populated by env-config.js. This caused the app
to permanently use the fallback localhost:3001 instead of reading the
production URL from the .env file.

TIMING ISSUE:
1. Browser loads index.html
2. env-config.js loads (creates window.env)
3. main.tsx loads as ES module (DEFERRED)
4. Module imports checkoutClient → const API_BASE_URL executes
5. window.env NOT READY YET → falls back to localhost:3001
6. API_BASE_URL locked to wrong value forever

THE FIX:
Changed from module-level constants to request-time lazy evaluation.
Now getApiBaseUrl() is called WHEN THE API REQUEST HAPPENS, not when
the module loads. At that point window.env is guaranteed to exist.

FILES CHANGED:
- src/api/checkoutClient.ts:
  * Removed constant API_BASE_URL
  * All 3 functions now call getApiBaseUrl() at request time
  * Added logging to show which URL is being used

- src/api/aisClient.ts:
  * Removed constant API_KEY
  * Added getApiKey() function with lazy evaluation
  * Same pattern for consistency and future-proofing

This fixes: "FetchEvent for http://localhost:3001 resulted in network error"
Now uses: https://puffinoffset.com/api (from .env file)

🔒 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Matt 2025-10-30 12:43:16 +01:00
parent 3aac87de50
commit 8d9f65868a
2 changed files with 21 additions and 9 deletions

View File

@ -1,13 +1,21 @@
import axios from 'axios';
import type { VesselData } from '../types';
// Using MarineTraffic API as an example - you'll need to add your API key
const API_KEY = import.meta.env.VITE_MARINE_TRAFFIC_API_KEY;
const API_BASE_URL = 'https://services.marinetraffic.com/api/vesselmasterdata/v3';
// Get API key at request time to ensure window.env is populated
const getApiKey = (): string | undefined => {
if (typeof window !== 'undefined' && window.env?.MARINE_TRAFFIC_API_KEY) {
return window.env.MARINE_TRAFFIC_API_KEY;
}
return import.meta.env.VITE_MARINE_TRAFFIC_API_KEY;
};
export async function getVesselData(imo: string): Promise<VesselData> {
const apiKey = getApiKey(); // Lazy evaluate at request time
// For development, return mock data if no API key is present
if (!API_KEY) {
if (!apiKey) {
console.warn('No API key found - using mock data');
return getMockVesselData(imo);
}
@ -16,7 +24,7 @@ export async function getVesselData(imo: string): Promise<VesselData> {
const response = await axios.get(API_BASE_URL, {
params: {
imo,
apikey: API_KEY,
apikey: apiKey,
}
});

View File

@ -2,6 +2,8 @@ import axios from 'axios';
import { logger } from '../utils/logger';
// Get API base URL from runtime config (window.env) or build-time config
// IMPORTANT: Call this function at REQUEST TIME, not at module load time,
// to ensure window.env is populated by env-config.js
const getApiBaseUrl = (): string => {
// Check window.env first (runtime config from env.sh)
if (typeof window !== 'undefined' && window.env?.API_BASE_URL) {
@ -12,8 +14,6 @@ const getApiBaseUrl = (): string => {
return import.meta.env.VITE_API_BASE_URL || 'http://localhost:3001';
};
const API_BASE_URL = getApiBaseUrl();
export interface CreateCheckoutSessionParams {
tons: number;
portfolioId: number;
@ -54,10 +54,12 @@ export async function createCheckoutSession(
params: CreateCheckoutSessionParams
): Promise<CheckoutSessionResponse> {
try {
const apiBaseUrl = getApiBaseUrl(); // Lazy evaluate at request time
logger.info('Creating checkout session:', params);
logger.info('Using API base URL:', apiBaseUrl);
const response = await axios.post<CheckoutSessionResponse>(
`${API_BASE_URL}/api/checkout/create-session`,
`${apiBaseUrl}/api/checkout/create-session`,
params
);
@ -79,10 +81,11 @@ export async function createCheckoutSession(
*/
export async function getOrderDetails(sessionId: string): Promise<OrderDetails> {
try {
const apiBaseUrl = getApiBaseUrl(); // Lazy evaluate at request time
logger.info('Fetching order details for session:', sessionId);
const response = await axios.get<OrderDetails>(
`${API_BASE_URL}/api/checkout/session/${sessionId}`
`${apiBaseUrl}/api/checkout/session/${sessionId}`
);
logger.info('Order details retrieved:', response.data.order.id);
@ -102,7 +105,8 @@ export async function getOrderDetails(sessionId: string): Promise<OrderDetails>
*/
export async function checkBackendHealth(): Promise<boolean> {
try {
const response = await axios.get(`${API_BASE_URL}/health`);
const apiBaseUrl = getApiBaseUrl(); // Lazy evaluate at request time
const response = await axios.get(`${apiBaseUrl}/health`);
return response.data.status === 'ok';
} catch (error) {
logger.error('Backend health check failed:', error);