34 lines
1.2 KiB
TypeScript
34 lines
1.2 KiB
TypeScript
import { timingSafeEqual } from "node:crypto";
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
|
|
function sameValue(left: string, right: string) {
|
|
const leftBuffer = Buffer.from(left);
|
|
const rightBuffer = Buffer.from(right);
|
|
return leftBuffer.length === rightBuffer.length && timingSafeEqual(leftBuffer, rightBuffer);
|
|
}
|
|
|
|
export function proxy(request: NextRequest) {
|
|
if (request.nextUrl.pathname === "/api/health") return NextResponse.next();
|
|
|
|
const password = process.env.STUDIO_PASSWORD;
|
|
const username = process.env.STUDIO_USERNAME || "studio";
|
|
if (!password) {
|
|
if (process.env.NODE_ENV !== "production") return NextResponse.next();
|
|
return NextResponse.json({ error: "STUDIO_PASSWORD is not configured" }, { status: 503 });
|
|
}
|
|
|
|
const authorization = request.headers.get("authorization") || "";
|
|
const expected = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
|
|
if (!sameValue(authorization, expected)) {
|
|
return new NextResponse("Authentication required", {
|
|
status: 401,
|
|
headers: { "WWW-Authenticate": 'Basic realm="Rednote Tool Studio", charset="UTF-8"' },
|
|
});
|
|
}
|
|
return NextResponse.next();
|
|
}
|
|
|
|
export const config = {
|
|
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
|
|
};
|