rough bubbletea tui (#56)

This commit is contained in:
Michael Quigley 2023-01-10 14:52:28 -05:00
parent d984cbf832
commit 0119e54d43
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62
7 changed files with 113 additions and 31 deletions

View File

@ -1,8 +1,10 @@
package main
import (
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/openziti-test-kitchen/zrok/endpoints"
"strings"
"time"
)
@ -12,19 +14,12 @@ type shareModel struct {
frontendEndpoints []string
shareMode string
backendMode string
requests []*shareRequestModel
requests []*endpoints.BackendRequest
logMessages []string
width int
height int
}
type shareRequestModel struct {
stamp time.Time
remoteAddr string
verb string
path string
}
func newShareModel(shareToken string, frontendEndpoints []string, shareMode, backendMode string) *shareModel {
return &shareModel{
shareToken: shareToken,
@ -38,6 +33,12 @@ func (m *shareModel) Init() tea.Cmd { return nil }
func (m *shareModel) 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
shareHeaderStyle.Width(m.width - 30)
@ -63,7 +64,7 @@ func (m *shareModel) View() string {
shareHeaderStyle.Render(strings.Join(m.frontendEndpoints, "\n")),
configHeaderStyle.Render(m.renderConfig()),
)
requests := requestsStyle.Render("hello")
requests := requestsStyle.Render(m.renderBackendRequests())
all := lipgloss.JoinVertical(lipgloss.Left, topRow, requests)
return all
}
@ -85,6 +86,35 @@ func (m *shareModel) renderConfig() string {
return out
}
func (m *shareModel) renderBackendRequests() string {
out := ""
maxRows := requestsStyle.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 *shareModel) 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 shareHeaderStyle = lipgloss.NewStyle().
Height(3).
PaddingTop(1).PaddingLeft(2).PaddingBottom(1).PaddingRight(2).
@ -101,7 +131,7 @@ var configHeaderStyle = lipgloss.NewStyle().
var requestsStyle = lipgloss.NewStyle().
Height(3).
PaddingTop(1).PaddingLeft(2).PaddingBottom(1).PaddingRight(2).
PaddingLeft(2).PaddingRight(2).
BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("63"))
@ -109,3 +139,8 @@ var shareModePublicStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#0F0")
var shareModePrivateStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#F00"))
var backendModeProxyStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500"))
var backendModeWebStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#0CC"))
var timeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#444"))
var addressStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500"))
var getStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("98"))
var postStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("101"))
var otherMethodStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("166"))

