/** * 根级别 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(); }