added json output for logs when Accept: application/json

This commit is contained in:
Stefan Etringer 2025-07-10 13:07:09 +00:00
parent 495f26caed
commit 47ba7aa229
1 changed files with 11 additions and 187 deletions

View File

@ -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 subpaths 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 <base href="/proxyAgent/">
// if ct := resp.Header.Get("Content-Type"); strings.HasPrefix(ct, "text/html") {
// body, _ := io.ReadAll(resp.Body)
// patched := bytes.Replace(
// body,
// []byte("<head>"),
// []byte(`<head><base href="/proxyAgent/">`),
// 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/<agentKey>/whatever…
tail := strings.TrimPrefix(r.URL.Path, "/proxyAgent/")