// SPDX-License-Identifier: AGPL-3.0-or-later package app import ( "context" "errors" "fmt" "slices" "strings" "time" "connectrpc.com/connect" "github.com/spf13/cobra" "google.golang.org/protobuf/types/known/timestamppb" "bnema.dev/ermite-api/gen/ermite" "bnema.dev/ermite/internal/adapters/client" ) // jsonToken is the stable JSON shape of a stored token. The secret is only // present in the output of `token create`. type jsonToken struct { ID string `json:"id"` Label string `json:"label"` Scopes []string `json:"scopes"` Repos []string `json:"repos"` // BranchPrefixes limits the branches the token may write; empty means all. BranchPrefixes []string `json:"branch_prefixes"` ExpiresAt *string `json:"expires_at"` CreatedAt *string `json:"created_at"` LastUsedAt *string `json:"last_used_at"` } type jsonCreatedToken struct { jsonToken Secret string `json:"secret"` } func toJSONToken(t *ermite.Token) jsonToken { scopes := make([]string, 0, len(t.GetScopes())) for _, s := range t.GetScopes() { scopes = append(scopes, client.ScopeName(s)) } repos := t.GetRepos() if repos == nil { repos = []string{} } prefixes := t.GetBranchPrefixes() if prefixes == nil { prefixes = []string{} } return jsonToken{ ID: t.GetId(), Label: t.GetLabel(), Scopes: scopes, Repos: repos, BranchPrefixes: prefixes, ExpiresAt: jsonTime(t.GetExpiresAt()), CreatedAt: jsonTime(t.GetCreatedAt()), LastUsedAt: jsonTime(t.GetLastUsedAt()), } } const jsonTokenHelp = ` JSON fields of a token (--json): id, label, scopes (array of names like "repo:read"), repos (array; empty means every repository), branch_prefixes (array; empty means every branch), expires_at, created_at and last_used_at (RFC 3339 UTC, null when unset or never used).` func newTokenCmd(env *cliEnv) *cobra.Command { token := &cobra.Command{ Use: "token", Short: "Manage API tokens on a remote server", Long: "Create, list and revoke API tokens. Needs the token:admin scope (or the server's admin token).", } token.AddCommand(newTokenCreateCmd(env), newTokenListCmd(env), newTokenRevokeCmd(env)) return token } func newTokenCreateCmd(env *cliEnv) *cobra.Command { var ( preset string scopes []string repos []string prefixes []string expires string ) cmd := &cobra.Command{ Use: "create