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}