From 541c239b7e9a3da7c4cb398c20d325bf4957d5ef Mon Sep 17 00:00:00 2001 From: Joel Male Date: Thu, 26 Feb 2026 21:36:41 +1000 Subject: [PATCH] feat: add update-token command to replace API token in-place --- README.md | 11 ++++++----- main.go | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 01be0d7..e9d6954 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,12 @@ ## What It Collects ## CLI Commands ```bash -sudo pulsewise-collector version # show version, check for updates -sudo pulsewise-collector update # download and install latest version -sudo pulsewise-collector status # show config, service state, last pulse -sudo pulsewise-collector dump # print full metrics payload as JSON -sudo pulsewise-collector uninstall # remove binary, config, and service +sudo pulsewise-collector version # show version, check for updates +sudo pulsewise-collector update # download and install latest version +sudo pulsewise-collector update-token # replace the API token and restart +sudo pulsewise-collector status # show config, service state, last pulse +sudo pulsewise-collector dump # print full metrics payload as JSON +sudo pulsewise-collector uninstall # remove binary, config, and service ``` ## Configuration diff --git a/main.go b/main.go index ada1c93..a050e7a 100644 --- a/main.go +++ b/main.go @@ -241,6 +241,9 @@ func main() { case "dump": runDumpCommand() return + case "update-token": + runUpdateTokenCommand() + return } } @@ -395,6 +398,60 @@ func reexecWithSudo() { } } +func runUpdateTokenCommand() { + if os.Getuid() != 0 { + reexecWithSudo() + return + } + + if len(os.Args) < 3 || strings.TrimSpace(os.Args[2]) == "" { + fmt.Fprintf(os.Stderr, "\n %sUsage:%s pulsewise-collector update-token \n\n", ansiBold, ansiReset) + os.Exit(1) + } + newToken := strings.TrimSpace(os.Args[2]) + + // Resolve config path (same logic as loadConfigFromFile) + configPath := configFilePath + if _, err := os.Stat(configPath); os.IsNotExist(err) { + homeDir, _ := os.UserHomeDir() + if homeDir != "" { + local := filepath.Join(homeDir, ".config", "pulsewise-collector", "config") + if _, err := os.Stat(local); err == nil { + configPath = local + } + } + } + + // Read existing config, replace TOKEN line + data, err := os.ReadFile(configPath) + if err != nil { + fmt.Fprintf(os.Stderr, "\n %sError:%s could not read config: %v\n\n", ansiRed, ansiReset, err) + os.Exit(1) + } + + lines := strings.Split(string(data), "\n") + tokenFound := false + for i, line := range lines { + if strings.HasPrefix(strings.TrimSpace(line), "TOKEN=") { + lines[i] = "TOKEN=" + newToken + tokenFound = true + break + } + } + if !tokenFound { + // Prepend if TOKEN line is somehow missing + lines = append([]string{"TOKEN=" + newToken}, lines...) + } + + if err := os.WriteFile(configPath, []byte(strings.Join(lines, "\n")), 0600); err != nil { + fmt.Fprintf(os.Stderr, "\n %sError:%s could not write config: %v\n\n", ansiRed, ansiReset, err) + os.Exit(1) + } + + fmt.Printf("\n %sToken updated.%s\n", ansiGreen, ansiReset) + restartService() +} + func runStatusCommand() { if os.Getuid() != 0 { reexecWithSudo()