added json output for logs when Accept: application/json
This commit is contained in:
		
							parent
							
								
									495f26caed
								
							
						
					
					
						commit
						47ba7aa229
					
				| 
						 | 
					@ -146,6 +146,17 @@ func (a *App) logsHandler(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		return
 | 
							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)
 | 
						a.renderTemplate(w, "logs_partial.html", logs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -177,193 +188,6 @@ func (a *App) getAgentIds(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	return
 | 
						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 <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) {
 | 
					func (app *App) proxyAgentHandler(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
    // path is /proxyAgent/<agentKey>/whatever…
 | 
					    // path is /proxyAgent/<agentKey>/whatever…
 | 
				
			||||||
    tail := strings.TrimPrefix(r.URL.Path, "/proxyAgent/")
 | 
					    tail := strings.TrimPrefix(r.URL.Path, "/proxyAgent/")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue