74 lines
2.1 KiB
TypeScript
74 lines
2.1 KiB
TypeScript
/**
|
|
* 根级别 Middleware — 全局 cookie 存在性校验。
|
|
*
|
|
* 运行于 Edge Runtime,只能使用 request.cookies 等 Edge 兼容 API。
|
|
* 不做 token 有效性验证(那需要 fetch 后端,会阻塞每个请求),
|
|
* 只检查 hunjing-access cookie 是否存在。
|
|
*
|
|
* 未认证的 API 请求 → 返回 401 JSON
|
|
* 未认证的页面请求 → 重定向 /login
|
|
* 公开路径(/login、/_next/*、/favicon.ico 等)→ 放行
|
|
*/
|
|
import { NextResponse } from "next/server";
|
|
import type { NextRequest } from "next/server";
|
|
|
|
export const config = {
|
|
matcher: [
|
|
// 仅匹配需要登录的路径:API 路由和受保护的页面
|
|
"/api/proxy/:path*",
|
|
"/api/sse/:path*",
|
|
"/api/auth/me",
|
|
"/api/auth/refresh",
|
|
"/chat/:path*",
|
|
"/memory/:path*",
|
|
"/characters/:path*",
|
|
"/personality/:path*",
|
|
"/tools/:path*",
|
|
"/runs/:path*",
|
|
"/archive/:path*",
|
|
"/admin/:path*",
|
|
],
|
|
};
|
|
|
|
/** 公开路径白名单 — 无需登录即可访问 */
|
|
const PUBLIC_PATHS = new Set([
|
|
"/api/auth/login",
|
|
"/api/auth/logout",
|
|
"/login",
|
|
"/register",
|
|
]);
|
|
|
|
/**
|
|
* 判断请求是否为页面导航(Accept: text/html)。
|
|
* API 请求(fetch/EventSource/XMLHttpRequest)返回 JSON 即可,不重定向。
|
|
*/
|
|
function isPageNavigation(req: NextRequest): boolean {
|
|
const accept = req.headers.get("accept") ?? "";
|
|
return accept.includes("text/html");
|
|
}
|
|
|
|
export function middleware(req: NextRequest) {
|
|
// 公开路径放行
|
|
if (PUBLIC_PATHS.has(req.nextUrl.pathname)) {
|
|
return NextResponse.next();
|
|
}
|
|
|
|
// 检查 hunjing-access cookie
|
|
const accessToken = req.cookies.get("hunjing-access")?.value;
|
|
if (!accessToken) {
|
|
// API 请求 → 返回 401 JSON
|
|
if (!isPageNavigation(req)) {
|
|
return NextResponse.json(
|
|
{ error: { code: "unauthorized", message: "未登录,请先登录。" } },
|
|
{ status: 401 },
|
|
);
|
|
}
|
|
// 页面导航 → 重定向到 /login
|
|
const loginUrl = new URL("/login", req.url);
|
|
loginUrl.searchParams.set("redirect", req.nextUrl.pathname);
|
|
return NextResponse.redirect(loginUrl);
|
|
}
|
|
|
|
return NextResponse.next();
|
|
}
|