From 41a11c5f83cb6be3256200e7dae218e6c35806c9 Mon Sep 17 00:00:00 2001 From: swve Date: Thu, 26 Sep 2024 23:29:05 +0200 Subject: [PATCH] feat: init sitemaps generation --- apps/web/app/api/sitemap/route.ts | 61 ++++++++++++++++++++++++++++ apps/web/middleware.ts | 24 +++++++++++ apps/web/services/courses/courses.ts | 6 +-- 3 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 apps/web/app/api/sitemap/route.ts diff --git a/apps/web/app/api/sitemap/route.ts b/apps/web/app/api/sitemap/route.ts new file mode 100644 index 00000000..b457ec29 --- /dev/null +++ b/apps/web/app/api/sitemap/route.ts @@ -0,0 +1,61 @@ +import { getUriWithOrg } from '@services/config/config'; +import { getOrgCourses } from '@services/courses/courses'; +import { getOrganizationContextInfo } from '@services/organizations/orgs'; +import { NextRequest, NextResponse } from 'next/server'; + + +export async function GET(request: NextRequest) { + const orgSlug = request.headers.get('X-Sitemap-Orgslug'); + + if (!orgSlug) { + return NextResponse.json({ error: 'Missing X-Sitemap-Orgslug header' }, { status: 400 }); + } + + const orgInfo = await getOrganizationContextInfo(orgSlug, null); + const courses = await getOrgCourses(orgSlug, null); + + const host = request.headers.get('host'); + if (!host) { + return NextResponse.json({ error: 'Missing host header' }, { status: 400 }); + } + + const baseUrl = getUriWithOrg(orgSlug, '/'); + + const sitemapUrls: SitemapUrl[] = [ + { loc: baseUrl, priority: 1.0, changefreq: 'daily' }, + ...courses.map((course: { course_uuid: string }) => ({ + loc: `${baseUrl}course/${course.course_uuid.replace('course_', '')}`, + priority: 0.8, + changefreq: 'weekly' + })) + ]; + + const sitemap = generateSitemap(baseUrl, sitemapUrls); + + return new NextResponse(sitemap, { + headers: { + 'Content-Type': 'application/xml', + }, + }); +} + +interface SitemapUrl { + loc: string; + priority: number; + changefreq: string; + } + + function generateSitemap(baseUrl: string, urls: SitemapUrl[]): string { + const urlEntries = urls.map(({ loc, priority, changefreq }) => ` + + ${loc} + ${priority.toFixed(1)} + ${changefreq} + `).join(''); + + return ` + + ${urlEntries} + `; + } + diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts index 7ce10397..32ce38d6 100644 --- a/apps/web/middleware.ts +++ b/apps/web/middleware.ts @@ -21,6 +21,7 @@ export const config = { * 5. all root files inside /public (e.g. /favicon.ico) */ '/((?!api|_next|fonts|umami|examples|[\\w-]+\\.\\w+).*)', + '/sitemap.xml', ], } @@ -99,6 +100,29 @@ export default async function middleware(req: NextRequest) { } } + if (pathname.startsWith('/sitemap.xml')) { + let orgslug: string; + + if (hosting_mode === 'multi') { + orgslug = fullhost + ? fullhost.replace(`.${LEARNHOUSE_DOMAIN}`, '') + : (default_org as string); + } else { + // Single hosting mode + orgslug = default_org as string; + } + + const sitemapUrl = new URL(`/api/sitemap`, req.url); + + // Create a response object + const response = NextResponse.rewrite(sitemapUrl); + + // Set the orgslug in a header + response.headers.set('X-Sitemap-Orgslug', orgslug); + + return response; + } + // Multi Organization Mode if (hosting_mode === 'multi') { // Get the organization slug from the URL diff --git a/apps/web/services/courses/courses.ts b/apps/web/services/courses/courses.ts index e3f6bd1d..ba6a1e90 100644 --- a/apps/web/services/courses/courses.ts +++ b/apps/web/services/courses/courses.ts @@ -12,12 +12,12 @@ import { */ export async function getOrgCourses( - org_id: number, + org_slug: string, next: any, - access_token: any + access_token?: any ) { const result: any = await fetch( - `${getAPIUrl()}courses/org_slug/${org_id}/page/1/limit/10`, + `${getAPIUrl()}courses/org_slug/${org_slug}/page/1/limit/10`, RequestBodyWithAuthHeader('GET', null, next, access_token) ) const res = await errorHandling(result)