mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 06:14:59 +00:00
- codegen: Low-code React app with JSON-driven component system - packagerepo: Schema-driven package repository with backend/frontend - postgres: Next.js app with Drizzle ORM and PostgreSQL Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
89 lines
2.6 KiB
TypeScript
89 lines
2.6 KiB
TypeScript
import type { NextFetchEvent, NextRequest } from 'next/server';
|
|
import { detectBot } from '@arcjet/next';
|
|
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
|
|
import createMiddleware from 'next-intl/middleware';
|
|
import { NextResponse } from 'next/server';
|
|
import arcjet from '@/libs/Arcjet';
|
|
import { routing } from './libs/I18nRouting';
|
|
|
|
const handleI18nRouting = createMiddleware(routing);
|
|
|
|
const isProtectedRoute = createRouteMatcher([
|
|
'/dashboard(.*)',
|
|
'/:locale/dashboard(.*)',
|
|
]);
|
|
|
|
const isAuthPage = createRouteMatcher([
|
|
'/sign-in(.*)',
|
|
'/:locale/sign-in(.*)',
|
|
'/sign-up(.*)',
|
|
'/:locale/sign-up(.*)',
|
|
]);
|
|
|
|
// Admin routes that should bypass i18n routing
|
|
const ADMIN_ROUTE_PREFIX = '/admin';
|
|
const ADMIN_API_PREFIX = '/api/admin';
|
|
|
|
// Improve security with Arcjet
|
|
const aj = arcjet.withRule(
|
|
detectBot({
|
|
mode: 'LIVE',
|
|
// Block all bots except the following
|
|
allow: [
|
|
// See https://docs.arcjet.com/bot-protection/identifying-bots
|
|
'CATEGORY:SEARCH_ENGINE', // Allow search engines
|
|
'CATEGORY:PREVIEW', // Allow preview links to show OG images
|
|
'CATEGORY:MONITOR', // Allow uptime monitoring services
|
|
],
|
|
}),
|
|
);
|
|
|
|
export default async function proxy(
|
|
request: NextRequest,
|
|
event: NextFetchEvent,
|
|
) {
|
|
// Skip i18n routing for admin and API routes
|
|
if (request.nextUrl.pathname.startsWith(ADMIN_ROUTE_PREFIX)
|
|
|| request.nextUrl.pathname.startsWith(ADMIN_API_PREFIX)) {
|
|
return NextResponse.next();
|
|
}
|
|
|
|
// Verify the request with Arcjet
|
|
// Use `process.env` instead of Env to reduce bundle size in middleware
|
|
if (process.env.ARCJET_KEY) {
|
|
const decision = await aj.protect(request);
|
|
|
|
if (decision.isDenied()) {
|
|
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
|
}
|
|
}
|
|
|
|
// Clerk keyless mode doesn't work with i18n, this is why we need to run the middleware conditionally
|
|
if (
|
|
isAuthPage(request) || isProtectedRoute(request)
|
|
) {
|
|
return clerkMiddleware(async (auth, req) => {
|
|
if (isProtectedRoute(req)) {
|
|
const locale = req.nextUrl.pathname.match(/(\/.*)\/dashboard/)?.at(1) ?? '';
|
|
|
|
const signInUrl = new URL(`${locale}/sign-in`, req.url);
|
|
|
|
await auth.protect({
|
|
unauthenticatedUrl: signInUrl.toString(),
|
|
});
|
|
}
|
|
|
|
return handleI18nRouting(req);
|
|
})(request, event);
|
|
}
|
|
|
|
return handleI18nRouting(request);
|
|
}
|
|
|
|
export const config = {
|
|
// Match all pathnames except for
|
|
// - … if they start with `/_next`, `/_vercel` or `monitoring`
|
|
// - … the ones containing a dot (e.g. `favicon.ico`)
|
|
matcher: '/((?!_next|_vercel|monitoring|.*\\..*).*)',
|
|
};
|