File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
import axios, {type AxiosRequestConfig, type AxiosResponse} from 'axios';
import type {ApiResponse} from "../type/apiResponse.ts";
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? '';
const ADMIN_LOGIN_URL = '/uat/uia/actionSecurityLogin.do';
const ADMIN_ID = 'admin';
const ADMIN_PASSWORD = '1';
const MAX_RETRY_COUNT = 3;
class ApiClient {
private loginPromise: Promise<void> | null = null;
private readonly client = axios.create({
baseURL: API_BASE_URL,
withCredentials: true,
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
validateStatus: () => true,
});
private async login() {
if (!this.loginPromise) {
this.loginPromise = this.client.post<ApiResponse<unknown>>(
ADMIN_LOGIN_URL,
new URLSearchParams({
id: ADMIN_ID,
password: ADMIN_PASSWORD,
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
},
).then((response) => {
if (response.status < 200 || response.status >= 300) {
throw new Error(`Login failed: ${response.status}`);
}
const contentType = String(response.headers['content-type'] ?? '');
if (contentType.includes('application/json')) {
const result = response.data;
if (!result.success) {
throw new Error(result.message);
}
}
}).finally(() => {
this.loginPromise = null;
});
}
return this.loginPromise;
}
private needsLogin(response: AxiosResponse<unknown>) {
if (response.status === 401 || response.status === 403) {
return true;
}
const contentType = String(response.headers['content-type'] ?? '');
if (contentType.includes('text/html') && typeof response.data === 'string') {
return (
response.data.includes('/uat/uia/actionMain.do') ||
response.data.includes('actionSecurityLogin.do')
);
}
if (contentType.includes('application/json') && this.isApiResponse(response.data)) {
return !response.data.success && /login|로그인/i.test(response.data.message ?? '');
}
return false;
}
private async request<T>(
config: AxiosRequestConfig,
retryCount = 0,
): Promise<AxiosResponse<ApiResponse<T>>> {
const response = await this.client.request<ApiResponse<T>>(config);
if (this.needsLogin(response)) {
if (retryCount >= MAX_RETRY_COUNT) {
throw new Error('Authentication retry count exceeded');
}
await this.login();
return this.request<T>(config, retryCount + 1);
}
return response;
}
async get<T>(path: string, params?: AxiosRequestConfig['params']): Promise<T> {
const response = await this.request<T>({
url: path,
method: 'GET',
params,
});
return this.parseJson<T>(response);
}
async post<T>(
path: string,
body?: unknown,
): Promise<T> {
const response = await this.request<T>({
url: path,
method: 'POST',
data: body,
headers: {
'Content-Type': 'application/json',
},
});
return this.parseJson<T>(response);
}
private parseJson<T>(response: AxiosResponse<ApiResponse<T>>): T {
if (response.status < 200 || response.status >= 300) {
throw new Error(`API request failed: ${response.status}`);
}
const result = response.data;
if (!this.isApiResponse<T>(result)) {
throw new Error('Invalid API response');
}
if (!result.success) {
throw new Error(result.message ?? 'API request failed');
}
return result.data;
}
private isApiResponse<T>(data: unknown): data is ApiResponse<T> {
return typeof data === 'object' && data !== null && 'success' in data;
}
}
export const apiClient = new ApiClient();