package main import ( "context" "encoding/json" "flag" "fmt" "log" "os" "os/signal" "syscall" "bugs.watch/agent/internal/agent" "bugs.watch/agent/internal/config" "bugs.watch/agent/internal/dbmanager" ) func main() { // Peek at the first arg before flag parsing to detect sub-commands. if len(os.Args) > 1 && os.Args[1] == "db" { runDBCmd(os.Args[2:]) return } cfg, err := config.Load() if err != nil { log.Fatalf("configuration error: %v", err) } flag.StringVar(&cfg.DBConfigPath, "db-config", cfg.DBConfigPath, "path to databases.json (default /etc/bugswatch/databases.json, or $BUGSWATCH_DB_CONFIG)") flag.Parse() ctx, cancel := context.WithCancel(context.Background()) defer cancel() sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigCh cancel() }() agent.Run(ctx, cfg) } // ── db sub-commands ────────────────────────────────────────────────────────── func runDBCmd(args []string) { if len(args) == 0 { fmt.Fprintln(os.Stderr, "usage: bugswatch db [args]") os.Exit(1) } // Allow --db-config as the first flag of a db sub-command. cfgPath := config.DefaultDBConfigPath if v := os.Getenv("BUGSWATCH_DB_CONFIG"); v != "" { cfgPath = v } if len(args) >= 2 && args[0] == "--db-config" { cfgPath = args[1] args = args[2:] } switch args[0] { case "add": if len(args) < 3 { fmt.Fprintln(os.Stderr, "usage: bugswatch db add ") fmt.Fprintln(os.Stderr, "drivers: mysql, mariadb, postgres, redis, mongodb, sqlite") os.Exit(1) } dbAdd(cfgPath, args[1], args[2]) case "remove": if len(args) < 2 { fmt.Fprintln(os.Stderr, "usage: bugswatch db remove ") os.Exit(1) } dbRemove(cfgPath, args[1]) case "list": dbList(cfgPath) default: fmt.Fprintf(os.Stderr, "unknown db command %q\n", args[0]) os.Exit(1) } } func dbAdd(cfgPath, driver, url string) { entries, err := dbmanager.ReadEntries(cfgPath) if err != nil { log.Fatalf("read config: %v", err) } found := false for i, e := range entries { if e.Driver == driver { entries[i].URL = url found = true break } } if !found { entries = append(entries, dbmanager.Entry{Driver: driver, URL: url}) } if err := dbmanager.WriteEntries(cfgPath, entries); err != nil { log.Fatalf("write config: %v", err) } fmt.Printf("added %s → %s\n", driver, cfgPath) } func dbRemove(cfgPath, driver string) { entries, err := dbmanager.ReadEntries(cfgPath) if err != nil { log.Fatalf("read config: %v", err) } next := entries[:0] for _, e := range entries { if e.Driver != driver { next = append(next, e) } } if err := dbmanager.WriteEntries(cfgPath, next); err != nil { log.Fatalf("write config: %v", err) } fmt.Printf("removed %s from %s\n", driver, cfgPath) } func dbList(cfgPath string) { entries, err := dbmanager.ReadEntries(cfgPath) if err != nil { log.Fatalf("read config: %v", err) } if len(entries) == 0 { fmt.Println("no database monitors configured") return } enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") enc.Encode(entries) }