diff --git a/weed/admin/handlers/admin_handlers.go b/weed/admin/handlers/admin_handlers.go index 007d1e14b..a9d5862aa 100644 --- a/weed/admin/handlers/admin_handlers.go +++ b/weed/admin/handlers/admin_handlers.go @@ -30,6 +30,7 @@ type AdminHandlers struct { mqHandlers *MessageQueueHandlers serviceAccountHandlers *ServiceAccountHandlers groupHandlers *GroupHandlers + monitoringHandlers *MonitoringHandlers } // NewAdminHandlers creates a new instance of AdminHandlers @@ -43,6 +44,7 @@ func NewAdminHandlers(adminServer *dash.AdminServer, store sessions.Store) *Admi mqHandlers := NewMessageQueueHandlers(adminServer) serviceAccountHandlers := NewServiceAccountHandlers(adminServer) groupHandlers := NewGroupHandlers(adminServer) + monitoringHandlers := NewMonitoringHandlers(adminServer) return &AdminHandlers{ adminServer: adminServer, sessionStore: store, @@ -55,6 +57,7 @@ func NewAdminHandlers(adminServer *dash.AdminServer, store sessions.Store) *Admi mqHandlers: mqHandlers, serviceAccountHandlers: serviceAccountHandlers, groupHandlers: groupHandlers, + monitoringHandlers: monitoringHandlers, } } @@ -130,6 +133,14 @@ func (h *AdminHandlers) registerUIRoutes(r *mux.Router) { r.HandleFunc("/cluster/volume-servers", h.clusterHandlers.ShowClusterVolumeServers).Methods(http.MethodGet) r.HandleFunc("/cluster/mount-clients", h.clusterHandlers.ShowMountClients).Methods(http.MethodGet) + // Monitoring routes + r.HandleFunc("/monitoring", h.monitoringHandlers.ShowOverview).Methods(http.MethodGet) + r.HandleFunc("/monitoring/volume-servers", h.monitoringHandlers.ShowVolumeServers).Methods(http.MethodGet) + r.HandleFunc("/monitoring/filers", h.monitoringHandlers.ShowFilers).Methods(http.MethodGet) + r.HandleFunc("/monitoring/s3", h.monitoringHandlers.ShowS3).Methods(http.MethodGet) + r.HandleFunc("/monitoring/masters", h.monitoringHandlers.ShowMasters).Methods(http.MethodGet) + r.HandleFunc("/monitoring/workers", h.monitoringHandlers.ShowWorkers).Methods(http.MethodGet) + // Storage management routes r.HandleFunc("/storage/volumes", h.clusterHandlers.ShowClusterVolumes).Methods(http.MethodGet) r.HandleFunc("/storage/volumes/{id}/{server}", h.clusterHandlers.ShowVolumeDetails).Methods(http.MethodGet) diff --git a/weed/admin/handlers/monitoring_handlers.go b/weed/admin/handlers/monitoring_handlers.go new file mode 100644 index 000000000..76a8d1e37 --- /dev/null +++ b/weed/admin/handlers/monitoring_handlers.go @@ -0,0 +1,55 @@ +package handlers + +import ( + "net/http" + + "github.com/a-h/templ" + "github.com/seaweedfs/seaweedfs/weed/admin/dash" + "github.com/seaweedfs/seaweedfs/weed/admin/view/app" + "github.com/seaweedfs/seaweedfs/weed/admin/view/layout" +) + +// MonitoringHandlers serves the metrics monitoring pages. +type MonitoringHandlers struct { + adminServer *dash.AdminServer +} + +func NewMonitoringHandlers(adminServer *dash.AdminServer) *MonitoringHandlers { + return &MonitoringHandlers{adminServer: adminServer} +} + +func (h *MonitoringHandlers) ShowOverview(w http.ResponseWriter, r *http.Request) { + h.render(w, r, app.Monitoring) +} + +func (h *MonitoringHandlers) ShowVolumeServers(w http.ResponseWriter, r *http.Request) { + h.render(w, r, app.MonitoringVolumeServers) +} + +func (h *MonitoringHandlers) ShowFilers(w http.ResponseWriter, r *http.Request) { + h.render(w, r, app.MonitoringFilers) +} + +func (h *MonitoringHandlers) ShowS3(w http.ResponseWriter, r *http.Request) { + h.render(w, r, app.MonitoringS3) +} + +func (h *MonitoringHandlers) ShowMasters(w http.ResponseWriter, r *http.Request) { + h.render(w, r, app.MonitoringMasters) +} + +func (h *MonitoringHandlers) ShowWorkers(w http.ResponseWriter, r *http.Request) { + h.render(w, r, app.MonitoringWorkers) +} + +// render builds the monitoring data once and wraps the given page in the layout. +func (h *MonitoringHandlers) render(w http.ResponseWriter, r *http.Request, page func(dash.MonitoringData) templ.Component) { + data := h.adminServer.GetMonitoringData() + + username := usernameOrDefault(r) + w.Header().Set("Content-Type", "text/html") + viewCtx := layout.NewViewContext(r, username, dash.CSRFTokenFromContext(r.Context())) + if err := layout.Layout(viewCtx, page(*data)).Render(r.Context(), w); err != nil { + writeJSONError(w, http.StatusInternalServerError, "Failed to render template: "+err.Error()) + } +}