Gateway/pkg/meter/meter.go
Claude b33b5548ec
feat: LetheanGateway binary scaffold
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>
2026-04-04 04:54:43 +01:00

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,
}
}