diff --git a/internal/server/http/ui_sources.go b/internal/server/http/ui_sources.go index 8d48c90..cdcd978 100644 --- a/internal/server/http/ui_sources.go +++ b/internal/server/http/ui_sources.go @@ -310,6 +310,20 @@ func (s *Server) handleUISourceGroupDelete(w stdhttp.ResponseWriter, r *stdhttp. return } + // Refuse to delete the host's last source group — every host + // needs at least one to be backup-able. UI disables the button + // in this case; this guards against form-replay / curl. + groups, err := s.deps.Store.ListSourceGroupsByHost(r.Context(), host.ID) + if err != nil { + slog.Error("ui sources: count groups", "err", err) + stdhttp.Error(w, "internal", stdhttp.StatusInternalServerError) + return + } + if len(groups) <= 1 { + stdhttp.Error(w, "this is the host's only source group — create another one first", stdhttp.StatusConflict) + return + } + if err := s.deps.Store.DeleteSourceGroup(r.Context(), host.ID, gid); err != nil { if errors.Is(err, store.ErrNotFound) { stdhttp.NotFound(w, r) diff --git a/web/templates/pages/host_sources.html b/web/templates/pages/host_sources.html index 6452fff..80abfc7 100644 --- a/web/templates/pages/host_sources.html +++ b/web/templates/pages/host_sources.html @@ -66,6 +66,9 @@ {{if gt $row.UsedBy 0}} + {{else if eq (len $page.Groups) 1}} + {{else}}