fixes
This commit is contained in:
parent
41105e2215
commit
0ce149bf89
@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios';
|
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||||
import type { OffsetOrder, Portfolio } from '../types';
|
import type { OffsetOrder, Portfolio } from '../types';
|
||||||
import { config } from '../utils/config';
|
import { config } from '../utils/config';
|
||||||
|
|
||||||
@ -44,9 +44,13 @@ const DEFAULT_PORTFOLIO: Portfolio = {
|
|||||||
// Create API client with error handling, timeout, and retry logic
|
// Create API client with error handling, timeout, and retry logic
|
||||||
const createApiClient = () => {
|
const createApiClient = () => {
|
||||||
if (!config.wrenApiKey) {
|
if (!config.wrenApiKey) {
|
||||||
|
console.error('Wren API token is missing! Token:', config.wrenApiKey);
|
||||||
|
console.error('Environment:', window?.env ? JSON.stringify(window.env) : 'No window.env available');
|
||||||
throw new Error('Wren API token is not configured');
|
throw new Error('Wren API token is not configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('[wrenClient] Creating API client with key:', config.wrenApiKey ? '********' + config.wrenApiKey.slice(-4) : 'MISSING');
|
||||||
|
|
||||||
const client = axios.create({
|
const client = axios.create({
|
||||||
baseURL: 'https://api.wren.co/v1',
|
baseURL: 'https://api.wren.co/v1',
|
||||||
headers: {
|
headers: {
|
||||||
@ -54,40 +58,45 @@ const createApiClient = () => {
|
|||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
timeout: 10000, // 10 second timeout
|
timeout: 10000, // 10 second timeout
|
||||||
validateStatus: (status) => status >= 200 && status < 500, // Handle 4xx errors gracefully
|
validateStatus: (status: number) => status >= 200 && status < 500, // Handle 4xx errors gracefully
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add request interceptor for logging
|
// Add request interceptor for logging
|
||||||
client.interceptors.request.use(
|
client.interceptors.request.use(
|
||||||
(config) => {
|
(config: AxiosRequestConfig) => {
|
||||||
if (!config.headers.Authorization) {
|
if (!config.headers?.Authorization) {
|
||||||
throw new Error('API token is required');
|
throw new Error('API token is required');
|
||||||
}
|
}
|
||||||
|
console.log('[wrenClient] Making API request to:', config.url);
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
(error) => {
|
(error: Error) => {
|
||||||
console.error('Request configuration error:', error.message);
|
console.error('[wrenClient] Request configuration error:', error.message);
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add response interceptor for error handling
|
// Add response interceptor for error handling
|
||||||
client.interceptors.response.use(
|
client.interceptors.response.use(
|
||||||
(response) => response,
|
(response: AxiosResponse) => {
|
||||||
(error) => {
|
console.log('[wrenClient] Received API response:', response.status);
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
(error: unknown) => {
|
||||||
if (axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
if (error.code === 'ECONNABORTED') {
|
if (error.code === 'ECONNABORTED') {
|
||||||
console.warn('Request timeout, using fallback data');
|
console.warn('[wrenClient] Request timeout, using fallback data');
|
||||||
return Promise.resolve({ data: { portfolios: [DEFAULT_PORTFOLIO] } });
|
return Promise.resolve({ data: { portfolios: [DEFAULT_PORTFOLIO] } });
|
||||||
}
|
}
|
||||||
if (!error.response) {
|
if (!error.response) {
|
||||||
console.warn('Network error, using fallback data');
|
console.warn('[wrenClient] Network error, using fallback data');
|
||||||
return Promise.resolve({ data: { portfolios: [DEFAULT_PORTFOLIO] } });
|
return Promise.resolve({ data: { portfolios: [DEFAULT_PORTFOLIO] } });
|
||||||
}
|
}
|
||||||
if (error.response.status === 401) {
|
if (error.response.status === 401) {
|
||||||
console.warn('Authentication failed, using fallback data');
|
console.warn('[wrenClient] Authentication failed, using fallback data');
|
||||||
return Promise.resolve({ data: { portfolios: [DEFAULT_PORTFOLIO] } });
|
return Promise.resolve({ data: { portfolios: [DEFAULT_PORTFOLIO] } });
|
||||||
}
|
}
|
||||||
|
console.error('[wrenClient] API error:', error.response?.status, error.response?.data);
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
@ -104,24 +113,26 @@ const logError = (error: unknown) => {
|
|||||||
message: error.message,
|
message: error.message,
|
||||||
stack: error.stack
|
stack: error.stack
|
||||||
};
|
};
|
||||||
console.error('API Error:', JSON.stringify(errorInfo, null, 2));
|
console.error('[wrenClient] API Error:', JSON.stringify(errorInfo, null, 2));
|
||||||
} else {
|
} else {
|
||||||
console.error('Unknown error:', String(error));
|
console.error('[wrenClient] Unknown error:', String(error));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getPortfolios(): Promise<Portfolio[]> {
|
export async function getPortfolios(): Promise<Portfolio[]> {
|
||||||
try {
|
try {
|
||||||
if (!config.wrenApiKey) {
|
if (!config.wrenApiKey) {
|
||||||
console.warn('No Wren API token configured, using fallback portfolio');
|
console.warn('[wrenClient] No Wren API token configured, using fallback portfolio');
|
||||||
return [DEFAULT_PORTFOLIO];
|
return [DEFAULT_PORTFOLIO];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('[wrenClient] Getting portfolios with token:', config.wrenApiKey ? '********' + config.wrenApiKey.slice(-4) : 'MISSING');
|
||||||
|
|
||||||
const api = createApiClient();
|
const api = createApiClient();
|
||||||
const response = await api.get('/portfolios');
|
const response = await api.get('/portfolios');
|
||||||
|
|
||||||
if (!response.data?.portfolios?.length) {
|
if (!response.data?.portfolios?.length) {
|
||||||
console.warn('No portfolios returned from API, using fallback');
|
console.warn('[wrenClient] No portfolios returned from API, using fallback');
|
||||||
return [DEFAULT_PORTFOLIO];
|
return [DEFAULT_PORTFOLIO];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +162,7 @@ export async function getPortfolios(): Promise<Portfolio[]> {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error);
|
logError(error);
|
||||||
console.warn('Failed to fetch portfolios from API, using fallback');
|
console.warn('[wrenClient] Failed to fetch portfolios from API, using fallback');
|
||||||
return [DEFAULT_PORTFOLIO];
|
return [DEFAULT_PORTFOLIO];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,9 +174,12 @@ export async function createOffsetOrder(
|
|||||||
): Promise<OffsetOrder> {
|
): Promise<OffsetOrder> {
|
||||||
try {
|
try {
|
||||||
if (!config.wrenApiKey) {
|
if (!config.wrenApiKey) {
|
||||||
|
console.error('[wrenClient] Cannot create order - missing API token');
|
||||||
throw new Error('Carbon offset service is currently unavailable. Please contact support.');
|
throw new Error('Carbon offset service is currently unavailable. Please contact support.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[wrenClient] Creating offset order: portfolio=${portfolioId}, tons=${tons}`);
|
||||||
|
|
||||||
const api = createApiClient();
|
const api = createApiClient();
|
||||||
const response = await api.post('/orders', {
|
const response = await api.post('/orders', {
|
||||||
portfolio_id: portfolioId,
|
portfolio_id: portfolioId,
|
||||||
@ -199,18 +213,31 @@ export async function createOffsetOrder(
|
|||||||
createdAt: order.created_at,
|
createdAt: order.created_at,
|
||||||
dryRun: order.dry_run
|
dryRun: order.dry_run
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error: unknown) {
|
||||||
logError(error);
|
logError(error);
|
||||||
|
|
||||||
if (axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
if (error.code === 'ECONNABORTED') {
|
const axiosError = error as AxiosError;
|
||||||
|
|
||||||
|
console.error('[wrenClient] Axios error details:', {
|
||||||
|
status: axiosError.response?.status,
|
||||||
|
data: axiosError.response?.data,
|
||||||
|
config: {
|
||||||
|
url: axiosError.config?.url,
|
||||||
|
method: axiosError.config?.method,
|
||||||
|
headers: axiosError.config?.headers ? 'Headers present' : 'No headers',
|
||||||
|
baseURL: axiosError.config?.baseURL
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (axiosError.code === 'ECONNABORTED') {
|
||||||
throw new Error('Request timed out. Please try again.');
|
throw new Error('Request timed out. Please try again.');
|
||||||
}
|
}
|
||||||
if (!error.response) {
|
if (!axiosError.response) {
|
||||||
throw new Error('Network error. Please check your connection and try again.');
|
throw new Error('Network error. Please check your connection and try again.');
|
||||||
}
|
}
|
||||||
if (error.response.status === 401) {
|
if (axiosError.response.status === 401) {
|
||||||
throw new Error('Carbon offset service is currently unavailable. Please try again later or contact support.');
|
throw new Error('Carbon offset service authentication failed. Please check your API token.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,19 +5,23 @@ interface Config {
|
|||||||
isProduction: boolean;
|
isProduction: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First try to get from window.env (for containerized environment)
|
// Get environment variables either from window.env (for Docker) or import.meta.env (for development)
|
||||||
// Fall back to Vite's import.meta.env (for development)
|
|
||||||
const getEnv = (key: string): string => {
|
const getEnv = (key: string): string => {
|
||||||
// Remove VITE_ prefix when checking window.env
|
// Extract the name without VITE_ prefix
|
||||||
const windowKey = key.replace('VITE_', '');
|
const varName = key.replace('VITE_', '');
|
||||||
|
|
||||||
// Check if window.env exists and has the variable
|
// First check if window.env exists and has the variable
|
||||||
if (typeof window !== 'undefined' && window.env && window.env[windowKey]) {
|
if (typeof window !== 'undefined' && window.env) {
|
||||||
return window.env[windowKey] || '';
|
// In Docker, the env.sh script has already removed the VITE_ prefix
|
||||||
|
const envValue = window.env[varName];
|
||||||
|
if (envValue) {
|
||||||
|
console.log(`Using ${varName} from window.env`);
|
||||||
|
return envValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to Vite's import.meta.env
|
// Fall back to Vite's import.meta.env (for development)
|
||||||
// Ensure we're only returning string values
|
// Here we need the full name with VITE_ prefix
|
||||||
const value = import.meta.env[key];
|
const value = import.meta.env[key];
|
||||||
return typeof value === 'string' ? value : '';
|
return typeof value === 'string' ? value : '';
|
||||||
};
|
};
|
||||||
@ -37,7 +41,8 @@ export const config: Config = {
|
|||||||
|
|
||||||
// Validate required environment variables
|
// Validate required environment variables
|
||||||
if (!config.wrenApiKey) {
|
if (!config.wrenApiKey) {
|
||||||
console.error('Missing required environment variable: VITE_WREN_API_TOKEN');
|
console.error('Missing required environment variable: WREN_API_TOKEN');
|
||||||
|
console.error('Current environment:', window?.env ? JSON.stringify(window.env) : 'No window.env available');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log config in development
|
// Log config in development
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user