crush-level-web/src/proxy.ts

117 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
// 需要认证的路由
const protectedRoutes = [
'/profile',
'/profile/account',
'/profile/edit',
'/settings',
'/login/fields',
'/chat',
'/contact',
'/vip',
'/wallet',
'/wallet/transactions',
'/crushcoin',
];
// 已登录用户不应该访问的路由
const authRoutes = ['/login'];
const DEVICE_ID_COOKIE_NAME = 'sd';
// 生成设备ID
function generateDeviceId(userAgent?: string): string {
const timestamp = Date.now().toString(36);
const randomStr = Math.random().toString(36).substring(2, 15);
const browserInfo = userAgent ? userAgent.replace(/\s/g, '').substring(0, 10) : 'server';
return `did_${timestamp}_${randomStr}_${browserInfo}`.toLowerCase();
}
export default function proxy(request: NextRequest) {
const { pathname } = request.nextUrl;
// 获取现有设备ID
let deviceId = request.cookies.get(DEVICE_ID_COOKIE_NAME)?.value;
let needSetCookie = false;
if (!deviceId) {
// 生成新的设备ID
const userAgent = request.headers.get('user-agent') || undefined;
deviceId = generateDeviceId(userAgent);
needSetCookie = true;
} else {
// console.log('✅ [MIDDLEWARE] 获取现有设备ID:', deviceId);
}
// 认证逻辑
const token = request.cookies.get('st')?.value;
const isAuthenticated = !!token;
const isProtectedRoute = protectedRoutes.some((route) => pathname.startsWith(route));
const isAuthRoute = authRoutes.some(
(route) => pathname.startsWith(route) && !pathname.startsWith('/login/fields')
);
// 如果是受保护的路由但用户未登录,重定向到登录页
if (isProtectedRoute && !isAuthenticated) {
console.log('🚫 [MIDDLEWARE] 重定向到登录页:', pathname);
const loginUrl = new URL('/login', request.url);
loginUrl.searchParams.set('redirect', pathname);
return NextResponse.redirect(loginUrl);
}
// 如果已登录用户访问认证页面,重定向到首页
if (isAuthRoute && isAuthenticated) {
console.log('🔄 [MIDDLEWARE] 已登录用户重定向到首页:', pathname);
return NextResponse.redirect(new URL('/', request.url));
}
// 在请求头中添加认证状态和设备ID供服务端组件使用
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-authenticated', isAuthenticated.toString());
requestHeaders.set('x-device-id', deviceId); // 确保设备ID被传递
if (token) {
requestHeaders.set('x-auth-token', token);
}
// 创建响应
const response = NextResponse.next({
request: {
headers: requestHeaders,
},
});
// 如果locale cookie不存在设置为en-US
if (!request.cookies.get('locale')?.value) {
response.cookies.set('locale', 'en-US', {
maxAge: 365 * 24 * 60 * 60, // 365天
httpOnly: false,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
});
}
// 如果需要设置设备ID cookie
if (needSetCookie) {
response.cookies.set(DEVICE_ID_COOKIE_NAME, deviceId, {
maxAge: 365 * 24 * 60 * 60, // 365天
httpOnly: false,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
});
}
return response;
}
export const config = {
matcher: [
// 匹配所有路径除了静态文件和API路由
'/((?!api|_next/static|_next/image|favicon.ico|public).*)',
],
};