fix ops admin frontend fallback

This commit is contained in:
2569718930@qq.com
2026-06-14 22:57:10 +08:00
parent 7fb82c5eba
commit 0e2b076535
2 changed files with 52 additions and 4 deletions
@@ -43,6 +43,19 @@ export function runTests() {
"ops server page gate must honor localhost ops access before Supabase/admin-email redirects",
);
assert(
opsAdminSource.includes("verifyOpsAdminWithBackend") &&
opsAdminSource.includes("POLYWEATHER_API_BASE_URL") &&
opsAdminSource.includes("/api/ops/online-users") &&
opsAdminSource.includes("Authorization") &&
opsAdminSource.includes("BACKEND_ENTITLEMENT_HEADER") &&
opsAdminSource.includes("await supabase.auth.getSession()") &&
opsAdminSource.includes("if (allowedEmails.includes(email))") &&
opsAdminSource.includes("if (await verifyOpsAdminWithBackend(accessToken))") &&
!opsAdminSource.includes("if (!allowedEmails.length || !hasSupabaseServerEnv())"),
"ops admin page gate must fall back to the backend ops admin check when the frontend admin email env is missing or stale",
);
assert(
opsProxyAuthSource.includes("isLocalOpsAccessHost") &&
opsProxyAuthSource.includes("x-forwarded-host") &&
+39 -4
View File
@@ -1,5 +1,6 @@
import { cookies, headers } from "next/headers";
import { redirect } from "next/navigation";
import { BACKEND_ENTITLEMENT_HEADER } from "@/lib/backend-auth";
import { isLocalOpsAccessHost } from "@/lib/ops-local-access";
import {
createSupabaseServerClient,
@@ -7,6 +8,7 @@ import {
hasSupabaseSessionCookieValues,
} from "@/lib/supabase/server";
const API_BASE = process.env.POLYWEATHER_API_BASE_URL;
const LOCAL_DEV_OPS_EMAIL = "local-dev@polyweather.local";
function parseAdminEmails() {
@@ -16,6 +18,30 @@ function parseAdminEmails() {
.filter(Boolean);
}
async function verifyOpsAdminWithBackend(accessToken: string) {
const token = String(accessToken || "").trim();
if (!API_BASE || !token) return false;
const headers = new Headers({
Accept: "application/json",
Authorization: `Bearer ${token}`,
});
const backendToken = process.env.POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN?.trim();
if (backendToken) {
headers.set(BACKEND_ENTITLEMENT_HEADER, backendToken);
}
try {
const response = await fetch(`${API_BASE.replace(/\/+$/, "")}/api/ops/online-users`, {
cache: "no-store",
headers,
});
return response.ok;
} catch {
return false;
}
}
export async function requireOpsAdmin(nextPath = "/ops") {
const headerStore = await headers();
const requestHost =
@@ -25,7 +51,7 @@ export async function requireOpsAdmin(nextPath = "/ops") {
}
const allowedEmails = parseAdminEmails();
if (!allowedEmails.length || !hasSupabaseServerEnv()) {
if (!hasSupabaseServerEnv()) {
redirect("/");
}
@@ -57,9 +83,18 @@ export async function requireOpsAdmin(nextPath = "/ops") {
if (!email) {
redirect(`/auth/login?next=${encodeURIComponent(nextPath)}`);
}
if (!allowedEmails.includes(email)) {
redirect("/");
if (allowedEmails.includes(email)) {
return { email };
}
return { email };
const {
data: { session },
} = await supabase.auth.getSession();
const accessToken = session?.access_token || "";
if (await verifyOpsAdminWithBackend(accessToken)) {
return { email };
}
redirect("/");
}