diff --git a/backend/app/api/gateway.py b/backend/app/api/gateway.py index 5efc8552..a3e2d807 100644 --- a/backend/app/api/gateway.py +++ b/backend/app/api/gateway.py @@ -38,12 +38,14 @@ def _query_to_resolve_input( gateway_url: str | None = Query(default=None), gateway_token: str | None = Query(default=None), gateway_disable_device_pairing: bool = Query(default=False), + gateway_allow_insecure_tls: bool = Query(default=False), ) -> GatewayResolveQuery: return GatewaySessionService.to_resolve_query( board_id=board_id, gateway_url=gateway_url, gateway_token=gateway_token, gateway_disable_device_pairing=gateway_disable_device_pairing, + gateway_allow_insecure_tls=gateway_allow_insecure_tls, ) diff --git a/backend/app/schemas/gateway_api.py b/backend/app/schemas/gateway_api.py index 13cc2094..188a25b2 100644 --- a/backend/app/schemas/gateway_api.py +++ b/backend/app/schemas/gateway_api.py @@ -22,6 +22,7 @@ class GatewayResolveQuery(SQLModel): gateway_url: str | None = None gateway_token: str | None = None gateway_disable_device_pairing: bool = False + gateway_allow_insecure_tls: bool = False class GatewaysStatusResponse(SQLModel): diff --git a/backend/app/services/openclaw/session_service.py b/backend/app/services/openclaw/session_service.py index 377856f8..2f7e3e1b 100644 --- a/backend/app/services/openclaw/session_service.py +++ b/backend/app/services/openclaw/session_service.py @@ -66,12 +66,14 @@ class GatewaySessionService(OpenClawDBService): gateway_url: str | None, gateway_token: str | None, gateway_disable_device_pairing: bool = False, + gateway_allow_insecure_tls: bool = False, ) -> GatewayResolveQuery: return GatewayResolveQuery( board_id=board_id, gateway_url=gateway_url, gateway_token=gateway_token, gateway_disable_device_pairing=gateway_disable_device_pairing, + gateway_allow_insecure_tls=gateway_allow_insecure_tls, ) @staticmethod @@ -112,6 +114,7 @@ class GatewaySessionService(OpenClawDBService): GatewayClientConfig( url=raw_url, token=(params.gateway_token or "").strip() or None, + allow_insecure_tls=params.gateway_allow_insecure_tls, disable_device_pairing=params.gateway_disable_device_pairing, ), None, diff --git a/backend/tests/test_gateway_resolver.py b/backend/tests/test_gateway_resolver.py index ccbf673d..2c5da95a 100644 --- a/backend/tests/test_gateway_resolver.py +++ b/backend/tests/test_gateway_resolver.py @@ -3,7 +3,10 @@ from __future__ import annotations from uuid import uuid4 +import pytest + from app.models.gateways import Gateway +from app.schemas.gateway_api import GatewayResolveQuery from app.services.openclaw.gateway_resolver import ( gateway_client_config, optional_gateway_client_config, @@ -14,6 +17,7 @@ from app.services.openclaw.session_service import GatewaySessionService def _gateway( *, disable_device_pairing: bool, + allow_insecure_tls: bool = False, url: str = "ws://gateway.example:18789/ws", token: str | None = " secret-token ", ) -> Gateway: @@ -25,6 +29,7 @@ def _gateway( token=token, workspace_root="~/.openclaw", disable_device_pairing=disable_device_pairing, + allow_insecure_tls=allow_insecure_tls, ) @@ -43,6 +48,14 @@ def test_optional_gateway_client_config_maps_disable_device_pairing() -> None: assert config.disable_device_pairing is False +def test_gateway_client_config_maps_allow_insecure_tls() -> None: + config = gateway_client_config( + _gateway(disable_device_pairing=False, allow_insecure_tls=True), + ) + + assert config.allow_insecure_tls is True + + def test_optional_gateway_client_config_returns_none_for_missing_or_blank_url() -> None: assert optional_gateway_client_config(None) is None assert ( @@ -62,3 +75,28 @@ def test_to_resolve_query_keeps_gateway_disable_device_pairing_value() -> None: ) assert resolved.gateway_disable_device_pairing is True + + +def test_to_resolve_query_keeps_gateway_allow_insecure_tls_value() -> None: + resolved = GatewaySessionService.to_resolve_query( + board_id=None, + gateway_url="wss://gateway.example:18789/ws", + gateway_token="secret-token", + gateway_allow_insecure_tls=True, + ) + + assert resolved.gateway_allow_insecure_tls is True + + +@pytest.mark.asyncio +async def test_resolve_gateway_keeps_gateway_allow_insecure_tls_for_direct_url() -> None: + service = GatewaySessionService(session=object()) # type: ignore[arg-type] + _, config, _ = await service.resolve_gateway( + GatewayResolveQuery( + gateway_url="wss://gateway.example:18789/ws", + gateway_allow_insecure_tls=True, + ), + user=None, + ) + + assert config.allow_insecure_tls is True diff --git a/frontend/src/api/generated/model/gatewayRead.ts b/frontend/src/api/generated/model/gatewayRead.ts index ed048337..e7dde24e 100644 --- a/frontend/src/api/generated/model/gatewayRead.ts +++ b/frontend/src/api/generated/model/gatewayRead.ts @@ -12,7 +12,7 @@ export interface GatewayRead { name: string; url: string; workspace_root: string; - allow_insecure_tls: boolean; + allow_insecure_tls?: boolean; disable_device_pairing?: boolean; id: string; organization_id: string; diff --git a/frontend/src/api/generated/model/gatewaysStatusApiV1GatewaysStatusGetParams.ts b/frontend/src/api/generated/model/gatewaysStatusApiV1GatewaysStatusGetParams.ts index 21de85f1..ae875a56 100644 --- a/frontend/src/api/generated/model/gatewaysStatusApiV1GatewaysStatusGetParams.ts +++ b/frontend/src/api/generated/model/gatewaysStatusApiV1GatewaysStatusGetParams.ts @@ -10,4 +10,5 @@ export type GatewaysStatusApiV1GatewaysStatusGetParams = { gateway_url?: string | null; gateway_token?: string | null; gateway_disable_device_pairing?: boolean; + gateway_allow_insecure_tls?: boolean; }; diff --git a/frontend/src/app/gateways/[gatewayId]/edit/page.tsx b/frontend/src/app/gateways/[gatewayId]/edit/page.tsx index b60b6771..7b4b1246 100644 --- a/frontend/src/app/gateways/[gatewayId]/edit/page.tsx +++ b/frontend/src/app/gateways/[gatewayId]/edit/page.tsx @@ -132,6 +132,7 @@ export default function EditGatewayPage() { gatewayUrl: resolvedGatewayUrl, gatewayToken: resolvedGatewayToken, gatewayDisableDevicePairing: resolvedDisableDevicePairing, + gatewayAllowInsecureTls: resolvedAllowInsecureTls, }); setGatewayCheckStatus(ok ? "success" : "error"); setGatewayCheckMessage(message); @@ -205,7 +206,11 @@ export default function EditGatewayPage() { setGatewayCheckMessage(null); }} onWorkspaceRootChange={setWorkspaceRoot} - onAllowInsecureTlsChange={setAllowInsecureTls} + onAllowInsecureTlsChange={(next) => { + setAllowInsecureTls(next); + setGatewayCheckStatus("idle"); + setGatewayCheckMessage(null); + }} /> ); diff --git a/frontend/src/app/gateways/new/page.tsx b/frontend/src/app/gateways/new/page.tsx index 99a9130e..7e752c6d 100644 --- a/frontend/src/app/gateways/new/page.tsx +++ b/frontend/src/app/gateways/new/page.tsx @@ -88,6 +88,7 @@ export default function NewGatewayPage() { gatewayUrl, gatewayToken, gatewayDisableDevicePairing: disableDevicePairing, + gatewayAllowInsecureTls: allowInsecureTls, }); setGatewayCheckStatus(ok ? "success" : "error"); setGatewayCheckMessage(message); @@ -156,7 +157,11 @@ export default function NewGatewayPage() { setGatewayCheckMessage(null); }} onWorkspaceRootChange={setWorkspaceRoot} - onAllowInsecureTlsChange={setAllowInsecureTls} + onAllowInsecureTlsChange={(next) => { + setAllowInsecureTls(next); + setGatewayCheckStatus("idle"); + setGatewayCheckMessage(null); + }} /> ); diff --git a/frontend/src/components/gateways/GatewayForm.tsx b/frontend/src/components/gateways/GatewayForm.tsx index 49b7ff01..5e36599c 100644 --- a/frontend/src/components/gateways/GatewayForm.tsx +++ b/frontend/src/components/gateways/GatewayForm.tsx @@ -150,21 +150,30 @@ export function GatewayForm({ -