2026-08-23 20:39:22 +03:00
|
|
|
package dashboard
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"net/http"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type ipsPageData struct {
|
|
|
|
|
PageData
|
2026-08-24 10:29:08 +03:00
|
|
|
Items []ipQueueItem
|
|
|
|
|
FIPSettleSeconds int
|
2026-09-18 18:47:11 +03:00
|
|
|
PollSeconds int
|
2026-08-23 20:39:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ipDetailData struct {
|
|
|
|
|
PageData
|
|
|
|
|
Detail ipDetailResponse
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) handleIPsPage(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
items, err := s.CA.ListIPs(r.Context())
|
2026-08-24 10:29:08 +03:00
|
|
|
settings, settingsErr := s.CA.GetOrchestratorSettings(r.Context())
|
|
|
|
|
if err == nil {
|
|
|
|
|
err = settingsErr
|
|
|
|
|
}
|
2026-09-18 18:47:11 +03:00
|
|
|
data := ipsPageData{Items: items, FIPSettleSeconds: settings.FIPSettleSeconds, PollSeconds: s.Cfg.OverviewPollIntervalS}
|
2026-08-23 20:39:22 +03:00
|
|
|
data.ActiveNav = "ips"
|
|
|
|
|
data.Banner = bannerFor(err)
|
|
|
|
|
s.renderPage(w, "ips_page", data)
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-18 18:47:11 +03:00
|
|
|
// handleIPsFragment re-renders just the ips_table fragment, for the
|
|
|
|
|
// periodic auto-refresh poll (see ips_content's hx-trigger="every Ns") —
|
|
|
|
|
// identical to a mutating action's post-update render, just with no
|
|
|
|
|
// actionErr of its own.
|
|
|
|
|
func (s *Server) handleIPsFragment(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
s.renderIPsTable(w, r, nil)
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-23 20:39:22 +03:00
|
|
|
func (s *Server) handleIPDetail(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
ip := r.PathValue("ip")
|
|
|
|
|
detail, err := s.CA.GetIP(r.Context(), ip)
|
|
|
|
|
data := ipDetailData{Detail: detail}
|
|
|
|
|
data.ActiveNav = "ips"
|
|
|
|
|
data.Banner = bannerFor(err)
|
|
|
|
|
s.renderPage(w, "ip_detail_page", data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// renderIPsTable re-fetches the current queue and renders the ips_table
|
|
|
|
|
// fragment, tagging actionErr (if any) on the shared error banner. Called
|
|
|
|
|
// after every mutating /ips/* request so the table always reflects true
|
|
|
|
|
// current state regardless of whether the mutation itself succeeded.
|
|
|
|
|
func (s *Server) renderIPsTable(w http.ResponseWriter, r *http.Request, actionErr error) {
|
|
|
|
|
items, listErr := s.CA.ListIPs(r.Context())
|
|
|
|
|
if actionErr == nil {
|
|
|
|
|
actionErr = listErr
|
|
|
|
|
}
|
2026-08-24 10:29:08 +03:00
|
|
|
settings, settingsErr := s.CA.GetOrchestratorSettings(r.Context())
|
|
|
|
|
if actionErr == nil {
|
|
|
|
|
actionErr = settingsErr
|
|
|
|
|
}
|
|
|
|
|
s.renderFragment(w, "ips_table", ipsPageData{Items: items, FIPSettleSeconds: settings.FIPSettleSeconds}, actionErr)
|
2026-08-23 20:39:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) handleIPsSubmit(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
|
|
|
s.renderIPsTable(w, r, fmt.Errorf("invalid form: %w", err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
addresses := splitList(r.PostFormValue("addresses"))
|
|
|
|
|
if len(addresses) == 0 {
|
|
|
|
|
s.renderIPsTable(w, r, &apiErr{Status: http.StatusBadRequest, Message: "укажите хотя бы один адрес"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
_, err := s.CA.SubmitIPs(r.Context(), addresses)
|
|
|
|
|
s.renderIPsTable(w, r, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) handleIPRecheck(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
ip := r.PathValue("ip")
|
|
|
|
|
_, err := s.CA.SubmitIPs(r.Context(), []string{ip})
|
|
|
|
|
s.renderIPsTable(w, r, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) handleIPCancel(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
ip := r.PathValue("ip")
|
|
|
|
|
err := s.CA.CancelIP(r.Context(), ip)
|
|
|
|
|
s.renderIPsTable(w, r, err)
|
|
|
|
|
}
|
2026-08-23 22:24:55 +03:00
|
|
|
|
|
|
|
|
func (s *Server) handleIPDelete(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
ip := r.PathValue("ip")
|
|
|
|
|
err := s.CA.DeleteIP(r.Context(), ip)
|
|
|
|
|
s.renderIPsTable(w, r, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) handleIPsDeleteSelected(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
|
|
|
s.renderIPsTable(w, r, fmt.Errorf("invalid form: %w", err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
addresses := r.Form["addresses"]
|
|
|
|
|
if len(addresses) == 0 {
|
|
|
|
|
s.renderIPsTable(w, r, &apiErr{Status: http.StatusBadRequest, Message: "ничего не выбрано"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
_, err := s.CA.DeleteIPs(r.Context(), addresses)
|
|
|
|
|
s.renderIPsTable(w, r, err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-18 18:47:11 +03:00
|
|
|
func (s *Server) handleIPsRecheckSelected(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
|
|
|
s.renderIPsTable(w, r, fmt.Errorf("invalid form: %w", err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
addresses := r.Form["addresses"]
|
|
|
|
|
if len(addresses) == 0 {
|
|
|
|
|
s.renderIPsTable(w, r, &apiErr{Status: http.StatusBadRequest, Message: "ничего не выбрано"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
_, err := s.CA.SubmitIPs(r.Context(), addresses)
|
|
|
|
|
s.renderIPsTable(w, r, err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-23 22:24:55 +03:00
|
|
|
func (s *Server) handleIPsClear(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
_, err := s.CA.ClearQueue(r.Context())
|
|
|
|
|
s.renderIPsTable(w, r, err)
|
|
|
|
|
}
|