proposal-system/web/src/lib/api/client.ts
Adam Moussa 6acfdabc8c
style(web): prettier format pass (mechanical)
npx prettier --write . with the new .prettierrc (singleQuote,
printWidth 100). No functional changes — enforced by format:check in
CI from this PR on.
2026-07-13 20:14:47 -04:00

66 lines
1.9 KiB
TypeScript

import axios from 'axios';
import { API_URL, STORAGE_KEY_TOKEN } from '../../constants';
import { AUTH_SESSION_CLEARED_EVENT, clearAuth } from '../auth/authStorage';
const apiClient = axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
apiClient.interceptors.request.use(
(config) => {
const tokenData = window.sessionStorage.getItem(STORAGE_KEY_TOKEN);
if (tokenData) {
try {
const parsed = JSON.parse(tokenData);
if (typeof parsed?.token === 'string') {
config.headers.Authorization = `Bearer ${parsed.token}`;
}
} catch {
// Invalid token data
}
}
return config;
},
(error) => Promise.reject(error),
);
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response) {
const { status, data } = error.response;
if (status === 401) {
// Fix: WEB-M2 — terminate the session atomically: clear storage, tell the
// AuthProvider to drop in-memory state, then hard-redirect as belt-and-braces.
clearAuth();
window.dispatchEvent(new Event(AUTH_SESSION_CLEARED_EVENT));
window.location.href = '/login';
return Promise.reject(new Error('Session expired. Please log in again.'));
}
if (status === 403) {
return Promise.reject(new Error('You do not have permission to perform this action.'));
}
if (status === 404) {
return Promise.reject(new Error('The requested resource was not found.'));
}
const message = data?.detail || data?.title || data?.message || 'An error occurred';
return Promise.reject(new Error(message));
}
if (error.request) {
return Promise.reject(new Error('No response from server. Please check your connection.'));
}
return Promise.reject(error);
},
);
export default apiClient;