mirror of
https://github.com/openziti/zrok.git
synced 2024-11-07 08:44:14 +01:00
zrok invite tui (#153)
This commit is contained in:
parent
3db9f992e4
commit
245e2a7f21
@ -2,13 +2,17 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/account"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/util"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/openziti/foundation/v2/term"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -17,6 +21,7 @@ func init() {
|
||||
|
||||
type inviteCommand struct {
|
||||
cmd *cobra.Command
|
||||
tui inviteTui
|
||||
}
|
||||
|
||||
func newInviteCommand() *inviteCommand {
|
||||
@ -25,50 +30,188 @@ func newInviteCommand() *inviteCommand {
|
||||
Short: "Invite a new user to zrok",
|
||||
Args: cobra.ExactArgs(0),
|
||||
}
|
||||
command := &inviteCommand{cmd: cmd}
|
||||
command := &inviteCommand{
|
||||
cmd: cmd,
|
||||
tui: newInviteTui(),
|
||||
}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *inviteCommand) run(_ *cobra.Command, _ []string) {
|
||||
email, err := term.Prompt("New Email: ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !util.IsValidEmail(email) {
|
||||
tui.Error(fmt.Sprintf("'%v' is not a valid email address", email), nil)
|
||||
}
|
||||
confirm, err := term.Prompt("Confirm Email: ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if confirm != email {
|
||||
tui.Error("entered emails do not match... aborting!", nil)
|
||||
if _, err := tea.NewProgram(&cmd.tui).Run(); err != nil {
|
||||
tui.Error("unable to run interface", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if cmd.tui.done {
|
||||
email := cmd.tui.inputs[0].Value()
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
tui.Error("error loading zrokdir", err)
|
||||
}
|
||||
|
||||
zrok, err := zrd.Client()
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("error creating zrok api client", err)
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
tui.Error("error loading zrokdir", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
req := account.NewInviteParams()
|
||||
req.Body = &rest_model_zrok.InviteRequest{
|
||||
Email: email,
|
||||
}
|
||||
_, err = zrok.Account.Invite(req)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("error creating invitation", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("invitation sent to '%v'!\n", email)
|
||||
zrok, err := zrd.Client()
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("error creating zrok api client", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
req := account.NewInviteParams()
|
||||
req.Body = &rest_model_zrok.InviteRequest{
|
||||
Email: email,
|
||||
}
|
||||
_, err = zrok.Account.Invite(req)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("error creating invitation", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("invitation sent to '%v'!\n", email)
|
||||
}
|
||||
}
|
||||
|
||||
type inviteTui struct {
|
||||
focusIndex int
|
||||
msg string
|
||||
inputs []textinput.Model
|
||||
cursorMode textinput.CursorMode
|
||||
done bool
|
||||
|
||||
msgOk string
|
||||
msgMismatch string
|
||||
focusedStyle lipgloss.Style
|
||||
blurredStyle lipgloss.Style
|
||||
errorStyle lipgloss.Style
|
||||
cursorStyle lipgloss.Style
|
||||
noStyle lipgloss.Style
|
||||
helpStyle lipgloss.Style
|
||||
focusedButton string
|
||||
blurredButton string
|
||||
}
|
||||
|
||||
func newInviteTui() inviteTui {
|
||||
m := inviteTui{
|
||||
inputs: make([]textinput.Model, 2),
|
||||
}
|
||||
m.focusedStyle = tui.WarningStyle
|
||||
m.blurredStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#555"))
|
||||
m.errorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#F00"))
|
||||
m.cursorStyle = m.focusedStyle.Copy()
|
||||
m.noStyle = lipgloss.NewStyle()
|
||||
m.helpStyle = m.blurredStyle.Copy()
|
||||
m.focusedButton = m.focusedStyle.Copy().Render("[ Submit ]")
|
||||
m.blurredButton = fmt.Sprintf("[ %v ]", m.blurredStyle.Render("Submit"))
|
||||
m.msgOk = m.noStyle.Render("Enter and confirm your email address...")
|
||||
m.msg = m.msgOk
|
||||
m.msgMismatch = m.errorStyle.Render("Email is invalid or does not match confirmation...")
|
||||
|
||||
var t textinput.Model
|
||||
for i := range m.inputs {
|
||||
t = textinput.New()
|
||||
t.CursorStyle = m.cursorStyle
|
||||
t.CharLimit = 96
|
||||
|
||||
switch i {
|
||||
case 0:
|
||||
t.Placeholder = "Email Address"
|
||||
t.Focus()
|
||||
t.PromptStyle = m.focusedStyle
|
||||
t.TextStyle = m.focusedStyle
|
||||
case 1:
|
||||
t.Placeholder = "Confirm Email"
|
||||
}
|
||||
|
||||
m.inputs[i] = t
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m inviteTui) Init() tea.Cmd { return textinput.Blink }
|
||||
|
||||
func (m *inviteTui) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "esc":
|
||||
return m, tea.Quit
|
||||
|
||||
case "tab", "shift+tab", "enter", "up", "down":
|
||||
s := msg.String()
|
||||
|
||||
if s == "enter" && m.focusIndex == len(m.inputs) {
|
||||
if util.IsValidEmail(m.inputs[0].Value()) && m.inputs[0].Value() == m.inputs[1].Value() {
|
||||
m.done = true
|
||||
return m, tea.Quit
|
||||
}
|
||||
m.msg = m.msgMismatch
|
||||
return m, nil
|
||||
}
|
||||
|
||||
if s == "up" || s == "shift+tab" {
|
||||
m.msg = m.msgOk
|
||||
m.focusIndex--
|
||||
} else {
|
||||
m.msg = m.msgOk
|
||||
m.focusIndex++
|
||||
}
|
||||
|
||||
if m.focusIndex > len(m.inputs) {
|
||||
m.focusIndex = 0
|
||||
} else if m.focusIndex < 0 {
|
||||
m.focusIndex = len(m.inputs)
|
||||
}
|
||||
|
||||
cmds := make([]tea.Cmd, len(m.inputs))
|
||||
for i := 0; i <= len(m.inputs)-1; i++ {
|
||||
if i == m.focusIndex {
|
||||
cmds[i] = m.inputs[i].Focus()
|
||||
m.inputs[i].PromptStyle = m.focusedStyle
|
||||
m.inputs[i].TextStyle = m.focusedStyle
|
||||
continue
|
||||
}
|
||||
m.inputs[i].Blur()
|
||||
m.inputs[i].PromptStyle = m.noStyle
|
||||
m.inputs[i].TextStyle = m.noStyle
|
||||
}
|
||||
|
||||
return m, tea.Batch(cmds...)
|
||||
}
|
||||
}
|
||||
|
||||
cmd := m.updateInputs(msg)
|
||||
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m *inviteTui) updateInputs(msg tea.Msg) tea.Cmd {
|
||||
cmds := make([]tea.Cmd, len(m.inputs))
|
||||
for i := range m.inputs {
|
||||
m.inputs[i], cmds[i] = m.inputs[i].Update(msg)
|
||||
}
|
||||
return tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
func (m inviteTui) View() string {
|
||||
var b strings.Builder
|
||||
b.WriteString(fmt.Sprintf("\n%v\n\n", m.msg))
|
||||
|
||||
for i := range m.inputs {
|
||||
b.WriteString(m.inputs[i].View())
|
||||
if i < len(m.inputs)-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
button := &m.blurredButton
|
||||
if m.focusIndex == len(m.inputs) {
|
||||
button = &m.focusedButton
|
||||
}
|
||||
_, _ = fmt.Fprintf(&b, "\n\n%s\n\n", *button)
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
1
cmd/zrok/inviteTui.go
Normal file
1
cmd/zrok/inviteTui.go
Normal file
@ -0,0 +1 @@
|
||||
package main
|
2
go.mod
2
go.mod
@ -40,8 +40,10 @@ require (
|
||||
require (
|
||||
github.com/Jeffail/gabs v1.4.0 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/charmbracelet/bubbles v0.14.0 // indirect
|
||||
github.com/containerd/console v1.0.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.8.2 // indirect
|
||||
|
12
go.sum
12
go.sum
@ -53,6 +53,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
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 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg=
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
@ -60,8 +62,13 @@ github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqO
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
||||
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og=
|
||||
github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc=
|
||||
github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4=
|
||||
github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUIte8WPvhck=
|
||||
github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU=
|
||||
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||
github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
|
||||
github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
|
||||
github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
@ -326,6 +333,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
@ -411,12 +419,14 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
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.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
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.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
|
||||
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.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
|
||||
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
|
||||
github.com/netfoundry/secretstream v0.1.2 h1:NgqrYytDnjKbOfWI29TT0SJM+RwB3yf9MIkJVJaU+J0=
|
||||
@ -488,6 +498,7 @@ github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMH
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shirou/gopsutil/v3 v3.22.8 h1:a4s3hXogo5mE2PfdfJIonDbstO/P+9JszdfhAHSzD9Y=
|
||||
github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI=
|
||||
@ -765,6 +776,7 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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=
|
||||
|
Loading…
Reference in New Issue
Block a user