Backend stability and security improvements: * internal/util/ — common RandomHex helper, removed 3 duplicates * ESL: deduplicated readMessage (locked/unlocked), net.JoinHostPort for IPv6 * AMI: synchronous reconnect() in readEventsLoop, net.JoinHostPort for IPv6 * Auth: /api/auth/refresh accepts Authorization header only (no ?token=) * decodeJSON: http.MaxBytesReader(1<<20) body limit * Trunks: gatewayParams() uses configured ESL.GatewayPrefix * Config: jwt_secret_env env-var fallback * FSCollector: time.After → time.NewTimer with defer Stop * Monitoring: Prometheus counters (route_requests, nodes_total/healthy, uptime) * go fmt pass across all internal/ packages
99 lines
3.3 KiB
Go
99 lines
3.3 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/pulse-lets-go/internal/models"
|
|
)
|
|
|
|
// handleZabbix — GET /api/monitoring/zabbix — возвращает JSON для Zabbix LLD + items.
|
|
func (a *API) handleZabbix(w http.ResponseWriter, r *http.Request) {
|
|
nodes := a.engine.GetAllNodes()
|
|
|
|
data := make([]models.ZabbixNode, 0, len(nodes))
|
|
for _, n := range nodes {
|
|
data = append(data, models.ZabbixNode{
|
|
NodeID: n.NodeID,
|
|
Status: n.Status,
|
|
ActiveCalls: n.ActiveCalls,
|
|
MaxCalls: n.MaxCalls,
|
|
IdleCPU: n.IdleCPU,
|
|
LoadAvg: n.LoadAvg,
|
|
CallFailureRate: n.CallFailureRate,
|
|
Score: n.Score,
|
|
Disabled: n.Disabled,
|
|
IsStale: n.IsStale,
|
|
SecondsAgo: n.SecondsAgo,
|
|
})
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, models.ZabbixResponse{Data: data})
|
|
}
|
|
|
|
// handlePrometheus — GET /api/monitoring/prometheus — text/plain метрики для Prometheus.
|
|
func (a *API) handlePrometheus(w http.ResponseWriter, r *http.Request) {
|
|
nodes := a.engine.GetAllNodes()
|
|
|
|
var sb strings.Builder
|
|
|
|
// Общие метрики (counter gauge)
|
|
stats := a.engine.GetHealthStats()
|
|
fmt.Fprintf(&sb, "# HELP pulse_route_requests_total Total number of route requests.\n")
|
|
fmt.Fprintf(&sb, "# TYPE pulse_route_requests_total counter\n")
|
|
fmt.Fprintf(&sb, "pulse_route_requests_total %d\n", stats.RouteRequests)
|
|
|
|
fmt.Fprintf(&sb, "# HELP pulse_nodes_total Total nodes registered.\n")
|
|
fmt.Fprintf(&sb, "# TYPE pulse_nodes_total gauge\n")
|
|
fmt.Fprintf(&sb, "pulse_nodes_total %d\n", stats.TotalNodes)
|
|
|
|
fmt.Fprintf(&sb, "# HELP pulse_nodes_healthy Healthy nodes count.\n")
|
|
fmt.Fprintf(&sb, "# TYPE pulse_nodes_healthy gauge\n")
|
|
fmt.Fprintf(&sb, "pulse_nodes_healthy %d\n", stats.HealthyNodes)
|
|
|
|
fmt.Fprintf(&sb, "# HELP pulse_uptime_seconds Uptime in seconds.\n")
|
|
fmt.Fprintf(&sb, "# TYPE pulse_uptime_seconds gauge\n")
|
|
fmt.Fprintf(&sb, "pulse_uptime_seconds %d\n", stats.UptimeSeconds)
|
|
|
|
fmt.Fprintf(&sb, "\n")
|
|
|
|
for _, n := range nodes {
|
|
id := sanitizePromLabel(n.NodeID)
|
|
|
|
fmt.Fprintf(&sb, "pulse_node_active_calls{node=\"%s\"} %d\n", id, n.ActiveCalls)
|
|
fmt.Fprintf(&sb, "pulse_node_max_calls{node=\"%s\"} %d\n", id, n.MaxCalls)
|
|
fmt.Fprintf(&sb, "pulse_node_idle_cpu{node=\"%s\"} %.2f\n", id, n.IdleCPU)
|
|
fmt.Fprintf(&sb, "pulse_node_load_avg{node=\"%s\"} %.2f\n", id, n.LoadAvg)
|
|
fmt.Fprintf(&sb, "pulse_node_call_failure_rate{node=\"%s\"} %.2f\n", id, n.CallFailureRate)
|
|
fmt.Fprintf(&sb, "pulse_node_score{node=\"%s\"} %.2f\n", id, n.Score)
|
|
fmt.Fprintf(&sb, "pulse_node_seconds_ago{node=\"%s\"} %.2f\n", id, n.SecondsAgo)
|
|
|
|
disabledVal := 0
|
|
staleVal := 0
|
|
statusVal := 1 // ok
|
|
if n.Disabled {
|
|
disabledVal = 1
|
|
}
|
|
if n.IsStale {
|
|
staleVal = 1
|
|
}
|
|
if n.Status != "ok" {
|
|
statusVal = 0
|
|
}
|
|
|
|
fmt.Fprintf(&sb, "pulse_node_disabled{node=\"%s\"} %d\n", id, disabledVal)
|
|
fmt.Fprintf(&sb, "pulse_node_stale{node=\"%s\"} %d\n", id, staleVal)
|
|
fmt.Fprintf(&sb, "pulse_node_status_ok{node=\"%s\"} %d\n", id, statusVal)
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(sb.String()))
|
|
}
|
|
|
|
// sanitizePromLabel заменяет символы, недопустимые в Prometheus-лейблах.
|
|
func sanitizePromLabel(s string) string {
|
|
return strings.ReplaceAll(strings.ReplaceAll(s, ".", "_"), "-", "_")
|
|
}
|