Restrict observability endpoints to ops admins

This commit is contained in:
2569718930@qq.com
2026-06-13 02:35:30 +08:00
parent be1d93fc09
commit 9d120d7215
6 changed files with 75 additions and 25 deletions
+30 -8
View File
@@ -1,5 +1,10 @@
import { NextRequest, NextResponse } from "next/server";
import { proxyBackendJsonGet } from "@/lib/api-proxy";
import { buildProxyExceptionResponse } from "@/lib/api-proxy";
import {
applyAuthResponseCookies,
buildBackendRequestHeaders,
} from "@/lib/backend-auth";
import { requireOpsProxyAuth } from "@/lib/ops-proxy-auth";
const API_BASE = process.env.POLYWEATHER_API_BASE_URL;
@@ -11,11 +16,28 @@ export async function GET(req: NextRequest) {
);
}
return proxyBackendJsonGet(req, {
cacheControl: "public, max-age=0, s-maxage=30, stale-while-revalidate=120",
detailLimit: 500,
publicMessage: "Failed to fetch system status",
revalidateSeconds: 30,
url: `${API_BASE}/api/system/status`,
});
try {
const auth = await buildBackendRequestHeaders(req);
const authError = requireOpsProxyAuth(req, auth);
if (authError) return authError;
const res = await fetch(`${API_BASE}/api/system/status`, {
cache: "no-store",
headers: auth.headers,
});
const raw = await res.text();
const response = new NextResponse(raw, {
headers: {
"Cache-Control": "no-store",
"Cloudflare-CDN-Cache-Control": "no-store",
"Content-Type": res.headers.get("content-type") || "application/json",
},
status: res.status,
});
return applyAuthResponseCookies(response, auth.response);
} catch (error) {
return buildProxyExceptionResponse(error, {
publicMessage: "Failed to fetch system status",
});
}
}
@@ -186,6 +186,7 @@ export function runTests() {
"app/api/ops/users/route.ts",
"app/api/ops/users/grant-points/route.ts",
"app/api/ops/view-logs/route.ts",
"app/api/system/status/route.ts",
]) {
const routeSource = fs.readFileSync(path.join(projectRoot, route), "utf8");
assert(
@@ -211,14 +212,10 @@ export function runTests() {
const optionalRefreshIndex = middlewareSource.indexOf(
"function shouldRefreshOptionalSupabaseSession",
);
const systemStatusPublicIndex = middlewareSource.indexOf(
'pathname === "/api/system/status"',
);
assert(
systemStatusPublicIndex >= 0 &&
optionalRefreshIndex >= 0 &&
systemStatusPublicIndex < optionalRefreshIndex,
"middleware must treat public system status as public API instead of optional Supabase session refresh",
optionalRefreshIndex >= 0 &&
!middlewareSource.includes('pathname === "/api/system/status"'),
"middleware must not treat system status as public after it becomes an ops-only API",
);
for (const route of paymentRoutes) {
-1
View File
@@ -36,7 +36,6 @@ function isPublicApi(pathname: string) {
pathname === "/api/cities" ||
pathname === "/api/payments/config" ||
pathname === "/api/scan/terminal" ||
pathname === "/api/system/status" ||
pathname === "/api/vitals" ||
/^\/api\/city\/[^/]+$/i.test(pathname) ||
/^\/api\/city\/[^/]+\/summary$/i.test(pathname) ||