mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-22 09:51:01 +00:00
Cli setup command (#3384)
Co-authored-by: Robert Kaussow <xoxys@rknet.org>
This commit is contained in:
parent
1026f95f7e
commit
03c891eb93
17 changed files with 665 additions and 43 deletions
|
@ -21,6 +21,12 @@ import (
|
|||
)
|
||||
|
||||
var GlobalFlags = append([]cli.Flag{
|
||||
&cli.StringFlag{
|
||||
EnvVars: []string{"WOODPECKER_CONFIG"},
|
||||
Name: "config",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "path to config file",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
EnvVars: []string{"WOODPECKER_TOKEN"},
|
||||
Name: "token",
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal/config"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/update"
|
||||
)
|
||||
|
||||
|
@ -17,7 +18,7 @@ var (
|
|||
)
|
||||
|
||||
func Before(c *cli.Context) error {
|
||||
if err := SetupGlobalLogger(c); err != nil {
|
||||
if err := setupGlobalLogger(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -49,7 +50,7 @@ func Before(c *cli.Context) error {
|
|||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
return config.Load(c)
|
||||
}
|
||||
|
||||
func After(_ *cli.Context) error {
|
||||
|
|
|
@ -20,6 +20,6 @@ import (
|
|||
"go.woodpecker-ci.org/woodpecker/v2/shared/logger"
|
||||
)
|
||||
|
||||
func SetupGlobalLogger(c *cli.Context) error {
|
||||
func setupGlobalLogger(c *cli.Context) error {
|
||||
return logger.SetupGlobalLogger(c, false)
|
||||
}
|
||||
|
|
131
cli/internal/config/config.go
Normal file
131
cli/internal/config/config.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/zalando/go-keyring"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ServerURL string `json:"server_url"`
|
||||
Token string `json:"-"`
|
||||
LogLevel string `json:"log_level"`
|
||||
}
|
||||
|
||||
func Load(c *cli.Context) error {
|
||||
// If the command is setup, we don't need to load the config
|
||||
if firstArg := c.Args().First(); firstArg == "setup" {
|
||||
return nil
|
||||
}
|
||||
|
||||
config, err := Get(c, c.String("config"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config == nil && !c.IsSet("server-url") && !c.IsSet("token") {
|
||||
log.Info().Msg("The woodpecker-cli is not yet set up. Please run `woodpecker-cli setup`")
|
||||
return errors.New("woodpecker-cli is not setup")
|
||||
}
|
||||
|
||||
if !c.IsSet("server") {
|
||||
err = c.Set("server", config.ServerURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !c.IsSet("token") {
|
||||
err = c.Set("token", config.Token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !c.IsSet("log-level") {
|
||||
err = c.Set("log-level", config.LogLevel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getConfigPath(configPath string) (string, error) {
|
||||
if configPath != "" {
|
||||
return configPath, nil
|
||||
}
|
||||
|
||||
configPath, err := xdg.ConfigFile("woodpecker/config.json")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return configPath, nil
|
||||
}
|
||||
|
||||
func Get(ctx *cli.Context, _configPath string) (*Config, error) {
|
||||
configPath, err := getConfigPath(_configPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(configPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
log.Debug().Err(err).Msg("Failed to read the config file")
|
||||
return nil, err
|
||||
} else if err != nil && os.IsNotExist(err) {
|
||||
log.Debug().Msg("The config file does not exist")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
c := &Config{}
|
||||
err = json.Unmarshal(content, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// load token from keyring
|
||||
service := ctx.App.Name
|
||||
secret, err := keyring.Get(service, c.ServerURL)
|
||||
if err != nil && !errors.Is(err, keyring.ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil {
|
||||
c.Token = secret
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func Save(ctx *cli.Context, _configPath string, c *Config) error {
|
||||
config, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configPath, err := getConfigPath(_configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save token to keyring
|
||||
service := ctx.App.Name
|
||||
err = keyring.Set(service, c.ServerURL, c.Token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.WriteFile(configPath, config, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
88
cli/setup/setup.go
Normal file
88
cli/setup/setup.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package setup
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal/config"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/setup/ui"
|
||||
)
|
||||
|
||||
// Command exports the setup command.
|
||||
var Command = &cli.Command{
|
||||
Name: "setup",
|
||||
Usage: "setup the woodpecker-cli for the first time",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "server-url",
|
||||
Usage: "The URL of the woodpecker server",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Usage: "The token to authenticate with the woodpecker server",
|
||||
},
|
||||
},
|
||||
Action: setup,
|
||||
}
|
||||
|
||||
func setup(c *cli.Context) error {
|
||||
_config, err := config.Get(c, c.String("config"))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if _config != nil {
|
||||
setupAgain, err := ui.Confirm("The woodpecker-cli was already configured. Do you want to configure it again?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !setupAgain {
|
||||
log.Info().Msg("Configuration skipped")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
serverURL := c.String("server-url")
|
||||
|
||||
if serverURL == "" {
|
||||
serverURL, err = ui.Ask("Enter the URL of the woodpecker server", "https://ci.woodpecker-ci.org", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if serverURL == "" {
|
||||
return errors.New("server URL cannot be empty")
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.Contains(serverURL, "://") {
|
||||
serverURL = "https://" + serverURL
|
||||
}
|
||||
|
||||
token := c.String("token")
|
||||
if token == "" {
|
||||
token, err = receiveTokenFromUI(c.Context, serverURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
return errors.New("no token received from the UI")
|
||||
}
|
||||
}
|
||||
|
||||
err = config.Save(c, c.String("config"), &config.Config{
|
||||
ServerURL: serverURL,
|
||||
Token: token,
|
||||
LogLevel: "info",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Msg("The woodpecker-cli has been successfully setup")
|
||||
|
||||
return nil
|
||||
}
|
117
cli/setup/token_fetcher.go
Normal file
117
cli/setup/token_fetcher.go
Normal file
|
@ -0,0 +1,117 @@
|
|||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func receiveTokenFromUI(c context.Context, serverURL string) (string, error) {
|
||||
port := randomPort()
|
||||
|
||||
tokenReceived := make(chan string)
|
||||
|
||||
srv := &http.Server{Addr: fmt.Sprintf("127.0.0.1:%d", port)}
|
||||
srv.Handler = setupRouter(tokenReceived)
|
||||
|
||||
go func() {
|
||||
log.Debug().Msgf("Listening for token response on :%d", port)
|
||||
_ = srv.ListenAndServe()
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
log.Debug().Msg("Shutting down server")
|
||||
_ = srv.Shutdown(c)
|
||||
}()
|
||||
|
||||
err := openBrowser(fmt.Sprintf("%s/cli/auth?port=%d", serverURL, port))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// wait for token to be received or timeout
|
||||
select {
|
||||
case token := <-tokenReceived:
|
||||
return token, nil
|
||||
case <-c.Done():
|
||||
return "", c.Err()
|
||||
case <-time.After(5 * time.Minute):
|
||||
return "", errors.New("timed out waiting for token")
|
||||
}
|
||||
}
|
||||
|
||||
func setupRouter(tokenReceived chan string) *gin.Engine {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
e := gin.New()
|
||||
e.UseRawPath = true
|
||||
e.Use(gin.Recovery())
|
||||
|
||||
e.Use(func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
})
|
||||
|
||||
e.POST("/token", func(c *gin.Context) {
|
||||
data := struct {
|
||||
Token string `json:"token"`
|
||||
}{}
|
||||
|
||||
err := c.BindJSON(&data)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("Failed to bind JSON")
|
||||
c.JSON(400, gin.H{
|
||||
"error": "invalid request",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
tokenReceived <- data.Token
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"ok": "true",
|
||||
})
|
||||
})
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func openBrowser(url string) error {
|
||||
var err error
|
||||
|
||||
log.Debug().Msgf("Opening browser with URL: %s", url)
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
err = exec.Command("xdg-open", url).Start()
|
||||
case "windows":
|
||||
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
case "darwin":
|
||||
err = exec.Command("open", url).Start()
|
||||
default:
|
||||
err = fmt.Errorf("unsupported platform")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func randomPort() int {
|
||||
s1 := rand.NewSource(time.Now().UnixNano())
|
||||
r1 := rand.New(s1)
|
||||
return r1.Intn(10000) + 20000
|
||||
}
|
79
cli/setup/ui/ask.go
Normal file
79
cli/setup/ui/ask.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
type askModel struct {
|
||||
prompt string
|
||||
required bool
|
||||
textInput textinput.Model
|
||||
err error
|
||||
}
|
||||
|
||||
func (m askModel) Init() tea.Cmd {
|
||||
return textinput.Blink
|
||||
}
|
||||
|
||||
func (m askModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmd tea.Cmd
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.Type {
|
||||
case tea.KeyEnter:
|
||||
if !m.required || (m.required && strings.TrimSpace(m.textInput.Value()) != "") {
|
||||
return m, tea.Quit
|
||||
}
|
||||
case tea.KeyCtrlC, tea.KeyEsc:
|
||||
return m, tea.Quit
|
||||
}
|
||||
default:
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
m.textInput, cmd = m.textInput.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m askModel) View() string {
|
||||
return fmt.Sprintf(
|
||||
"%s\n\n%s\n\n%s",
|
||||
m.prompt,
|
||||
m.textInput.View(),
|
||||
"(esc to quit)",
|
||||
) + "\n"
|
||||
}
|
||||
|
||||
func Ask(prompt, placeholder string, required bool) (string, error) {
|
||||
ti := textinput.New()
|
||||
ti.Placeholder = placeholder
|
||||
ti.Focus()
|
||||
ti.CharLimit = 156
|
||||
ti.Width = 40
|
||||
|
||||
p := tea.NewProgram(askModel{
|
||||
prompt: prompt,
|
||||
textInput: ti,
|
||||
required: required,
|
||||
err: nil,
|
||||
})
|
||||
|
||||
_m, err := p.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
m, ok := _m.(askModel)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unexpected model: %T", _m)
|
||||
}
|
||||
|
||||
text := strings.TrimSpace(m.textInput.Value())
|
||||
|
||||
return text, nil
|
||||
}
|
71
cli/setup/ui/confirm.go
Normal file
71
cli/setup/ui/confirm.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
type confirmModel struct {
|
||||
confirmed bool
|
||||
prompt string
|
||||
err error
|
||||
}
|
||||
|
||||
func (m confirmModel) Init() tea.Cmd {
|
||||
return textinput.Blink
|
||||
}
|
||||
|
||||
func (m confirmModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmd tea.Cmd
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
if msg.Runes != nil {
|
||||
switch msg.Runes[0] {
|
||||
case 'y':
|
||||
m.confirmed = true
|
||||
return m, tea.Quit
|
||||
case 'n':
|
||||
m.confirmed = false
|
||||
return m, tea.Quit
|
||||
}
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
case tea.KeyCtrlC, tea.KeyEsc:
|
||||
return m, tea.Quit
|
||||
}
|
||||
default:
|
||||
return m, nil
|
||||
}
|
||||
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m confirmModel) View() string {
|
||||
return fmt.Sprintf(
|
||||
"%s y / n (esc to quit)",
|
||||
m.prompt,
|
||||
) + "\n"
|
||||
}
|
||||
|
||||
func Confirm(prompt string) (bool, error) {
|
||||
p := tea.NewProgram(confirmModel{
|
||||
prompt: prompt,
|
||||
err: nil,
|
||||
})
|
||||
|
||||
_m, err := p.Run()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
m, ok := _m.(confirmModel)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("unexpected model: %T", _m)
|
||||
}
|
||||
|
||||
return m.confirmed, nil
|
||||
}
|
|
@ -29,6 +29,7 @@ import (
|
|||
"go.woodpecker-ci.org/woodpecker/v2/cli/registry"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/repo"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/secret"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/setup"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/update"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/user"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/version"
|
||||
|
@ -58,6 +59,7 @@ func newApp() *cli.App {
|
|||
lint.Command,
|
||||
loglevel.Command,
|
||||
cron.Command,
|
||||
setup.Command,
|
||||
update.Command,
|
||||
}
|
||||
|
||||
|
|
15
go.mod
15
go.mod
|
@ -7,10 +7,13 @@ require (
|
|||
codeberg.org/6543/go-yaml2json v1.0.0
|
||||
codeberg.org/6543/xyaml v1.1.0
|
||||
github.com/6543/logfile-open v1.2.1
|
||||
github.com/adrg/xdg v0.4.0
|
||||
github.com/alessio/shellescape v1.4.2
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||
github.com/caddyserver/certmagic v0.20.0
|
||||
github.com/cenkalti/backoff/v4 v4.2.1
|
||||
github.com/charmbracelet/bubbles v0.18.0
|
||||
github.com/charmbracelet/bubbletea v0.25.0
|
||||
github.com/distribution/reference v0.5.0
|
||||
github.com/docker/cli v24.0.9+incompatible
|
||||
github.com/docker/docker v24.0.9+incompatible
|
||||
|
@ -50,6 +53,7 @@ require (
|
|||
github.com/urfave/cli/v2 v2.27.1
|
||||
github.com/xanzy/go-gitlab v0.97.0
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/zalando/go-keyring v0.2.3
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/crypto v0.19.0
|
||||
golang.org/x/net v0.21.0
|
||||
|
@ -72,13 +76,17 @@ require (
|
|||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bytedance/sonic v1.10.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/danieljoos/wincred v1.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
|
@ -97,6 +105,7 @@ require (
|
|||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
|
@ -120,6 +129,7 @@ require (
|
|||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||
github.com/mholt/acmez v1.2.0 // indirect
|
||||
|
@ -127,6 +137,9 @@ require (
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
|
@ -135,7 +148,7 @@ require (
|
|||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.45.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/rivo/uniseg v0.4.6 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
|
|
37
go.sum
37
go.sum
|
@ -20,8 +20,12 @@ github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6Xge
|
|||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
|
||||
github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
|
@ -38,6 +42,12 @@ github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqy
|
|||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
||||
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
|
||||
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
||||
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
||||
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
|
||||
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||
|
@ -49,6 +59,8 @@ github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4M
|
|||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
||||
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
|
@ -57,6 +69,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
|
|||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
|
||||
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -81,8 +95,9 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
|||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
|
||||
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/expr-lang/expr v1.16.1 h1:Na8CUcMdyGbnNpShY7kzcHCU7WqxuL+hnxgHZ4vaz/A=
|
||||
|
@ -138,6 +153,8 @@ github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF
|
|||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
|
@ -299,6 +316,9 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
|||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
|
@ -325,6 +345,12 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
|||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
|
@ -364,9 +390,10 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k
|
|||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
|
||||
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
|
||||
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
|
@ -438,6 +465,8 @@ github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsr
|
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms=
|
||||
github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
|
||||
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
|
||||
|
@ -537,11 +566,13 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
1
web/components.d.ts
vendored
1
web/components.d.ts
vendored
|
@ -110,6 +110,7 @@ declare module 'vue' {
|
|||
Tabs: typeof import('./src/components/layout/scaffold/Tabs.vue')['default']
|
||||
TextField: typeof import('./src/components/form/TextField.vue')['default']
|
||||
UserAPITab: typeof import('./src/components/user/UserAPITab.vue')['default']
|
||||
UserCLIAndAPITab: typeof import('./src/components/user/UserCLIAndAPITab.vue')['default']
|
||||
UserGeneralTab: typeof import('./src/components/user/UserGeneralTab.vue')['default']
|
||||
UserSecretsTab: typeof import('./src/components/user/UserSecretsTab.vue')['default']
|
||||
Warning: typeof import('./src/components/atomic/Warning.vue')['default']
|
||||
|
|
|
@ -486,15 +486,13 @@
|
|||
"pr_warning": "Please be careful with this option as a bad actor can submit a malicious pull request that exposes your secrets."
|
||||
}
|
||||
},
|
||||
"api": {
|
||||
"api": "API",
|
||||
"desc": "Personal Access Token and API usage",
|
||||
"cli_and_api": {
|
||||
"cli_and_api": "CLI & API",
|
||||
"desc": "Personal Access Token, CLI and API usage",
|
||||
"token": "Personal Access Token",
|
||||
"shell_setup": "Shell setup",
|
||||
"api_usage": "Example API Usage",
|
||||
"cli_usage": "Example CLI Usage",
|
||||
"dl_cli": "Download CLI",
|
||||
"shell_setup_before": "do shell setup steps before",
|
||||
"download_cli": "Download CLI",
|
||||
"reset_token": "Reset token",
|
||||
"swagger_ui": "Swagger UI"
|
||||
}
|
||||
|
@ -508,5 +506,12 @@
|
|||
"running_version": "You are running Woodpecker {0}",
|
||||
"update_woodpecker": "Please update your Woodpecker instance to {0}",
|
||||
"global_level_secret": "global secret",
|
||||
"org_level_secret": "organization secret"
|
||||
"org_level_secret": "organization secret",
|
||||
"login_to_cli": "Login to CLI",
|
||||
"login_to_cli_description": "By continuing you will be logged in to the CLI.",
|
||||
"abort": "Abort",
|
||||
"cli_login_success": "Login to CLI successful",
|
||||
"cli_login_failed": "Login to CLI failed",
|
||||
"cli_login_denied": "Login to CLI denied",
|
||||
"return_to_cli": "You can now close this tab and return to the CLI."
|
||||
}
|
||||
|
|
|
@ -1,43 +1,38 @@
|
|||
<template>
|
||||
<Settings :title="$t('user.settings.api.api')" :desc="$t('user.settings.api.desc')">
|
||||
<InputField :label="$t('user.settings.api.token')">
|
||||
<Settings :title="$t('user.settings.cli_and_api.cli_and_api')" :desc="$t('user.settings.cli_and_api.desc')">
|
||||
<InputField :label="$t('user.settings.cli_and_api.cli_usage')">
|
||||
<template #titleActions>
|
||||
<Button class="ml-auto" :text="$t('user.settings.api.reset_token')" @click="resetToken" />
|
||||
<a :href="cliDownload" target="_blank" class="ml-4 text-wp-link-100 hover:text-wp-link-200">{{
|
||||
$t('user.settings.cli_and_api.download_cli')
|
||||
}}</a>
|
||||
</template>
|
||||
<pre class="code-box">{{ usageWithCli }}</pre>
|
||||
</InputField>
|
||||
|
||||
<InputField :label="$t('user.settings.cli_and_api.token')">
|
||||
<template #titleActions>
|
||||
<Button class="ml-auto" :text="$t('user.settings.cli_and_api.reset_token')" @click="resetToken" />
|
||||
</template>
|
||||
<pre class="code-box">{{ token }}</pre>
|
||||
</InputField>
|
||||
|
||||
<InputField :label="$t('user.settings.api.shell_setup')">
|
||||
<pre class="code-box">{{ usageWithShell }}</pre>
|
||||
</InputField>
|
||||
|
||||
<InputField :label="$t('user.settings.api.api_usage')">
|
||||
<InputField :label="$t('user.settings.cli_and_api.api_usage')">
|
||||
<template #titleActions>
|
||||
<a
|
||||
v-if="enableSwagger"
|
||||
:href="`${address}/swagger/index.html`"
|
||||
target="_blank"
|
||||
class="ml-4 text-wp-link-100 hover:text-wp-link-200"
|
||||
>{{ $t('user.settings.api.swagger_ui') }}</a
|
||||
>{{ $t('user.settings.cli_and_api.swagger_ui') }}</a
|
||||
>
|
||||
</template>
|
||||
<pre class="code-box">{{ usageWithCurl }}</pre>
|
||||
</InputField>
|
||||
|
||||
<InputField :label="$t('user.settings.api.cli_usage')">
|
||||
<template #titleActions>
|
||||
<a :href="cliDownload" target="_blank" class="ml-4 text-wp-link-100 hover:text-wp-link-200">{{
|
||||
$t('user.settings.api.dl_cli')
|
||||
}}</a>
|
||||
</template>
|
||||
<pre class="code-box">{{ usageWithCli }}</pre>
|
||||
</InputField>
|
||||
</Settings>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
import InputField from '~/components/form/InputField.vue';
|
||||
|
@ -45,7 +40,6 @@ import Settings from '~/components/layout/Settings.vue';
|
|||
import useApiClient from '~/compositions/useApiClient';
|
||||
import useConfig from '~/compositions/useConfig';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { rootPath, enableSwagger } = useConfig();
|
||||
|
||||
const apiClient = useApiClient();
|
||||
|
@ -57,17 +51,15 @@ onMounted(async () => {
|
|||
|
||||
const address = `${window.location.protocol}//${window.location.host}${rootPath}`; // port is included in location.host
|
||||
|
||||
const usageWithShell = computed(() => {
|
||||
const usageWithCurl = computed(() => {
|
||||
let usage = `export WOODPECKER_SERVER="${address}"\n`;
|
||||
usage += `export WOODPECKER_TOKEN="${token.value}"\n`;
|
||||
usage += `\n`;
|
||||
usage += `# curl -i \${WOODPECKER_SERVER}/api/user -H "Authorization: Bearer \${WOODPECKER_TOKEN}"`;
|
||||
return usage;
|
||||
});
|
||||
|
||||
const usageWithCurl = `# ${t(
|
||||
'user.settings.api.shell_setup_before',
|
||||
)}\ncurl -i \${WOODPECKER_SERVER}/api/user -H "Authorization: Bearer \${WOODPECKER_TOKEN}"`;
|
||||
|
||||
const usageWithCli = `# ${t('user.settings.api.shell_setup_before')}\nwoodpecker info`;
|
||||
const usageWithCli = `# woodpecker setup --server-url ${address}`;
|
||||
|
||||
const cliDownload = 'https://github.com/woodpecker-ci/woodpecker/releases';
|
||||
|
|
@ -166,6 +166,11 @@ const routes: RouteRecordRaw[] = [
|
|||
meta: { blank: true },
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: `${rootPath}/cli/auth`,
|
||||
component: () => import('~/views/cli/Auth.vue'),
|
||||
meta: { authentication: 'required' },
|
||||
},
|
||||
|
||||
// TODO: deprecated routes => remove after some time
|
||||
{
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
<Tab id="secrets" :title="$t('user.settings.secrets.secrets')">
|
||||
<UserSecretsTab />
|
||||
</Tab>
|
||||
<Tab id="api" :title="$t('user.settings.api.api')">
|
||||
<UserAPITab />
|
||||
<Tab id="cli-and-api" :title="$t('user.settings.cli_and_api.cli_and_api')">
|
||||
<UserCLIAndAPITab />
|
||||
</Tab>
|
||||
</Scaffold>
|
||||
</template>
|
||||
|
@ -17,7 +17,7 @@
|
|||
<script lang="ts" setup>
|
||||
import Scaffold from '~/components/layout/scaffold/Scaffold.vue';
|
||||
import Tab from '~/components/layout/scaffold/Tab.vue';
|
||||
import UserAPITab from '~/components/user/UserAPITab.vue';
|
||||
import UserCLIAndAPITab from '~/components/user/UserCLIAndAPITab.vue';
|
||||
import UserGeneralTab from '~/components/user/UserGeneralTab.vue';
|
||||
import UserSecretsTab from '~/components/user/UserSecretsTab.vue';
|
||||
import useConfig from '~/compositions/useConfig';
|
||||
|
|
80
web/src/views/cli/Auth.vue
Normal file
80
web/src/views/cli/Auth.vue
Normal file
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<div class="flex flex-col gap-4 m-auto">
|
||||
<div class="text-center text-wp-text-100">
|
||||
<img src="../../assets/logo.svg" alt="CLI" class="w-32 m-auto mb-8" />
|
||||
<template v-if="state === 'confirm'">
|
||||
<h1 class="text-4xl font-bold">{{ $t('login_to_cli') }}</h1>
|
||||
<p class="text-2xl">{{ $t('login_to_cli_description') }}</p>
|
||||
</template>
|
||||
<template v-else-if="state === 'success'">
|
||||
<h1 class="text-4xl font-bold">{{ $t('cli_login_success') }}</h1>
|
||||
<p class="text-2xl">{{ $t('return_to_cli') }}</p>
|
||||
</template>
|
||||
<template v-else-if="state === 'failed'">
|
||||
<h1 class="text-4xl font-bold mt-4">{{ $t('cli_login_failed') }}</h1>
|
||||
<p class="text-2xl">{{ $t('return_to_cli') }}</p>
|
||||
</template>
|
||||
<template v-else-if="state === 'denied'">
|
||||
<h1 class="text-4xl font-bold mt-4">{{ $t('cli_login_denied') }}</h1>
|
||||
<p class="text-2xl">{{ $t('return_to_cli') }}</p>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-if="state === 'confirm'" class="flex gap-4 justify-center">
|
||||
<Button :text="$t('login_to_cli')" color="green" @click="sendToken(false)" />
|
||||
<Button :text="$t('abort')" color="red" @click="abortLogin" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const state = ref<'confirm' | 'success' | 'failed' | 'denied'>('confirm');
|
||||
|
||||
async function sendToken(abort = false) {
|
||||
const port = route.query.port as string;
|
||||
if (!port) {
|
||||
throw new Error('Unexpected: port not found');
|
||||
}
|
||||
|
||||
const address = `http://localhost:${port}`;
|
||||
|
||||
const token = abort ? '' : await apiClient.getToken();
|
||||
|
||||
const resp = await fetch(`${address}/token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ token }),
|
||||
});
|
||||
|
||||
if (abort) {
|
||||
state.value = 'denied';
|
||||
window.close();
|
||||
return;
|
||||
}
|
||||
|
||||
const data = (await resp.json()) as { ok: string };
|
||||
if (data.ok === 'true') {
|
||||
state.value = 'success';
|
||||
} else {
|
||||
state.value = 'failed';
|
||||
// eslint-disable-next-line no-alert
|
||||
alert(t('cli_login_failed'));
|
||||
}
|
||||
}
|
||||
|
||||
async function abortLogin() {
|
||||
await sendToken(true);
|
||||
}
|
||||
</script>
|
Loading…
Reference in a new issue