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,7 +49,7 @@ type valueSpec struct {
|
||||
isRand bool
|
||||
}
|
||||
|
||||
func fixed(v float64) valueSpec { return valueSpec{fixed: v} }
|
||||
func fixed(v float64) valueSpec { return valueSpec{fixed: v} }
|
||||
func rrange(min, max float64) valueSpec {
|
||||
return valueSpec{min: min, max: max, isRand: true}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ type nodeSpec struct {
|
||||
idleCPU valueSpec
|
||||
loadAvg valueSpec
|
||||
failRate valueSpec
|
||||
sipGateway string // SIP-адрес для авто-создания транка
|
||||
sipGateway string // SIP-адрес для авто-создания транка
|
||||
}
|
||||
|
||||
func (ns *nodeSpec) generate() nodeMetric {
|
||||
|
||||
@@ -32,12 +32,15 @@ func (fc *FSCollector) Connect() error {
|
||||
fc.client.ConnectWithRetry()
|
||||
|
||||
// Ждём подключения с таймаутом
|
||||
timer := time.NewTimer(100 * time.Millisecond)
|
||||
defer timer.Stop()
|
||||
|
||||
for !fc.client.IsConnected() {
|
||||
timer.Reset(100 * time.Millisecond)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("esl connect timeout")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
// Выходим из цикла после ConnectWithRetry
|
||||
case <-timer.C:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,18 +27,18 @@ import (
|
||||
|
||||
// Config — конфигурация агента (agent.json).
|
||||
type Config struct {
|
||||
NodeID string `json:"node_id"` // уникальный ID ноды (uc06, ses-sip)
|
||||
Type string `json:"type"` // "freeswitch" или "asterisk"
|
||||
NatsURL string `json:"nats_url"` // NATS URL
|
||||
NatsUser string `json:"nats_user,omitempty"`
|
||||
NatsPassword string `json:"nats_password,omitempty"`
|
||||
IntervalSec int `json:"interval_sec"` // интервал сбора (default: 5)
|
||||
MaxCalls int `json:"max_calls"` // ёмкость ноды (default: 250)
|
||||
SIPGateway string `json:"sip_gateway"` // SIP-адрес для auto-транка
|
||||
SIPGatewayAuto bool `json:"sip_gateway_auto"` // авто-определить из PBX
|
||||
FailureWindow int `json:"failure_window"` // окно для call_failure_rate (default: 1000)
|
||||
ESL ESLCfg `json:"esl,omitempty"`
|
||||
AMI AMICfg `json:"ami,omitempty"`
|
||||
NodeID string `json:"node_id"` // уникальный ID ноды (uc06, ses-sip)
|
||||
Type string `json:"type"` // "freeswitch" или "asterisk"
|
||||
NatsURL string `json:"nats_url"` // NATS URL
|
||||
NatsUser string `json:"nats_user,omitempty"`
|
||||
NatsPassword string `json:"nats_password,omitempty"`
|
||||
IntervalSec int `json:"interval_sec"` // интервал сбора (default: 5)
|
||||
MaxCalls int `json:"max_calls"` // ёмкость ноды (default: 250)
|
||||
SIPGateway string `json:"sip_gateway"` // SIP-адрес для auto-транка
|
||||
SIPGatewayAuto bool `json:"sip_gateway_auto"` // авто-определить из PBX
|
||||
FailureWindow int `json:"failure_window"` // окно для call_failure_rate (default: 1000)
|
||||
ESL ESLCfg `json:"esl,omitempty"`
|
||||
AMI AMICfg `json:"ami,omitempty"`
|
||||
}
|
||||
|
||||
// ESLCfg — настройки подключения к FreeSWITCH ESL.
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
|
||||
// Publisher публикует метрики в NATS.
|
||||
type Publisher struct {
|
||||
nc *natsgo.Conn
|
||||
nodeID string
|
||||
nc *natsgo.Conn
|
||||
nodeID string
|
||||
}
|
||||
|
||||
// NewPublisher создаёт NATS publisher.
|
||||
|
||||
@@ -15,9 +15,9 @@ func nowTS() int64 {
|
||||
|
||||
// SystemStats содержит системные метрики (CPU, load).
|
||||
type SystemStats struct {
|
||||
Load1 float64 // load average 1m
|
||||
Load5 float64 // load average 5m
|
||||
Load15 float64 // load average 15m
|
||||
Load1 float64 // load average 1m
|
||||
Load5 float64 // load average 5m
|
||||
Load15 float64 // load average 15m
|
||||
IdleCPU float64 // процент простоя CPU
|
||||
}
|
||||
|
||||
|
||||
+14
-18
@@ -1,16 +1,16 @@
|
||||
// SIP тестер для e2e проверки pulse-lets-go + FreeSWITCH.
|
||||
// Два режима:
|
||||
// uas — отвечает 200 OK на SIP INVITE, симулирует PBX-ноду
|
||||
// uac — шлёт SIP INVITE в FreeSWITCH, симулирует оператора
|
||||
//
|
||||
// uas — отвечает 200 OK на SIP INVITE, симулирует PBX-ноду
|
||||
// uac — шлёт SIP INVITE в FreeSWITCH, симулирует оператора
|
||||
//
|
||||
// Примеры:
|
||||
// ./siptest -mode uas (PBX-нода на порту 5090)
|
||||
// ./siptest -mode uac -r 10 -l 100 (оператор, 10 CPS, до 100 одновременных)
|
||||
//
|
||||
// ./siptest -mode uas (PBX-нода на порту 5090)
|
||||
// ./siptest -mode uac -r 10 -l 100 (оператор, 10 CPS, до 100 одновременных)
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -19,6 +19,8 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pulse-lets-go/internal/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -29,9 +31,9 @@ const (
|
||||
|
||||
// Статистика
|
||||
var (
|
||||
callsSent atomic.Int64
|
||||
callsOK atomic.Int64
|
||||
callsFailed atomic.Int64
|
||||
callsSent atomic.Int64
|
||||
callsOK atomic.Int64
|
||||
callsFailed atomic.Int64
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -103,7 +105,7 @@ func buildSIPResponse(invite, localAddr string) string {
|
||||
// Добавляем tag к To если его нет
|
||||
to = strings.TrimSpace(to)
|
||||
if !strings.Contains(to, ";tag=") {
|
||||
to += ";tag=uas-" + randomHex(4)
|
||||
to += ";tag=uas-" + util.RandomHex(4)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("SIP/2.0 200 OK\r\n"+
|
||||
@@ -213,8 +215,8 @@ func runUAC(rate, limit, maxCalls int, dest, caller, host string) {
|
||||
}
|
||||
|
||||
func buildSIPInvite(dest, caller string, callNum int, srcAddr, fsAddr string) string {
|
||||
callID := fmt.Sprintf("call-%d-%s@%s", callNum, randomHex(4), "127.0.0.1")
|
||||
branch := "z9hG4bK-" + randomHex(8)
|
||||
callID := fmt.Sprintf("call-%d-%s@%s", callNum, util.RandomHex(4), "127.0.0.1")
|
||||
branch := "z9hG4bK-" + util.RandomHex(8)
|
||||
|
||||
return fmt.Sprintf("INVITE sip:%s@%s SIP/2.0\r\n"+
|
||||
"Via: SIP/2.0/UDP %s;branch=%s\r\n"+
|
||||
@@ -227,9 +229,3 @@ func buildSIPInvite(dest, caller string, callNum int, srcAddr, fsAddr string) st
|
||||
"\r\n",
|
||||
dest, fsAddr, srcAddr, branch, caller, callNum, dest, callID, caller, srcAddr)
|
||||
}
|
||||
|
||||
func randomHex(n int) string {
|
||||
b := make([]byte, n)
|
||||
rand.Read(b)
|
||||
return hex.EncodeToString(b)[:n]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user