diff --git a/src/app/site/[site]/sitemap.xml/route.ts b/src/app/site/[site]/sitemap.xml/route.ts new file mode 100644 index 00000000..cd116447 --- /dev/null +++ b/src/app/site/[site]/sitemap.xml/route.ts @@ -0,0 +1,60 @@ +import dayjs from "dayjs" +import { NextRequest } from "next/server" + +import { QueryClient } from "@tanstack/react-query" + +import { getSiteLink } from "~/lib/helpers" +import { NextServerResponse } from "~/lib/server-helper" +import { PageVisibilityEnum } from "~/lib/types" +import { fetchGetPagesBySite } from "~/queries/page.server" +import { fetchGetSite } from "~/queries/site.server" + +export const GET = async (req: NextRequest) => { + const response = new NextServerResponse() + + const queryClient = new QueryClient() + // http://localhost:2222/site/innei-4525/sitemap.xml + // http://innei-4525.localhost:2222/sitemap.xml + // find innei-5425 site handle by regexp + const domainOrSubdomain = + req.nextUrl.pathname.match(/\/site\/(.*)\/sitemap.xml/)?.[1] || + req.headers.get("x-handle") || + // http://innei-4525.localhost:2222/sitemap.xml + req.headers.get("host")?.split(".")[0] || + "" + + if (!domainOrSubdomain) { + return response.status(400).json({ + message: "domainOrSubdomain is not validate", + }) + } + + const site = await fetchGetSite(domainOrSubdomain, queryClient) + + const pages = await fetchGetPagesBySite( + { + characterId: site?.characterId, + type: "post", + visibility: PageVisibilityEnum.Published, + }, + queryClient, + ) + + const link = getSiteLink({ + subdomain: site?.handle || "", + }) + return response.headers({ + "Content-Type": "text/xml", + "Access-Control-Allow-Methods": "GET", + "Access-Control-Allow-Origin": "*", + }).text(` + + ${pages.list?.map((page) => { + return ` + ${link}/${page.metadata.content.slug} + ${dayjs(page.updatedAt).format("YYYY-MM-DD")} + ` + }).join(` + `)} + `) +} diff --git a/src/lib/server-helper.ts b/src/lib/server-helper.ts index 8dc094bf..839a2366 100644 --- a/src/lib/server-helper.ts +++ b/src/lib/server-helper.ts @@ -15,6 +15,7 @@ export const getQuery = (req: Request) => { export class NextServerResponse { #status: number = 200 + #headers = new Headers() constructor() {} status(status: number) { @@ -25,12 +26,9 @@ export class NextServerResponse { json(data: any) { const nextData = JSON.stringify(data) - return new Response(nextData, { - status: this.#status, - headers: { - "Content-Type": "application/json", - }, - }) + this.#headers.set("Content-Type", "application/json") + + return new Response(nextData, this.makeResponseOptions()) } send(data: any) { @@ -42,11 +40,23 @@ export class NextServerResponse { return this.json(data) } - return new Response(data, { status: this.#status }) + return new Response(data, this.makeResponseOptions()) + } + + text(text: string) { + return new Response(text, this.makeResponseOptions()) } end() { - return new Response("", { status: this.#status }) + return new Response("", this.makeResponseOptions()) + } + + headers(headers: Record) { + for (const [key, value] of Object.entries(headers)) { + this.#headers.set(key, value) + } + + return this } rss(data: any, format = "json") { @@ -72,4 +82,11 @@ export class NextServerResponse { }) } } + + private makeResponseOptions() { + return { + status: this.#status, + headers: this.#headers, + } + } } diff --git a/src/middleware.ts b/src/middleware.ts index 4a0f2b2d..ea0f8f2a 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -56,8 +56,7 @@ export default async function middleware(req: NextRequest) { pathname.match(/^\/(workbox|worker|fallback)-\w+\.js(\.map)?$/) || pathname === "/sw.js" || pathname === "/sw.js.map" || - pathname === "/robots.txt" || - pathname === "/sitemap.xml" + pathname === "/robots.txt" ) { return NextResponse.next() } @@ -84,6 +83,7 @@ export default async function middleware(req: NextRequest) { const requestHeaders = new Headers(req.headers) requestHeaders.set("x-pathname", req.nextUrl.pathname) requestHeaders.set("x-search", req.nextUrl.search) + requestHeaders.set("x-handle", tenant.subdomain || "") if (tenant?.subdomain) { const url = req.nextUrl.clone()