access tui (#56)

This commit is contained in:
Michael Quigley 2023-01-10 16:38:53 -05:00
parent a63b66ad97
commit 98b8c8c8a1
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62
6 changed files with 168 additions and 18 deletions

111
cmd/zrok/accessModel.go Normal file
View File

@ -0,0 +1,111 @@
package main
import (
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/openziti-test-kitchen/zrok/endpoints"
"strings"
"time"
)
type accessModel struct {
shrToken string
localEndpoint string
requests []*endpoints.BackendRequest
width int
height int
}
func newAccessModel(shrToken, localEndpoint string) *accessModel {
return &accessModel{
shrToken: shrToken,
localEndpoint: localEndpoint,
}
}
func (m *accessModel) Init() tea.Cmd { return nil }
func (m *accessModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case *endpoints.BackendRequest:
m.requests = append([]*endpoints.BackendRequest{msg}, m.requests...)
if len(m.requests) > 2048 {
m.requests = m.requests[:2048]
}
case tea.WindowSizeMsg:
m.width = msg.Width
accessHeaderStyle.Width(m.width - 2)
accessRequestsStyle.Width(m.width - 2)
m.height = msg.Height
accessRequestsStyle.Height(m.height - 7)
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
case "ctrl+l":
return m, tea.ClearScreen
}
}
return m, nil
}
func (m *accessModel) View() string {
return lipgloss.JoinVertical(
lipgloss.Left,
accessHeaderStyle.Render(fmt.Sprintf("%v -> %v", m.localEndpoint, m.shrToken)),
accessRequestsStyle.Render(m.renderRequests()),
)
}
func (m *accessModel) renderRequests() string {
out := ""
maxRows := accessRequestsStyle.GetHeight()
for i := 0; i < maxRows && i < len(m.requests); i++ {
req := m.requests[i]
out += fmt.Sprintf("%v %v -> %v %v",
timeStyle.Render(req.Stamp.Format(time.RFC850)),
addressStyle.Render(req.RemoteAddr),
m.renderMethod(req.Method),
req.Path,
)
if i != maxRows-1 {
out += "\n"
}
}
return out
}
func (m *accessModel) renderMethod(method string) string {
switch strings.ToLower(method) {
case "get":
return getStyle.Render(method)
case "post":
return postStyle.Render(method)
default:
return otherMethodStyle.Render(method)
}
}
var accessHeaderStyle = lipgloss.NewStyle().
Height(3).
PaddingTop(1).PaddingLeft(2).PaddingBottom(1).PaddingRight(2).
BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("63")).
Align(lipgloss.Center)
var accessRequestsStyle = lipgloss.NewStyle().
Height(3).
PaddingLeft(2).PaddingRight(2).
BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("63"))
type accessLogWriter struct{}
func (w accessLogWriter) Write(p []byte) (n int, err error) {
return len(p), nil
}

View File

