From 47ba7aa22925d19e7bbd66901bdbdac754c4d554 Mon Sep 17 00:00:00 2001 From: Stefan Etringer Date: Thu, 10 Jul 2025 13:07:09 +0000 Subject: [PATCH] added json output for logs when Accept: application/json --- src/server/webapp/handlers.go | 198 ++-------------------------------- 1 file changed, 11 insertions(+), 187 deletions(-) diff --git a/src/server/webapp/handlers.go b/src/server/webapp/handlers.go index 3278d2a..3d6dd7e 100644 --- a/src/server/webapp/handlers.go +++ b/src/server/webapp/handlers.go @@ -146,6 +146,17 @@ func (a *App) logsHandler(w http.ResponseWriter, r *http.Request) { return } + if strings.Contains(r.Header.Get("Accept"), "json") { + w.Header().Set("Content-Type", "application/json") + jsonData, err := json.Marshal(logs) + if err != nil { + http.Error(w, "Failed to encode agents to JSON", http.StatusInternalServerError) + return + } + w.Write(jsonData) + return + } + a.renderTemplate(w, "logs_partial.html", logs) } @@ -177,193 +188,6 @@ func (a *App) getAgentIds(w http.ResponseWriter, r *http.Request) { return } -// func readCookie(r *http.Request, name string) (string, bool){ -// c, err := r.Cookie(name) -// if err != nil { return "", false } -// v, _ := url.QueryUnescape(c.Value) -// return v, true -// } - -// func setCookie(h http.Header, name, val string) { -// h.Add("Set-Cookie", (&http.Cookie{ -// Name: name, -// Value: url.QueryEscape(val), -// Path: "/proxyAgent", -// SameSite: http.SameSiteLaxMode, -// }).String()) -// } - -// // proxyAgentHandler tunnels HTTP and WebSocket traffic to an agent -// // selected by ?ip=…&port=… . It keeps the path *after* /proxyAgent, -// // removes the two query parameters, disables HTTP/2 (mandatory for WS), -// // and streams with no buffering. -// func (a *App) proxyAgentHandler(w http.ResponseWriter, r *http.Request) { -// ip := r.URL.Query().Get("ip") -// port := r.URL.Query().Get("port") -// if ip == "" || port == "" { -// http.Error(w, "ip and port query parameters are required", http.StatusBadRequest) -// return -// } - -// // We leave the scheme "http" even for WebSockets – the Upgrade -// // header does the rest. (Only cosmetic to change it to "ws”.) -// target, err := url.Parse("http://" + ip + ":" + port) -// if err != nil { -// http.Error(w, "invalid ip/port: "+err.Error(), http.StatusBadRequest) -// return -// } - -// proxy := &httputil.ReverseProxy{ -// Director: func(req *http.Request) { -// // Point to the agent -// req.URL.Scheme = target.Scheme -// req.URL.Host = target.Host - -// // Trim the first "/proxyAgent" prefix -// if strings.HasPrefix(req.URL.Path, "/proxyAgent") { -// req.URL.Path = strings.TrimPrefix(req.URL.Path, "/proxyAgent") -// if req.URL.Path == "" { -// req.URL.Path = "/" -// } -// } - -// // Scrub ip/port from downstream query -// q := req.URL.Query() -// q.Del("ip") -// q.Del("port") -// req.URL.RawQuery = q.Encode() - -// // Preserve Host (many CLI-style servers care) -// req.Host = target.Host -// }, - -// // Critical tweaks for WebSockets -// Transport: &http.Transport{ -// Proxy: http.ProxyFromEnvironment, -// DialContext: (&net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}).DialContext, -// ForceAttemptHTTP2: false, // MUST be HTTP/1.1 for WS -// ResponseHeaderTimeout: 0, -// }, - -// FlushInterval: -1, // stream bytes immediately -// } - -// proxy.ServeHTTP(w, r) -// } - -// Registration (keep the slash so sub‑paths are included): -// webMux.HandleFunc("/proxyAgent/", app.proxyAgentHandler) - -//func (app *App) proxyAgentHandler(w http.ResponseWriter, r *http.Request) { - -// //------------------------------------------------------------------ -// // 0. Helpers for cookie handling -// //------------------------------------------------------------------ -// readCookie := func(name string) (string, bool) { -// c, err := r.Cookie(name) -// if err != nil { return "", false } -// v, _ := url.QueryUnescape(c.Value) -// return v, true -// } -// writeCookie := func(h http.Header, name, val string) { -// h.Add("Set-Cookie", (&http.Cookie{ -// Name: name, -// Value: url.QueryEscape(val), -// Path: "/proxyAgent", // cookie visible only here -// SameSite: http.SameSiteLaxMode, -// }).String()) -// } - -// //------------------------------------------------------------------ -// // 1. Decide which agent to talk to -// //------------------------------------------------------------------ -// ip, port := r.URL.Query().Get("ip"), r.URL.Query().Get("port") -// // fallback to cookies if query params are absent -// if ip == "" || port == "" { -// if v, ok := readCookie("proxyagent_ip"); ok { ip = v } -// if v, ok := readCookie("proxyagent_port"); ok { port = v } -// } -// if ip == "" || port == "" { -// http.Error(w, "ip and port query parameters are required", http.StatusBadRequest) -// return -// } - -// target, err := url.Parse("http://" + ip + ":" + port) -// if err != nil { -// http.Error(w, "invalid ip/port: "+err.Error(), http.StatusBadRequest) -// return -// } - -// // keep copies for ModifyResponse (Director will delete them) -// ipForCookie, portForCookie := ip, port - -// //------------------------------------------------------------------ -// // 2. Reverse proxy -// //------------------------------------------------------------------ -// proxy := &httputil.ReverseProxy{ - -// Director: func(req *http.Request) { -// // 2.1 upstream address -// req.URL.Scheme = target.Scheme -// req.URL.Host = target.Host - -// // 2.2 strip ONE leading /proxyAgent -// if strings.HasPrefix(req.URL.Path, "/proxyAgent") { -// req.URL.Path = strings.TrimPrefix(req.URL.Path, "/proxyAgent") -// if req.URL.Path == "" { req.URL.Path = "/" } -// } - -// // 2.3 remove ip/port from outgoing query -// q := req.URL.Query() -// q.Del("ip"); q.Del("port") -// req.URL.RawQuery = q.Encode() - -// // 2.4 keep original Host header -// req.Host = target.Host - -// // 2.5 force uncompressed HTML so we can patch it -// req.Header.Del("Accept-Encoding") -// }, - -// Transport: &http.Transport{ -// Proxy: http.ProxyFromEnvironment, -// DialContext: (&net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}).DialContext, -// ForceAttemptHTTP2: false, // must stay on h1 for WebSocket upgrade -// ResponseHeaderTimeout: 0, -// }, - -// FlushInterval: -1, // instant streaming for xterm.js - -// //------------------------------------------------------------------ -// // 3. Response rewrite & cookie injection -// //------------------------------------------------------------------ -// ModifyResponse: func(resp *http.Response) error { - -// // 3.1 always (once per agent) set the cookies -// writeCookie(resp.Header, "proxyagent_ip", ipForCookie) -// writeCookie(resp.Header, "proxyagent_port", portForCookie) - -// // 3.2 if the payload is HTML, inject -// if ct := resp.Header.Get("Content-Type"); strings.HasPrefix(ct, "text/html") { -// body, _ := io.ReadAll(resp.Body) -// patched := bytes.Replace( -// body, -// []byte(""), -// []byte(``), -// 1) - -// resp.Body = io.NopCloser(bytes.NewReader(patched)) -// resp.ContentLength = int64(len(patched)) -// resp.Header.Set("Content-Length", strconv.Itoa(len(patched))) -// } - -// return nil -// }, -// } - -// proxy.ServeHTTP(w, r) -//} - func (app *App) proxyAgentHandler(w http.ResponseWriter, r *http.Request) { // path is /proxyAgent//whatever… tail := strings.TrimPrefix(r.URL.Path, "/proxyAgent/")