OnDisconnect creates a host_down alert when an agent's WebSocket disconnects. Called by the agent WebSocket disconnect handler.
(ctx context.Context, d *database.DB, apiID string, tenantHost string, emit *notifications.Emitter, log *slog.Logger)
| 138 | // OnDisconnect creates a host_down alert when an agent's WebSocket disconnects. |
| 139 | // Called by the agent WebSocket disconnect handler. |
| 140 | func OnDisconnect(ctx context.Context, d *database.DB, apiID string, tenantHost string, emit *notifications.Emitter, log *slog.Logger) { |
| 141 | enabled, err := IsAlertsEnabled(ctx, d) |
| 142 | if err != nil || !enabled { |
| 143 | return |
| 144 | } |
| 145 | cfg, err := GetConfigForType(ctx, d, "host_down") |
| 146 | if err != nil || cfg == nil || !cfg.IsEnabled { |
| 147 | return |
| 148 | } |
| 149 | |
| 150 | host, err := d.Queries.GetHostByApiID(ctx, apiID) |
| 151 | if err != nil { |
| 152 | return |
| 153 | } |
| 154 | shouldCreate := cfg.IsEnabled |
| 155 | if host.HostDownAlertsEnabled != nil { |
| 156 | shouldCreate = *host.HostDownAlertsEnabled |
| 157 | } |
| 158 | if !shouldCreate { |
| 159 | return |
| 160 | } |
| 161 | |
| 162 | activeAlerts, _ := d.Queries.ListActiveAlertsByType(ctx, "host_down") |
| 163 | for _, a := range activeAlerts { |
| 164 | var meta map[string]interface{} |
| 165 | if len(a.Metadata) > 0 { |
| 166 | _ = json.Unmarshal(a.Metadata, &meta) |
| 167 | } |
| 168 | if meta != nil { |
| 169 | if hid, ok := meta["host_id"].(string); ok && hid == host.ID { |
| 170 | _ = d.Queries.UpdateAlert(ctx, a.ID) |
| 171 | log.Debug("host_down: updated existing alert on disconnect", "api_id", apiID, "host_id", host.ID) |
| 172 | return |
| 173 | } |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | hostName := hostDisplayNameFromRow(host) |
| 178 | severity := DefaultSeverity(cfg.DefaultSeverity, "warning") |
| 179 | lastUpdate := time.Now() |
| 180 | if host.LastUpdate.Valid { |
| 181 | lastUpdate = host.LastUpdate.Time |
| 182 | } |
| 183 | meta := map[string]interface{}{ |
| 184 | "host_id": host.ID, |
| 185 | "host_name": hostName, |
| 186 | "last_update": lastUpdate, |
| 187 | "threshold_minutes": 0, |
| 188 | "disconnect_reason": "websocket", |
| 189 | } |
| 190 | title := "Host " + hostName + " disconnected" |
| 191 | msg := fmt.Sprintf("Host \"%s\" WebSocket connection lost. Last update: %s", hostName, lastUpdate.Format(time.RFC3339)) |
| 192 | |
| 193 | // Emit event — notification routing decides which destinations receive it |
| 194 | // (including internal alerts if that destination is enabled). |
| 195 | if emit != nil { |
| 196 | emit.EmitEvent(ctx, d, tenantHost, notifications.Event{ |
| 197 | Type: "host_down", Severity: severity, Title: title, Message: msg, |
nothing calls this directly
no test coverage detected