mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
<!-- Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md --> adds 2027-track npm package and updates middleware. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added asynchronous visit tracking to improve analytics collection. * **Chores** * Added a new tracking dependency. * Refined middleware: switched to a default export with improved typing and async handling. * Expanded redirect path mappings and improved header handling for more reliable navigation. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
78 lines
2.6 KiB
TypeScript
78 lines
2.6 KiB
TypeScript
import { trackVisit } from '2027-track';
|
|
import { runAsynchronously } from '@stackframe/stack-shared/dist/utils/promises';
|
|
import { NextFetchEvent, NextRequest, NextResponse } from 'next/server';
|
|
|
|
export default function middleware(request: NextRequest, event: NextFetchEvent) {
|
|
const { pathname } = request.nextUrl;
|
|
|
|
// Track AI agent visits
|
|
const trackPromise = trackVisit({
|
|
host: request.headers.get('host') ?? request.nextUrl.host,
|
|
path: pathname,
|
|
userAgent: request.headers.get('user-agent') ?? '',
|
|
accept: request.headers.get('accept') ?? '',
|
|
country: request.headers.get('x-vercel-ip-country') ?? undefined,
|
|
});
|
|
runAsynchronously(trackPromise);
|
|
event.waitUntil(trackPromise);
|
|
|
|
// Redirect old concepts paths to new apps paths
|
|
const movedToApps = [
|
|
'api-keys',
|
|
'emails',
|
|
'oauth',
|
|
'orgs-and-teams',
|
|
'permissions',
|
|
'webhooks',
|
|
];
|
|
|
|
if (pathname.startsWith('/docs/concepts/')) {
|
|
const pageName = pathname.replace('/docs/concepts/', '');
|
|
if (movedToApps.includes(pageName)) {
|
|
const url = request.nextUrl.clone();
|
|
url.pathname = `/docs/apps/${pageName}`;
|
|
return NextResponse.redirect(url, 301); // 301 = permanent redirect
|
|
}
|
|
}
|
|
|
|
// Only apply to docs and api pages (not already .mdx requests)
|
|
// Match /docs, /docs/, /docs/... and /api, /api/, /api/...
|
|
const isDocsPath = pathname === '/docs' || pathname.startsWith('/docs/');
|
|
const isApiPath = pathname === '/api' || pathname.startsWith('/api/');
|
|
|
|
if ((isDocsPath || isApiPath) && !pathname.endsWith('.mdx')) {
|
|
const acceptHeader = request.headers.get('accept') ?? '';
|
|
|
|
// Parse Accept header by splitting on commas to properly handle MIME type ordering
|
|
const acceptTypes = acceptHeader.split(',').map((t: string) => t.trim().split(';')[0]);
|
|
|
|
// Find the index of each MIME type in the Accept header
|
|
const plainIndex = acceptTypes.findIndex(
|
|
(t: string) => t === 'text/plain' || t === 'text/markdown'
|
|
);
|
|
const htmlIndex = acceptTypes.findIndex((t: string) => t === 'text/html');
|
|
|
|
// Prefer markdown if text/plain or text/markdown appears before text/html (or text/html doesn't exist)
|
|
const prefersMarkdown = plainIndex !== -1 && (htmlIndex === -1 || plainIndex < htmlIndex);
|
|
|
|
if (prefersMarkdown) {
|
|
// Rewrite to the LLM markdown endpoint
|
|
const url = request.nextUrl.clone();
|
|
url.pathname = `/llms.mdx${pathname.replace(/^\/(docs|api)/, '')}`;
|
|
|
|
// Preserve query parameters (platform, framework, etc.)
|
|
return NextResponse.rewrite(url);
|
|
}
|
|
}
|
|
|
|
return NextResponse.next();
|
|
}
|
|
|
|
export const config = {
|
|
matcher: [
|
|
'/docs/:path*',
|
|
'/api/:path*',
|
|
],
|
|
};
|
|
|