Initial vibeproxy implementation
Self-contained HTTP/HTTPS/TCP/UDP reverse proxy manager written in Go, with SQLite storage, a Bootstrap 5 admin UI (Go templates, no JS framework), and a REST API under /api/v1. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QTXFrNP1imCW7jtfL5SZrw
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
// Package stats computes dashboard/API statistics from request logs and
|
||||
// the live runtime snapshot.
|
||||
package stats
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/owen/vibeproxy/internal/database"
|
||||
"github.com/owen/vibeproxy/internal/models"
|
||||
"github.com/owen/vibeproxy/internal/runtime"
|
||||
)
|
||||
|
||||
type Dashboard struct {
|
||||
ActiveProxyHosts int `json:"active_proxy_hosts"`
|
||||
ActiveTCPStreams int `json:"active_tcp_streams"`
|
||||
ActiveUDPStreams int `json:"active_udp_streams"`
|
||||
RequestsLastHour int `json:"requests_last_hour"`
|
||||
RequestsPerSecond float64 `json:"requests_per_second"`
|
||||
TotalRequestsToday int `json:"total_requests_today"`
|
||||
StatusBreakdown []database.StatusCount `json:"status_breakdown"`
|
||||
BackendHealth []BackendHealthEntry `json:"backend_health"`
|
||||
CertsExpiringSoon []models.Certificate `json:"certs_expiring_soon"`
|
||||
TopHosts []database.HostCount `json:"top_hosts"`
|
||||
TopClients []database.HostCount `json:"top_clients"`
|
||||
RecentErrors []models.RequestLog `json:"recent_errors"`
|
||||
}
|
||||
|
||||
type BackendHealthEntry struct {
|
||||
ProxyHost string `json:"proxy_host"`
|
||||
Backend string `json:"backend"`
|
||||
Healthy bool `json:"healthy"`
|
||||
}
|
||||
|
||||
func Compute(db *sql.DB, snap *runtime.Snapshot) (*Dashboard, error) {
|
||||
d := &Dashboard{}
|
||||
|
||||
seen := map[int64]bool{}
|
||||
for _, rt := range snap.HostsByDomain {
|
||||
if seen[rt.Model.ID] {
|
||||
continue
|
||||
}
|
||||
seen[rt.Model.ID] = true
|
||||
if rt.Model.Type == models.ProxyHostTypeProxy {
|
||||
d.ActiveProxyHosts++
|
||||
}
|
||||
if rt.Pool != nil {
|
||||
for _, b := range rt.Pool.Backends {
|
||||
d.BackendHealth = append(d.BackendHealth, BackendHealthEntry{
|
||||
ProxyHost: rt.Model.Name,
|
||||
Backend: b.Model.Host,
|
||||
Healthy: b.Healthy(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
streams, err := database.ListStreams(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, s := range streams {
|
||||
if !s.Enabled {
|
||||
continue
|
||||
}
|
||||
if s.Protocol == models.StreamTCP {
|
||||
d.ActiveTCPStreams++
|
||||
} else {
|
||||
d.ActiveUDPStreams++
|
||||
}
|
||||
}
|
||||
|
||||
requestsLastHour, err := database.CountRequestLogsSince(db, "-1 hours")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.RequestsLastHour = requestsLastHour
|
||||
d.RequestsPerSecond = float64(requestsLastHour) / 3600.0
|
||||
|
||||
totalToday, err := database.CountRequestLogsSince(db, "-24 hours")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.TotalRequestsToday = totalToday
|
||||
|
||||
statuses, err := database.StatusBreakdownSince(db, "-24 hours")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.StatusBreakdown = statuses
|
||||
|
||||
topHosts, err := database.TopHostsSince(db, "-24 hours", 10)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.TopHosts = topHosts
|
||||
|
||||
topClients, err := database.TopClientsSince(db, "-24 hours", 10)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.TopClients = topClients
|
||||
|
||||
recentErrors, err := database.RecentErrorsSince(db, "-24 hours", 20)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.RecentErrors = recentErrors
|
||||
|
||||
certs, err := database.ListCertificates(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range certs {
|
||||
if c.NotAfter == nil {
|
||||
continue
|
||||
}
|
||||
days := int(c.NotAfter.Sub(time.Now()).Hours() / 24)
|
||||
if days <= 30 {
|
||||
d.CertsExpiringSoon = append(d.CertsExpiringSoon, c)
|
||||
}
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
Reference in New Issue
Block a user