// 서버 유틸리티 함수들 'use server'; import axios, { AxiosRequestConfig } from 'axios'; import https from 'https'; import { cookies } from 'next/headers' import { ResultDto, TokenData } from '@/dtos/response/common'; import BoardManager from '@/types/forum/boardManager'; import { CLAIM_NAME_IDENTIFIER, CLAIM_EMAIL, CLAIM_NAME } from '@/constants/common'; import { fetchMemberInfo } from '@/lib/api/account'; const API_URL = process.env.API_URL; const agent = new https.Agent({ rejectUnauthorized: false }); // eslint-disable-next-line @typescript-eslint/no-explicit-any /* export async function fetchJson(url: string, options: AxiosRequestConfig = {}): Promise> { try { const actionURL = `${API_URL}${url.startsWith("/") ? url : `/${url}`}`; const httpsAgent = (process.env.NODE_ENV === 'production' ? undefined : agent); const cookieHeader = (await cookies()).getAll().map(c => `${c.name}=${c.value}`).join("; "); const res = await axios({ url: actionURL, ...options, httpsAgent: httpsAgent, // timeout: 10000, headers: { ...(options.headers || {}), Cookie: cookieHeader, } }); const setCookieHeader = res.headers['set-cookie']; if (setCookieHeader) { for (const cookieString of setCookieHeader) { const [cookieName, cookieValue] = cookieString.split("="); (await cookies()).set(cookieName, cookieValue.split(";")[0]); } } return res.data as ResultDto; // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err: any) { let message = '알 수 없는 오류가 발생했습니다.'; let status = 500; if (axios.isAxiosError(err)) { message = err.response?.data?.message || err.message; status = err.response?.status || 500; } else if (err instanceof Error) { message = err.message; } console.warn(`[${status}] ${message}`); return { ok: false, status: status, message: message, data: null, errors: null } satisfies ResultDto; } } */ export async function fetchJson(url: string, options: RequestInit = {}): Promise> { try { const actionURL = `${API_URL}${url.startsWith('/') ? url : `/${url}`}`; const cookie = (await cookies()).getAll().map(c => `${c.name}=${c.value}`).join("; "); const res = await fetch(actionURL, { ...options, headers: { ...(options.headers || {}), Cookie: cookie, 'Accept': 'application/json' }, cache: 'no-store' }); if (res.status === 204) { throw new Error('No Content'); } return await res.json() as ResultDto; } catch (err) { let message = '서버와 통신이 불가합니다.'; const status = 500; if (err instanceof Error) { message = err.message; } console.warn(`[${status}] ${message}`); return { ok: false, status: status, message: message, data: null, errors: null } satisfies ResultDto; } } export async function getAccessToken(): Promise { return (await cookies()).get('accessToken')?.value ?? null; } export async function getRefreshToken(): Promise { return (await cookies()).get('refreshToken')?.value ?? null; } // 로그인 전력 확인 export async function isAuthenticated(): Promise { return Boolean(getAccessToken()); } // API 서버 URL 조회 export async function getAPIUrl(): Promise { return process.env.API_URL as string; } // SignalR 서버 URL 조회 export async function getSignalRUrl(): Promise { return process.env.SIGNALR_URL as string; } // JWT 토큰에서 사용자 정보 추출 export async function getTokenData(): Promise { try { const token = await getAccessToken(); if (!token) { throw new Error('Access token not found'); } const base64URL = token.split('.')[1]; // JWT의 Payload 부분 const base64 = base64URL.replace(/-/g, '+').replace(/_/g, '/'); // Base64 형식 변환 const jsonPayload = decodeURIComponent( atob(base64).split('').map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('') ); const payload = JSON.parse(jsonPayload); return { id: payload[CLAIM_NAME_IDENTIFIER] || null, email: payload[CLAIM_EMAIL] || null, name: payload[CLAIM_NAME] || null }; } catch { return null; } } // 첫번째 오류 조회 export async function getFirstError(errors: Record | null): Promise { return (errors ? Object.values(errors).flat()[0] ?? null : null); } // 서버 응답 메시지 분석 export async function throwError(res: ResultDto): Promise { if (res.ok) { return; } let message:string|null = await getFirstError(res.errors); if (!message && res.message) { message = res.message; } switch (res.status) { case 400: throw Error(message || '잘못된 요청입니다.'); case 401: throw Error('로그인 후 이용해 주세요.'); case 403: throw Error('권한이 없습니다.'); case 404: throw Error('요청하신 페이지를 찾을 수 없습니다.'); } if (message) { throw Error(message); } } // 게시판, 게시글, 댓글 등 권한 확인 export async function checkPermission(permission: number, boardManager: BoardManager[]): Promise { if (permission > -1) { const member = await fetchMemberInfo(); if (member.ok && member.data) { if ( // 게시판 접근 권한이 회원 등급보다 높거나 permission < member.data.memberGrade.order || // 게시판 관리자에 포함되어 있으면 권한이 있음 boardManager.some(manager => member.data!.id && manager.user.email == member.data!.email) ) { return true; } } return false; } return true; }