feat(ui,server): surface active instance service URLs in About settings (#2669)
Show the running instance's local server URL and tunnel URL (when a tunnel is active) as labeled, click-to-open buttons on the About page. /api/system/info now reports the instance port and tunnel URL, resolved lazily from the tunnel runtime so each Git-worktree instance identifies itself in the UI without parsing terminal output. Refs OPE-194
This commit is contained in:
@@ -19,6 +19,8 @@ export const createBootstrapRuntime = (dependencies) => {
|
||||
serverStartedAt,
|
||||
gracefulShutdown,
|
||||
getHealthSnapshot,
|
||||
getServerPort,
|
||||
getTunnelUrl,
|
||||
verboseRequestLogs,
|
||||
uiPassword,
|
||||
tunnelAuthController,
|
||||
@@ -81,6 +83,8 @@ export const createBootstrapRuntime = (dependencies) => {
|
||||
gracefulShutdown,
|
||||
getHealthSnapshot,
|
||||
getServerId,
|
||||
getServerPort,
|
||||
getTunnelUrl,
|
||||
tunnelAuthController,
|
||||
uiAuthController,
|
||||
});
|
||||
|
||||
@@ -67,6 +67,12 @@ export const registerServerStatusRoutes = (app, dependencies) => {
|
||||
serverStartedAt,
|
||||
gracefulShutdown,
|
||||
getHealthSnapshot,
|
||||
// Port this OpenChamber instance serves on and the tunnel public URL (if
|
||||
// a tunnel is active). Exposed on /api/system/info so the UI can surface
|
||||
// the active instance's service URLs. Optional: older wiring omits them
|
||||
// and the endpoint reports null.
|
||||
getServerPort = () => null,
|
||||
getTunnelUrl = () => null,
|
||||
// Stable server identity (hash of the public signing key — not a secret).
|
||||
// Exposed on /health and /api/version so a client can verify that a
|
||||
// learned/probed address belongs to the expected server BEFORE sending its
|
||||
@@ -356,6 +362,8 @@ export const registerServerStatusRoutes = (app, dependencies) => {
|
||||
runtime: runtimeName,
|
||||
pid: process.pid,
|
||||
startedAt: serverStartedAt,
|
||||
port: getServerPort(),
|
||||
tunnelUrl: getTunnelUrl(),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -791,4 +791,46 @@ describe('client auth routes', () => {
|
||||
socket: { remoteAddress: '203.0.113.10' },
|
||||
})).toBe('unknown-public');
|
||||
});
|
||||
|
||||
it('reports null port and tunnel URL on /api/system/info when no getters are wired', async () => {
|
||||
const app = express();
|
||||
registerServerStatusRoutes(app, {
|
||||
process,
|
||||
serverStartedAt: '2026-01-01T00:00:00.000Z',
|
||||
gracefulShutdown: vi.fn(async () => {}),
|
||||
getHealthSnapshot: () => ({ status: 'ok' }),
|
||||
openchamberVersion: '1.0.0',
|
||||
runtimeName: 'test',
|
||||
express,
|
||||
});
|
||||
|
||||
const response = await request(app).get('/api/system/info');
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.openchamberVersion).toBe('1.0.0');
|
||||
expect(response.body.runtime).toBe('test');
|
||||
expect(response.body.pid).toBeTypeOf('number');
|
||||
expect(response.body.startedAt).toBeTypeOf('string');
|
||||
expect(response.body.port).toBeNull();
|
||||
expect(response.body.tunnelUrl).toBeNull();
|
||||
});
|
||||
|
||||
it('reports the instance port and tunnel URL on /api/system/info from the wired getters', async () => {
|
||||
const app = express();
|
||||
registerServerStatusRoutes(app, {
|
||||
process,
|
||||
serverStartedAt: '2026-01-01T00:00:00.000Z',
|
||||
gracefulShutdown: vi.fn(async () => {}),
|
||||
getHealthSnapshot: () => ({ status: 'ok' }),
|
||||
openchamberVersion: '1.0.0',
|
||||
runtimeName: 'test',
|
||||
express,
|
||||
getServerPort: () => 9988,
|
||||
getTunnelUrl: () => 'https://worktree-a.example.trycloudflare.com',
|
||||
});
|
||||
|
||||
const response = await request(app).get('/api/system/info');
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.port).toBe(9988);
|
||||
expect(response.body.tunnelUrl).toBe('https://worktree-a.example.trycloudflare.com');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user