Pulse/internal/api/agent_handlers_base.go
2026-03-18 16:06:30 +00:00

92 lines
2.5 KiB
Go

package api
import (
"context"
"sync"
"github.com/rcourtman/pulse-go-rewrite/internal/monitoring"
"github.com/rcourtman/pulse-go-rewrite/internal/websocket"
)
// baseAgentHandlers provides the shared monitor-management and broadcast logic
// for Docker, Kubernetes, and Unified Agent handler types.
type baseAgentHandlers struct {
stateMu sync.RWMutex
mtMonitor *monitoring.MultiTenantMonitor
defaultMonitor *monitoring.Monitor
wsHub *websocket.Hub
}
// newBaseAgentHandlers constructs the shared base, resolving the default org
// monitor from the multi-tenant manager when no explicit monitor is given.
func newBaseAgentHandlers(mtm *monitoring.MultiTenantMonitor, m *monitoring.Monitor, hub *websocket.Hub) baseAgentHandlers {
if m == nil && mtm != nil {
if mon, err := mtm.GetMonitor("default"); err == nil {
m = mon
}
}
return baseAgentHandlers{mtMonitor: mtm, defaultMonitor: m, wsHub: hub}
}
// SetMonitor updates the single-tenant monitor reference.
func (b *baseAgentHandlers) SetMonitor(m *monitoring.Monitor) {
b.stateMu.Lock()
defer b.stateMu.Unlock()
b.defaultMonitor = m
}
// SetMultiTenantMonitor updates the multi-tenant monitor manager and
// refreshes the default-org monitor fallback.
func (b *baseAgentHandlers) SetMultiTenantMonitor(mtm *monitoring.MultiTenantMonitor) {
var defaultMonitor *monitoring.Monitor
if mtm != nil {
if m, err := mtm.GetMonitor("default"); err == nil {
defaultMonitor = m
}
}
b.stateMu.Lock()
defer b.stateMu.Unlock()
b.mtMonitor = mtm
if defaultMonitor != nil {
b.defaultMonitor = defaultMonitor
}
}
// getMonitor returns the monitor for the organization in the request context,
// falling back to the default-org monitor.
func (b *baseAgentHandlers) getMonitor(ctx context.Context) *monitoring.Monitor {
b.stateMu.RLock()
mtMonitor := b.mtMonitor
defaultMonitor := b.defaultMonitor
b.stateMu.RUnlock()
orgID := GetOrgID(ctx)
if mtMonitor != nil {
if m, err := mtMonitor.GetMonitor(orgID); err == nil && m != nil {
return m
}
}
return defaultMonitor
}
// broadcastState pushes monitor state to tenant-scoped WebSocket clients when
// context includes an org ID, falling back to global broadcast for default paths.
func (b *baseAgentHandlers) broadcastState(ctx context.Context) {
if b.wsHub == nil {
return
}
monitor := b.getMonitor(ctx)
if monitor == nil {
return
}
state := monitor.BuildFrontendState()
orgID := GetOrgID(ctx)
if orgID != "" {
go b.wsHub.BroadcastStateToTenant(orgID, state)
return
}
go b.wsHub.BroadcastState(state)
}