Files
helix-engage/src/lib/api-client.ts

96 lines
3.0 KiB
TypeScript

const API_URL = import.meta.env.VITE_API_URL ?? 'http://localhost:4100';
class AuthError extends Error {
constructor(message = 'Authentication required') {
super(message);
this.name = 'AuthError';
}
}
const getStoredToken = (): string | null => localStorage.getItem('helix_access_token');
const getRefreshToken = (): string | null => localStorage.getItem('helix_refresh_token');
const storeTokens = (accessToken: string, refreshToken: string) => {
localStorage.setItem('helix_access_token', accessToken);
localStorage.setItem('helix_refresh_token', refreshToken);
};
const clearTokens = () => {
localStorage.removeItem('helix_access_token');
localStorage.removeItem('helix_refresh_token');
};
export const apiClient = {
async login(email: string, password: string): Promise<{
accessToken: string;
refreshToken: string;
user?: {
id?: string;
email?: string;
firstName?: string;
lastName?: string;
avatarUrl?: string;
role?: string;
platformRoles?: string[];
};
}> {
const response = await fetch(`${API_URL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (!response.ok) {
const data = await response.json().catch(() => ({}));
throw new Error(data.message ?? 'Login failed');
}
const data = await response.json();
storeTokens(data.accessToken, data.refreshToken);
return data;
},
async graphql<T>(query: string, variables?: Record<string, unknown>): Promise<T> {
const token = getStoredToken();
if (!token) throw new AuthError();
const response = await fetch(`${API_URL}/graphql`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({ query, variables }),
});
if (response.status === 401) {
clearTokens();
throw new AuthError();
}
const json = await response.json();
if (json.errors) {
console.error('GraphQL errors:', json.errors);
throw new Error(json.errors[0]?.message ?? 'GraphQL error');
}
return json.data;
},
async healthCheck(): Promise<{ status: string; platform: { reachable: boolean } }> {
try {
const response = await fetch(`${API_URL}/api/health`, { signal: AbortSignal.timeout(3000) });
if (!response.ok) return { status: 'down', platform: { reachable: false } };
return response.json();
} catch {
return { status: 'down', platform: { reachable: false } };
}
},
getStoredToken,
getRefreshToken,
storeTokens,
clearTokens,
isAuthenticated: () => !!getStoredToken(),
};