103 lines
2.3 KiB
Go
103 lines
2.3 KiB
Go
|
|
package handlers
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"net/http"
|
||
|
|
"strconv"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"crowdsec-dashy/internal/crowdsec"
|
||
|
|
)
|
||
|
|
|
||
|
|
// AlertsHandler manages the alerts page and its POST actions.
|
||
|
|
type AlertsHandler struct {
|
||
|
|
deps Deps
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewAlertsHandler(deps Deps) *AlertsHandler {
|
||
|
|
return &AlertsHandler{deps: deps}
|
||
|
|
}
|
||
|
|
|
||
|
|
// AlertsData is passed to the alerts template.
|
||
|
|
type AlertsData struct {
|
||
|
|
PageData
|
||
|
|
Alerts []crowdsec.Alert
|
||
|
|
Filter crowdsec.AlertFilter
|
||
|
|
}
|
||
|
|
|
||
|
|
// List renders the alerts list.
|
||
|
|
func (h *AlertsHandler) List(w http.ResponseWriter, r *http.Request) {
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
filter := crowdsec.AlertFilter{
|
||
|
|
Limit: 200,
|
||
|
|
Scenario: r.URL.Query().Get("scenario"),
|
||
|
|
IP: r.URL.Query().Get("ip"),
|
||
|
|
Since: r.URL.Query().Get("since"),
|
||
|
|
}
|
||
|
|
|
||
|
|
alerts, err := h.deps.LAPI.ListAlerts(ctx, filter)
|
||
|
|
if err != nil {
|
||
|
|
h.deps.Renderer.RenderError(w, http.StatusBadGateway, "failed to fetch alerts")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
pd := NewPageData(r, "Alerts", h.deps.CLIAvailable, h.deps.PollInterval)
|
||
|
|
if f := readFlash(r); f.Message != "" {
|
||
|
|
pd.Flash = f
|
||
|
|
}
|
||
|
|
|
||
|
|
h.deps.Renderer.Render(w, "alerts", AlertsData{
|
||
|
|
PageData: pd,
|
||
|
|
Alerts: alerts,
|
||
|
|
Filter: filter,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// Delete processes alert deletion (POST; `id` field may repeat for bulk).
|
||
|
|
func (h *AlertsHandler) Delete(w http.ResponseWriter, r *http.Request) {
|
||
|
|
r.Body = http.MaxBytesReader(w, r.Body, 4096)
|
||
|
|
if err := r.ParseForm(); err != nil {
|
||
|
|
flashRedirect(w, r, "/alerts", "error", "invalid form data")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if !checkCSRF(r) {
|
||
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ids := r.Form["id"]
|
||
|
|
if len(ids) == 0 {
|
||
|
|
flashRedirect(w, r, "/alerts", "error", "no alert selected")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
var errs []string
|
||
|
|
deleted := 0
|
||
|
|
for _, s := range ids {
|
||
|
|
id, err := strconv.ParseInt(s, 10, 64)
|
||
|
|
if err != nil || id <= 0 {
|
||
|
|
errs = append(errs, fmt.Sprintf("invalid id: %q", s))
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if err := h.deps.LAPI.DeleteAlert(ctx, id); err != nil {
|
||
|
|
errs = append(errs, fmt.Sprintf("id %d: %v", id, err))
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
deleted++
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(errs) > 0 {
|
||
|
|
flashRedirect(w, r, "/alerts", "error", strings.Join(errs, "; "))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
flashRedirect(w, r, "/alerts", "success", fmt.Sprintf("%d alert(s) deleted", deleted))
|
||
|
|
}
|