125 lines
3.3 KiB
Go
125 lines
3.3 KiB
Go
|
|
package handlers
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"net/http"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"crowdsec-dashy/internal/crowdsec"
|
||
|
|
)
|
||
|
|
|
||
|
|
// BouncersHandler manages the bouncers page and its POST actions.
|
||
|
|
type BouncersHandler struct {
|
||
|
|
deps Deps
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewBouncersHandler(deps Deps) *BouncersHandler {
|
||
|
|
return &BouncersHandler{deps: deps}
|
||
|
|
}
|
||
|
|
|
||
|
|
// BouncersData is passed to the bouncers template.
|
||
|
|
type BouncersData struct {
|
||
|
|
PageData
|
||
|
|
Bouncers []crowdsec.Bouncer
|
||
|
|
NewBouncer *crowdsec.AddBouncerResult // set immediately after add; shown exactly once
|
||
|
|
}
|
||
|
|
|
||
|
|
// List renders the bouncers list.
|
||
|
|
func (h *BouncersHandler) List(w http.ResponseWriter, r *http.Request) {
|
||
|
|
pd := NewPageData(r, "Bouncers", h.deps.CLIAvailable, h.deps.PollInterval)
|
||
|
|
if f := readFlash(r); f.Message != "" {
|
||
|
|
pd.Flash = f
|
||
|
|
}
|
||
|
|
|
||
|
|
var bouncers []crowdsec.Bouncer
|
||
|
|
if h.deps.CLIAvailable {
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
var err error
|
||
|
|
bouncers, err = h.deps.CLI.ListBouncers(ctx)
|
||
|
|
if err != nil {
|
||
|
|
pd.Flash = FlashMessage{Type: "error", Message: fmt.Sprintf("cscli error: %v", err)}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
h.deps.Renderer.Render(w, "bouncers", BouncersData{PageData: pd, Bouncers: bouncers})
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add registers a new bouncer and renders the API key reveal page (no redirect).
|
||
|
|
// The key is shown exactly once in the POST response and never stored by the UI.
|
||
|
|
func (h *BouncersHandler) Add(w http.ResponseWriter, r *http.Request) {
|
||
|
|
r.Body = http.MaxBytesReader(w, r.Body, 4096)
|
||
|
|
if err := r.ParseForm(); err != nil {
|
||
|
|
flashRedirect(w, r, "/bouncers", "error", "invalid form data")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if !checkCSRF(r) {
|
||
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if !h.deps.CLIAvailable {
|
||
|
|
flashRedirect(w, r, "/bouncers", "error", "cscli not available")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
name := r.FormValue("name")
|
||
|
|
if ok, _ := matchName(name); !ok {
|
||
|
|
flashRedirect(w, r, "/bouncers", "error", "invalid name: use 1-64 alphanumeric/dash/underscore characters")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
result, err := h.deps.CLI.AddBouncer(ctx, name)
|
||
|
|
if err != nil {
|
||
|
|
flashRedirect(w, r, "/bouncers", "error", fmt.Sprintf("failed to add bouncer: %v", err))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
bouncers, _ := h.deps.CLI.ListBouncers(ctx)
|
||
|
|
|
||
|
|
pd := NewPageData(r, "Bouncers", h.deps.CLIAvailable, h.deps.PollInterval)
|
||
|
|
h.deps.Renderer.Render(w, "bouncers", BouncersData{
|
||
|
|
PageData: pd,
|
||
|
|
Bouncers: bouncers,
|
||
|
|
NewBouncer: result,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// Delete removes a bouncer by name.
|
||
|
|
func (h *BouncersHandler) Delete(w http.ResponseWriter, r *http.Request) {
|
||
|
|
r.Body = http.MaxBytesReader(w, r.Body, 4096)
|
||
|
|
if err := r.ParseForm(); err != nil {
|
||
|
|
flashRedirect(w, r, "/bouncers", "error", "invalid form data")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if !checkCSRF(r) {
|
||
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if !h.deps.CLIAvailable {
|
||
|
|
flashRedirect(w, r, "/bouncers", "error", "cscli not available")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
name := r.FormValue("name")
|
||
|
|
if ok, _ := matchName(name); !ok {
|
||
|
|
flashRedirect(w, r, "/bouncers", "error", "invalid bouncer name")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
if err := h.deps.CLI.DeleteBouncer(ctx, name); err != nil {
|
||
|
|
flashRedirect(w, r, "/bouncers", "error", fmt.Sprintf("failed to delete bouncer: %v", err))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
flashRedirect(w, r, "/bouncers", "success", fmt.Sprintf("Bouncer %q deleted", name))
|
||
|
|
}
|