docs: Add content negotiation to apps/wiki
This commit is contained in:
@@ -11,7 +11,8 @@ export async function GET(_req: Request, props: {params: Promise<{slug?: string[
|
||||
|
||||
return new Response(await getLLMText(page), {
|
||||
headers: {
|
||||
'Content-Type': 'text/markdown',
|
||||
'Content-Type': 'text/markdown; charset=utf-8',
|
||||
'Vary': 'Accept',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
type Negotiated = 'markdown' | 'html' | 'none';
|
||||
|
||||
function parseAccept(accept: string): Array<{ type: string; q: number }> {
|
||||
return accept.split(',').map(part => {
|
||||
const segments = part.trim().split(';');
|
||||
const type = segments[0]?.trim().toLowerCase() ?? '';
|
||||
const qParam = segments.slice(1).find(s => s.trim().startsWith('q='));
|
||||
const q = qParam ? parseFloat(qParam.split('=')[1] ?? '1') : 1;
|
||||
return { type, q: isNaN(q) ? 1 : q };
|
||||
});
|
||||
}
|
||||
|
||||
function getQ(types: Array<{ type: string; q: number }>, target: string): number {
|
||||
const exact = types.find(t => t.type === target);
|
||||
if (exact) return exact.q;
|
||||
const [main] = target.split('/');
|
||||
const sub = types.find(t => t.type === `${main}/*`);
|
||||
if (sub) return sub.q;
|
||||
const wildcard = types.find(t => t.type === '*/*');
|
||||
return wildcard ? wildcard.q : -1;
|
||||
}
|
||||
|
||||
function negotiate(accept: string): Negotiated {
|
||||
if (!accept) return 'html';
|
||||
const types = parseAccept(accept);
|
||||
const mdQ = getQ(types, 'text/markdown');
|
||||
const htmlQ = getQ(types, 'text/html');
|
||||
if (mdQ <= 0 && htmlQ <= 0) return 'none';
|
||||
if (mdQ > 0 && mdQ >= htmlQ) return 'markdown';
|
||||
return 'html';
|
||||
}
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const accept = request.headers.get('accept') ?? '';
|
||||
const { pathname } = request.nextUrl;
|
||||
const result = negotiate(accept);
|
||||
|
||||
if (result === 'none') {
|
||||
return new NextResponse(null, { status: 406, headers: { 'Vary': 'Accept' } });
|
||||
}
|
||||
|
||||
if (result === 'markdown') {
|
||||
const rewriteUrl = new URL(`/llms.mdx${pathname}`, request.url);
|
||||
const response = NextResponse.rewrite(rewriteUrl);
|
||||
response.headers.set('Vary', 'Accept');
|
||||
return response;
|
||||
}
|
||||
|
||||
const response = NextResponse.next();
|
||||
response.headers.set('Vary', 'Accept');
|
||||
return response;
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: ['/((?!llms\\.mdx|api/search|_next|.*\\.(?:png|jpg|jpeg|gif|svg|ico|webp|woff|woff2|ttf|css|js)).*)',],
|
||||
};
|
||||
Reference in New Issue
Block a user