token.go

  1package user
  2
  3import (
  4	"strconv"
  5	"strings"
  6	"time"
  7
  8	"github.com/caarlos0/duration"
  9	"github.com/caarlos0/tablewriter"
 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)
 15
 16func init() {
 17	cmd := &cobra.Command{
 18		Use:     "token",
 19		Aliases: []string{"access-token"},
 20		Short:   "Manage access tokens",
 21	}
 22
 23	var createExpiresIn string
 24	createCmd := &cobra.Command{
 25		Use:   "create NAME",
 26		Short: "Create a new access token",
 27		Args:  cobra.MinimumNArgs(1),
 28		RunE: func(cmd *cobra.Command, args []string) error {
 29			ctx := cmd.Context()
 30			be := backend.FromContext(ctx)
 31			name := strings.Join(args, " ")
 32
 33			user := proto.UserFromContext(ctx)
 34			if user == nil {
 35				return proto.ErrUserNotFound
 36			}
 37
 38			var expiresAt time.Time
 39			var expiresIn time.Duration
 40			if createExpiresIn != "" {
 41				d, err := duration.Parse(createExpiresIn)
 42				if err != nil {
 43					return err
 44				}
 45
 46				expiresIn = d
 47				expiresAt = time.Now().Add(d)
 48			}
 49
 50			token, err := be.CreateAccessToken(ctx, user, name, expiresAt)
 51			if err != nil {
 52				return err
 53			}
 54
 55			notice := "Access token created"
 56			if expiresIn != 0 {
 57				notice += " (expires in " + humanize.Time(expiresAt) + ")"
 58			}
 59
 60			cmd.PrintErrln(notice)
 61			cmd.Println(token)
 62
 63			return nil
 64		},
 65	}
 66
 67	createCmd.Flags().StringVar(&createExpiresIn, "expires-in", "", "Token expiration time (e.g. 1y, 3mo, 2w, 5d4h, 1h30m)")
 68
 69	listCmd := &cobra.Command{
 70		Use:     "list",
 71		Aliases: []string{"ls"},
 72		Short:   "List access tokens",
 73		Args:    cobra.NoArgs,
 74		RunE: func(cmd *cobra.Command, _ []string) error {
 75			ctx := cmd.Context()
 76			be := backend.FromContext(ctx)
 77
 78			user := proto.UserFromContext(ctx)
 79			if user == nil {
 80				return proto.ErrUserNotFound
 81			}
 82
 83			tokens, err := be.ListAccessTokens(ctx, user)
 84			if err != nil {
 85				return err
 86			}
 87
 88			if len(tokens) == 0 {
 89				cmd.Println("No tokens found")
 90				return nil
 91			}
 92
 93			now := time.Now()
 94			return tablewriter.Render(
 95				cmd.OutOrStdout(),
 96				tokens,
 97				[]string{"ID", "Name", "Created At", "Expires In"},
 98				func(t proto.AccessToken) ([]string, error) {
 99					expiresAt := "-"
100					if !t.ExpiresAt.IsZero() {
101						if now.After(t.ExpiresAt) {
102							expiresAt = "expired"
103						} else {
104							expiresAt = humanize.Time(t.ExpiresAt)
105						}
106					}
107
108					return []string{
109						strconv.FormatInt(t.ID, 10),
110						t.Name,
111						humanize.Time(t.CreatedAt),
112						expiresAt,
113					}, nil
114				},
115			)
116		},
117	}
118
119	deleteCmd := &cobra.Command{
120		Use:     "delete ID",
121		Aliases: []string{"rm", "remove"},
122		Short:   "Delete an access token",
123		Args:    cobra.ExactArgs(1),
124		RunE: func(cmd *cobra.Command, args []string) error {
125			ctx := cmd.Context()
126			be := backend.FromContext(ctx)
127
128			user := proto.UserFromContext(ctx)
129			if user == nil {
130				return proto.ErrUserNotFound
131			}
132
133			id, err := strconv.ParseInt(args[0], 10, 64)
134			if err != nil {
135				return err
136			}
137
138			if err := be.DeleteAccessToken(ctx, user, id); err != nil {
139				return err
140			}
141
142			cmd.PrintErrln("Access token deleted")
143			return nil
144		},
145	}
146
147	cmd.AddCommand(
148		createCmd,
149		listCmd,
150		deleteCmd,
151	)
152
153	Command.AddCommand(cmd)
154}