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

106 lines
3.1 KiB
Go

package api
import (
"encoding/json"
"net/http"
"time"
"github.com/rs/zerolog/log"
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
)
// MonitoredSystemLedgerEntry represents a single counted top-level monitored
// system.
type MonitoredSystemLedgerEntry struct {
Name string `json:"name"`
Type string `json:"type"`
Status string `json:"status"` // "online", "offline", "unknown"
LastSeen string `json:"last_seen"` // RFC3339 or empty
Source string `json:"source"`
}
// MonitoredSystemLedgerResponse is the response for GET /api/license/monitored-system-ledger.
type MonitoredSystemLedgerResponse struct {
Systems []MonitoredSystemLedgerEntry `json:"systems"`
Total int `json:"total"`
Limit int `json:"limit"` // 0 = unlimited
}
func EmptyMonitoredSystemLedgerResponse() MonitoredSystemLedgerResponse {
return MonitoredSystemLedgerResponse{}.NormalizeCollections()
}
func (r MonitoredSystemLedgerResponse) NormalizeCollections() MonitoredSystemLedgerResponse {
if r.Systems == nil {
r.Systems = []MonitoredSystemLedgerEntry{}
}
return r
}
func (r *Router) handleMonitoredSystemLedger(w http.ResponseWriter, req *http.Request) {
orgID := GetOrgID(req.Context())
// Get canonical monitored systems from the unified ReadState surface.
var systems []unifiedresources.MonitoredSystemRecord
var monitorResolved bool
if r.mtMonitor != nil {
monitor, monErr := r.mtMonitor.GetMonitor(orgID)
if monErr != nil {
log.Warn().Err(monErr).Str("org", orgID).Msg("monitored-system-ledger: failed to resolve tenant monitor")
}
if monitor != nil {
if rs := monitor.GetUnifiedReadState(); rs != nil {
systems = unifiedresources.MonitoredSystems(rs)
}
monitorResolved = true
}
}
// Fallback to the default monitor only for the default org to avoid cross-tenant data leaks.
if !monitorResolved && orgID == "default" && r.monitor != nil {
if rs := r.monitor.GetUnifiedReadState(); rs != nil {
systems = unifiedresources.MonitoredSystems(rs)
}
}
entries := make([]MonitoredSystemLedgerEntry, 0, len(systems))
for _, system := range systems {
entries = append(entries, MonitoredSystemLedgerEntry{
Name: system.Name,
Type: system.Type,
Status: normalizeStatus(string(system.Status)),
LastSeen: formatLastSeen(system.LastSeen),
Source: system.Source,
})
}
limit := maxMonitoredSystemsLimitForContext(req.Context())
resp := EmptyMonitoredSystemLedgerResponse()
resp.Systems = entries
resp.Total = len(entries)
resp.Limit = limit
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp.NormalizeCollections())
}
// ---------------------------------------------------------------------------
// Status helpers
// ---------------------------------------------------------------------------
func normalizeStatus(s string) string {
switch s {
case "online", "offline":
return s
default:
return "unknown"
}
}
func formatLastSeen(t time.Time) string {
if t.IsZero() {
return ""
}
return t.UTC().Format(time.RFC3339)
}