rename: bridges/valves → tunnels (one term across types + API + UI)
CI / validate (push) Successful in 7s
CI / docker (push) Failing after 5s

API shape:
  GET /api/connections → GET /api/tunnels
  body: {"connections": […]} → {"tunnels": […]}

Type rename (package stays "bridge" — internal):
  Valve         → Listener
  clientBridge  → tunnel
  ConnSnapshot  → TunnelSnapshot

Log messages mirror the new vocab ("listener open/close", "tunnel
open/idle evict/forward failed"). UI header is now "Active tunnels"
and the empty state reads "no active tunnels".

server-manager's dashboard polls /infra/svc-proxy/api/tunnels and
shows "N tunnels" on the svc-proxy infra card.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 18:48:17 +02:00
parent 7fbe2555fb
commit 41a7e39754
5 changed files with 163 additions and 160 deletions
+7 -7
View File
@@ -55,7 +55,7 @@
</header>
<main>
<section>
<h2><span>Active connections <span class="count" id="conn-count"></span></span></h2>
<h2><span>Active tunnels <span class="count" id="conn-count"></span></span></h2>
<table>
<thead>
<tr>
@@ -66,7 +66,7 @@
</thead>
<tbody id="conn-rows"></tbody>
</table>
<div class="empty" id="conn-empty">no active bridges</div>
<div class="empty" id="conn-empty">no active tunnels</div>
</section>
<section>
<h2><span>Logs</span><button onclick="document.getElementById('logbox').innerHTML=''">clear</button></h2>
@@ -88,20 +88,20 @@ const fmtAgo = secs => {
};
async function refreshConnections() {
try {
const r = await fetch('./api/connections');
const r = await fetch('./api/tunnels');
const j = await r.json();
const rows = document.getElementById('conn-rows');
const empty = document.getElementById('conn-empty');
const count = document.getElementById('conn-count');
rows.innerHTML = '';
const now = new Date(j.at).getTime();
if (!j.connections || j.connections.length === 0) {
if (!j.tunnels || j.tunnels.length === 0) {
empty.style.display = '';
count.textContent = '';
} else {
empty.style.display = 'none';
count.textContent = '(' + j.connections.length + ')';
for (const c of j.connections) {
count.textContent = '(' + j.tunnels.length + ')';
for (const c of j.tunnels) {
const opened = new Date(c.opened_at).getTime();
const ageSecs = (now - opened) / 1000;
const idleCls = c.idle_seconds > 60 ? 'dead' : c.idle_seconds > 30 ? 'stale' : '';
@@ -118,7 +118,7 @@ async function refreshConnections() {
rows.appendChild(tr);
}
}
document.getElementById('meta').textContent = '— ' + j.connections.length + ' bridges';
document.getElementById('meta').textContent = '— ' + j.tunnels.length + ' tunnels';
} catch (e) {
document.getElementById('meta').textContent = '— api error';
}