From 2c42f8954705fdf1859b36592d7abf9ed4be40f2 Mon Sep 17 00:00:00 2001 From: Erik Brakkee Date: Sat, 17 Aug 2024 10:37:20 +0200 Subject: [PATCH] moved all ui stuff to the ui package. The structure of converge server is now much more clear in the package // structure below pkg/server. --- cmd/converge/converge.go | 11 ++++---- cmd/converge/pagehandler.go | 28 ------------------- pkg/server/admin/doc.go | 4 +++ pkg/server/matchmaker/doc.go | 6 ++++ pkg/server/prometheus/doc.go | 4 +++ .../server/ui}/convergeaccess.go | 17 +++++++++-- pkg/server/ui/doc.go | 6 ++++ pkg/server/ui/pagehandler.go | 27 ++++++++++++++++++ {cmd/converge => pkg/server/ui}/usage.go | 9 +++--- pkg/server/{matchmaker => ui}/websessions.go | 16 ++--------- 10 files changed, 73 insertions(+), 55 deletions(-) delete mode 100644 cmd/converge/pagehandler.go create mode 100644 pkg/server/admin/doc.go create mode 100644 pkg/server/matchmaker/doc.go create mode 100644 pkg/server/prometheus/doc.go rename {cmd/converge => pkg/server/ui}/convergeaccess.go (73%) create mode 100644 pkg/server/ui/doc.go create mode 100644 pkg/server/ui/pagehandler.go rename {cmd/converge => pkg/server/ui}/usage.go (86%) rename pkg/server/{matchmaker => ui}/websessions.go (90%) diff --git a/cmd/converge/converge.go b/cmd/converge/converge.go index 53b8c55..d1aad5a 100644 --- a/cmd/converge/converge.go +++ b/cmd/converge/converge.go @@ -6,6 +6,7 @@ import ( "git.wamblee.org/converge/pkg/models" "git.wamblee.org/converge/pkg/server/matchmaker" "git.wamblee.org/converge/pkg/server/prometheus" + "git.wamblee.org/converge/pkg/server/ui" "git.wamblee.org/converge/pkg/support/websocketutil" "log" "net" @@ -128,7 +129,7 @@ func main() { // And the MatchMaker. The MatchMakers sends state notifications to websessions // and prometheus. notifications := NewStateNotifier(throttlingInterval) - websessions := matchmaker.NewWebSessions(notifications.webNotificationChannel) + websessions := ui.NewWebSessions(notifications.webNotificationChannel) // monitoring prometheusMux := http.NewServeMux() prometheus.SetupPrometheus(prometheusMux, notifications.prometheusNotificationChannel) @@ -161,18 +162,18 @@ func setupWebUI(context HttpContext, registrationService websocketutil.WebSocket context.HandleFunc("ws/sessions", sessionService.Handle) // create filehandler with templating for html files. - context.Handle("docs/", http.StripPrefix("docs/", http.HandlerFunc(pageHandler))) + context.Handle("docs/", http.StripPrefix("docs/", http.HandlerFunc(ui.PageHandler))) context.Handle("static/", http.StripPrefix("static/", http.FileServer(http.Dir(staticdir)))) context.Handle("downloads/", http.StripPrefix("downloads/", http.FileServer(http.Dir(downloaddir)))) // create usage generator - context.HandleFunc("usage", generateCLIExammple) + context.HandleFunc("usage", ui.GenerateCLIExammples) return context } -func setupWebSockets(admin *matchmaker.MatchMaker, websessions *matchmaker.WebSessions) (websocketutil.WebSocketService, websocketutil.WebSocketService, websocketutil.WebSocketService) { +func setupWebSockets(admin *matchmaker.MatchMaker, websessions *ui.WebSessions) (websocketutil.WebSocketService, websocketutil.WebSocketService, websocketutil.WebSocketService) { // websocket endpoints // For agents connecting @@ -214,7 +215,7 @@ func setupWebSockets(admin *matchmaker.MatchMaker, websessions *matchmaker.WebSe ctx, cancel := context.WithCancel(context.Background()) websession := websessions.NewSession(conn, ctx, cancel) defer websessions.SessionClosed(websession) - location, err := matchmaker.GetUserLocation(r) + location, err := ui.GetUserLocation(r) if err != nil { panic(err) } diff --git a/cmd/converge/pagehandler.go b/cmd/converge/pagehandler.go deleted file mode 100644 index 48e836e..0000000 --- a/cmd/converge/pagehandler.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - templates2 "git.wamblee.org/converge/pkg/server/ui" - "net/http" -) - -func pageHandler(w http.ResponseWriter, r *http.Request) { - access := getConvergeAccess(r) - - switch r.URL.Path { - case "": - fallthrough - case "/": - fallthrough - case "index.html": - templates2.AboutTab().Render(r.Context(), w) - // TODO use contexts later. - case "usage.html": - templates2.UsageTab(access).Render(r.Context(), w) - case "downloads.html": - templates2.DownloadsTab().Render(r.Context(), w) - case "sessions.html": - templates2.SessionsTab(nil, nil, access.Location).Render(r.Context(), w) - default: - http.NotFound(w, r) - } -} diff --git a/pkg/server/admin/doc.go b/pkg/server/admin/doc.go new file mode 100644 index 0000000..954257b --- /dev/null +++ b/pkg/server/admin/doc.go @@ -0,0 +1,4 @@ +// Basic administration of the matchmaker. +// Put in its separate package to make sure only public methods can be called because +// these are the only ones that are thread-safe. +package admin diff --git a/pkg/server/matchmaker/doc.go b/pkg/server/matchmaker/doc.go new file mode 100644 index 0000000..ff14e86 --- /dev/null +++ b/pkg/server/matchmaker/doc.go @@ -0,0 +1,6 @@ +// The matchmaker package matches up agents and clients based on their rendez-vous +// ids and pumps data between client and agent. This is the core cunctionality of +// converge. It uses notifications with the new state in case something changes. +// The notifications are routed to components that are interested in them: the web ui +// and prometheus integration. +package matchmaker diff --git a/pkg/server/prometheus/doc.go b/pkg/server/prometheus/doc.go new file mode 100644 index 0000000..0ede448 --- /dev/null +++ b/pkg/server/prometheus/doc.go @@ -0,0 +1,4 @@ +// Prometheus integration of converge. It relies on state notifications of converge that +// describe the current state. It computes the deltas based on the state itself and updates +// prometheus metrics accordingly. +package prometheus diff --git a/cmd/converge/convergeaccess.go b/pkg/server/ui/convergeaccess.go similarity index 73% rename from cmd/converge/convergeaccess.go rename to pkg/server/ui/convergeaccess.go index 966281d..b06e539 100644 --- a/cmd/converge/convergeaccess.go +++ b/pkg/server/ui/convergeaccess.go @@ -1,13 +1,24 @@ -package main +package ui import ( "git.wamblee.org/converge/pkg/models" - "git.wamblee.org/converge/pkg/server/matchmaker" "net/http" "regexp" "strings" + "time" ) +func GetUserLocation(r *http.Request) (*time.Location, error) { + tzName := r.URL.Query().Get("timezone") + if tzName == "" { + tzName = r.Header.Get("X-Timezone") + } + if tzName == "" { + tzName = "UTC" + } + return time.LoadLocation(tzName) +} + func getConvergeAccess(r *http.Request) models.ConvergeAccess { pattern := regexp.MustCompile("^(.*)/usage$") @@ -32,7 +43,7 @@ func getConvergeAccess(r *http.Request) models.ConvergeAccess { } } - location, err := matchmaker.GetUserLocation(r) + location, err := GetUserLocation(r) if err != nil { panic(err) } diff --git a/pkg/server/ui/doc.go b/pkg/server/ui/doc.go new file mode 100644 index 0000000..fdffda4 --- /dev/null +++ b/pkg/server/ui/doc.go @@ -0,0 +1,6 @@ +// This provides the user interface consisting of a number of an information page, +// a downloads page, an interfactive usage page and a sessions page. +// The usage page uses HTMX to get updated CLI examples based on the user's input. +// The sessions page uses HTMX with a websocket so that the server can push the live +// status of the server to clients. +package ui diff --git a/pkg/server/ui/pagehandler.go b/pkg/server/ui/pagehandler.go new file mode 100644 index 0000000..15bbcef --- /dev/null +++ b/pkg/server/ui/pagehandler.go @@ -0,0 +1,27 @@ +package ui + +import ( + "net/http" +) + +func PageHandler(w http.ResponseWriter, r *http.Request) { + access := getConvergeAccess(r) + + switch r.URL.Path { + case "": + fallthrough + case "/": + fallthrough + case "index.html": + AboutTab().Render(r.Context(), w) + // TODO use contexts later. + case "usage.html": + UsageTab(access).Render(r.Context(), w) + case "downloads.html": + DownloadsTab().Render(r.Context(), w) + case "sessions.html": + SessionsTab(nil, nil, access.Location).Render(r.Context(), w) + default: + http.NotFound(w, r) + } +} diff --git a/cmd/converge/usage.go b/pkg/server/ui/usage.go similarity index 86% rename from cmd/converge/usage.go rename to pkg/server/ui/usage.go index 9e383fa..185aead 100644 --- a/cmd/converge/usage.go +++ b/pkg/server/ui/usage.go @@ -1,8 +1,7 @@ -package main +package ui import ( "fmt" - "git.wamblee.org/converge/pkg/server/ui" "github.com/gliderlabs/ssh" "math/rand" "net/http" @@ -11,7 +10,7 @@ import ( "strings" ) -func generateCLIExammple(w http.ResponseWriter, r *http.Request) { +func GenerateCLIExammples(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { http.Error(w, "Error parsing form", http.StatusBadRequest) @@ -32,7 +31,7 @@ func generateCLIExammple(w http.ResponseWriter, r *http.Request) { downloadCommand := r.FormValue("download-command") certificateValidation := r.FormValue("certificate-validation") != "" sshPublicKeys := strings.Split(keysString, "\n") - usageInputs := ui.NewUsageInputs(id, sshPublicKeys, remoteShells, localShells, downloadCommand, + usageInputs := NewUsageInputs(id, sshPublicKeys, remoteShells, localShells, downloadCommand, certificateValidation) if generatedId { usageInputs.ErrorMessages = append(usageInputs.ErrorMessages, "rendez-vous id is randomly generated, changes on every page refresh") @@ -64,7 +63,7 @@ func generateCLIExammple(w http.ResponseWriter, r *http.Request) { "No valid public keys configured. Without these the agent will not work.") } - err = ui.ShellUsage(access, usageInputs).Render(r.Context(), w) + err = ShellUsage(access, usageInputs).Render(r.Context(), w) if err != nil { http.Error(w, err.Error(), 500) } diff --git a/pkg/server/matchmaker/websessions.go b/pkg/server/ui/websessions.go similarity index 90% rename from pkg/server/matchmaker/websessions.go rename to pkg/server/ui/websessions.go index 1acb34c..8fac497 100644 --- a/pkg/server/matchmaker/websessions.go +++ b/pkg/server/ui/websessions.go @@ -1,9 +1,8 @@ -package matchmaker +package ui import ( "context" "git.wamblee.org/converge/pkg/models" - "git.wamblee.org/converge/pkg/server/ui" "log" "net" "net/http" @@ -98,17 +97,6 @@ func (sessions *WebSessions) NewSession(wsConnection net.Conn, ctx context.Conte return session } -func GetUserLocation(r *http.Request) (*time.Location, error) { - tzName := r.URL.Query().Get("timezone") - if tzName == "" { - tzName = r.Header.Get("X-Timezone") - } - if tzName == "" { - tzName = "UTC" - } - return time.LoadLocation(tzName) -} - func (session *WebSession) WriteNotifications(location *time.Location, ctx context.Context, cancel context.CancelFunc) { timer := time.NewTicker(10 * time.Second) defer timer.Stop() @@ -138,7 +126,7 @@ func (session *WebSession) WriteNotifications(location *time.Location, ctx conte func (session *WebSession) writeNotificationToClient(location *time.Location, notification *models.State) bool { agents, clients := notification.Slices() - err := ui.State(agents, clients, location).Render(context.Background(), session.conn) + err := State(agents, clients, location).Render(context.Background(), session.conn) if err != nil { log.Printf("WS connection closed: %v", err) return false