zrok invite tui (#153)

This commit is contained in:
Michael Quigley 2023-01-11 14:26:56 -05:00
parent 3db9f992e4
commit 245e2a7f21
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62
4 changed files with 196 additions and 38 deletions

View File

@ -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
View File

@ -0,0 +1 @@
package main

2
go.mod
View File

@ -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
View File

@ -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=