Files
web-temp/layers/utils/apiUtil.ts
2025-09-09 03:34:09 +00:00

182 lines
4.4 KiB
TypeScript

import { ref } from "vue";
/**
* 공통 API 호출
*
* @param {string} method - 요청 메소드 (GET, POST 등)
* @param {string} url - API URL
* @param {object} query - 요청 쿼리 파라미터
* @param {object} headers - 요청 헤더
* @param {object} body - 요청 본문
* @param {string} key - 캐시 키
* @param {object} loading - 로딩 설정
*/
export const commonFetch = async (
method:
| "GET"
| "HEAD"
| "PATCH"
| "POST"
| "PUT"
| "DELETE"
| "CONNECT"
| "OPTIONS"
| "TRACE" = "GET", // Required
url: string, // Required
{
query, // Optional
headers, // Optional
body, // Optional
key, // Optional
loading = false, // Optional
}: {
query?: object | null;
headers?: object | null;
body?: object | null;
key?: string | null;
loading?: { localId?: string } | boolean;
} = {}
) => {
let result = null;
const currCallerId = ref("");
const currCallerDetail = ref("");
// 로딩 스토어 가져오기 (클라이언트에서만)
let loadingStore: ReturnType<typeof useLoadingStore> | null = null;
if (import.meta.client) {
try {
loadingStore = useLoadingStore();
// 로딩 시작
loadingStore.setLoading(true);
if (loading && loadingStore) {
if (typeof loading === "object" && loading.localId) {
loadingStore.startLocalLoading(loading.localId);
} else {
loadingStore.startFullLoading();
}
}
} catch (e) {
console.warn("[Warning] Loading store not available:", e);
}
}
try {
const { callerId, callerDetail } = storeToRefs(useCallerInfoStore());
currCallerId.value = `${callerId.value}`;
currCallerDetail.value = `${callerDetail.value}`;
} catch (e) {
// SSR: pinia store 생성 전이므로 빈 값('') 세팅
}
try {
const options: {
method:
| "GET"
| "HEAD"
| "PATCH"
| "POST"
| "PUT"
| "DELETE"
| "CONNECT"
| "OPTIONS"
| "TRACE";
headers: Record<string, string>;
query?: object;
body?: object;
key?: string;
} = {
method,
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
};
if (query) {
options.query = query;
}
// 플랫폼 환경 API 호출 시 Caller-Id, Caller-Detail 헤더 추가
if (url.includes(".onstove.com") || url.includes(".gate8.com")) {
const callerInfo = {
"Caller-Id": `${currCallerId.value}`,
"Caller-Detail": `${currCallerDetail.value}`,
};
options.headers = { ...options.headers, ...callerInfo };
}
if (headers) {
options.headers = { ...options.headers, ...headers };
}
if (body) {
options.body = body;
}
if (key) {
options.key = key;
}
result = await $fetch(url, options);
} catch (e: unknown) {
console.error("[Exception] apiUtil.commonFetch: ", e);
const error = e as {
data?: unknown;
statusCode?: number;
statusMessage?: string;
};
result = error.data || {
code: error.statusCode,
message: error.statusMessage,
};
} finally {
// 로딩 종료
if (loadingStore) {
loadingStore.setLoading(false);
if (loading) {
if (typeof loading === "object" && loading.localId) {
loadingStore.stopLocalLoading(loading.localId);
} else {
loadingStore.stopFullLoading();
}
}
}
}
return result;
};
/**
* 사용자 IP 조회
*
* @param {object} request - 요청 객체
*/
export const getTrueClientIp = (request: {
headers: Record<string, string>;
socket: { remoteAddress?: string };
}) => {
const requestHeaders = request.headers;
const targetHeaders = [
"True-Client-IP",
"X-Real-IP",
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_CLIENT_IP",
"HTTP_X_FORWARDED_FOR",
];
for (const targetHeader of targetHeaders) {
let ip =
requestHeaders[targetHeader] ||
requestHeaders[targetHeader.toLowerCase()];
if (ip !== undefined && ip != null && ip !== "") {
if (ip.includes(",")) {
ip = ip.split(",")[0];
}
return ip;
}
}
if (
request.socket.remoteAddress !== undefined &&
request.socket.remoteAddress != null &&
request.socket.remoteAddress !== ""
) {
return request.socket.remoteAddress;
}
return "";
};