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 | 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 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 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 '' }