118 lines
3.2 KiB
TypeScript
118 lines
3.2 KiB
TypeScript
import axios from "axios";
|
|
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
|
|
import Cookies from "js-cookie";
|
|
import { AUTH_USER_ID, CLAIM_NAME } from "./env";
|
|
import { errorInterceptor } from "./error-interceptor";
|
|
|
|
interface ClientConfig {
|
|
baseURL: string;
|
|
siteId: string;
|
|
}
|
|
|
|
class Client {
|
|
private static instance: Client;
|
|
private axiosInstance: AxiosInstance;
|
|
|
|
private constructor(config: ClientConfig) {
|
|
this.axiosInstance = axios.create({
|
|
baseURL: config.baseURL,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"X-Site-Id": config.siteId,
|
|
},
|
|
withCredentials: true,
|
|
});
|
|
|
|
this.axiosInstance.interceptors.request.use((config) => {
|
|
// X-Device-Id 자동 주입 로직 (기기에 고유한 UUID 세팅 및 쿠키/로컬스토리지 보관)
|
|
let deviceId = Cookies.get("device-id");
|
|
if (!deviceId && typeof window !== "undefined") {
|
|
deviceId = localStorage.getItem("device-id") || undefined;
|
|
if (!deviceId) {
|
|
deviceId = generateUUID();
|
|
localStorage.setItem("device-id", deviceId);
|
|
}
|
|
Cookies.set("device-id", deviceId, { expires: 365, path: "/" });
|
|
}
|
|
|
|
if (deviceId) {
|
|
config.headers["X-Device-Id"] = deviceId;
|
|
}
|
|
|
|
const token = Cookies.get(CLAIM_NAME);
|
|
const userId = Cookies.get(AUTH_USER_ID);
|
|
|
|
if (!token || !userId) {
|
|
return config;
|
|
}
|
|
|
|
config.headers[CLAIM_NAME] = token;
|
|
config.headers[AUTH_USER_ID] = userId;
|
|
|
|
return config;
|
|
});
|
|
|
|
this.axiosInstance.interceptors.response.use(
|
|
(response) => response,
|
|
(error) => errorInterceptor(error),
|
|
);
|
|
}
|
|
|
|
public static getInstance(config: ClientConfig): Client {
|
|
if (!Client.instance) {
|
|
Client.instance = new Client(config);
|
|
}
|
|
return Client.instance;
|
|
}
|
|
|
|
public async get<T = unknown, R = AxiosResponse<T>>(
|
|
url: string,
|
|
config?: AxiosRequestConfig,
|
|
): Promise<R> {
|
|
const response = await this.axiosInstance.get<T, R>(url, config);
|
|
return response;
|
|
}
|
|
|
|
public async post<T = unknown, R = AxiosResponse<T>, D = unknown>(
|
|
url: string,
|
|
data?: D,
|
|
config?: AxiosRequestConfig<D>,
|
|
): Promise<R> {
|
|
const response = await this.axiosInstance.post<T, R>(url, data, config);
|
|
return response;
|
|
}
|
|
|
|
public async put<T = unknown, R = AxiosResponse<T>, D = unknown>(
|
|
url: string,
|
|
data?: D,
|
|
config?: AxiosRequestConfig<D>,
|
|
): Promise<R> {
|
|
const response = await this.axiosInstance.put<T, R>(url, data, config);
|
|
return response;
|
|
}
|
|
|
|
public async delete<T = unknown, R = AxiosResponse<T>>(
|
|
url: string,
|
|
config?: AxiosRequestConfig,
|
|
): Promise<R> {
|
|
const response = await this.axiosInstance.delete<T, R>(url, config);
|
|
return response;
|
|
}
|
|
}
|
|
|
|
export type { ClientConfig };
|
|
export { Client };
|
|
|
|
// 기기 식별을 위한 안전한 v4 UUID 생성 폴백 헬퍼 함수
|
|
function generateUUID(): string {
|
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
return crypto.randomUUID();
|
|
}
|
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
const r = (Math.random() * 16) | 0;
|
|
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
|