@ -1,8 +1,10 @@
package main package main
import ( import (
tea "github.com/charmbracelet/bubbletea"
"github.com/go-openapi/runtime" "github.com/go-openapi/runtime"
httptransport "github.com/go-openapi/runtime/client" httptransport "github.com/go-openapi/runtime/client"
"github.com/openziti-test-kitchen/zrok/endpoints"
"github.com/openziti-test-kitchen/zrok/endpoints/privateFrontend" "github.com/openziti-test-kitchen/zrok/endpoints/privateFrontend"
"github.com/openziti-test-kitchen/zrok/rest_client_zrok" "github.com/openziti-test-kitchen/zrok/rest_client_zrok"
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/share" "github.com/openziti-test-kitchen/zrok/rest_client_zrok/share"
@ -84,6 +86,7 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) {
cfg := privateFrontend.DefaultConfig("backend") cfg := privateFrontend.DefaultConfig("backend")
cfg.ShrToken = shrToken cfg.ShrToken = shrToken
cfg.Address = cmd.bindAddress cfg.Address = cmd.bindAddress
cfg.RequestsChan = make(chan *endpoints.BackendRequest, 1024)
c := make(chan os.Signal) c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM) signal.Notify(c, os.Interrupt, syscall.SIGTERM)
@ -101,13 +104,36 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) {
panic(err) panic(err)
} }
logrus.Infof("access your share at: %v", endpointUrl.String()) go func() {
if err := frontend.Run(); err != nil {
if err := frontend.Run(); err != nil { if !panicInstead {
if !panicInstead { tui.Error("unable to run frontend", err)
tui.Error("unable to run frontend", err) }
} }
}()
mdl := newAccessModel(shrToken, endpointUrl.String())
prg := tea.NewProgram(mdl, tea.WithAltScreen())
go func() {
for {
select {
case req := <-cfg.RequestsChan:
if req != nil {
prg.Send(req)
}
}
}
}()
logrus.SetOutput(&accessLogWriter{})
if _, err := prg.Run(); err != nil {
tui.Error("An error occurred", err)
} }
close(cfg.RequestsChan)
cmd.destroy(accessResp.Payload.FrontendToken, zrd.Env.ZId, shrToken, zrok, auth)
} }
func (cmd *accessPrivateCommand) destroy(frotendName, envZId, shrToken string, zrok *rest_client_zrok.Zrok, auth runtime.ClientAuthInfoWriter) { func (cmd *accessPrivateCommand) destroy(frotendName, envZId, shrToken string, zrok *rest_client_zrok.Zrok, auth runtime.ClientAuthInfoWriter) {

View File

@ -10,7 +10,7 @@ import (
) )
type shareModel struct { type shareModel struct {
shareToken string shrToken string
frontendDescriptions []string frontendDescriptions []string
shareMode string shareMode string
backendMode string backendMode string
@ -20,9 +20,9 @@ type shareModel struct {
height int height int
} }
func newShareModel(shareToken string, frontendEndpoints []string, shareMode, backendMode string) *shareModel { func newShareModel(shrToken string, frontendEndpoints []string, shareMode, backendMode string) *shareModel {
return &shareModel{ return &shareModel{
shareToken: shareToken, shrToken: shrToken,
frontendDescriptions: frontendEndpoints, frontendDescriptions: frontendEndpoints,
shareMode: shareMode, shareMode: shareMode,
backendMode: backendMode, backendMode: backendMode,
@ -42,10 +42,11 @@ func (m *shareModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
m.width = msg.Width m.width = msg.Width
shareHeaderStyle.Width(m.width - 30) shareHeaderStyle.Width(m.width - 30)
configHeaderStyle.Width(26) shareConfigStyle.Width(26)
shareRequestsStyle.Width(m.width - 2)
m.height = msg.Height m.height = msg.Height
requestsStyle.Width(m.width - 2) shareRequestsStyle.Height(m.height - (len(m.frontendDescriptions) + 6))
requestsStyle.Height(m.height - (len(m.frontendDescriptions) + 6))
case tea.KeyMsg: case tea.KeyMsg:
switch msg.String() { switch msg.String() {
@ -62,9 +63,9 @@ func (m *shareModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m *shareModel) View() string { func (m *shareModel) View() string {
topRow := lipgloss.JoinHorizontal(lipgloss.Top, topRow := lipgloss.JoinHorizontal(lipgloss.Top,
shareHeaderStyle.Render(strings.Join(m.frontendDescriptions, "\n")), shareHeaderStyle.Render(strings.Join(m.frontendDescriptions, "\n")),
configHeaderStyle.Render(m.renderConfig()), shareConfigStyle.Render(m.renderConfig()),
) )
requests := requestsStyle.Render(m.renderBackendRequests()) requests := shareRequestsStyle.Render(m.renderBackendRequests())
all := lipgloss.JoinVertical(lipgloss.Left, topRow, requests) all := lipgloss.JoinVertical(lipgloss.Left, topRow, requests)
return all return all
} }
@ -88,7 +89,7 @@ func (m *shareModel) renderConfig() string {
func (m *shareModel) renderBackendRequests() string { func (m *shareModel) renderBackendRequests() string {
out := "" out := ""
maxRows := requestsStyle.GetHeight() maxRows := shareRequestsStyle.GetHeight()
for i := 0; i < maxRows && i < len(m.requests); i++ { for i := 0; i < maxRows && i < len(m.requests); i++ {
req := m.requests[i] req := m.requests[i]
out += fmt.Sprintf("%v %v -> %v %v", out += fmt.Sprintf("%v %v -> %v %v",
@ -122,14 +123,14 @@ var shareHeaderStyle = lipgloss.NewStyle().
BorderForeground(lipgloss.Color("63")). BorderForeground(lipgloss.Color("63")).
Align(lipgloss.Center) Align(lipgloss.Center)
var configHeaderStyle = lipgloss.NewStyle(). var shareConfigStyle = lipgloss.NewStyle().
Height(3). Height(3).
PaddingTop(1).PaddingLeft(2).PaddingBottom(1).PaddingRight(2). PaddingTop(1).PaddingLeft(2).PaddingBottom(1).PaddingRight(2).
BorderStyle(lipgloss.RoundedBorder()). BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("63")). BorderForeground(lipgloss.Color("63")).
Align(lipgloss.Center) Align(lipgloss.Center)
var requestsStyle = lipgloss.NewStyle(). var shareRequestsStyle = lipgloss.NewStyle().
Height(3). Height(3).
PaddingLeft(2).PaddingRight(2). PaddingLeft(2).PaddingRight(2).
BorderStyle(lipgloss.RoundedBorder()). BorderStyle(lipgloss.RoundedBorder()).

View File

@ -185,7 +185,7 @@ func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) {
} else { } else {
shareDescription := fmt.Sprintf("access your share with: %v", tui.CodeStyle.Render(fmt.Sprintf("zrok access private %v", resp.Payload.ShrToken))) shareDescription := fmt.Sprintf("access your share with: %v", tui.CodeStyle.Render(fmt.Sprintf("zrok access private %v", resp.Payload.ShrToken)))
mdl := newShareModel(resp.Payload.ShrToken, []string{shareDescription}, "public", cmd.backendMode) mdl := newShareModel(resp.Payload.ShrToken, []string{shareDescription}, "private", cmd.backendMode)
prg := tea.NewProgram(mdl, tea.WithAltScreen()) prg := tea.NewProgram(mdl, tea.WithAltScreen())
go func() { go func() {

View File

@ -1,9 +1,12 @@
package privateFrontend package privateFrontend
import "github.com/openziti-test-kitchen/zrok/endpoints"
type Config struct { type Config struct {
IdentityName string IdentityName string
ShrToken string ShrToken string
Address string Address string
RequestsChan chan *endpoints.BackendRequest
} }
func DefaultConfig(identityName string) *Config { func DefaultConfig(identityName string) *Config {

View File

@ -16,6 +16,7 @@ import (
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"time"
) )
type httpFrontend struct { type httpFrontend struct {
@ -75,6 +76,14 @@ func newServiceProxy(cfg *Config, ctx ziti.Context) (*httputil.ReverseProxy, err
proxy := serviceTargetProxy(cfg, ctx) proxy := serviceTargetProxy(cfg, ctx)
director := proxy.Director director := proxy.Director
proxy.Director = func(req *http.Request) { proxy.Director = func(req *http.Request) {
if cfg.RequestsChan != nil {
cfg.RequestsChan <- &endpoints.BackendRequest{
Stamp: time.Now(),
RemoteAddr: fmt.Sprintf("%v", req.Header["X-Real-Ip"]),
Method: req.Method,
Path: req.URL.String(),
}
}
director(req) director(req)
req.Header.Set("X-Proxy", "zrok") req.Header.Set("X-Proxy", "zrok")
} }
@ -98,7 +107,7 @@ func serviceTargetProxy(cfg *Config, ctx ziti.Context) *httputil.ReverseProxy {
logrus.Warn("no config!") logrus.Warn("no config!")
} }
if target, err := url.Parse(fmt.Sprintf("http://%v", targetShrToken)); err == nil { if target, err := url.Parse(fmt.Sprintf("http://%v", targetShrToken)); err == nil {
logrus.Infof("[%v] -> %v", targetShrToken, req.URL) logrus.Debugf("[%v] -> %v", targetShrToken, req.URL)
targetQuery := target.RawQuery targetQuery := target.RawQuery
req.URL.Scheme = target.Scheme req.URL.Scheme = target.Scheme