Replaces Python+HAProxy gateway stack with a single Go binary: - pkg/pairing: handshake with lthn.io (pair + heartbeat) - pkg/meter: per-session bandwidth metering - cmd/gateway: main binary with admin API on :8880 - Uses core/api for HTTP, pairs via /v1/gateway/* endpoints Eliminates: Python lvpn, HAProxy config templating, legacy session management. Keeps: WireGuard (managed via go-process when ready). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
84 lines
1.8 KiB
Go
84 lines
1.8 KiB
Go
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
// Package meter tracks bandwidth usage per session for billing.
|
|
//
|
|
// m := meter.New()
|
|
// m.RecordBytes("session-123", 1048576)
|
|
// snapshot := m.Snapshot()
|
|
package meter
|
|
|
|
import "sync"
|
|
|
|
// Meter tracks bandwidth usage across sessions.
|
|
type Meter struct {
|
|
mu sync.RWMutex
|
|
sessions map[string]int64 // session ID → bytes
|
|
totalBytes int64
|
|
totalSessions int64
|
|
activeSessions int64
|
|
}
|
|
|
|
// New creates a bandwidth meter.
|
|
//
|
|
// m := meter.New()
|
|
func New() *Meter {
|
|
return &Meter{
|
|
sessions: make(map[string]int64),
|
|
}
|
|
}
|
|
|
|
// RecordBytes adds bytes to a session's usage.
|
|
//
|
|
// m.RecordBytes("session-abc", 1048576)
|
|
func (m *Meter) RecordBytes(sessionID string, bytes int64) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if _, exists := m.sessions[sessionID]; !exists {
|
|
m.totalSessions++
|
|
m.activeSessions++
|
|
}
|
|
m.sessions[sessionID] += bytes
|
|
m.totalBytes += bytes
|
|
}
|
|
|
|
// EndSession marks a session as complete.
|
|
//
|
|
// m.EndSession("session-abc")
|
|
func (m *Meter) EndSession(sessionID string) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if _, exists := m.sessions[sessionID]; exists {
|
|
m.activeSessions--
|
|
}
|
|
}
|
|
|
|
// TotalBytes returns total bytes served.
|
|
func (m *Meter) TotalBytes() int64 {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
return m.totalBytes
|
|
}
|
|
|
|
// ActiveSessions returns current active session count.
|
|
func (m *Meter) ActiveSessions() int64 {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
return m.activeSessions
|
|
}
|
|
|
|
// Snapshot returns current stats for heartbeat reporting.
|
|
//
|
|
// stats := m.Snapshot()
|
|
func (m *Meter) Snapshot() map[string]interface{} {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
return map[string]interface{}{
|
|
"connections": m.activeSessions,
|
|
"load": m.activeSessions * 10, // rough load estimate
|
|
"bytes_since_last": m.totalBytes,
|
|
"total_sessions": m.totalSessions,
|
|
}
|
|
}
|