From e2af29aa5962a6d4151c15532a288668740d5b26 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sun, 21 Jan 2024 13:38:05 +0100 Subject: [PATCH] make request list dynamic and connect to SSE server --- internal/tui/model.go | 85 ++++++++++++++++++++++++++-------------- internal/tui/types.go | 21 ++++++++-- internal/tui/url.go | 4 -- server/httpd/response.go | 3 ++ server/httpd/url.go | 7 ++++ 5 files changed, 82 insertions(+), 38 deletions(-) diff --git a/internal/tui/model.go b/internal/tui/model.go index 23928f9..72575cb 100644 --- a/internal/tui/model.go +++ b/internal/tui/model.go @@ -11,12 +11,14 @@ import ( "strings" "time" + "github.com/adelowo/sdump" "github.com/adelowo/sdump/config" "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/spinner" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/r3labs/sse/v2" "golang.org/x/term" ) @@ -24,13 +26,16 @@ type model struct { title string spinner spinner.Model - cfg *config.Config - dumpURL *url.URL - err error + cfg *config.Config + dumpURL *url.URL + pubChannel string + err error requestList list.Model httpClient *http.Client + sseClient *sse.Client + receiveChan chan item detailedRequestView viewport.Model } @@ -45,38 +50,15 @@ func initialModel(cfg *config.Config) model { httpClient: &http.Client{ Timeout: time.Minute, }, - requestList: list.New([]list.Item{ - item{ - title: "oops", - desc: "oops oops oops oops", - ip: "0.0.0.0", - }, - item{ - title: "omo", - desc: "omo oops oops oops", - ip: "0.0.0.0", - }, - }, list.NewDefaultDelegate(), 0, 0), + requestList: list.New([]list.Item{}, list.NewDefaultDelegate(), 0, 0), detailedRequestView: viewport.New(100, 50), + sseClient: sse.NewClient(fmt.Sprintf("%s/events", cfg.HTTP.Domain)), + receiveChan: make(chan item), } m.requestList.Title = "Incoming requests" m.requestList.SetShowTitle(true) - b := new(bytes.Buffer) - - err := highlightCode(b, ` - {"name": "lanre"} - `) - // TODO: handle this probably. TUI design is the most important - // bit right now - // Replace all panics with showing on the TUI instead - if err != nil { - panic(err) - } - - m.detailedRequestView.SetContent(b.String()) - return m } @@ -97,6 +79,33 @@ func (m model) Init() tea.Cmd { m.createEndpoint) } +func (m model) listenForNextItem() tea.Msg { + err := m.sseClient.Subscribe(m.pubChannel, func(msg *sse.Event) { + var sseEvent struct { + Request sdump.RequestDefinition `json:"request"` + ID string `json:"id"` + } + + if err := json.NewDecoder(bytes.NewBuffer(msg.Data)).Decode(&sseEvent); err != nil { + panic(err) + } + + m.receiveChan <- item{ + ID: sseEvent.ID, + Request: sseEvent.Request, + } + }) + if err != nil { + panic(err) + } + + return nil +} + +func (m model) waitForNextItem() tea.Msg { + return ItemMsg{item: <-m.receiveChan} +} + func (m model) createEndpoint() tea.Msg { // err can be safely ignored req, _ := http.NewRequest(http.MethodPost, @@ -116,6 +125,9 @@ func (m model) createEndpoint() tea.Msg { URL struct { HumanReadableEndpoint string `json:"human_readable_endpoint,omitempty"` } `json:"url,omitempty"` + SSE struct { + Channel string `json:"channel,omitempty"` + } `json:"sse,omitempty"` } if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { @@ -123,7 +135,8 @@ func (m model) createEndpoint() tea.Msg { } return DumpURLMsg{ - URL: response.URL.HumanReadableEndpoint, + URL: response.URL.HumanReadableEndpoint, + SSEChannel: response.SSE.Channel, } } @@ -149,6 +162,18 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd } + m.pubChannel = msg.SSEChannel + go m.listenForNextItem() + return m, m.waitForNextItem + + case ItemMsg: + + m.requestList.SetItems([]list.Item{ + msg.item, + }) + + return m, m.waitForNextItem + case tea.WindowSizeMsg: h, v := lipgloss.NewStyle().Margin(1, 2).GetFrameSize() diff --git a/internal/tui/types.go b/internal/tui/types.go index b547bfe..6144c05 100644 --- a/internal/tui/types.go +++ b/internal/tui/types.go @@ -1,11 +1,23 @@ package tui import ( + "fmt" "net" "net/http" "time" + + "github.com/adelowo/sdump" ) +type DumpURLMsg struct { + URL string `json:"url,omitempty"` + SSEChannel string `json:"sse_channel,omitempty"` +} + +type ItemMsg struct { + item item +} + type IncomingHTTPRequest struct { Method string `json:"method,omitempty"` ID string `json:"id,omitempty"` @@ -22,9 +34,10 @@ type incomingHTTPRequestMsg struct { } type item struct { - title, desc, ip string + ID string + Request sdump.RequestDefinition } -func (i item) Title() string { return i.title } -func (i item) Description() string { return i.desc } -func (i item) FilterValue() string { return i.title } +func (i item) Title() string { return fmt.Sprintf("%s %s", i.ID, i.Request.IPAddress) } +func (i item) Description() string { return "Here is my long ass description" } +func (i item) FilterValue() string { return i.ID } diff --git a/internal/tui/url.go b/internal/tui/url.go index 9c29acd..89aa0ee 100644 --- a/internal/tui/url.go +++ b/internal/tui/url.go @@ -1,5 +1 @@ package tui - -type DumpURLMsg struct { - URL string `json:"url,omitempty"` -} diff --git a/server/httpd/response.go b/server/httpd/response.go index a8f4ac4..f6b7bcf 100644 --- a/server/httpd/response.go +++ b/server/httpd/response.go @@ -42,5 +42,8 @@ type createdURLEndpointResponse struct { Identifier string `json:"identifier,omitempty"` HumanReadableEndpoint string `json:"human_readable_endpoint,omitempty"` } `json:"url,omitempty"` + SSE struct { + Channel string `json:"channel,omitempty"` + } `json:"sse,omitempty"` APIStatus } diff --git a/server/httpd/url.go b/server/httpd/url.go index fb6c54a..b855194 100644 --- a/server/httpd/url.go +++ b/server/httpd/url.go @@ -50,6 +50,11 @@ func (u *urlHandler) create(w http.ResponseWriter, r *http.Request) { _ = render.Render(w, r, &createdURLEndpointResponse{ APIStatus: newAPIStatus(http.StatusOK, "created url endpoint"), + SSE: struct { + Channel string "json:\"channel,omitempty\"" + }{ + Channel: endpoint.PubChannel(), + }, URL: struct { FQDN string "json:\"fqdn,omitempty\"" Identifier string "json:\"identifier,omitempty\"" @@ -127,9 +132,11 @@ func (u *urlHandler) ingest(w http.ResponseWriter, r *http.Request) { var sseEvent struct { Request sdump.RequestDefinition `json:"request"` + ID string `json:"id"` } sseEvent.Request = ingestedRequest.Request + sseEvent.ID = ingestedRequest.ID.String() if err := json.NewEncoder(b).Encode(&sseEvent); err != nil { logger.WithError(err).Error("could not format SSE event")