feat: proxy local service embeds

This commit is contained in:
Haitao Pan 2026-06-13 07:47:03 +08:00
parent d460a6e63e
commit f8a27b8058
3 changed files with 62 additions and 2 deletions

57
api/proxy.go Normal file
View File

@ -0,0 +1,57 @@
package main
import (
"net/http"
"net/http/httputil"
"net/url"
"strings"
)
func NewLocalProxy(target string, prefix string) http.Handler {
upstream, err := url.Parse(target)
if err != nil {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError)
})
}
proxy := httputil.NewSingleHostReverseProxy(upstream)
originalDirector := proxy.Director
proxy.Director = func(r *http.Request) {
originalDirector(r)
r.URL.Scheme = upstream.Scheme
r.URL.Host = upstream.Host
r.URL.Path = strings.TrimPrefix(r.URL.Path, prefix)
if r.URL.Path == "" {
r.URL.Path = "/"
}
r.Host = upstream.Host
}
proxy.ModifyResponse = func(resp *http.Response) error {
stripFrameBlockingHeaders(resp.Header)
return nil
}
return proxy
}
func stripFrameBlockingHeaders(header http.Header) {
header.Del("X-Frame-Options")
csp := header.Get("Content-Security-Policy")
if csp == "" {
return
}
header.Set("Content-Security-Policy", removeCSPDirective(csp, "frame-ancestors"))
}
func removeCSPDirective(csp string, directive string) string {
parts := strings.Split(csp, ";")
kept := parts[:0]
for _, part := range parts {
trimmed := strings.TrimSpace(part)
if trimmed == "" || strings.HasPrefix(strings.ToLower(trimmed), directive) {
continue
}
kept = append(kept, trimmed)
}
return strings.Join(kept, "; ")
}

View File

@ -29,6 +29,9 @@ func (s *Server) routes() http.Handler {
mux.HandleFunc("/health", s.healthHandler)
mux.HandleFunc("/services", s.servicesHandler)
mux.HandleFunc("/metrics/simple", s.metricsHandler)
mux.Handle("/proxy/openclaw/", NewLocalProxy("http://127.0.0.1:18789", "/proxy/openclaw"))
mux.Handle("/proxy/vault/", NewLocalProxy("http://127.0.0.1:8200", "/proxy/vault"))
mux.Handle("/ui/", NewLocalProxy("http://127.0.0.1:8200", ""))
return mux
}

View File

@ -48,8 +48,8 @@ export type ServiceDef = NavItem & {
export const serviceRegistry: ServiceDef[] = [
{ id: 'workspace', label: 'Overview', icon: 'home', href: '#workspace', kind: 'internal', group: 0 },
{ id: 'openclaw', label: 'OpenClaw', icon: 'claw', href: 'http://127.0.0.1:18789/channels', kind: 'embed', group: 1, port: 18789, match: ['openclaw', 'gateway'], frameMode: 'external' },
{ id: 'vault', label: 'Vault Server', icon: 'shield', href: 'http://127.0.0.1:8200/ui/', kind: 'embed', group: 1, port: 8200, match: ['vault'], frameMode: 'external' },
{ id: 'openclaw', label: 'OpenClaw', icon: 'claw', href: 'http://127.0.0.1:8788/proxy/openclaw/channels', kind: 'embed', group: 1, port: 18789, match: ['openclaw', 'gateway'] },
{ id: 'vault', label: 'Vault Server', icon: 'shield', href: 'http://127.0.0.1:8788/proxy/vault/ui/', kind: 'embed', group: 1, port: 8200, match: ['vault'] },
{ id: 'litellm', label: 'LiteLLM Admin UI', icon: 'chart', href: 'http://localhost:4000/ui', kind: 'embed', group: 1, port: 4000, match: ['litellm', 'lite'] },
{ id: 'bridge', label: 'Bridge', icon: 'bridge', href: '#bridge', kind: 'internal', group: 2, match: ['bridge'] },
{ id: 'runtime', label: 'Runtime', icon: 'cube', href: '#runtime', kind: 'internal', group: 2 },