mirror of
https://github.com/openziti/zrok.git
synced 2024-11-22 16:13:47 +01:00
access tui (#56)
This commit is contained in:
parent
a63b66ad97
commit
98b8c8c8a1
111
cmd/zrok/accessModel.go
Normal file
111
cmd/zrok/accessModel.go
Normal 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
|
||||||
|
}
|
@ -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) {
|
||||||
|
@ -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()).
|
||||||
|
@ -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() {
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user