refactor: common util package, ESL/AMI/security fixes, Prometheus metrics
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
This commit is contained in:
+49
-49
@@ -7,15 +7,15 @@ import "time"
|
||||
|
||||
// NodeMetric — сообщение, публикуемое нодой в NATS каждые 5 сек.
|
||||
type NodeMetric struct {
|
||||
NodeID string `json:"node_id"`
|
||||
TS int64 `json:"ts"` // unix timestamp
|
||||
Status string `json:"status"` // "ok", "degraded", "down"
|
||||
ActiveCalls int `json:"active_calls"`
|
||||
MaxCalls int `json:"max_calls"`
|
||||
IdleCPU float64 `json:"idle_cpu"` // 0..100 %
|
||||
LoadAvg float64 `json:"load_avg"` // system load average (1m)
|
||||
CallFailureRate float64 `json:"call_failure_rate"` // 0.0..100.0 %
|
||||
SIPGateway string `json:"sip_gateway,omitempty"` // SIP-адрес ноды для авто-создания транка
|
||||
NodeID string `json:"node_id"`
|
||||
TS int64 `json:"ts"` // unix timestamp
|
||||
Status string `json:"status"` // "ok", "degraded", "down"
|
||||
ActiveCalls int `json:"active_calls"`
|
||||
MaxCalls int `json:"max_calls"`
|
||||
IdleCPU float64 `json:"idle_cpu"` // 0..100 %
|
||||
LoadAvg float64 `json:"load_avg"` // system load average (1m)
|
||||
CallFailureRate float64 `json:"call_failure_rate"` // 0.0..100.0 %
|
||||
SIPGateway string `json:"sip_gateway,omitempty"` // SIP-адрес ноды для авто-создания транка
|
||||
}
|
||||
|
||||
// --- In-memory состояние ноды в engine ---
|
||||
@@ -24,8 +24,8 @@ type NodeMetric struct {
|
||||
// Содержит данные последней метрики + поля ручного управления.
|
||||
type NodeState struct {
|
||||
NodeMetric
|
||||
Disabled bool `json:"disabled"`
|
||||
DisabledReason string `json:"disabled_reason"`
|
||||
Disabled bool `json:"disabled"`
|
||||
DisabledReason string `json:"disabled_reason"`
|
||||
Score float64 `json:"score"`
|
||||
LethalReason string `json:"lethal_reason,omitempty"` // причина, если score = -100
|
||||
}
|
||||
@@ -46,7 +46,7 @@ type Trunk struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type TrunkType `json:"type"`
|
||||
NodeID string `json:"node_id,omitempty"` // только для balance
|
||||
NodeID string `json:"node_id,omitempty"` // только для balance
|
||||
Gateway string `json:"gateway"`
|
||||
Codecs []string `json:"codecs,omitempty"`
|
||||
Context string `json:"context,omitempty"`
|
||||
@@ -68,10 +68,10 @@ const (
|
||||
|
||||
// User — модель пользователя (из users.json).
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
PasswordHash string `json:"password_hash"`
|
||||
Role UserRole `json:"role"`
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
PasswordHash string `json:"password_hash"`
|
||||
Role UserRole `json:"role"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
@@ -96,27 +96,27 @@ type LoginResponse struct {
|
||||
// RouteResponse — ответ на запрос маршрутизации.
|
||||
type RouteResponse struct {
|
||||
// Успех
|
||||
NodeID string `json:"node_id,omitempty"`
|
||||
Score float64 `json:"score,omitempty"`
|
||||
SIPGateway string `json:"sip_gateway,omitempty"`
|
||||
NodeID string `json:"node_id,omitempty"`
|
||||
Score float64 `json:"score,omitempty"`
|
||||
SIPGateway string `json:"sip_gateway,omitempty"`
|
||||
|
||||
// Fallback
|
||||
Fallback bool `json:"fallback,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Fallback bool `json:"fallback,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
|
||||
// Общая информация о нодах
|
||||
Nodes []RouteNodeInfo `json:"nodes,omitempty"`
|
||||
Nodes []RouteNodeInfo `json:"nodes,omitempty"`
|
||||
|
||||
// Ошибка
|
||||
Error string `json:"error,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// RouteNodeInfo — информация о ноде в ответе маршрутизации.
|
||||
type RouteNodeInfo struct {
|
||||
ID string `json:"id"`
|
||||
Score float64 `json:"score"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// --- API: Nodes ---
|
||||
@@ -129,20 +129,20 @@ type ToggleNodeRequest struct {
|
||||
|
||||
// NodeInfo — информация о ноде для фронта.
|
||||
type NodeInfo struct {
|
||||
NodeID string `json:"node_id"`
|
||||
TS int64 `json:"ts"`
|
||||
Status string `json:"status"`
|
||||
ActiveCalls int `json:"active_calls"`
|
||||
MaxCalls int `json:"max_calls"`
|
||||
IdleCPU float64 `json:"idle_cpu"`
|
||||
LoadAvg float64 `json:"load_avg"`
|
||||
NodeID string `json:"node_id"`
|
||||
TS int64 `json:"ts"`
|
||||
Status string `json:"status"`
|
||||
ActiveCalls int `json:"active_calls"`
|
||||
MaxCalls int `json:"max_calls"`
|
||||
IdleCPU float64 `json:"idle_cpu"`
|
||||
LoadAvg float64 `json:"load_avg"`
|
||||
CallFailureRate float64 `json:"call_failure_rate"`
|
||||
Disabled bool `json:"disabled"`
|
||||
DisabledReason string `json:"disabled_reason,omitempty"`
|
||||
Score float64 `json:"score"`
|
||||
LethalReason string `json:"lethal_reason,omitempty"`
|
||||
IsStale bool `json:"is_stale"`
|
||||
SecondsAgo float64 `json:"seconds_ago"`
|
||||
Disabled bool `json:"disabled"`
|
||||
DisabledReason string `json:"disabled_reason,omitempty"`
|
||||
Score float64 `json:"score"`
|
||||
LethalReason string `json:"lethal_reason,omitempty"`
|
||||
IsStale bool `json:"is_stale"`
|
||||
SecondsAgo float64 `json:"seconds_ago"`
|
||||
}
|
||||
|
||||
// --- API: Users ---
|
||||
@@ -191,17 +191,17 @@ type UpdateTrunkRequest struct {
|
||||
|
||||
// ZabbixNode — элемент LLD (Low-Level Discovery) для Zabbix.
|
||||
type ZabbixNode struct {
|
||||
NodeID string `json:"{#NODE_ID}"`
|
||||
Status string `json:"{#STATUS}"`
|
||||
ActiveCalls int `json:"{#ACTIVE_CALLS}"`
|
||||
MaxCalls int `json:"{#MAX_CALLS}"`
|
||||
IdleCPU float64 `json:"{#IDLE_CPU}"`
|
||||
LoadAvg float64 `json:"{#LOAD_AVG}"`
|
||||
NodeID string `json:"{#NODE_ID}"`
|
||||
Status string `json:"{#STATUS}"`
|
||||
ActiveCalls int `json:"{#ACTIVE_CALLS}"`
|
||||
MaxCalls int `json:"{#MAX_CALLS}"`
|
||||
IdleCPU float64 `json:"{#IDLE_CPU}"`
|
||||
LoadAvg float64 `json:"{#LOAD_AVG}"`
|
||||
CallFailureRate float64 `json:"{#CALL_FAILURE_RATE}"`
|
||||
Score float64 `json:"{#SCORE}"`
|
||||
Disabled bool `json:"{#DISABLED}"`
|
||||
IsStale bool `json:"{#IS_STALE}"`
|
||||
SecondsAgo float64 `json:"{#SECONDS_AGO}"`
|
||||
Score float64 `json:"{#SCORE}"`
|
||||
Disabled bool `json:"{#DISABLED}"`
|
||||
IsStale bool `json:"{#IS_STALE}"`
|
||||
SecondsAgo float64 `json:"{#SECONDS_AGO}"`
|
||||
}
|
||||
|
||||
// ZabbixResponse — ответ на запрос Zabbix LLD.
|
||||
@@ -213,6 +213,6 @@ type ZabbixResponse struct {
|
||||
|
||||
// WsMetricsMessage — сообщение, отправляемое по WebSocket.
|
||||
type WsMetricsMessage struct {
|
||||
Type string `json:"type"` // "nodes_update", "node_toggle", etc
|
||||
Type string `json:"type"` // "nodes_update", "node_toggle", etc
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user