mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:40:44 +00:00
fix(gateway): defer http auth imports
This commit is contained in:
@@ -60,6 +60,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/startup: read embedded-run activity from a lightweight shared state module so restart deferral no longer imports the embedded runner during Gateway boot. Thanks @vincentkoc.
|
||||
- Gateway/startup: defer MCP loopback server imports until Gateway shutdown so normal boot no longer loads the loopback HTTP/tool schema stack just to register close handlers. Thanks @vincentkoc.
|
||||
- Gateway/startup: resolve channel runtime helpers asynchronously only when an enabled/configured channel starts, so no-channel Gateway boot skips auto-reply, media, pairing, and outbound channel helper imports. Thanks @vincentkoc.
|
||||
- Gateway/startup: lazy-load HTTP auth, canvas auth, and plugin route scope helpers from their request paths so Gateway bind no longer pays those utility graphs during boot. Thanks @vincentkoc.
|
||||
- CLI/Gateway: use a parse-only config snapshot for plain `gateway status` reads and reuse same-path service config context so status no longer spends tens of seconds in full config validation before printing. Thanks @vincentkoc.
|
||||
- Lobster/Gateway: memoize repeated Ajv schema compilation before loading the embedded Lobster runtime so scheduled workflows and `llm.invoke` loops stop growing gateway heap on content-identical schemas. Fixes #71148. Thanks @cmi525, @vsolaz, and @vincentkoc.
|
||||
- Codex harness: normalize cached input tokens before session/context accounting so prompt cache reads are not double-counted in `/status`, `session_status`, or persisted `sessionEntry.totalTokens`. Fixes #69298. Thanks @richardmqq.
|
||||
|
||||
@@ -8,7 +8,12 @@ import {
|
||||
import { createServer as createHttpsServer } from "node:https";
|
||||
import type { TlsOptions } from "node:tls";
|
||||
import type { WebSocketServer } from "ws";
|
||||
import { A2UI_PATH, CANVAS_WS_PATH, handleA2uiHttpRequest } from "../canvas-host/a2ui.js";
|
||||
import {
|
||||
A2UI_PATH,
|
||||
CANVAS_HOST_PATH,
|
||||
CANVAS_WS_PATH,
|
||||
handleA2uiHttpRequest,
|
||||
} from "../canvas-host/a2ui.js";
|
||||
import type { CanvasHostHandler } from "../canvas-host/server.js";
|
||||
import { resolveBundledChannelGatewayAuthBypassPaths } from "../channels/plugins/gateway-auth-bypass.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
@@ -56,17 +61,10 @@ import {
|
||||
resolveHookChannel,
|
||||
resolveHookDeliver,
|
||||
} from "./hooks.js";
|
||||
import {
|
||||
type AuthorizedGatewayHttpRequest,
|
||||
authorizeGatewayHttpRequestOrReply,
|
||||
getBearerToken,
|
||||
resolveHttpBrowserOriginPolicy,
|
||||
} from "./http-auth-utils.js";
|
||||
import type { AuthorizedGatewayHttpRequest } from "./http-auth-utils.js";
|
||||
import { sendGatewayAuthFailure, setDefaultSecurityHeaders } from "./http-common.js";
|
||||
import { resolveRequestClientIp } from "./net.js";
|
||||
import { DEDUPE_MAX, DEDUPE_TTL_MS } from "./server-constants.js";
|
||||
import { authorizeCanvasRequest, isCanvasPath } from "./server/http-auth.js";
|
||||
import { resolvePluginRouteRuntimeOperatorScopes } from "./server/plugin-route-runtime-scopes.js";
|
||||
import {
|
||||
isProtectedPluginRoutePathFromContext,
|
||||
resolvePluginRoutePathContext,
|
||||
@@ -109,6 +107,11 @@ let toolsInvokeHttpModulePromise: Promise<typeof import("./tools-invoke-http.js"
|
||||
let voiceClawRealtimeUpgradeModulePromise:
|
||||
| Promise<typeof import("./voiceclaw-realtime/upgrade.js")>
|
||||
| undefined;
|
||||
let canvasAuthModulePromise: Promise<typeof import("./server/http-auth.js")> | undefined;
|
||||
let httpAuthUtilsModulePromise: Promise<typeof import("./http-auth-utils.js")> | undefined;
|
||||
let pluginRouteRuntimeScopesModulePromise:
|
||||
| Promise<typeof import("./server/plugin-route-runtime-scopes.js")>
|
||||
| undefined;
|
||||
|
||||
function getIdentityAvatarModule() {
|
||||
identityAvatarModulePromise ??= import("../agents/identity-avatar.js");
|
||||
@@ -165,6 +168,21 @@ function getVoiceClawRealtimeUpgradeModule() {
|
||||
return voiceClawRealtimeUpgradeModulePromise;
|
||||
}
|
||||
|
||||
function getCanvasAuthModule() {
|
||||
canvasAuthModulePromise ??= import("./server/http-auth.js");
|
||||
return canvasAuthModulePromise;
|
||||
}
|
||||
|
||||
function getHttpAuthUtilsModule() {
|
||||
httpAuthUtilsModulePromise ??= import("./http-auth-utils.js");
|
||||
return httpAuthUtilsModulePromise;
|
||||
}
|
||||
|
||||
function getPluginRouteRuntimeScopesModule() {
|
||||
pluginRouteRuntimeScopesModulePromise ??= import("./server/plugin-route-runtime-scopes.js");
|
||||
return pluginRouteRuntimeScopesModulePromise;
|
||||
}
|
||||
|
||||
type HookDispatchers = {
|
||||
dispatchWakeHook: (value: { text: string; mode: "now" | "next-heartbeat" }) => void;
|
||||
dispatchAgentHook: (value: HookAgentDispatchPayload) => string;
|
||||
@@ -283,6 +301,16 @@ function isA2uiPath(pathname: string): boolean {
|
||||
return pathname === A2UI_PATH || pathname.startsWith(`${A2UI_PATH}/`);
|
||||
}
|
||||
|
||||
function isCanvasPath(pathname: string): boolean {
|
||||
return (
|
||||
pathname === A2UI_PATH ||
|
||||
pathname.startsWith(`${A2UI_PATH}/`) ||
|
||||
pathname === CANVAS_HOST_PATH ||
|
||||
pathname.startsWith(`${CANVAS_HOST_PATH}/`) ||
|
||||
pathname === CANVAS_WS_PATH
|
||||
);
|
||||
}
|
||||
|
||||
function shouldEnforceDefaultPluginGatewayAuth(pathContext: PluginRoutePathContext): boolean {
|
||||
return (
|
||||
pathContext.malformedEncoding ||
|
||||
@@ -304,6 +332,7 @@ async function canRevealReadinessDetails(params: {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { getBearerToken, resolveHttpBrowserOriginPolicy } = await getHttpAuthUtilsModule();
|
||||
const bearerToken = getBearerToken(params.req);
|
||||
const authResult = await authorizeHttpGatewayConnect({
|
||||
auth: params.resolvedAuth,
|
||||
@@ -474,6 +503,7 @@ function buildPluginRequestStages(params: {
|
||||
if ((await params.getGatewayAuthBypassPaths()).has(params.requestPath)) {
|
||||
return false;
|
||||
}
|
||||
const { authorizeGatewayHttpRequestOrReply } = await getHttpAuthUtilsModule();
|
||||
const requestAuth = await authorizeGatewayHttpRequestOrReply({
|
||||
req: params.req,
|
||||
res: params.res,
|
||||
@@ -487,6 +517,8 @@ function buildPluginRequestStages(params: {
|
||||
}
|
||||
pluginGatewayAuthSatisfied = true;
|
||||
pluginGatewayRequestAuth = requestAuth;
|
||||
const { resolvePluginRouteRuntimeOperatorScopes } =
|
||||
await getPluginRouteRuntimeScopesModule();
|
||||
pluginRequestOperatorScopes = resolvePluginRouteRuntimeOperatorScopes(
|
||||
params.req,
|
||||
requestAuth,
|
||||
@@ -1092,6 +1124,7 @@ export function createGatewayHttpServer(opts: {
|
||||
if (!isCanvasPath(scopedRequestPath)) {
|
||||
return false;
|
||||
}
|
||||
const { authorizeCanvasRequest } = await getCanvasAuthModule();
|
||||
const ok = await authorizeCanvasRequest({
|
||||
req,
|
||||
auth: resolvedAuth,
|
||||
@@ -1258,6 +1291,7 @@ export function attachGatewayUpgradeHandler(opts: {
|
||||
const url = new URL(req.url ?? "/", "http://localhost");
|
||||
if (canvasHost) {
|
||||
if (url.pathname === CANVAS_WS_PATH) {
|
||||
const { authorizeCanvasRequest } = await getCanvasAuthModule();
|
||||
const ok = await authorizeCanvasRequest({
|
||||
req,
|
||||
auth: resolvedAuth,
|
||||
|
||||
Reference in New Issue
Block a user