summaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
authorRikki <i@rikki.moe>2025-04-15 10:46:39 +0800
committerRikki <i@rikki.moe>2025-04-15 10:46:39 +0800
commit7a00af46de206b9d38f22c955e8820faedeedc31 (patch)
tree21429dff30c3478ea0a19e2a774e801773d0155a /main.go
parentfb886134a635a632f128a48e891631de566b6baa (diff)
restructure project
Diffstat (limited to 'main.go')
-rw-r--r--main.go207
1 files changed, 0 insertions, 207 deletions
diff --git a/main.go b/main.go
deleted file mode 100644
index 7451840..0000000
--- a/main.go
+++ /dev/null
@@ -1,207 +0,0 @@
-package main
-
-import (
- "context"
- "database/sql"
- "flag"
- "fmt"
- "os"
- "os/signal"
- "syscall"
- "time"
-
- "github.com/jedib0t/go-pretty/table"
- "github.com/jedib0t/go-pretty/text"
- _ "github.com/mattn/go-sqlite3"
- "github.com/sirupsen/logrus"
- "google.golang.org/grpc"
-
- "go.rikki.moe/v2stat/command"
-)
-
-var (
- flagInterval = flag.Int("interval", 300, "Interval in seconds to record stats")
- flagDatabase = flag.String("db", "", "Path to SQLite database")
- flagServer = flag.String("server", "127.0.0.1:8080", "V2Ray API server address")
- flagLogLevel = flag.String("log-level", "info", "Log level (debug, info, warn, error, fatal, panic)")
-)
-
-var DefaultDBPaths = []string{
- "v2stat.db",
- "traffic.db",
- "/var/lib/v2stat/traffic.db",
- "/usr/local/share/v2stat/traffic.db",
- "/opt/apps/v2stat/traffic.db",
-}
-
-type V2Stat struct {
- logger *logrus.Logger
- db *sql.DB
- stat command.StatsServiceClient
-}
-
-func main() {
- flag.Parse()
- args := flag.Args()
-
- if len(args) < 1 {
- fmt.Println("Usage: v2stat <command> [args]")
- fmt.Println("Available commands: daemon, query")
- os.Exit(1)
- }
-
- logger := setupLogger(*flagLogLevel)
- db := setupDatabase(logger, *flagDatabase)
- defer db.Close()
-
- v2stat := &V2Stat{
- logger: logger,
- db: db,
- }
-
- switch args[0] {
- case "daemon":
- conn, err := grpc.NewClient(*flagServer, grpc.WithInsecure())
- if err != nil {
- logger.Fatalf("Failed to dial gRPC server: %v", err)
- }
- defer conn.Close()
- v2stat.stat = command.NewStatsServiceClient(conn)
- runServer(v2stat)
-
- case "query":
- handleQuery(v2stat, args[1:])
-
- default:
- fmt.Println("Unknown command:", args[0])
- fmt.Println("Available commands: daemon, query")
- os.Exit(1)
- }
-}
-
-func setupLogger(levelStr string) *logrus.Logger {
- level, err := logrus.ParseLevel(levelStr)
- if err != nil {
- logrus.Fatalf("Invalid log level: %v", err)
- }
- logger := logrus.New()
- logger.SetLevel(level)
- return logger
-}
-
-func setupDatabase(logger *logrus.Logger, dbpath string) *sql.DB {
- if dbpath == "" {
- for _, path := range DefaultDBPaths {
- if _, err := os.Stat(path); err == nil {
- dbpath = path
- break
- }
- }
- if dbpath == "" {
- logger.Fatal("No database path provided and no default database found.")
- }
- }
- logger.Infof("Using database: %s", dbpath)
-
- db, err := sql.Open("sqlite3", dbpath)
- if err != nil {
- logger.Fatalf("Failed to open database: %v", err)
- }
- return db
-}
-
-func runServer(v2stat *V2Stat) {
- logger := v2stat.logger
-
- if err := v2stat.InitDB(); err != nil {
- logger.Fatalf("Failed to initialize database: %v", err)
- }
-
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
-
- sigCh := make(chan os.Signal, 1)
- signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
- defer signal.Stop(sigCh)
-
- ticker := time.NewTicker(time.Duration(*flagInterval) * time.Second)
- defer ticker.Stop()
-
- for {
- logger.Info("Recording stats...")
- if err := v2stat.RecordNow(ctx); err != nil {
- logger.Errorf("Failed to record stats: %v", err)
- }
-
- select {
- case <-ticker.C:
- continue
- case <-sigCh:
- logger.Info("Received shutdown signal, exiting.")
- return
- case <-ctx.Done():
- logger.Info("Context canceled, exiting.")
- return
- }
- }
-}
-
-func handleQuery(v2stat *V2Stat, args []string) {
- if len(args) == 0 {
- fmt.Println("Usage: v2stat query <connection_name>")
- fmt.Println("Available connections:")
- conns, err := v2stat.QueryConn()
- if err != nil {
- v2stat.logger.Fatalf("Failed to query connection: %v", err)
- }
- for _, c := range conns {
- fmt.Printf("\t%s\n", c.String())
- }
- return
- }
-
- connStr := args[0]
- conn, ok := ParseConnInfo(connStr)
- if !ok {
- v2stat.logger.Fatalf("Invalid connection format: %s", connStr)
- }
-
- stats, err := v2stat.QueryStatsHourly(&conn)
- if err != nil {
- v2stat.logger.Fatalf("Failed to query stats: %v", err)
- }
-
- printStatsTable(stats)
-}
-
-func printStatsTable(stats []TrafficStat) {
- tb := table.NewWriter()
- tb.SetOutputMirror(os.Stdout)
- tb.AppendHeader(table.Row{"Time", "Downlink", "Uplink"})
-
- var totalDown, totalUp int64
-
- for _, stat := range stats {
- totalDown += stat.Downlink
- totalUp += stat.Uplink
- tb.AppendRow(table.Row{stat.Time, sizeToHuman(stat.Downlink), sizeToHuman(stat.Uplink)})
- }
-
- tb.AppendFooter(table.Row{"Total", sizeToHuman(totalDown), sizeToHuman(totalUp)})
- style := table.StyleLight
- style.Format.Footer = text.FormatDefault
- tb.SetStyle(style)
- tb.Render()
-}
-
-func sizeToHuman(size int64) string {
- units := []string{"B", "KiB", "MiB", "GiB", "TiB"}
- value := float64(size)
- for _, unit := range units {
- if value < 1024 {
- return fmt.Sprintf("%7.2f %s", value, unit)
- }
- value /= 1024
- }
- return fmt.Sprintf("%7.2f %s", value, "PiB")
-} \ No newline at end of file