mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-23 12:15:42 +00:00
fix: Allow dismissing AI findings without Pro license. Related to #885
Users who accumulated AI patrol findings before the patrol-without-AI bug was fixed (24c4bb0b) could not dismiss them because the dismiss and resolve endpoints required a Pro license. Changes: - Remove Pro license requirement from /api/ai/patrol/dismiss endpoint - Remove Pro license requirement from /api/ai/patrol/resolve endpoint - Add ClearAll() method to FindingsStore for bulk clearing - Add DELETE /api/ai/patrol/findings endpoint for clearing all findings - Add "Clear All" button to AI Insights UI Users can now dismiss or resolve any findings they can see, and admins can clear all findings at once if needed.
This commit is contained in:
@@ -584,6 +584,20 @@ func (s *FindingsStore) GetAll(startTime *time.Time) []*Finding {
|
||||
return result
|
||||
}
|
||||
|
||||
// ClearAll removes all findings from the store
|
||||
// Returns the number of findings removed
|
||||
func (s *FindingsStore) ClearAll() int {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
count := len(s.findings)
|
||||
s.findings = make(map[string]*Finding)
|
||||
s.byResource = make(map[string][]string)
|
||||
s.activeCounts = make(map[FindingSeverity]int)
|
||||
s.scheduleSave()
|
||||
return count
|
||||
}
|
||||
|
||||
// Cleanup removes old resolved findings
|
||||
func (s *FindingsStore) Cleanup(maxAge time.Duration) int {
|
||||
s.mu.Lock()
|
||||
|
||||
@@ -2718,6 +2718,48 @@ func (h *AISettingsHandler) HandleSuppressFinding(w http.ResponseWriter, r *http
|
||||
}
|
||||
}
|
||||
|
||||
// HandleClearAllFindings clears all AI findings (DELETE /api/ai/patrol/findings)
|
||||
// This allows users to clear accumulated findings, especially useful for users who
|
||||
// accumulated findings before the patrol-without-AI bug was fixed.
|
||||
func (h *AISettingsHandler) HandleClearAllFindings(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// Require authentication (already wrapped by RequireAuth in router)
|
||||
if !CheckAuth(h.config, w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check for confirm parameter
|
||||
if r.URL.Query().Get("confirm") != "true" {
|
||||
http.Error(w, "confirm=true query parameter required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
patrol := h.aiService.GetPatrolService()
|
||||
if patrol == nil {
|
||||
http.Error(w, "Patrol service not available", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
findings := patrol.GetFindings()
|
||||
count := findings.ClearAll()
|
||||
|
||||
log.Info().Int("count", count).Msg("Cleared all AI findings")
|
||||
|
||||
response := map[string]interface{}{
|
||||
"success": true,
|
||||
"cleared": count,
|
||||
"message": fmt.Sprintf("Cleared %d findings", count),
|
||||
}
|
||||
|
||||
if err := utils.WriteJSONResponse(w, response); err != nil {
|
||||
log.Error().Err(err).Msg("Failed to write clear findings response")
|
||||
}
|
||||
}
|
||||
|
||||
// HandleGetFindingsHistory returns all findings including resolved for history (GET /api/ai/patrol/history)
|
||||
func (h *AISettingsHandler) HandleGetFindingsHistory(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
|
||||
+15
-3
@@ -1161,14 +1161,26 @@ func (r *Router) setupRoutes() {
|
||||
// Mutation endpoints (run, acknowledge, dismiss, etc.) return 402 to prevent unauthorized actions
|
||||
r.mux.HandleFunc("/api/ai/patrol/status", RequireAuth(r.config, r.aiSettingsHandler.HandleGetPatrolStatus))
|
||||
r.mux.HandleFunc("/api/ai/patrol/stream", RequireAuth(r.config, RequireLicenseFeature(r.licenseHandlers.Service(), license.FeatureAIPatrol, r.aiSettingsHandler.HandlePatrolStream)))
|
||||
r.mux.HandleFunc("/api/ai/patrol/findings", RequireAuth(r.config, r.aiSettingsHandler.HandleGetPatrolFindings))
|
||||
r.mux.HandleFunc("/api/ai/patrol/findings", RequireAuth(r.config, func(w http.ResponseWriter, req *http.Request) {
|
||||
switch req.Method {
|
||||
case http.MethodGet:
|
||||
r.aiSettingsHandler.HandleGetPatrolFindings(w, req)
|
||||
case http.MethodDelete:
|
||||
// Clear all findings - doesn't require Pro license so users can clean up accumulated findings
|
||||
r.aiSettingsHandler.HandleClearAllFindings(w, req)
|
||||
default:
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}))
|
||||
r.mux.HandleFunc("/api/ai/patrol/history", RequireAuth(r.config, r.aiSettingsHandler.HandleGetFindingsHistory))
|
||||
r.mux.HandleFunc("/api/ai/patrol/run", RequireAdmin(r.config, RequireLicenseFeature(r.licenseHandlers.Service(), license.FeatureAIPatrol, r.aiSettingsHandler.HandleForcePatrol)))
|
||||
r.mux.HandleFunc("/api/ai/patrol/acknowledge", RequireAuth(r.config, RequireLicenseFeature(r.licenseHandlers.Service(), license.FeatureAIPatrol, r.aiSettingsHandler.HandleAcknowledgeFinding)))
|
||||
r.mux.HandleFunc("/api/ai/patrol/dismiss", RequireAuth(r.config, RequireLicenseFeature(r.licenseHandlers.Service(), license.FeatureAIPatrol, r.aiSettingsHandler.HandleDismissFinding)))
|
||||
// Dismiss and resolve don't require Pro license - users should be able to clear findings they can see
|
||||
// This is especially important for users who accumulated findings before fixing the patrol-without-AI bug
|
||||
r.mux.HandleFunc("/api/ai/patrol/dismiss", RequireAuth(r.config, r.aiSettingsHandler.HandleDismissFinding))
|
||||
r.mux.HandleFunc("/api/ai/patrol/suppress", RequireAuth(r.config, RequireLicenseFeature(r.licenseHandlers.Service(), license.FeatureAIPatrol, r.aiSettingsHandler.HandleSuppressFinding)))
|
||||
r.mux.HandleFunc("/api/ai/patrol/snooze", RequireAuth(r.config, RequireLicenseFeature(r.licenseHandlers.Service(), license.FeatureAIPatrol, r.aiSettingsHandler.HandleSnoozeFinding)))
|
||||
r.mux.HandleFunc("/api/ai/patrol/resolve", RequireAuth(r.config, RequireLicenseFeature(r.licenseHandlers.Service(), license.FeatureAIPatrol, r.aiSettingsHandler.HandleResolveFinding)))
|
||||
r.mux.HandleFunc("/api/ai/patrol/resolve", RequireAuth(r.config, r.aiSettingsHandler.HandleResolveFinding))
|
||||
r.mux.HandleFunc("/api/ai/patrol/runs", RequireAuth(r.config, r.aiSettingsHandler.HandleGetPatrolRunHistory))
|
||||
// Suppression rules management (also Pro-only since they control LLM behavior)
|
||||
// GET returns empty array for unlicensed, POST returns 402
|
||||
|
||||
Reference in New Issue
Block a user