fonts-vault/middleware.ts
bo.yu 2348e8dc56 chore(admin): remove documentation and implementation files
- Delete dashboard structure documentation (DASHBOARD_STRUCTURE.md)
- Delete implementation summary files from admin, brands, categories, sync, and docs modules
- Delete admin dashboard test file (admin-dashboard.test.tsx)
- Delete auth testing documentation (TESTING.md)
- Delete database README documentation
- Remove task completion tracking file from sync module
- Clean up implementation artifacts and documentation clutter to maintain repository hygiene
2025-11-26 16:56:19 +08:00

123 lines
3.5 KiB
TypeScript

import { auth } from '@/lib/auth/config';
import { NextResponse } from 'next/server';
/**
* Next.js Middleware for authentication and authorization
*
* Protected routes:
* - /admin/* - Requires authentication
* - API routes with POST, PUT, DELETE methods - Requires authentication
*
* Public routes:
* - / - Home page
* - /fonts/* - Font browsing and details
* - /docs/* - Documentation
* - /login - Login page
* - GET /api/fonts - Font listing
* - GET /api/css - CSS generation
* - GET /api/brands - Brand listing
* - GET /api/categories - Category listing
*/
// Define public paths that don't require authentication
const publicPaths = ['/', '/login', '/fonts', '/docs'];
// Define public API paths (GET only)
const publicApiPaths = ['/api/auth', '/api/fonts', '/api/css', '/api/brands', '/api/categories'];
// Check if a path matches any pattern in the list
function matchesPath(pathname: string, patterns: string[]): boolean {
return patterns.some((pattern) => {
if (pattern.endsWith('*')) {
return pathname.startsWith(pattern.slice(0, -1));
}
return pathname === pattern || pathname.startsWith(`${pattern}/`);
});
}
export default auth((req) => {
const { pathname } = req.nextUrl;
const isLoggedIn = !!req.auth;
const disableAuth = process.env.DISABLE_AUTH === 'true';
// Allow access to static files and Next.js internals
if (pathname.startsWith('/_next') || pathname.startsWith('/static') || pathname.includes('.')) {
return NextResponse.next();
}
// Check if this is an admin route
const isAdminRoute = pathname.startsWith('/admin');
// Check if this is an API route
const isApiRoute = pathname.startsWith('/api');
// Handle admin routes - require authentication
if (isAdminRoute) {
if (disableAuth) {
return NextResponse.next();
}
if (!isLoggedIn) {
const loginUrl = new URL('/login', req.url);
loginUrl.searchParams.set('callbackUrl', pathname);
return NextResponse.redirect(loginUrl);
}
return NextResponse.next();
}
// Handle API routes
if (isApiRoute) {
const method = req.method;
// Allow all auth-related API calls
if (pathname.startsWith('/api/auth')) {
return NextResponse.next();
}
// For public API paths, allow GET requests without authentication
if (method === 'GET' && matchesPath(pathname, publicApiPaths)) {
return NextResponse.next();
}
// All other API routes (POST, PUT, DELETE, or non-public GET) require authentication
if (!isLoggedIn && !disableAuth) {
return NextResponse.json(
{
code: 401,
message: '未授权访问,请先登录',
status: 'error',
},
{ status: 401 }
);
}
return NextResponse.next();
}
// Handle public routes - allow access without authentication
if (matchesPath(pathname, publicPaths)) {
return NextResponse.next();
}
// Redirect to login if trying to access protected route without authentication
if (!isLoggedIn) {
const loginUrl = new URL('/login', req.url);
loginUrl.searchParams.set('callbackUrl', pathname);
return NextResponse.redirect(loginUrl);
}
return NextResponse.next();
});
// Configure which routes the middleware should run on
export const config = {
matcher: [
/*
* Match all request paths except:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* - public files (public folder)
*/
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
};