Skip to content

Commit 6750f71

Browse files
authored
Merge pull request #24 from SoulKyu/hidden-alert-filtering
Hidden alert filtering
2 parents 2c49cc8 + a9e643a commit 6750f71

17 files changed

+802
-80
lines changed

internal/webui/handlers/dashboard_handlers.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package handlers
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"log"
67
"net/http"
8+
"regexp"
79
"sort"
810
"strconv"
911
"strings"
@@ -204,6 +206,22 @@ func parseDashboardFilters(c *gin.Context) webuimodels.DashboardFilters {
204206
}
205207
}
206208

209+
// Parse filter-specific hidden alerts (JSON)
210+
if hiddenAlertsJSON := c.Query("filterHiddenAlerts"); hiddenAlertsJSON != "" {
211+
var hiddenAlerts []webuimodels.FilterHiddenAlert
212+
if err := json.Unmarshal([]byte(hiddenAlertsJSON), &hiddenAlerts); err == nil {
213+
filters.FilterHiddenAlerts = hiddenAlerts
214+
}
215+
}
216+
217+
// Parse filter-specific hidden rules (JSON)
218+
if hiddenRulesJSON := c.Query("filterHiddenRules"); hiddenRulesJSON != "" {
219+
var hiddenRules []webuimodels.FilterHiddenRule
220+
if err := json.Unmarshal([]byte(hiddenRulesJSON), &hiddenRules); err == nil {
221+
filters.FilterHiddenRules = hiddenRules
222+
}
223+
}
224+
207225
return filters
208226
}
209227

