Watch
1
0
Fork
You've already forked RedFlag
0

global events query + handler — fleet-wide notification bell feed

This commit is contained in:
Fimeg 2026-06-11 11:33:07 -04:00
commit 6debe1f729
2 changed files with 90 additions and 0 deletions

View file

@ -0,0 +1,57 @@
package handlers
import (
"log"
"net/http"
"strconv"
"github.com/Fimeg/RedFlag/server/internal/database/queries"
"github.com/Fimeg/RedFlag/server/internal/services"
"github.com/gin-gonic/gin"
)
// GlobalEventsHandler serves the fleet-wide event feed for the notification bell.
type GlobalEventsHandler struct {
agentQueries *queries.AgentQueries
}
// NewGlobalEventsHandler creates a GlobalEventsHandler.
func NewGlobalEventsHandler(aq *queries.AgentQueries) *GlobalEventsHandler {
return &GlobalEventsHandler{agentQueries: aq}
}
// GetGlobalEvents returns recent system events across all agents.
// GET /api/v1/events?severity=error,critical,warning&limit=50
func (h *GlobalEventsHandler) GetGlobalEvents(c *gin.Context) {
severity := c.Query("severity")
limitStr := c.DefaultQuery("limit", "50")
limit, err := strconv.Atoi(limitStr)
if err != nil || limit < 1 {
limit = 50
}
if limit > 200 {
limit = 200
}
events, err := h.agentQueries.GetGlobalEvents(severity, limit)
if err != nil {
log.Printf("ERROR: Failed to fetch global events: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to fetch events"})
return
}
// Populate narrative for each event — UI consumes rendered text.
for i := range events {
events[i].Narrative = services.RenderSystemEvent(
events[i].EventType,
events[i].EventSubtype,
events[i].Message,
events[i].Metadata,
)
}
c.JSON(http.StatusOK, gin.H{
"events": events,
"total": len(events),
})
}

View file

@ -478,6 +478,39 @@ func (q *AgentQueries) GetAgentEvents(agentID uuid.UUID, severity string, limit
return events, nil
}
// GetGlobalEvents retrieves recent system events across all agents.
// Used by the notification bell to surface fleet-wide activity.
func (q *AgentQueries) GetGlobalEvents(severity string, limit int) ([]models.SystemEvent, error) {
query := `
SELECT id, agent_id, event_type, event_subtype, severity, component,
message, metadata, created_at
FROM system_events
ORDER BY created_at DESC
LIMIT $1
`
args := []interface{}{limit}
if severity != "" {
query = `
SELECT id, agent_id, event_type, event_subtype, severity, component,
message, metadata, created_at
FROM system_events
WHERE severity = ANY(string_to_array($1, ','))
ORDER BY created_at DESC
LIMIT $2
`
args = []interface{}{severity, limit}
}
var events []models.SystemEvent
err := q.db.Select(&events, query, args...)
if err != nil {
return nil, fmt.Errorf("failed to fetch global events: %w", err)
}
return events, nil
}
// SetAgentUpdating marks an agent as updating with nonce
func (q *AgentQueries) SetAgentUpdating(agentID string, isUpdating bool, targetVersion string) error {
query := `