10 Commits

Author SHA1 Message Date
Maksim Totmin
78785e54e1 feat: production-grade routing cache + stale detection fixes
Three-tier routing cache (ESL global -> file -> HTTP) eliminates HTTP
from call path for 2500-5000 concurrent calls. Lua reads cached route
in ~0.1us instead of blocking on api:execute('curl', ...).

Engine fixes:
- recalcBestLocked now skips stale nodes (was 5 min bug -> now ~10-15s)
- PickNodeForCall action_type='node' checks staleness for consistency
- onMetric callback pushes cache on every metric (no ticker delay)

Config:
- stale_threshold_sec default 20 -> 10 (industry standard)
- contrib/ included in Makefile deploy target
- route.lua paths updated for /opt/pulse-lets-go
2026-06-25 22:23:16 +07:00
Maksim Totmin
a7722af29e feat: CID-based routing rules engine with flexible match conditions
Routing rules:
- RouteRule model: 3 match fields (caller_id, dest, ingress_trunk regex),
  3 action types (node/pool/auto), time-based (days of week, time range)
- Engine router.go: CompiledRule with pre-compiled regex,
  PickNodeForCall with cascade (dead node → next rule), pickNodeFromPool
- Separate sync.RWMutex for Router — zero contention admin vs traffic
- Validate-before-save pattern: engine validates before JSON save
- Graceful degradation: corrupted rules skipped, no rules → PickNode()

Persistence:
- data/routing.json with atomic save (tmp→rename), config.Manager
- Auto-created empty on first start

API (admin only):
- GET/POST/PUT/DELETE /api/routing/rules
- PUT /api/routing/rules/:id/toggle
- PUT /api/routing/rules/reorder

Integration:
- /api/route now uses PickNodeForCall with caller_id/dest/ingress_trunk
  query params (already sent by route.lua)
- RouteResponse includes matched_rule field for debugging
- Main.go loads routing rules on startup with graceful fallback

Web UI:
- /routing page: table with priority arrows, inline edit, create form,
  regex test tool, toggle, delete, action badges, hits counter

Tests:
- 28 tests: matchRule, time matching, PickNodeForCall cascade,
  pool scoring, validation, hit counting, compilation
2026-06-25 21:51:37 +07:00
Maksim Totmin
2d9d179b0a feat: eviction trunk cleanup + gateway auto-update on re-registration 2026-06-25 21:01:58 +07:00
Maksim Totmin
b0697da8e4 feat: weighted random routing + mode switching + EWMA smoothing + stale eviction
- Weighted random routing (math/rand/v2 per-goroutine ChaCha8)
- PUT /api/route/mode for runtime mode switch (best / weighted_random)
- EWMA score smoothing (configurable smoothing_factor, default 0.3)
- Periodic stale node eviction (5 min threshold, 60s interval)
- Non-blocking WS broadcast (per-client buffered channel + writePump)
- Background rate limiter cleanup goroutine
- In-memory trunk gateway cache (zero disk I/O in hot path)
- Configurable load_avg multiplier (default 50.0, backward compat)
- UI mode indicator + admin Switch button in dashboard
- 42 tests: weighted random, mode switching, EWMA, eviction, API integration
2026-06-25 20:42:46 +07:00
Maksim Totmin
3a082923cf feat: balancer dashboard metrics + fix WebSocket Hijacker 2026-06-25 19:59:26 +07:00
Maksim Totmin
40b8afb150 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
2026-06-25 19:30:36 +07:00
Maksim Totmin
c713a4f142 feat: metrics agent for FreeSWITCH and Asterisk nodes
- pulse-lets-go-agent: collects metrics from PBX nodes via ESL (FreeSWITCH) or AMI (Asterisk)
- AMI client (raw TCP) for Asterisk Manager Interface
- Collector interface: collect, connect, subscribe hangup events, close
- FreeSWITCH collector: show channels, max_sessions, idle_cpu
- Asterisk collector: CoreShowChannels, CoreSettings
- System collector: /proc/loadavg, /proc/stat (CPU idle)
- Failure tracker: sliding window for call_failure_rate
- NATS publisher: pulse.metrics.<node_id> every 5 seconds
- Graceful shutdown, retry logic (5 failures → status=down)
- Template configs for UC nodes (Asterisk) and ses-sip (FreeSWITCH)
2026-06-25 16:59:10 +07:00
Maksim Totmin
f25f8e4d01 feat: auto-trunk creation, SIP gateway in metrics, gateway WS broadcast
- SIPGateway field in NodeMetric model
- HasNode() engine method
- OnNewNode callback in NATS subscriber
- Auto-create balance trunk from new node metric
- BroadcastGatewayEvent on WS hub
- Gateway status indicators in trunks UI (green/red/gray)
- SPA fallback for frontend routing
2026-06-25 16:56:49 +07:00
Maksim Totmin
533a1fba49 feat: ESL client, gateway management, event subscription
- Raw TCP ESL client with reconnect
- GatewayAdd/GatewayDelete/GatewaySyncAll
- SOFIA::gateway_* event processing
- SendAsync for deadlock-free bgapi commands
2026-06-25 16:56:42 +07:00
Maksim Totmin
44f5fa88e5 first commit 2026-06-25 14:27:41 +07:00