package main import ( "context" "errors" "fmt" "io" "log" "net/http" "os" "github.com/spf13/viper" "github.com/urfave/cli/v3" ) var ( server string token string ) func load() error { viper.SetConfigName("config") viper.SetConfigType("toml") viper.AddConfigPath("$HOME/.local/share/keys") err := viper.ReadInConfig() if err != nil { return err } server = viper.GetString("server") if server == "" { return errors.New("'server' property is required") } token = viper.GetString("token") if token == "" { return errors.New("'token' property is required") } return nil } func makeReq(method string, url string) error { req, err := http.NewRequest(method, url, nil) if err != nil { return err } req.Header.Set("Authorization", token) res, err := http.DefaultClient.Do(req) if err != nil { return err } recv, err := io.ReadAll(res.Body) if err != nil { return err } if res.StatusCode != 200 { fmt.Printf("<-- %d\n", res.StatusCode) } fmt.Printf("%s", recv) return nil } func main() { cmd := &cli.Command{ Name: "keys-admin", Usage: "keys project server administration tool", Commands: []*cli.Command{ { Name: "version", Usage: "print version and exit", Action: func(context.Context, *cli.Command) error { fmt.Println("version 1.0") return nil }, }, { Name: "clients", Usage: "fetch all clients", Flags: []cli.Flag{ &cli.StringFlag{ Name: "id", Usage: "optional id of the client to fetch", }, }, Action: clients, }, { Name: "logs", Usage: "fetch all/specific logs", Flags: []cli.Flag{ &cli.StringFlag{ Name: "id", Usage: "optional id of the client to log", }, &cli.StringFlag{ Name: "date", Usage: "optional date of the log, id required, in ISO8601 date format", }, &cli.StringFlag{ Name: "skip", Usage: "optional skip bytes in log file", }, &cli.StringFlag{ Name: "take", Usage: "optional take bytes from log file", }, }, Action: logs, }, { Name: "name", Usage: "rename a client", Flags: []cli.Flag{ &cli.StringFlag{ Name: "id", Usage: "id of the client", Required: true, }, &cli.StringFlag{ Name: "new", Usage: "new name of the client", Required: true, }, }, Action: name, }, { Name: "exit", Usage: "request a client to disconnect", Flags: []cli.Flag{ &cli.StringFlag{ Name: "id", Usage: "id of the client", Required: true, }, }, Action: exit, }, }, } err := cmd.Run(context.Background(), os.Args) if err != nil { log.Fatal(err) } } func clients(ctx context.Context, cmd *cli.Command) error { err := load() if err != nil { return err } var url string id := cmd.String("id") if id != "" { url = fmt.Sprintf("%s/admin/clients/%s", server, id) } else { url = fmt.Sprintf("%s/admin/clients", server) } return makeReq(http.MethodGet, url) } func logs(ctx context.Context, cmd *cli.Command) error { err := load() if err != nil { return err } var url string id := cmd.String("id") if id != "" { params := map[string]string{ "date": cmd.String("date"), "skip": cmd.String("skip"), "take": cmd.String("take"), } paramsStr := "" for k, v := range params { if v == "" { continue } if paramsStr == "" { paramsStr = fmt.Sprintf("?%s=%s", k, v) } else { paramsStr += fmt.Sprintf("&%s=%s", k, v) } } url = fmt.Sprintf("%s/admin/logs/%s%s", server, id, paramsStr) } else { url = fmt.Sprintf("%s/admin/logs", server) } return makeReq(http.MethodGet, url) } func name(ctx context.Context, cmd *cli.Command) error { err := load() if err != nil { return err } id := cmd.String("id") name := cmd.String("new") url := fmt.Sprintf("%s/admin/name/%s?name=%s", server, id, name) return makeReq(http.MethodPost, url) } func exit(ctx context.Context, cmd *cli.Command) error { err := load() if err != nil { return err } id := cmd.String("id") url := fmt.Sprintf("%s/admin/exit/%s", server, id) return makeReq(http.MethodPost, url) }