157 lines
6.8 KiB
JavaScript
157 lines
6.8 KiB
JavaScript
const GET_PROXY_PREFIXES = Object.freeze(["/v1/"]);
|
|
const GET_PROXY_ROUTES = new Set(["/v1", "/auth/session", "/auth/bootstrap"]);
|
|
const POST_PROXY_ROUTES = new Set([
|
|
"/auth/login",
|
|
"/auth/register",
|
|
"/auth/logout",
|
|
"/v1/agent/chat",
|
|
"/v1/agent/chat/cancel",
|
|
"/v1/agent/chat/steer",
|
|
"/v1/agent/sessions",
|
|
"/v1/workbench/commands",
|
|
"/v1/workbench/launches",
|
|
"/v1/hwpod-node-ops",
|
|
"/v1/web-performance",
|
|
"/v1/m3/io",
|
|
"/v1/skills/uploads"
|
|
]);
|
|
const PUBLIC_PROXY_ROUTES = new Set([
|
|
"GET /auth/session",
|
|
"GET /auth/bootstrap",
|
|
"POST /auth/login",
|
|
"POST /auth/register",
|
|
"POST /auth/logout",
|
|
"POST /v1/agent/chat",
|
|
"POST /v1/agent/chat/cancel",
|
|
"POST /v1/agent/chat/steer",
|
|
"POST /v1/web-performance"
|
|
]);
|
|
|
|
export function cloudWebProxyRoutePolicy(method, pathname) {
|
|
const normalizedMethod = normalizeMethod(method);
|
|
const normalizedPath = normalizePathname(pathname);
|
|
const routeKey = `${normalizedMethod} ${normalizedPath}`;
|
|
const proxy = isCloudApiProxyRoute(normalizedMethod, normalizedPath);
|
|
const publicRoute = proxy && (
|
|
PUBLIC_PROXY_ROUTES.has(routeKey) ||
|
|
isPublicCodeAgentPollRoute(normalizedMethod, normalizedPath)
|
|
);
|
|
return {
|
|
proxy,
|
|
authRequired: proxy && !publicRoute,
|
|
publicRoute,
|
|
routeKey
|
|
};
|
|
}
|
|
|
|
export function isCloudApiProxyRoute(method, pathname) {
|
|
const normalizedMethod = normalizeMethod(method);
|
|
const normalizedPath = normalizePathname(pathname);
|
|
if (normalizedMethod === "GET") {
|
|
return GET_PROXY_ROUTES.has(normalizedPath) ||
|
|
GET_PROXY_PREFIXES.some((prefix) => normalizedPath.startsWith(prefix));
|
|
}
|
|
if (normalizedMethod === "POST") {
|
|
return POST_PROXY_ROUTES.has(normalizedPath) ||
|
|
isCaseRunWriteProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isAdminAccessWriteProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isProviderProfileManagementProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isProjectManagementWriteProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isApiKeyWriteProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isAdminBillingWriteProxyRoute(normalizedMethod, normalizedPath) ||
|
|
normalizedPath.startsWith("/v1/rpc/") ||
|
|
normalizedPath.startsWith("/v1/agent/sessions/");
|
|
}
|
|
if (normalizedMethod === "PATCH" || normalizedMethod === "PUT") {
|
|
return isAdminAccessWriteProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isAdminBillingWriteProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isProviderProfileManagementProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isProjectManagementWriteProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isApiKeyWriteProxyRoute(normalizedMethod, normalizedPath);
|
|
}
|
|
if (normalizedMethod === "DELETE") {
|
|
return isAdminAccessWriteProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isProviderProfileManagementProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isProjectManagementWriteProxyRoute(normalizedMethod, normalizedPath) ||
|
|
isApiKeyWriteProxyRoute(normalizedMethod, normalizedPath);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function isCaseRunWriteProxyRoute(method, pathname) {
|
|
if (method !== "POST") return false;
|
|
if (pathname === "/v1/caserun/runs") return true;
|
|
const parts = pathname.split("/").filter(Boolean);
|
|
return parts.length === 5 && parts[0] === "v1" && parts[1] === "caserun" && parts[2] === "runs" && parts[4] === "cancel";
|
|
}
|
|
|
|
function isAdminAccessWriteProxyRoute(method, pathname) {
|
|
if (method === "POST" && pathname === "/v1/admin/access/check") return true;
|
|
|
|
const parts = pathname.split("/").filter(Boolean);
|
|
if (parts.length < 5 || parts[0] !== "v1" || parts[1] !== "admin" || parts[2] !== "access" || parts[3] !== "users") return false;
|
|
|
|
if (method === "PATCH" && parts.length === 5) return true;
|
|
if ((method === "PUT" || method === "DELETE") && parts.length === 8 && parts[5] === "tools" && parts[7] === "can-use") return true;
|
|
return false;
|
|
}
|
|
|
|
function isProviderProfileManagementProxyRoute(method, pathname) {
|
|
const parts = pathname.split("/").filter(Boolean);
|
|
if (parts.length < 4 || parts[0] !== "v1" || parts[1] !== "admin" || parts[2] !== "provider-profiles") return false;
|
|
if (method === "DELETE" && parts.length === 4) return true;
|
|
if (method === "PUT" && parts.length === 5 && parts[4] === "credential") return true;
|
|
if (method === "PUT" && parts.length === 5 && parts[4] === "config") return true;
|
|
if (method === "POST" && parts.length === 5 && parts[4] === "validate") return true;
|
|
if (method === "POST" && parts.length === 5 && parts[4] === "test") return true;
|
|
return false;
|
|
}
|
|
|
|
function isApiKeyWriteProxyRoute(method, pathname) {
|
|
if (pathname === "/v1/api-keys" && method === "POST") return true;
|
|
if (!pathname.startsWith("/v1/api-keys/")) return false;
|
|
if (method === "PATCH") return true;
|
|
if (method === "DELETE") return true;
|
|
if (method !== "POST") return false;
|
|
return pathname.endsWith("/regenerate");
|
|
}
|
|
|
|
function isAdminBillingWriteProxyRoute(method, pathname) {
|
|
const parts = pathname.split("/").filter(Boolean);
|
|
if (parts.length < 4 || parts[0] !== "v1" || parts[1] !== "admin" || parts[2] !== "billing" || parts[3] !== "users") return false;
|
|
if (method === "POST" && parts.length === 4) return true;
|
|
if (method === "PATCH" && parts.length === 5) return true;
|
|
if (method === "PATCH" && parts.length === 6 && parts[5] === "status") return true;
|
|
if (method === "POST" && parts.length === 7 && parts[5] === "credits" && parts[6] === "adjust") return true;
|
|
return false;
|
|
}
|
|
|
|
function isProjectManagementWriteProxyRoute(method, pathname) {
|
|
if (method === "POST" && pathname === "/v1/project-management/workbench-links") return true;
|
|
if (method === "POST" && pathname === "/v1/project-management/mdtodo/sources") return true;
|
|
if ((method === "PATCH" || method === "DELETE") && /^\/v1\/project-management\/mdtodo\/sources\/[^/]+$/u.test(pathname)) return true;
|
|
if (method === "POST" && /^\/v1\/project-management\/mdtodo\/sources\/[^/]+\/(?:probe|reindex)$/u.test(pathname)) return true;
|
|
if (method === "PATCH" && /^\/v1\/project-management\/mdtodo\/files\/[^/]+\/content$/u.test(pathname)) return true;
|
|
if (method === "POST" && pathname === "/v1/project-management/mdtodo/tasks") return true;
|
|
if ((method === "PATCH" || method === "DELETE") && /^\/v1\/project-management\/mdtodo\/tasks\/[^/]+$/u.test(pathname)) return true;
|
|
return false;
|
|
}
|
|
|
|
function isPublicCodeAgentPollRoute(method, pathname) {
|
|
return method === "GET" && (
|
|
pathname.startsWith("/v1/agent/turns/") ||
|
|
pathname.startsWith("/v1/agent/chat/result/") ||
|
|
pathname.startsWith("/v1/agent/traces/")
|
|
);
|
|
}
|
|
|
|
function normalizeMethod(method) {
|
|
return String(method || "GET").trim().toUpperCase() || "GET";
|
|
}
|
|
|
|
function normalizePathname(pathname) {
|
|
const value = String(pathname || "/").trim();
|
|
if (!value || value[0] !== "/") return "/";
|
|
return value.replace(/\/+$/u, "") || "/";
|
|
}
|