diff --git a/internal/cmd/server/rename/rename.go b/internal/cmd/server/rename/rename.go new file mode 100644 index 0000000..3a8c484 --- /dev/null +++ b/internal/cmd/server/rename/rename.go @@ -0,0 +1,102 @@ +package rename + +import ( + "context" + "fmt" + "strings" + + "github.com/spf13/cobra" + "github.com/zeabur/cli/internal/cmdutil" +) + +type Options struct { + id string + name string +} + +func NewCmdRename(f *cmdutil.Factory) *cobra.Command { + opts := &Options{} + + cmd := &cobra.Command{ + Use: "rename [server-id]", + Short: "Rename a dedicated server", + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) > 0 { + if opts.id != "" && opts.id != args[0] { + return fmt.Errorf("conflicting server IDs: arg=%q, --id=%q", args[0], opts.id) + } + opts.id = args[0] + } + return runRename(f, opts) + }, + } + + cmd.Flags().StringVar(&opts.id, "id", "", "Server ID") + cmd.Flags().StringVar(&opts.name, "name", "", "New server name") + + return cmd +} + +func runRename(f *cmdutil.Factory, opts *Options) error { + if f.Interactive { + return runRenameInteractive(f, opts) + } + return runRenameNonInteractive(f, opts) +} + +func runRenameInteractive(f *cmdutil.Factory, opts *Options) error { + if opts.id == "" { + servers, err := f.ApiClient.ListServers(context.Background()) + if err != nil { + return fmt.Errorf("list servers failed: %w", err) + } + if len(servers) == 0 { + return fmt.Errorf("no servers found") + } + + options := make([]string, len(servers)) + for i, s := range servers { + location := s.IP + if s.City != nil && s.Country != nil { + location = fmt.Sprintf("%s, %s", *s.City, *s.Country) + } else if s.Country != nil { + location = *s.Country + } + options[i] = fmt.Sprintf("%s (%s)", s.Name, location) + } + + idx, err := f.Prompter.Select("Select a server to rename", "", options) + if err != nil { + return err + } + opts.id = servers[idx].ID + } + + if strings.TrimSpace(opts.name) == "" { + name, err := f.Prompter.Input("New server name", "") + if err != nil { + return err + } + opts.name = name + } + + return runRenameNonInteractive(f, opts) +} + +func runRenameNonInteractive(f *cmdutil.Factory, opts *Options) error { + if opts.id == "" { + return fmt.Errorf("server-id or --id is required") + } + opts.name = strings.TrimSpace(opts.name) + if opts.name == "" { + return fmt.Errorf("--name is required") + } + + if err := f.ApiClient.RenameServer(context.Background(), opts.id, opts.name); err != nil { + return fmt.Errorf("rename server failed: %w", err) + } + + f.Log.Infof("Server <%s> renamed to %q", opts.id, opts.name) + return nil +} diff --git a/internal/cmd/server/server.go b/internal/cmd/server/server.go index 93b7202..275b0fc 100644 --- a/internal/cmd/server/server.go +++ b/internal/cmd/server/server.go @@ -10,6 +10,7 @@ import ( serverProviderCmd "github.com/zeabur/cli/internal/cmd/server/provider" serverRebootCmd "github.com/zeabur/cli/internal/cmd/server/reboot" serverRegionCmd "github.com/zeabur/cli/internal/cmd/server/region" + serverRenameCmd "github.com/zeabur/cli/internal/cmd/server/rename" serverRentCmd "github.com/zeabur/cli/internal/cmd/server/rent" serverSSHCmd "github.com/zeabur/cli/internal/cmd/server/ssh" "github.com/zeabur/cli/internal/cmdutil" @@ -25,6 +26,7 @@ func NewCmdServer(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(serverListCmd.NewCmdList(f)) cmd.AddCommand(serverGetCmd.NewCmdGet(f)) cmd.AddCommand(serverRebootCmd.NewCmdReboot(f)) + cmd.AddCommand(serverRenameCmd.NewCmdRename(f)) cmd.AddCommand(serverRentCmd.NewCmdRent(f)) cmd.AddCommand(serverProviderCmd.NewCmdProvider(f)) cmd.AddCommand(serverRegionCmd.NewCmdRegion(f)) diff --git a/pkg/api/interface.go b/pkg/api/interface.go index b2f9f85..81e33af 100644 --- a/pkg/api/interface.go +++ b/pkg/api/interface.go @@ -50,6 +50,7 @@ type ( ListServers(ctx context.Context) (model.ServerListItems, error) GetServer(ctx context.Context, id string) (*model.ServerDetail, error) RebootServer(ctx context.Context, id string) error + RenameServer(ctx context.Context, id, name string) error ListDedicatedServerProviders(ctx context.Context) ([]model.CloudProvider, error) ListDedicatedServerRegions(ctx context.Context, provider string) ([]model.DedicatedServerRegion, error) ListDedicatedServerPlans(ctx context.Context, provider, region string) (model.DedicatedServerPlans, error) diff --git a/pkg/api/server.go b/pkg/api/server.go index 9d793cc..be8b85c 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -52,6 +52,24 @@ func (c *client) RebootServer(ctx context.Context, id string) error { return nil } +func (c *client) RenameServer(ctx context.Context, id, name string) error { + var mutation struct { + UpdateServerName bool `graphql:"updateServerName(_id: $id, name: $name)"` + } + + err := c.Mutate(ctx, &mutation, V{ + "id": ObjectID(id), + "name": name, + }) + if err != nil { + return err + } + if !mutation.UpdateServerName { + return fmt.Errorf("rename server was not accepted") + } + return nil +} + func (c *client) ListDedicatedServerProviders(ctx context.Context) ([]model.CloudProvider, error) { var query struct { Providers []model.CloudProvider `graphql:"dedicatedServerProviders"`