View File

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/go-openapi/runtime"
httptransport "github.com/go-openapi/runtime/client"
"github.com/openziti-test-kitchen/zrok/endpoints"
"github.com/openziti-test-kitchen/zrok/endpoints/proxyBackend"
"github.com/openziti-test-kitchen/zrok/endpoints/webBackend"
"github.com/openziti-test-kitchen/zrok/model"
@ -175,7 +176,7 @@ func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) {
}
}
func (cmd *sharePrivateCommand) proxyBackendMode(cfg *proxyBackend.Config) (backendHandler, error) {
func (cmd *sharePrivateCommand) proxyBackendMode(cfg *proxyBackend.Config) (endpoints.BackendHandler, error) {
be, err := proxyBackend.NewBackend(cfg)
if err != nil {
return nil, errors.Wrap(err, "error creating http proxy backend")
@ -190,7 +191,7 @@ func (cmd *sharePrivateCommand) proxyBackendMode(cfg *proxyBackend.Config) (back
return be, nil
}
func (cmd *sharePrivateCommand) webBackendMode(cfg *webBackend.Config) (backendHandler, error) {
func (cmd *sharePrivateCommand) webBackendMode(cfg *webBackend.Config) (endpoints.BackendHandler, error) {
be, err := webBackend.NewBackend(cfg)
if err != nil {
return nil, errors.Wrap(err, "error creating http web backend")

View File

@ -5,6 +5,7 @@ import (
tea "github.com/charmbracelet/bubbletea"
"github.com/go-openapi/runtime"
httptransport "github.com/go-openapi/runtime/client"
"github.com/openziti-test-kitchen/zrok/endpoints"
"github.com/openziti-test-kitchen/zrok/endpoints/proxyBackend"
"github.com/openziti-test-kitchen/zrok/endpoints/webBackend"
"github.com/openziti-test-kitchen/zrok/model"
@ -137,13 +138,15 @@ func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) {
os.Exit(0)
}()
var bh backendHandler
var bh endpoints.BackendHandler
requestsChan := make(chan *endpoints.BackendRequest, 1024)
switch cmd.backendMode {
case "proxy":
cfg := &proxyBackend.Config{
IdentityPath: zif,
EndpointAddress: target,
ShrToken: resp.Payload.ShrToken,
RequestsChan: requestsChan,
}
bh, err = cmd.proxyBackendMode(cfg)
if err != nil {
@ -158,6 +161,7 @@ func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) {
IdentityPath: zif,
WebRoot: target,
ShrToken: resp.Payload.ShrToken,
RequestsChan: requestsChan,
}
bh, err = cmd.webBackendMode(cfg)
if err != nil {
@ -171,25 +175,35 @@ func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) {
tui.Error("invalid backend mode", nil)
}
_ = bh.Requests()()
//logrus.Infof("access your zrok share: %v", resp.Payload.FrontendProxyEndpoints[0])
//for {
// time.Sleep(5 * time.Second)
// logrus.Infof("requests: %d", bh.Requests()())
//}
mdl := newShareModel(resp.Payload.ShrToken, resp.Payload.FrontendProxyEndpoints, "public", cmd.backendMode)
prg := tea.NewProgram(mdl, tea.WithAltScreen())
go func() {
bh.Requests()()
for {
select {
case req := <-requestsChan:
prg.Send(req)
}
}
}()
prg := tea.NewProgram(newShareModel(resp.Payload.ShrToken, resp.Payload.FrontendProxyEndpoints, "public", cmd.backendMode), tea.WithAltScreen())
if _, err := prg.Run(); err != nil {
tui.Error("An error occurred", err)
}
close(requestsChan)
cmd.destroy(zrd.Env.ZId, resp.Payload.ShrToken, zrok, auth)
}
func (cmd *sharePublicCommand) proxyBackendMode(cfg *proxyBackend.Config) (backendHandler, error) {
func (cmd *sharePublicCommand) proxyBackendMode(cfg *proxyBackend.Config) (endpoints.BackendHandler, error) {
be, err := proxyBackend.NewBackend(cfg)
if err != nil {
return nil, errors.Wrap(err, "error creating http proxy backend")
@ -204,7 +218,7 @@ func (cmd *sharePublicCommand) proxyBackendMode(cfg *proxyBackend.Config) (backe
return be, nil
}
func (cmd *sharePublicCommand) webBackendMode(cfg *webBackend.Config) (backendHandler, error) {
func (cmd *sharePublicCommand) webBackendMode(cfg *webBackend.Config) (endpoints.BackendHandler, error) {
be, err := webBackend.NewBackend(cfg)
if err != nil {
return nil, errors.Wrap(err, "error creating http web backend")

View File

@ -6,10 +6,6 @@ import (
"os"
)
type backendHandler interface {
Requests() func() int32
}
func mustGetAdminAuth() runtime.ClientAuthInfoWriter {
adminToken := os.Getenv("ZROK_ADMIN_TOKEN")
if adminToken == "" {

View File

@ -3,6 +3,7 @@ package proxyBackend
import (
"context"
"fmt"
"github.com/openziti-test-kitchen/zrok/endpoints"
"github.com/openziti-test-kitchen/zrok/util"
"github.com/openziti/sdk-golang/ziti"
"github.com/openziti/sdk-golang/ziti/config"
@ -20,6 +21,7 @@ type Config struct {
IdentityPath string
EndpointAddress string
ShrToken string
RequestsChan chan *endpoints.BackendRequest
}
type backend struct {
@ -43,7 +45,7 @@ func NewBackend(cfg *Config) (*backend, error) {
return nil, errors.Wrap(err, "error listening")
}
proxy, err := newReverseProxy(cfg.EndpointAddress)
proxy, err := newReverseProxy(cfg.EndpointAddress, cfg.RequestsChan)
if err != nil {
return nil, err
}
@ -68,7 +70,7 @@ func (self *backend) Requests() func() int32 {
return self.requests
}
func newReverseProxy(target string) (*httputil.ReverseProxy, error) {
func newReverseProxy(target string, requests chan *endpoints.BackendRequest) (*httputil.ReverseProxy, error) {
targetURL, err := url.Parse(target)
if err != nil {
return nil, err
@ -81,9 +83,16 @@ func newReverseProxy(target string) (*httputil.ReverseProxy, error) {
proxy.Transport = tpt
director := proxy.Director
proxy.Director = func(req *http.Request) {
if requests != nil {
requests <- &endpoints.BackendRequest{
Stamp: time.Now(),
RemoteAddr: fmt.Sprintf("%v", req.Header["X-Real-Ip"]),
Method: req.Method,
Path: req.URL.String(),
}
}
fmt.Printf("proxy <= %v %v <= %v\n", req.Method, req.URL.String(), req.Header["X-Real-Ip"])
director(req)
logrus.Debugf("-> %v", req.URL.String())
req.Header.Set("X-Proxy", "zrok")
}
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {

View File

@ -6,8 +6,20 @@ import (
"github.com/sirupsen/logrus"
"net/url"
"strings"
"time"
)
type BackendHandler interface {
Requests() func() int32
}
type BackendRequest struct {
Stamp time.Time
RemoteAddr string
Method string
Path string
}
func GetRefreshedService(name string, ctx ziti.Context) (*edge.Service, bool) {
svc, found := ctx.GetService(name)
if !found {

View File

@ -2,6 +2,7 @@ package webBackend
import (
"fmt"
"github.com/openziti-test-kitchen/zrok/endpoints"
"github.com/openziti/sdk-golang/ziti"
"github.com/openziti/sdk-golang/ziti/config"
"github.com/openziti/sdk-golang/ziti/edge"
@ -14,6 +15,7 @@ type Config struct {
IdentityPath string
WebRoot string
ShrToken string
RequestsChan chan *endpoints.BackendRequest
}
type backend struct {
@ -36,11 +38,16 @@ func NewBackend(cfg *Config) (*backend, error) {
return nil, errors.Wrap(err, "error listening")
}
return &backend{
be := &backend{
cfg: cfg,
listener: listener,
handler: &requestLogger{handler: http.FileServer(http.Dir(cfg.WebRoot))},
}, nil
}
if cfg.RequestsChan != nil {
be.handler = &requestGrabber{requests: cfg.RequestsChan, handler: http.FileServer(http.Dir(cfg.WebRoot))}
} else {
be.handler = http.FileServer(http.Dir(cfg.WebRoot))
}
return be, nil
}
func (self *backend) Run() error {
@ -54,11 +61,19 @@ func (self *backend) Requests() func() int32 {
return func() int32 { return 0 }
}
type requestLogger struct {
handler http.Handler
type requestGrabber struct {
requests chan *endpoints.BackendRequest
handler http.Handler
}
func (rl *requestLogger) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
fmt.Printf("web <= %v %v <= %v\n", req.Method, req.URL.String(), req.Header["X-Real-Ip"])
func (rl *requestGrabber) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
if rl.requests != nil {
rl.requests <- &endpoints.BackendRequest{
Stamp: time.Now(),
RemoteAddr: fmt.Sprintf("%v", req.Header["X-Real-Ip"]),
Method: req.Method,
Path: req.URL.String(),
}
}
rl.handler.ServeHTTP(resp, req)
}