From 6deac2f76a632ac58ef45451e64abf346364d13b Mon Sep 17 00:00:00 2001 From: yushen Date: Fri, 30 Jan 2026 11:01:06 +0800 Subject: [PATCH] Add gateway URL configuration to Hub Console Allow changing the gateway connection URL at runtime via a new PUT /hub/gateway endpoint and a corresponding input form in the console UI. Co-Authored-By: Claude Opus 4.5 --- src/console/app.controller.ts | 10 ++++++++++ src/console/public/index.html | 29 +++++++++++++++++++++++++++++ src/hub/hub.ts | 31 ++++++++++++++++++++++--------- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/console/app.controller.ts b/src/console/app.controller.ts index 8bef2135..999878ed 100644 --- a/src/console/app.controller.ts +++ b/src/console/app.controller.ts @@ -2,6 +2,7 @@ import { Controller, Get, Post, + Put, Delete, Param, Body, @@ -23,6 +24,15 @@ export class AppController { }; } + @Put("hub/gateway") + updateGateway(@Body() body: { url: string }) { + this.hub.reconnect(body.url); + return { + url: this.hub.url, + connectionState: this.hub.connectionState, + }; + } + @Get("agents") listAgents() { return this.hub.listAgents().map((id) => { diff --git a/src/console/public/index.html b/src/console/public/index.html index 720439db..f07f0cf9 100644 --- a/src/console/public/index.html +++ b/src/console/public/index.html @@ -15,6 +15,9 @@ .agent-list { list-style: none; } .agent-list li { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid #222; font-family: monospace; font-size: 0.85rem; } .agent-list li:last-child { border-bottom: none; } + .gateway-form { display: flex; gap: 0.5rem; align-items: center; margin-top: 0.8rem; } + .gateway-form input { background: #0a0a0a; color: #e0e0e0; border: 1px solid #444; border-radius: 4px; padding: 0.4rem 0.6rem; font-size: 0.85rem; font-family: monospace; flex: 1; } + .gateway-form input:focus { outline: none; border-color: #666; } button { background: #2a2a2a; color: #e0e0e0; border: 1px solid #444; border-radius: 4px; padding: 0.4rem 0.8rem; cursor: pointer; font-size: 0.8rem; } button:hover { background: #3a3a3a; } .btn-create { background: #1a3a1a; border-color: #2a5a2a; } @@ -30,6 +33,10 @@

Hub Status

Loading...
+
+ + +
@@ -56,6 +63,11 @@ `State: ${hubRes.connectionState}` + `Agents: ${hubRes.agentCount}`; + const input = document.getElementById('gateway-url'); + if (!input.dataset.edited) { + input.value = hubRes.url; + } + const list = document.getElementById('agent-list'); if (agentsRes.length === 0) { list.innerHTML = '
  • No agents
  • '; @@ -66,6 +78,23 @@ } } + document.getElementById('gateway-url').addEventListener('input', function() { + this.dataset.edited = '1'; + }); + + async function updateGateway() { + const input = document.getElementById('gateway-url'); + const url = input.value.trim(); + if (!url) return; + await fetch(`${API}/hub/gateway`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ url }), + }); + input.dataset.edited = ''; + refresh(); + } + async function createAgent() { await fetch(`${API}/agents`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' }); refresh(); diff --git a/src/hub/hub.ts b/src/hub/hub.ts index f34c5935..c089e48d 100644 --- a/src/hub/hub.ts +++ b/src/hub/hub.ts @@ -7,8 +7,8 @@ import { GatewayClient } from "../shared/gateway-sdk/client.js"; export class Hub { private readonly agents = new Map(); private readonly agentSenders = new Map(); - private readonly client: GatewayClient; - readonly url: string; + private client: GatewayClient; + url: string; readonly path: string; readonly deviceId: string; @@ -21,9 +21,13 @@ export class Hub { this.url = url; this.path = path ?? "/ws"; this.deviceId = getDeviceId(); + this.client = this.createClient(this.url); + this.client.connect(); + } - this.client = new GatewayClient({ - url: this.url, + private createClient(url: string): GatewayClient { + const client = new GatewayClient({ + url, path: this.path, deviceId: this.deviceId, deviceType: "client", @@ -31,19 +35,19 @@ export class Hub { reconnectDelay: 1000, }); - this.client.onStateChange((state) => { + client.onStateChange((state) => { console.log(`[Hub] Connection state: ${state}`); }); - this.client.onRegistered((deviceId) => { + client.onRegistered((deviceId) => { console.log(`[Hub] Registered as: ${deviceId}`); }); - this.client.onError((err) => { + client.onError((err) => { console.error(`[Hub] Connection error:`, err.message); }); - this.client.onMessage((msg) => { + client.onMessage((msg) => { console.log(`[Hub] Received message: id=${msg.id} from=${msg.from} to=${msg.to} action=${msg.action} payload=${JSON.stringify(msg.payload)}`); const payload = msg.payload as { agentId?: string; content?: string } | undefined; const agentId = payload?.agentId; @@ -61,10 +65,19 @@ export class Hub { } }); - this.client.onSendError((err) => { + client.onSendError((err) => { console.error(`[Hub] Send error: messageId=${err.messageId} code=${err.code} error=${err.error}`); }); + return client; + } + + /** 重连到新的 Gateway 地址 */ + reconnect(url: string): void { + console.log(`[Hub] Reconnecting to ${url}`); + this.client.disconnect(); + this.url = url; + this.client = this.createClient(url); this.client.connect(); }