189 lines
4.1 KiB
TypeScript
189 lines
4.1 KiB
TypeScript
/**
|
|
* API 유틸리티 함수
|
|
* @description API 호출에 필요한 유틸리티 함수를 제공합니다.
|
|
*/
|
|
|
|
import type {
|
|
HttpMethod,
|
|
FetchOptions,
|
|
FetchRequestOptions,
|
|
FetchErrorObject,
|
|
RequestObject,
|
|
} from '#layers/types/api/common'
|
|
|
|
/**
|
|
* 로딩 상태를 시작하는 헬퍼 함수
|
|
*/
|
|
const startLoading = (
|
|
loadingStore: ReturnType<typeof useLoadingStore>,
|
|
loading: FetchOptions['loading']
|
|
) => {
|
|
if (!loadingStore) return
|
|
|
|
if (typeof loading === 'object' && loading.localId) {
|
|
loadingStore.startLocalLoading(loading.localId)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 로딩 상태를 종료하는 헬퍼 함수
|
|
*/
|
|
const stopLoading = (
|
|
loadingStore: ReturnType<typeof useLoadingStore>,
|
|
loading: FetchOptions['loading']
|
|
) => {
|
|
if (!loadingStore) return
|
|
|
|
if (typeof loading === 'object' && loading.localId) {
|
|
loadingStore.stopLocalLoading(loading.localId)
|
|
return
|
|
}
|
|
if (loading === true) {
|
|
loadingStore.finishApiLoading()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 요청 옵션을 구성하는 헬퍼 함수
|
|
*/
|
|
const buildRequestOptions = (
|
|
method: HttpMethod,
|
|
url: string,
|
|
options: FetchOptions
|
|
): FetchRequestOptions => {
|
|
let stoveGameId = ''
|
|
let callerDetail = ''
|
|
|
|
if (import.meta.client) {
|
|
try {
|
|
const gameDataStore = useGameDataStore()
|
|
stoveGameId = gameDataStore.gameData?.game_id || ''
|
|
} catch {
|
|
stoveGameId = ''
|
|
}
|
|
|
|
callerDetail = useCookie('sgs_da_uuid').value || ''
|
|
}
|
|
|
|
const requestOptions: FetchRequestOptions = {
|
|
method,
|
|
headers: {
|
|
'Content-Type': 'application/json;charset=UTF-8',
|
|
},
|
|
}
|
|
|
|
// 쿼리 파라미터 추가
|
|
if (options.query) {
|
|
requestOptions.query = options.query
|
|
}
|
|
|
|
// 플랫폼 환경 API 호출 시 Caller 헤더 추가
|
|
if (url.includes('.onstove.com') || url.includes('.gate8.com')) {
|
|
requestOptions.headers = {
|
|
...requestOptions.headers,
|
|
'Caller-Id': stoveGameId as string,
|
|
'Caller-Detail': callerDetail as string,
|
|
}
|
|
}
|
|
|
|
// 사용자 정의 헤더 추가
|
|
if (options.headers) {
|
|
requestOptions.headers = {
|
|
...requestOptions.headers,
|
|
...(options.headers as Record<string, string>),
|
|
}
|
|
}
|
|
|
|
// 본문 데이터 추가
|
|
if (options.body) {
|
|
requestOptions.body = options.body
|
|
}
|
|
|
|
// 캐시 키 추가
|
|
if (options.key) {
|
|
requestOptions.key = options.key
|
|
}
|
|
|
|
return requestOptions
|
|
}
|
|
|
|
/**
|
|
* 공통 API 호출 함수 (리팩토링된 버전)
|
|
*
|
|
* @param method - HTTP 메소드
|
|
* @param url - API URL
|
|
* @param options - 요청 옵션
|
|
* @returns API 응답 데이터
|
|
*/
|
|
export const commonFetch = async (
|
|
method: HttpMethod = 'GET',
|
|
url: string,
|
|
options: FetchOptions = {}
|
|
): Promise<any> => {
|
|
let loadingStore = null
|
|
if (import.meta.client) {
|
|
try {
|
|
loadingStore = useLoadingStore()
|
|
} catch {
|
|
loadingStore = null
|
|
}
|
|
}
|
|
|
|
startLoading(loadingStore, options.loading)
|
|
|
|
try {
|
|
const requestOptions = buildRequestOptions(method, url, options)
|
|
return await $fetch(url, requestOptions)
|
|
} catch (error) {
|
|
console.error('[Exception] apiUtil.commonFetch:', error)
|
|
const fetchError = error as FetchErrorObject
|
|
|
|
return (
|
|
fetchError.data || {
|
|
code: fetchError.statusCode,
|
|
message: fetchError.statusMessage,
|
|
}
|
|
)
|
|
} finally {
|
|
stopLoading(loadingStore, options.loading)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 사용자 IP 조회 함수 (리팩토링된 버전)
|
|
*
|
|
* @param request - 요청 객체
|
|
* @returns 클라이언트 IP 주소
|
|
*/
|
|
export const getTrueClientIp = (request: RequestObject): string => {
|
|
const { headers, socket } = request
|
|
|
|
// IP 헤더 우선순위 목록
|
|
const ipHeaders = [
|
|
'True-Client-IP',
|
|
'X-Real-IP',
|
|
'X-Forwarded-For',
|
|
'Proxy-Client-IP',
|
|
'WL-Proxy-Client-IP',
|
|
'HTTP_CLIENT_IP',
|
|
'HTTP_X_FORWARDED_FOR',
|
|
]
|
|
|
|
// 헤더에서 IP 추출
|
|
for (const header of ipHeaders) {
|
|
const ip = headers[header] || headers[header.toLowerCase()]
|
|
|
|
if (ip?.trim()) {
|
|
// 여러 IP가 있는 경우 첫 번째 IP 반환
|
|
return ip.includes(',') ? ip.split(',')[0].trim() : ip.trim()
|
|
}
|
|
}
|
|
|
|
// 소켓에서 직접 IP 추출
|
|
if (socket.remoteAddress?.trim()) {
|
|
return socket.remoteAddress.trim()
|
|
}
|
|
|
|
return ''
|
|
}
|