From b7cc9eca67399ad55932f920413d2dfad2858e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chyeonggkim=E2=80=9D?= <โ€œhyeonggkim@smilegate.comโ€> Date: Tue, 16 Sep 2025 09:45:00 +0900 Subject: [PATCH] feat: add dynamic robots.txt and sitemap.xml route handlers --- layers/server/routes/robots.txt.ts | 87 +++++++++++++++++++++++++++++ layers/server/routes/sitemap.xml.ts | 67 ++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 layers/server/routes/robots.txt.ts create mode 100644 layers/server/routes/sitemap.xml.ts diff --git a/layers/server/routes/robots.txt.ts b/layers/server/routes/robots.txt.ts new file mode 100644 index 0000000..6ece65c --- /dev/null +++ b/layers/server/routes/robots.txt.ts @@ -0,0 +1,87 @@ +// server/routes/robots.txt.ts +type RobotsConfig = { + userAgent?: string | string[] + allow?: string[] + disallow?: string[] + sitemap?: string | string[] + host?: string + cache?: { sMaxAge?: number; staleWhileRevalidate?: number } + } + + export default defineEventHandler(async (event) => { + const host = + (getHeader(event, "host") || getRequestHost(event)).toString() || ""; + const baseDomain = process.env.BASE_DOMAIN || ".onstove.com"; + const isGameAliasExtractable = host.includes(baseDomain); + + if (isGameAliasExtractable) { + const gameAlias = host.split(".")[0]; + console.log("๐Ÿš€ ~ 333 gameAlias:", gameAlias) + } + +// if (gameAlias && gameAlias !== "www") { +// event.context.gameAlias = gameAlias; +// } +// } + // robots ์„ค์ •์„ ์ง์ ‘ ๊ฐ€์ ธ์˜ค๊ธฐ (๋ฏธ๋“ค์›จ์–ด context ์‚ฌ์šฉ) + + let config: RobotsConfig; + + try { + // ์™ธ๋ถ€ API์—์„œ robots ์„ค์ • ๊ฐ€์ ธ์˜ค๊ธฐ + const response = await $fetch('https://static-pubcomm.gate8.com/dev/test0905/dataization/test0905_homepage_brand_siteConfig.json'); + + // robots ์„ค์ • ์ถ”์ถœ + const robotsConfig = response.robots?.rules?.[0] || { + userAgent: "*", + allow: ["/"], + disallow: ["/error2", "/inspection/", "/inspection/*", "/html/*"] + }; + + config = { + userAgent: robotsConfig.userAgent, + allow: robotsConfig.allow, + disallow: robotsConfig.disallow, + sitemap: robotsConfig.sitemap, + cache: { sMaxAge: 300, staleWhileRevalidate: 600 } + }; + } catch (error) { + console.error('Failed to fetch robots config:', error); + + // ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜ + config = { + userAgent: "*", + allow: ["/"], + disallow: ["/error", "/inspection/", "/inspection/*", "/html/*"], + sitemap: ["https://l922.onstove.com/sitemap.xml"], + host: "epic7.onstove.com", + cache: { sMaxAge: 300, staleWhileRevalidate: 600 } + }; + } + + setHeader(event, "Content-Type", "text/plain; charset=utf-8") + + // ์บ์‹œ ํ—ค๋” (CDN ์นœํ™”) + const sMax = config.cache?.sMaxAge ?? 300 + const swr = config.cache?.staleWhileRevalidate ?? 600 + setHeader(event, "Cache-Control", `public, s-maxage=${sMax}, stale-while-revalidate=${swr}`) + + // ์—ฌ๋Ÿฌ user-agent ์ง€์› + const agents = Array.isArray(config.userAgent) ? config.userAgent : [config.userAgent ?? "*"] + + const lines: string[] = [] + for (const ua of agents) { + lines.push(`User-agent: ${ua}`) + for (const p of config.allow ?? []) lines.push(`Allow: ${p}`) + for (const p of config.disallow ?? []) lines.push(`Disallow: ${p}`) + lines.push("") // ๋ธ”๋ก ๊ตฌ๋ถ„ ๊ณต๋ฐฑ + } + + const sitemaps = Array.isArray(config.sitemap) ? config.sitemap : (config.sitemap ? [config.sitemap] : []) + for (const sm of sitemaps) lines.push(`Sitemap: ${sm}`) + if (config.host) lines.push(`Host: ${config.host}`) + + // ๋งˆ์ง€๋ง‰ ๊ฐœํ–‰ + return lines.join("\n").trim() + "\n" + }) + \ No newline at end of file diff --git a/layers/server/routes/sitemap.xml.ts b/layers/server/routes/sitemap.xml.ts new file mode 100644 index 0000000..560ab65 --- /dev/null +++ b/layers/server/routes/sitemap.xml.ts @@ -0,0 +1,67 @@ +// server/routes/sitemap.xml.ts +type SitemapUrl = { + loc: string + lastmod?: string + changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never' + priority?: number +} + +type SitemapConfig = { + urls: SitemapUrl[] + cache?: { sMaxAge?: number; staleWhileRevalidate?: number } +} + +export default defineEventHandler(async (event) => { + const host = (getHeader(event, "host") || getRequestHost(event)).toString() || ""; + const baseDomain = process.env.BASE_DOMAIN || ".onstove.com"; + const isGameAliasExtractable = host.includes(baseDomain); + + let gameAlias = ""; + if (isGameAliasExtractable) { + gameAlias = host.split(".")[0] || ""; + } + + let config: SitemapConfig; + + + try { + // ์™ธ๋ถ€ API์—์„œ sitemap ์„ค์ • ๊ฐ€์ ธ์˜ค๊ธฐ + const response = await $fetch('https://static-pubcomm.gate8.com/dev/test0905/dataization/test0905_homepage_brand_siteConfig.json') as any; + + // sitemap ์„ค์ •์—์„œ urls๋งŒ ์ถ”์ถœ + const sitemapUrls = response.sitemap?.urls || []; + + config = { + urls: sitemapUrls, + cache: { sMaxAge: 3600, staleWhileRevalidate: 7200 } + }; + } catch (error) { + console.error('Failed to fetch sitemap config:', error); + + // ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ๋นˆ ๋ฐฐ์—ด ๋ฐ˜ํ™˜ + config = { + urls: [], + cache: { sMaxAge: 3600, staleWhileRevalidate: 7200 } + }; + } + + setHeader(event, "Content-Type", "application/xml; charset=utf-8"); + + // ์บ์‹œ ํ—ค๋” (CDN ์นœํ™”) + const sMax = config.cache?.sMaxAge ?? 3600; + const swr = config.cache?.staleWhileRevalidate ?? 7200; + setHeader(event, "Cache-Control", `public, s-maxage=${sMax}, stale-while-revalidate=${swr}`); + + // XML ์ƒ์„ฑ + const xml = ` + +${config.urls.map(url => ` + ${url.loc} + ${url.lastmod ? ` ${url.lastmod}` : ''} + ${url.changefreq ? ` ${url.changefreq}` : ''} + ${url.priority ? ` ${url.priority}` : ''} + `).join('\n')} +`; + + return xml; +});