Add admin dashboard at / with bearer-token overview API

This commit is contained in:
2026-10-25 15:12:33 +08:00
parent 4acf226455
commit f5fcdd7bbb
9 changed files with 545 additions and 10 deletions
+49 -9
View File
@@ -82,6 +82,41 @@ func (h *Hub) DrainAll() {
}
}
// BridgeInfo is a snapshot of one attached bridge for the admin API.
type BridgeInfo struct {
Session string
TunnelID string
ClientVersion string
Routes []string
Visitors int
MaxConns int
ConnectedAt time.Time
}
// BridgeInfos snapshots every attached bridge. Field reads are safe without
// the bridge lock because they are immutable after construction; only the
// visitor count needs the lock.
func (h *Hub) BridgeInfos() []BridgeInfo {
h.mu.Lock()
defer h.mu.Unlock()
out := make([]BridgeInfo, 0, len(h.bridges))
for b := range h.bridges {
b.mu.Lock()
visitors := len(b.conns)
b.mu.Unlock()
out = append(out, BridgeInfo{
Session: b.session,
TunnelID: b.tunnelID,
ClientVersion: b.clientVersion,
Routes: b.routes,
Visitors: visitors,
MaxConns: b.maxConns,
ConnectedAt: b.startedAt,
})
}
return out
}
func (h *Hub) register(b *Bridge) bool {
h.mu.Lock()
defer h.mu.Unlock()
@@ -128,6 +163,9 @@ type Bridge struct {
maxConns int
ws *websocket.Conn
clientVersion string
startedAt time.Time
outbox chan outbound
done chan struct{}
closeOnce sync.Once
@@ -198,15 +236,17 @@ func (h *Hub) attach(w http.ResponseWriter, r *http.Request, tunnel storedTunnel
}
b := &Bridge{
hub: h,
tunnelID: tunnel.ID,
session: sessionID(),
routes: routes,
maxConns: maxConnsPerBridge,
ws: ws,
outbox: make(chan outbound, 256),
done: make(chan struct{}),
conns: map[uint32]*visitorConn{},
hub: h,
tunnelID: tunnel.ID,
session: sessionID(),
routes: routes,
maxConns: maxConnsPerBridge,
ws: ws,
clientVersion: attach.Client.Version,
startedAt: time.Now(),
outbox: make(chan outbound, 256),
done: make(chan struct{}),
conns: map[uint32]*visitorConn{},
}
if attach.Client.MaxConns > 0 && attach.Client.MaxConns < b.maxConns {
b.maxConns = attach.Client.MaxConns