@@ -311,9 +329,26 @@ func getAcknowledgedAlerts() []*webuimodels.DashboardAlert {
311329
func applyDashboardFilters(alerts []*webuimodels.DashboardAlert, filters webuimodels.DashboardFilters, sessionID string) []*webuimodels.DashboardAlert {
312330
var filtered []*webuimodels.DashboardAlert
313331

332+
// Pre-compile filter-specific hidden rules for performance
333+
var compiledFilterRules map[int]*regexp.Regexp
334+
if len(filters.FilterHiddenRules) > 0 && hiddenAlertsService != nil {
335+
compiledFilterRules = hiddenAlertsService.CompileFilterRules(filters.FilterHiddenRules)
336+
}
337+
314338
for _, alert := range alerts {
315339
// Handle hidden alerts based on display mode
316-
isHidden := hiddenAlertsService != nil && hiddenAlertsService.IsAlertHidden(sessionID, alert)
340+
// Check both global hidden and filter-specific hidden (additive)
341+
isGlobalHidden := hiddenAlertsService != nil && hiddenAlertsService.IsAlertHidden(sessionID, alert)
342+
isFilterHidden := false
343+
if hiddenAlertsService != nil && (len(filters.FilterHiddenAlerts) > 0 || len(filters.FilterHiddenRules) > 0) {
344+
isFilterHidden = hiddenAlertsService.IsAlertHiddenByFilter(
345+
alert,
346+
filters.FilterHiddenAlerts,
347+
filters.FilterHiddenRules,
348+
compiledFilterRules,
349+
)
350+
}
351+
isHidden := isGlobalHidden || isFilterHidden
317352

318353
if filters.DisplayMode == webuimodels.DisplayModeHidden {
319354
// For hidden mode, only show hidden alerts

internal/webui/models/dashboard.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ type DashboardFilters struct {
9191
DisplayMode DashboardDisplayMode `json:"displayMode"`
9292
ViewMode DashboardViewMode `json:"viewMode"`
9393
ResolvedAlertsLimit int `json:"resolvedAlertsLimit,omitempty"` // Client-side limit for resolved alerts display
94+
95+
// Filter-specific hidden alerts (from active saved filter, additive with global hidden)
96+
FilterHiddenAlerts []FilterHiddenAlert `json:"filterHiddenAlerts,omitempty"`
97+
FilterHiddenRules []FilterHiddenRule `json:"filterHiddenRules,omitempty"`
9498
}
9599

96100
// DashboardSorting represents sorting configuration

internal/webui/models/filter_preset.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,30 @@ type FilterPresetData struct {
4242

4343
// Column Configuration
4444
ColumnConfigs []ColumnConfig `json:"column_configs,omitempty"`
45+
46+
// Filter-specific hidden alerts (additive with global hidden alerts)
47+
HiddenAlerts []FilterHiddenAlert `json:"hidden_alerts,omitempty"`
48+
49+
// Filter-specific hidden rules (additive with global hidden rules)
50+
HiddenRules []FilterHiddenRule `json:"hidden_rules,omitempty"`
51+
}
52+
53+
// FilterHiddenAlert represents an alert hidden specifically within a saved filter
54+
type FilterHiddenAlert struct {
55+
Fingerprint string `json:"fingerprint"`
56+
AlertName string `json:"alert_name"`
57+
Instance string `json:"instance,omitempty"`
58+
Reason string `json:"reason,omitempty"`
59+
}
60+
61+
// FilterHiddenRule represents a label-based hiding rule specific to a saved filter
62+
type FilterHiddenRule struct {
63+
Name string `json:"name"`
64+
Description string `json:"description,omitempty"`
65+
LabelKey string `json:"label_key"`
66+
LabelValue string `json:"label_value"`
67+
IsRegex bool `json:"is_regex"`
68+
IsEnabled bool `json:"is_enabled"`
4569
}
4670

4771
// FilterPresetRequest is used for creating/updating presets

internal/webui/services/hidden_alerts_service.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,77 @@ func (s *HiddenAlertsService) IsAlertHidden(sessionID string, alert *webuimodels
154154
return false
155155
}
156156

157+
// IsAlertHiddenByFilter checks if an alert is hidden by filter-specific hidden alerts/rules
158+
// This is used for per-filter hiding that's additive to global hidden alerts
159+
func (s *HiddenAlertsService) IsAlertHiddenByFilter(
160+
alert *webuimodels.DashboardAlert,
161+
filterHiddenAlerts []webuimodels.FilterHiddenAlert,
162+
filterHiddenRules []webuimodels.FilterHiddenRule,
163+
compiledRules map[int]*regexp.Regexp,
164+
) bool {
165+
if alert == nil {
166+
return false
167+
}
168+
169+
// Check specific hidden alerts by fingerprint
170+
for _, hiddenAlert := range filterHiddenAlerts {
171+
if hiddenAlert.Fingerprint == alert.Fingerprint {
172+
return true
173+
}
174+
}
175+
176+
// Check hidden rules
177+
for i, rule := range filterHiddenRules {
178+
if !rule.IsEnabled {
179+
continue
180+
}
181+
182+
// Check if the alert has the label
183+
labelValue, exists := alert.Labels[rule.LabelKey]
184+
if !exists {
185+
continue
186+
}
187+
188+
// Check if the label value matches
189+
if rule.IsRegex {
190+
// Use compiled regex if available
191+
if compiledRules != nil {
192+
if regex, ok := compiledRules[i]; ok && regex != nil {
193+
if regex.MatchString(labelValue) {
194+
return true
195+
}
196+
}
197+
}
198+
} else {
199+
// Exact match or empty value (match all)
200+
if rule.LabelValue == "" || rule.LabelValue == labelValue {
201+
return true
202+
}
203+
}
204+
}
205+
206+
return false
207+
}
208+
209+
// CompileFilterRules pre-compiles regex rules for a filter preset
210+
// Returns a map from rule index to compiled regex
211+
func (s *HiddenAlertsService) CompileFilterRules(rules []webuimodels.FilterHiddenRule) map[int]*regexp.Regexp {
212+
compiledRules := make(map[int]*regexp.Regexp)
213+
214+
for i, rule := range rules {
215+
if rule.IsRegex && rule.LabelValue != "" {
216+
regex, err := regexp.Compile(rule.LabelValue)
217+
if err != nil {
218+
log.Printf("Failed to compile filter regex for rule %s: %v", rule.Name, err)
219+
continue
220+
}
221+
compiledRules[i] = regex
222+
}
223+
}
224+
225+
return compiledRules
226+
}
227+
157228
// HideAlert hides a specific alert for a user
158229
func (s *HiddenAlertsService) HideAlert(sessionID string, alert *webuimodels.DashboardAlert, reason string) error {
159230
err := s.backendClient.HideAlert(sessionID, alert.Fingerprint, alert.AlertName, alert.Instance, reason)

internal/webui/static/css/output.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)