/** * πŸ“„ Proxy Route * API ν”„λ‘μ‹œ 라우트 * * - μ„œλ²„ API둜의 μš”μ²­ ν”„λ‘μ‹œ 처리 * - μΏ ν‚€ 기반 인증 토큰 및 λ¦¬ν”„λ ˆμ‹œ 토큰 관리 * - POST 및 PUT μš”μ²­ 핸듀링 * - μ‚¬μš©μž 인증 정보 검증 및 응닡 처리 * - μ—λŸ¬ 핸듀링 및 μƒνƒœ μ½”λ“œ λ°˜ν™˜ * - μ‚¬μ΄νŠΈ ID 및 헀더 μ„€μ • */ import { ACCESS_TOKEN, AUTH_USER_ID, CLAIM_NAME, COOKIE_DOMAIN, REFRESH_TOKEN, SERVER_API_URL, SITE_ID, } from "@/src/shared/config"; import { cookies } from "next/headers"; import { NextResponse } from "next/server"; async function handler( request: Request, params: { url: string[] }, method: "GET" | "POST" | "PUT" | "DELETE", ) { const cookieStore = await cookies(); const headers: Record = { "Content-Type": "application/json", "X-Site-Id": SITE_ID, }; const deviceId = request.headers.get("X-Device-Id"); if (deviceId) { headers["X-Device-Id"] = deviceId; } let body = {}; const isRefreshHandler = params.url.includes("refresh"); // silent refresh if (isRefreshHandler) { const refreshToken = cookieStore.get(REFRESH_TOKEN); headers[CLAIM_NAME] = refreshToken?.value ?? ""; if (!headers[CLAIM_NAME]) { return NextResponse.json( { message: "Can't refresh" }, { status: 401 }, ); } } try { if (!isRefreshHandler) { body = await request.json(); } const result = await fetch( `${SERVER_API_URL}/${params.url.join("/")}`, { method, headers, body: JSON.stringify(body), }, ); if (!result.ok) { return NextResponse.json( { message: "Invalid Credentials" }, { status: 401 }, ); } const accessToken = result.headers.get(ACCESS_TOKEN); const refreshToken = result.headers.get(REFRESH_TOKEN); const userId = result.headers.get(AUTH_USER_ID); if (accessToken && userId) { const response = NextResponse.json({ [ACCESS_TOKEN]: accessToken, [AUTH_USER_ID]: userId, }); if (refreshToken) { cookieStore.set(REFRESH_TOKEN, refreshToken, { httpOnly: true, sameSite: "lax", domain: getCookieDomain(), path: "/", }); } return response; } return NextResponse.json( { message: "Invalid Credentials" }, { status: 401 }, ); } catch (error) { return NextResponse.json( { message: "Server Error", error }, { status: 500 }, ); } } export async function POST( request: Request, props: { params: Promise<{ url: string[] }> }, ) { const params = await props.params; return handler(request, params, "POST"); } export async function PUT( request: Request, props: { params: Promise<{ url: string[] }> }, ) { const params = await props.params; return handler(request, params, "PUT"); } function getCookieDomain(): string | undefined { // κ°œλ°œμ—μ„œλŠ” ν˜„μž¬ μ‚¬μ΄νŠΈ μ „μš©, μš΄μ˜μ—μ„œλŠ” ν™˜κ²½λ³€μˆ˜μ˜ 곡용 도메인을 μ‚¬μš©ν•œλ‹€. return process.env.NODE_ENV === "production" ? COOKIE_DOMAIN || undefined : undefined; }