Files
endorphin-tabularium-docs/apps/docs/src/proxy.ts
T

74 lines
2.4 KiB
TypeScript

import { NextResponse, type NextRequest } from "next/server";
import { authConfig, SESSION_COOKIE, validSession } from "./lib/auth";
import { authenticateDocumentKey } from "./lib/document-keys";
export function proxy(request: NextRequest) {
const path = request.nextUrl.pathname;
const headers = {
"Cache-Control": "private, no-store",
"Referrer-Policy": "same-origin",
};
if (
path === "/login" ||
path === "/api/health" ||
path === "/api/auth/login" ||
path === "/api/auth/logout" ||
path.startsWith("/_next/static/")
) {
return NextResponse.next({ headers });
}
let config;
try {
config = authConfig();
} catch {
/* Missing or invalid files fail closed. */
}
if (!config)
return NextResponse.json(
{ error: "登录服务尚未配置,请联系管理员" },
{ status: 503, headers },
);
const authorization = request.headers.get("authorization");
const bearer = authorization?.startsWith("Bearer ")
? authorization.slice(7)
: undefined;
const token = bearer ?? request.cookies.get(SESSION_COOKIE)?.value;
// Document keys are scoped to the document API; they never authenticate other pages or APIs.
if (bearer && (path === "/api/documents" || path.startsWith("/api/documents/"))) {
try {
if (authenticateDocumentKey(bearer)) return NextResponse.next({ headers });
} catch {
return NextResponse.json({ error: "文档认证暂时不可用" }, { status: 503, headers });
}
}
if (validSession(token, config)) {
if (!["GET", "HEAD", "OPTIONS"].includes(request.method) && !bearer) {
try {
if (
!request.headers.get("origin") ||
new URL(request.headers.get("origin")!).host !==
request.headers.get("host") ||
request.headers.get("sec-fetch-site") === "cross-site"
)
throw new Error();
} catch {
return NextResponse.json(
{ error: "请求来源不受信任" },
{ status: 403, headers },
);
}
}
return NextResponse.next({ headers });
}
if (path.startsWith("/api/") || !["GET", "HEAD"].includes(request.method)) {
return NextResponse.json(
{ error: "请先使用动态码登录" },
{ status: 401, headers },
);
}
const login = request.nextUrl.clone();
login.pathname = "/login";
login.search = "";
login.searchParams.set("next", path + request.nextUrl.search);
return NextResponse.redirect(login, { headers });
}