1package cmd23import (4 "strconv"5 "strings"6 "time"78 "charm.land/lipgloss/v2/table"9 "github.com/caarlos0/duration"10 "github.com/charmbracelet/soft-serve/pkg/backend"11 "github.com/charmbracelet/soft-serve/pkg/proto"12 "github.com/dustin/go-humanize"13 "github.com/spf13/cobra"14)1516// TokenCommand returns a command that manages user access tokens.17func TokenCommand() *cobra.Command {18 cmd := &cobra.Command{19 Use: "token",20 Aliases: []string{"access-token"},21 Short: "Manage access tokens",22 }2324 var createExpiresIn string25 createCmd := &cobra.Command{26 Use: "create NAME",27 Short: "Create a new access token",28 Args: cobra.MinimumNArgs(1),29 RunE: func(cmd *cobra.Command, args []string) error {30 ctx := cmd.Context()31 be := backend.FromContext(ctx)32 name := strings.Join(args, " ")3334 user := proto.UserFromContext(ctx)35 if user == nil {36 return proto.ErrUserNotFound37 }3839 var expiresAt time.Time40 var expiresIn time.Duration41 if createExpiresIn != "" {42 d, err := duration.Parse(createExpiresIn)43 if err != nil {44 return err45 }4647 expiresIn = d48 expiresAt = time.Now().Add(d)49 }5051 token, err := be.CreateAccessToken(ctx, user, name, expiresAt)52 if err != nil {53 return err54 }5556 notice := "Access token created"57 if expiresIn != 0 {58 notice += " (expires in " + humanize.Time(expiresAt) + ")"59 }6061 cmd.PrintErrln(notice)62 cmd.Println(token)6364 return nil65 },66 }6768 createCmd.Flags().StringVar(&createExpiresIn, "expires-in", "", "Token expiration time (e.g. 1y, 3mo, 2w, 5d4h, 1h30m)")6970 listCmd := &cobra.Command{71 Use: "list",72 Aliases: []string{"ls"},73 Short: "List access tokens",74 Args: cobra.NoArgs,75 RunE: func(cmd *cobra.Command, _ []string) error {76 ctx := cmd.Context()77 be := backend.FromContext(ctx)7879 user := proto.UserFromContext(ctx)80 if user == nil {81 return proto.ErrUserNotFound82 }8384 tokens, err := be.ListAccessTokens(ctx, user)85 if err != nil {86 return err87 }8889 if len(tokens) == 0 {90 cmd.Println("No tokens found")91 return nil92 }9394 now := time.Now()95 table := table.New().Headers("ID", "Name", "Created At", "Expires In")96 for _, token := range tokens {97 expiresAt := "-"98 if !token.ExpiresAt.IsZero() {99 if now.After(token.ExpiresAt) {100 expiresAt = "expired"101 } else {102 expiresAt = humanize.Time(token.ExpiresAt)103 }104 }105106 table = table.Row(strconv.FormatInt(token.ID, 10),107 token.Name,108 humanize.Time(token.CreatedAt),109 expiresAt,110 )111 }112 cmd.Println(table)113 return nil114 },115 }116117 deleteCmd := &cobra.Command{118 Use: "delete ID",119 Aliases: []string{"rm", "remove"},120 Short: "Delete an access token",121 Args: cobra.ExactArgs(1),122 RunE: func(cmd *cobra.Command, args []string) error {123 ctx := cmd.Context()124 be := backend.FromContext(ctx)125126 user := proto.UserFromContext(ctx)127 if user == nil {128 return proto.ErrUserNotFound129 }130131 id, err := strconv.ParseInt(args[0], 10, 64)132 if err != nil {133 return err134 }135136 if err := be.DeleteAccessToken(ctx, user, id); err != nil {137 return err138 }139140 cmd.PrintErrln("Access token deleted")141 return nil142 },143 }144145 cmd.AddCommand(146 createCmd,147 listCmd,148 deleteCmd,149 )150151 return cmd152}