package api import ( "context" "time" "github.com/rcourtman/pulse-go-rewrite/internal/alerts" "github.com/rcourtman/pulse-go-rewrite/internal/config" "github.com/rcourtman/pulse-go-rewrite/internal/mock" "github.com/rcourtman/pulse-go-rewrite/internal/models" "github.com/rcourtman/pulse-go-rewrite/internal/monitoring" ) // buildAggregatorInputs gathers the same inputs the HTTP connections handler // uses so the alerts pipeline can derive identical Connection rows without // going through the HTTP layer. Returning an empty aggregatorInputs when a // dependency is unavailable keeps the alerts loop a no-op rather than a hard // failure. func buildAggregatorInputs(ctx context.Context, cfg *config.Config, persistence *config.ConfigPersistence, monitor *monitoring.Monitor) aggregatorInputs { inputs := aggregatorInputs{now: time.Now()} if cfg != nil { inputs.pveInstances = cfg.PVEInstances inputs.pbsInstances = cfg.PBSInstances inputs.pmgInstances = cfg.PMGInstances } if persistence != nil { if vmw, err := persistence.LoadVMwareConfig(); err == nil { inputs.vmwareInstances = vmw } if tn, err := persistence.LoadTrueNASConfig(); err == nil { inputs.truenasInstances = tn } if availability, err := persistence.LoadAvailabilityTargets(); err == nil { inputs.availabilityTargets = availability } } if monitor != nil { inputs.hosts = monitor.HostsSnapshot() inputs.agentDesiredConfigs = connectionAgentDesiredConfigFingerprints(monitor, inputs.hosts) inputs.instanceHealth = instanceHealthByKey(monitor.SchedulerHealth()) inputs.availabilityStatuses = monitor.AvailabilityStatusSnapshot() } else { inputs.hosts = []models.Host{} inputs.instanceHealth = map[string]monitoring.InstanceHealth{} inputs.availabilityStatuses = map[string]monitoring.AvailabilityProbeStatus{} } if mock.IsMockEnabled() { mockTargets, mockStatuses := mockAvailabilityConnectionInputs() inputs.availabilityTargets = mergeAvailabilityTargets(inputs.availabilityTargets, mockTargets) inputs.availabilityStatuses = mergeAvailabilityStatuses(inputs.availabilityStatuses, mockStatuses) } inputs.expectedAgentVersion = currentAgentTargetVersion() _ = ctx return inputs } // connectionTypeForAlerts maps the wire ConnectionType to the alerts package's // narrow platform set. Types that fall outside the platform set return false // so the caller drops the row before invoking CheckConnection. func connectionTypeForAlerts(t ConnectionType) (alerts.ConnectionType, bool) { switch t { case ConnectionTypePVE: return alerts.ConnectionTypePVE, true case ConnectionTypePBS: return alerts.ConnectionTypePBS, true case ConnectionTypePMG: return alerts.ConnectionTypePMG, true case ConnectionTypeVMware: return alerts.ConnectionTypeVMware, true case ConnectionTypeTrueNAS: return alerts.ConnectionTypeTrueNAS, true default: return "", false } } // snapshotConnectionsForAlerts translates the aggregator's rich Connection // rows into the narrow alerts.ConnectionSnapshot view. Non-platform types // (agent, availability, docker, kubernetes) are dropped because they have // their own alert lifecycles. func snapshotConnectionsForAlerts(connections []Connection) []alerts.ConnectionSnapshot { out := make([]alerts.ConnectionSnapshot, 0, len(connections)) for _, conn := range connections { alertType, ok := connectionTypeForAlerts(conn.Type) if !ok { continue } snap := alerts.ConnectionSnapshot{ ID: conn.ID, Name: conn.Name, Type: alertType, State: alerts.ConnectionState(conn.State), StateReason: conn.StateReason, Enabled: conn.Enabled, LastSeen: conn.LastSeen, } if conn.LastError != nil { snap.LastError = &alerts.ConnectionErrorSnapshot{ At: conn.LastError.At, Message: conn.LastError.Message, Category: conn.LastError.Category, } } out = append(out, snap) } return out } // BuildAlertConnectionSnapshots returns the platform-connection snapshots the // alerts package consumes for the connection-degraded check. This is the // monitor-loop counterpart to the HTTP connections handler — it runs the // same derivation but skips the JSON envelope. func BuildAlertConnectionSnapshots(ctx context.Context, cfg *config.Config, persistence *config.ConfigPersistence, monitor *monitoring.Monitor) []alerts.ConnectionSnapshot { inputs := buildAggregatorInputs(ctx, cfg, persistence, monitor) return snapshotConnectionsForAlerts(buildConnections(inputs)) }