Initial commit: stats-gateway gRPC service + dashboard + CLI

This commit is contained in:
2026-05-05 21:04:32 +05:00
commit 4059e94759
44 changed files with 2513 additions and 0 deletions
+89
View File
@@ -0,0 +1,89 @@
package main
import (
"context"
"encoding/json"
"flag"
"log"
"os"
"path/filepath"
"time"
"github.com/bergabruh/stats-gateway/internal/storage"
)
type sample struct {
TS string `json:"ts"`
HTTPMs float64 `json:"http_ms"`
TLSMs float64 `json:"tls_ms"`
TCPMs float64 `json:"tcp_ms"`
}
type nodeData struct {
Recent []sample `json:"recent"`
}
type snapshot struct {
GeneratedAt string `json:"generated_at"`
Nodes map[string]nodeData `json:"nodes"`
}
func main() {
dbPath := flag.String("db", "/var/lib/stats-gateway/stats.db", "")
out := flag.String("out", "/var/www/stats-site/data/snapshot.json", "")
limitPerNode := flag.Int("limit", 288, "max samples per node (288 = ~24h at 5min interval)")
flag.Parse()
st, err := storage.Open(*dbPath)
if err != nil {
log.Fatalf("open db: %v", err)
}
defer st.Close()
s := snapshot{
GeneratedAt: time.Now().UTC().Format(time.RFC3339),
Nodes: map[string]nodeData{},
}
ctx := context.Background()
for _, node := range []string{"fl", "pl1"} {
samples, err := st.QueryRecent(ctx, node, *limitPerNode, time.Time{})
if err != nil {
log.Printf("query %s: %v", node, err)
continue
}
nd := nodeData{Recent: make([]sample, 0, len(samples))}
for _, sm := range samples {
nd.Recent = append(nd.Recent, sample{
TS: sm.Timestamp.Format(time.RFC3339Nano),
HTTPMs: sm.HTTPLatencyMs,
TLSMs: sm.TLSHandshakeMs,
TCPMs: sm.TCPRttMs,
})
}
s.Nodes[node] = nd
}
if err := os.MkdirAll(filepath.Dir(*out), 0755); err != nil {
log.Fatalf("mkdir %s: %v", filepath.Dir(*out), err)
}
// Atomic write: write to .tmp then rename.
tmp := *out + ".tmp"
f, err := os.Create(tmp)
if err != nil {
log.Fatalf("create %s: %v", tmp, err)
}
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
if err := enc.Encode(s); err != nil {
f.Close()
os.Remove(tmp)
log.Fatalf("encode: %v", err)
}
f.Close()
if err := os.Rename(tmp, *out); err != nil {
log.Fatalf("rename: %v", err)
}
log.Printf("snapshot: nodes=%d → %s", len(s.Nodes), *out)
}