Files
crowdsec-dashy/internal/handlers/hub.go
T

208 lines
5.0 KiB
Go
Raw Normal View History

2026-05-17 08:28:16 +00:00
package handlers
import (
"context"
"fmt"
"net/http"
"time"
"crowdsec-dashy/internal/crowdsec"
)
// HubHandler manages the hub page (collections, parsers, scenarios, postoverflows).
type HubHandler struct {
deps Deps
}
func NewHubHandler(deps Deps) *HubHandler {
return &HubHandler{deps: deps}
}
// HubData is passed to the hub template.
type HubData struct {
PageData
Tab string
Items []crowdsec.HubItem // current tab's items
}
// List renders the hub page for the active tab.
func (h *HubHandler) List(w http.ResponseWriter, r *http.Request) {
pd := NewPageData(r, "Hub", h.deps.CLIAvailable, h.deps.PollInterval)
if f := readFlash(r); f.Message != "" {
pd.Flash = f
}
tab := sanitizeTab(r.URL.Query().Get("tab"))
data := HubData{PageData: pd, Tab: tab}
if h.deps.CLIAvailable {
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
defer cancel()
var err error
switch tab {
case "collections":
data.Items, err = h.deps.CLI.ListCollections(ctx)
case "parsers":
data.Items, err = h.deps.CLI.ListParsers(ctx)
case "scenarios":
data.Items, err = h.deps.CLI.ListScenarios(ctx)
case "postoverflows":
data.Items, err = h.deps.CLI.ListPostoverflows(ctx)
}
if err != nil {
data.PageData.Flash = FlashMessage{Type: "error", Message: fmt.Sprintf("cscli error: %v", err)}
}
}
h.deps.Renderer.Render(w, "hub", data)
}
// Install installs a hub item.
func (h *HubHandler) Install(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, 4096)
if err := r.ParseForm(); err != nil {
flashRedirect(w, r, "/hub", "error", "invalid form data")
return
}
if !checkCSRF(r) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
if !h.deps.CLIAvailable {
flashRedirect(w, r, "/hub", "error", "cscli not available")
return
}
kind := r.FormValue("kind")
name := r.FormValue("name")
tab := sanitizeTab(r.FormValue("tab"))
if err := validateHubKind(kind); err != nil {
flashRedirect(w, r, hubURL(tab), "error", err.Error())
return
}
ctx, cancel := context.WithTimeout(r.Context(), 120*time.Second)
defer cancel()
var err error
switch kind {
case "collections":
err = h.deps.CLI.InstallCollection(ctx, name)
case "parsers":
err = h.deps.CLI.InstallParser(ctx, name)
case "scenarios":
err = h.deps.CLI.InstallScenario(ctx, name)
default:
flashRedirect(w, r, hubURL(tab), "error", "unsupported hub type")
return
}
if err != nil {
flashRedirect(w, r, hubURL(tab), "error", fmt.Sprintf("install failed: %v", err))
return
}
flashRedirect(w, r, hubURL(tab), "success", fmt.Sprintf("Installed %s", name))
}
// Remove uninstalls a hub item.
func (h *HubHandler) Remove(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, 4096)
if err := r.ParseForm(); err != nil {
flashRedirect(w, r, "/hub", "error", "invalid form data")
return
}
if !checkCSRF(r) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
if !h.deps.CLIAvailable {
flashRedirect(w, r, "/hub", "error", "cscli not available")
return
}
kind := r.FormValue("kind")
name := r.FormValue("name")
tab := sanitizeTab(r.FormValue("tab"))
if err := validateHubKind(kind); err != nil {
flashRedirect(w, r, hubURL(tab), "error", err.Error())
return
}
ctx, cancel := context.WithTimeout(r.Context(), 120*time.Second)
defer cancel()
var err error
switch kind {
case "collections":
err = h.deps.CLI.RemoveCollection(ctx, name)
case "parsers":
err = h.deps.CLI.RemoveParser(ctx, name)
case "scenarios":
err = h.deps.CLI.RemoveScenario(ctx, name)
default:
flashRedirect(w, r, hubURL(tab), "error", "unsupported hub type")
return
}
if err != nil {
flashRedirect(w, r, hubURL(tab), "error", fmt.Sprintf("remove failed: %v", err))
return
}
flashRedirect(w, r, hubURL(tab), "success", fmt.Sprintf("Removed %s", name))
}
// Update runs cscli hub update + upgrade.
func (h *HubHandler) Update(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, 1024)
if err := r.ParseForm(); err != nil {
flashRedirect(w, r, "/hub", "error", "invalid form data")
return
}
if !checkCSRF(r) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
if !h.deps.CLIAvailable {
flashRedirect(w, r, "/hub", "error", "cscli not available")
return
}
ctx, cancel := context.WithTimeout(r.Context(), 180*time.Second)
defer cancel()
if err := h.deps.CLI.HubUpdate(ctx); err != nil {
flashRedirect(w, r, "/hub", "error", fmt.Sprintf("hub update failed: %v", err))
return
}
flashRedirect(w, r, "/hub", "success", "Hub updated and upgraded successfully")
}
func sanitizeTab(tab string) string {
switch tab {
case "collections", "parsers", "scenarios", "postoverflows":
return tab
}
return "collections"
}
func hubURL(tab string) string {
return "/hub?tab=" + tab
}
func validateHubKind(kind string) error {
switch kind {
case "collections", "parsers", "scenarios", "postoverflows":
return nil
}
return fmt.Errorf("invalid hub kind: %q", kind)
}