import { LRUCache } from 'lru-cache' // import { DEFAULT_LOCALE_COVERAGES } from '@/i18n.config' import { getTrueClientIp } from '#layers/utils/apiUtil' import { ssrGetFinalLocale } from '#layers/utils/localeUtil' import type { ResGetInspectionData, WebInspectionData } from '#layers/types/InspectionType' import { isStaticFile } from '#layers/utils/commonUtil' console.log("๐Ÿš€ ~ setCacheHeaders ~ event.node.res.setHeader:") /** * ์บ์‹œ ์ œ์–ด ํ—ค๋”๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ณตํ†ต ํ•จ์ˆ˜ * * @param event - ์ด๋ฒคํŠธ ๊ฐ์ฒด * @param cacheMode - ์บ์‹œ ๋ชจ๋“œ ์„ค์ • ('no-cache', 'short', 'medium', 'default') * @param customMaxAge - ์ปค์Šคํ…€ max-age ๊ฐ’ (์ดˆ ๋‹จ์œ„) */ function setCacheHeaders( event: { node: { res: { setHeader: (name: string, value: string) => void } } }, cacheMode: 'no-cache' | 'short' | 'medium' | 'default', customMaxAge?: number ): void { // ์›๋ž˜ setHeader ํ•จ์ˆ˜ ์ฐธ์กฐ ์ €์žฅ const originalSetHeader = event.node.res.setHeader // Cache-Control ํ—ค๋” ์„ค์ •๊ฐ’ ๊ฒฐ์ • let cacheControl: string switch (cacheMode) { case 'no-cache': cacheControl = 'no-cache, no-store, must-revalidate' // no-cache ๋ชจ๋“œ์ผ ๋•Œ๋Š” ์ถ”๊ฐ€ ํ—ค๋”๋„ ์„ค์ • event.node.res.setHeader('Pragma', 'no-cache') event.node.res.setHeader('Expires', '0') break case 'short': cacheControl = `public, max-age=${customMaxAge || 10}` break case 'medium': cacheControl = `public, max-age=${customMaxAge || 15}` break case 'default': default: cacheControl = `public, max-age=${customMaxAge || 60}` break } // Cache-Control ํ—ค๋”๋ฅผ ๊ฐ•์ œ๋กœ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด setHeader ๋ฉ”์†Œ๋“œ ์˜ค๋ฒ„๋ผ์ด๋“œ event.node.res.setHeader = function (name: string, value: string) { if (name.toLowerCase() === 'cache-control') { return originalSetHeader.call(this, name, cacheControl) } return originalSetHeader.call(this, name, value) } // ๋ฐ”๋กœ ์บ์‹œ ์ œ์–ด ํ—ค๋” ์ ์šฉ } const cache = new LRUCache({ max: 100, // ์บ์‹œ์— ์ €์žฅํ•  ์ตœ๋Œ€ ํ•ญ๋ชฉ ์ˆ˜ ttl: 1000 * 30 // 30์ดˆ ๋™์•ˆ ์บ์‹œ ์œ ์ง€ }) /** * ์ตœ์ข… ์–ธ์–ด ์ฟ ํ‚ค ์„ธํŒ… * * @param event - ์ด๋ฒคํŠธ ๊ฐ์ฒด * @param finalLocale - ์ตœ์ข… ์–ธ์–ด * @param baseDomain - ๊ธฐ๋ณธ ๋„๋ฉ”์ธ */ function setFinalLocaleCookie(event: any, finalLocale: string, baseDomain: string) { setCookie(event, 'LOCALE', finalLocale.toUpperCase(), { domain: baseDomain, path: '/', maxAge: 60 * 60 * 24 * 365 // 1๋…„ (์ดˆ ๋‹จ์œ„) }) } /** * Locale Middleware ์—ญํ•  ํ•จ์ˆ˜ * * @param event - ์ด๋ฒคํŠธ ๊ฐ์ฒด * @param finalLocale - ์ตœ์ข… ์–ธ์–ด */ function fnLocaleMiddleware(event: any, finalLocale: string) { const path = event?.node.req.url || '' let arrPath = [] let queryString = '' if (path.includes('?')) { // ์ฟผ๋ฆฌ์ŠคํŠธ๋ง ํฌํ•จ ์‹œ ์ˆœ์ˆ˜ ๊ฒฝ๋กœ๋งŒ ์ถ”์ถœ arrPath = path.split('?')[0].split('/') queryString = path.split('?')[1] } else { arrPath = path.split('/') queryString = '' } // ์ตœ์ข… ์–ธ์–ด ์„ธํŒ…๋œ ๊ฒฝ๋กœ ์ƒ์„ฑ const pathLocale = arrPath.length > 1 ? arrPath[1] : '' // URL์—์„œ ํ˜„์žฌ ์–ธ์–ด์™€ ์ตœ์ข… ์–ธ์–ด๊ฐ€ ๋‹ค๋ฅด๋ฉด ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ if (pathLocale !== finalLocale) { let newLocalePath = '' if (pathLocale === '') { newLocalePath = `/${finalLocale}` } else { arrPath[1] = finalLocale newLocalePath = arrPath.join('/') } if (queryString !== '') { newLocalePath += `?${queryString}` } event.node.res.statusCode = 302 event.node.res.setHeader('Location', newLocalePath) event.node.res.end() } } export default defineEventHandler(async (event) => { const config = useRuntimeConfig() const runType = `${config.public.runType}` const iBaseApiUrl = `${config.public.stoveApiUrlServer}` const gameId = `${event.context.gameData?.game_id}` const baseDomain = `${config.public.baseDomain}` if (['local', 'local-gate8', 'dev'].includes(runType)) { // Sandbox ์ด์ƒ ํ™˜๊ฒฝ์—์„œ๋งŒ ๋™์ž‘ ๋ฐ ํ™•์ธ ๊ฐ€๋Šฅ (local, dev๋Š” ํ†ต๊ณผ ์ฒ˜๋ฆฌ) try { // ์–ธ์–ด ์ฝ”๋“œ ์ถ”์ถœ const finalLocale = ssrGetFinalLocale(event?.node.req.url, event.node.req.headers) setFinalLocaleCookie(event, finalLocale, baseDomain) // ------------------------------------------------------------------------------- // [Locale Middleware] // ------------------------------------------------------------------------------- fnLocaleMiddleware(event, finalLocale) } catch (e) { console.error('[Exception] /server/middleware/middleware-global: ', e) } } else { // ------------------------------------------------------------------------------- // [Inspection Middleware] // ------------------------------------------------------------------------------- const fullPath = event.path // 1-1. ์ •์  ํŒŒ์ผ ํŒจ์Šค if (isStaticFile(event.path)) { return } // 1-2. /inspection ํŒจ์Šค if (fullPath.includes('/inspection')) { // ๋ฆฌํ„ด ๋˜๊ธฐ ์ „ ์–ธ์–ด ์ฟ ํ‚ค ์„ธํŒ… const finalLocale = ssrGetFinalLocale(event?.node.req.url, event.node.req.headers) setFinalLocaleCookie(event, finalLocale, baseDomain) return } // 1-3. ํŠน์ • ๊ฒฝ๋กœ ํŒจ์Šค (API, ๋ฆฌ์†Œ์Šค) if ( fullPath.startsWith('/api/') || fullPath.startsWith('/_nuxt/') || fullPath.includes('/assets/') || fullPath.includes('favicon') ) { return } // ์บ์‹œ ํ‚ค ์ƒ์„ฑ const cacheKey = 'inspection' try { // 2. ์–ธ์–ด ์ฝ”๋“œ ์ถ”์ถœ const finalLocale = ssrGetFinalLocale(event?.node.req.url, event.node.req.headers) setFinalLocaleCookie(event, finalLocale, baseDomain) // ์ดˆ๊ธฐํ™” let inspectionData // 3. ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋งŒ๋ฃŒ๋˜์—ˆ์„ ๋•Œ๋งŒ API ํ˜ธ์ถœ if (cache.has(cacheKey)) { inspectionData = cache.get(cacheKey) as WebInspectionData } else { const apiUrl = `${iBaseApiUrl}/pub-comm/v3.0/inspection/${gameId}` // ์ง์ ‘ $fetch ์‚ฌ์šฉ (composable ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ) const response = await $fetch(apiUrl, { method: 'GET', headers: { 'Content-Type': 'application/json' } }) inspectionData = response?.value?.inspection as WebInspectionData console.log("๐Ÿš€ 00000 inspectionData:", inspectionData) cache.set(cacheKey, inspectionData) // ์บ์‹œ์— ์ €์žฅ } // 4. ํ˜„์žฌ ์‹œ๊ฐ„๊ณผ ์ ๊ฒ€ ๊ธฐ๊ฐ„ ๋น„๊ต const currentTime = Date.now() const tsStartDate = inspectionData?.ts_start_date || 0 const tsEndDate = inspectionData?.ts_end_date || 0 const timeUntilInspectionSeconds = Math.floor((tsStartDate - currentTime) / 1000) // 5. ์ ๊ฒ€ ์ƒํƒœ๋ณ„ ์บ์‹œ ์„ค์ • if (inspectionData?.inspection_status === 1 && currentTime >= tsStartDate && currentTime <= tsEndDate) { /** * ์ ๊ฒ€ ์ค‘์ธ ๊ฒฝ์šฐ * - ์ ๊ฒ€ ์ƒํƒœ๊ฐ€ 1์ด๊ณ  ํ˜„์žฌ ์‹œ๊ฐ„์ด ์ ๊ฒ€ ์‹œ์ž‘๊ณผ ์ข…๋ฃŒ ์‚ฌ์ด์— ์žˆ๋Š”์ง€ ํ™•์ธ * - ์ ๊ฒ€ URL ๊ฒฝ๋กœ๊ฐ€ ์•„๋‹ ๊ฒฝ์šฐ no-cache ์„ค์ • * - ํ™”์ดํŠธ ๋ฆฌ์ŠคํŠธ ์ฒดํฌ */ // ์ ๊ฒ€ url path ๊ฐ€ ์•„๋‹ ๊ฒฝ์šฐ, no-cache ์„ค์ • const inspectionPath = `/${finalLocale}/inspection` if (fullPath !== inspectionPath) { setCacheHeaders(event, 'no-cache') } // ์ ๊ฒ€ ์ค‘์ผ ๋•Œ IP ํ•„ํ„ฐ๋ง ํ™œ์„ฑํ™” ์—ฌ๋ถ€ ํ™•์ธ if (inspectionData?.ip_filter_use_yn === 'Y') { const clientIP = getTrueClientIp(event.node.req) // ํ—ˆ์šฉ๋œ IP ๋ชฉ๋ก ํ™•์ธ if (!inspectionData?.ip_filter_list?.includes(clientIP)) { // ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ IP์ธ ๊ฒฝ์šฐ ์ ๊ฒ€ ํŽ˜์ด์ง€๋กœ ์ด๋™ event.node.res.statusCode = 302 event.node.res.setHeader('Location', inspectionPath) event.node.res.end() } else { // ํ™”์ดํŠธ ๋ฆฌ์ŠคํŠธ์ธ ๊ฒฝ์šฐ // ------------------------------------------------------------------------------- // [Locale Middleware] // ------------------------------------------------------------------------------- fnLocaleMiddleware(event, finalLocale) } } else { event.node.res.statusCode = 302 event.node.res.setHeader('Location', inspectionPath) event.node.res.end() } } else { /** * ์ ๊ฒ€์ด ์•„๋‹Œ ๊ฒฝ์šฐ * - ํ™ˆ ๊ฒฝ๋กœ๋Š” no-cache * - ์ ๊ฒ€ ์˜ˆ์ • ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ์บ์‹œ ์„ค์ • * - ์ ๊ฒ€ 5๋ถ„ ์ „: ์งง์€ ์บ์‹œ (10์ดˆ) * - ์ ๊ฒ€ 30๋ถ„ ์ „: ์ค‘๊ฐ„ ์บ์‹œ (15์ดˆ) * - ์ ๊ฒ€ 30๋ถ„ ์ดํ›„: ๊ธฐ๋ณธ ์บ์‹œ (60์ดˆ) */ // ํ™ˆ ๊ฒฝ๋กœ: ์บ์‹œ ์—†์Œ const isHomePath = [ '', '/' //, ...Object.values(DEFAULT_LOCALE_COVERAGES).flatMap((locale) => [`/${locale}`, `/${locale}/`]) ].includes(fullPath) if (isHomePath) { setCacheHeaders(event, 'no-cache') } else { // ์ ๊ฒ€ ์˜ˆ์ • ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ์บ์‹œ ์„ค์ • if (tsStartDate > 0 && timeUntilInspectionSeconds > 0) { if (timeUntilInspectionSeconds < 300) { // ์ ๊ฒ€ 5๋ถ„ ์ „: ์งง์€ ์บ์‹œ (10์ดˆ) setCacheHeaders(event, 'short', 10) } else if (timeUntilInspectionSeconds < 1800) { // ์ ๊ฒ€ 30๋ถ„ ์ „: ์ค‘๊ฐ„ ์บ์‹œ (15์ดˆ) setCacheHeaders(event, 'medium', 15) } else { // ์ ๊ฒ€ 30๋ถ„ ์ดํ›„: ๊ธฐ๋ณธ ์บ์‹œ (60์ดˆ) setCacheHeaders(event, 'default') } } } // ------------------------------------------------------------------------------- // [Locale Middleware] // ------------------------------------------------------------------------------- fnLocaleMiddleware(event, finalLocale) } // ์ •์ƒ ์ ‘์† ํ—ˆ์šฉ } catch (e) { console.error('[Exception] /server/middleware/middleware-02-global: ', e) } } })