Implement Mihomo external-controller support in configuration and gateway
- Added optional Mihomo configuration fields in `config.compose.yaml` and `config.example.yaml` for enhanced integration with the Mihomo external-controller. - Updated the `Gateway` to handle Mihomo API requests, including proxying and error handling for Mihomo-specific endpoints. - Enhanced the documentation in `GATEWAY_RUN.md` to guide users on configuring Mihomo integration. - Introduced new utility functions in the web client for interacting with Mihomo API endpoints, improving the overall user experience. - Updated the sidebar in the Svelte components to include a link to the Mihomo section, enhancing navigation.
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
type Gateway struct {
|
||||
parsed *config.Parsed
|
||||
proxies map[string]http.Handler
|
||||
mihomo map[string]http.Handler
|
||||
agg *aggregate.Handler
|
||||
geo *geoip.Service
|
||||
log *slog.Logger
|
||||
@@ -45,6 +46,7 @@ func NewGateway(p *config.Parsed, log *slog.Logger, geo *geoip.Service) (*Gatewa
|
||||
g := &Gateway{
|
||||
parsed: p,
|
||||
proxies: make(map[string]http.Handler),
|
||||
mihomo: make(map[string]http.Handler),
|
||||
geo: geo,
|
||||
log: log,
|
||||
transport: t,
|
||||
@@ -68,6 +70,19 @@ func NewGateway(p *config.Parsed, log *slog.Logger, geo *geoip.Service) (*Gatewa
|
||||
})
|
||||
})
|
||||
}
|
||||
for alias, m := range p.MihomoByAlias {
|
||||
a := alias
|
||||
strip := "/api/" + a + "/mihomo"
|
||||
g.mihomo[a] = proxy.NewMihomoForward(m.Base, strip, m.Auth, t, func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
log.Error("mihomo upstream error", "alias", a, "err", err)
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusBadGateway)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"ok": false,
|
||||
"error": map[string]string{"code": "bad_gateway", "message": "mihomo upstream unreachable"},
|
||||
})
|
||||
})
|
||||
}
|
||||
var aggCacheTTL time.Duration
|
||||
if p.Config.Aggregate != nil && p.Config.Aggregate.CacheTTLMs > 0 {
|
||||
aggCacheTTL = time.Duration(p.Config.Aggregate.CacheTTLMs) * time.Millisecond
|
||||
@@ -230,7 +245,11 @@ func routeEndpoint(path string) string {
|
||||
if i < 0 {
|
||||
return "proxy_root"
|
||||
}
|
||||
return "proxy_" + rest[i+1:]
|
||||
sub := rest[i+1:]
|
||||
if strings.HasPrefix(sub, "mihomo/") {
|
||||
return "mihomo_" + sub[len("mihomo/"):]
|
||||
}
|
||||
return "proxy_" + sub
|
||||
}
|
||||
return "ui"
|
||||
}
|
||||
@@ -285,7 +304,7 @@ func (g *Gateway) serve(w http.ResponseWriter, r *http.Request) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
rp, ok := g.proxies[alias]
|
||||
_, ok := g.proxies[alias]
|
||||
if !ok {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
@@ -295,7 +314,28 @@ func (g *Gateway) serve(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
return
|
||||
}
|
||||
rp.ServeHTTP(w, r)
|
||||
mihomoPfx := "/api/" + alias + "/mihomo"
|
||||
if strings.HasPrefix(r.URL.Path, mihomoPfx) {
|
||||
mh, have := g.mihomo[alias]
|
||||
if !have {
|
||||
proxy.MihomoJSONError(w, "mihomo_not_configured", "mihomo is not configured for this server")
|
||||
return
|
||||
}
|
||||
if r.URL.Path == mihomoPfx+"/meta" && r.Method == http.MethodGet {
|
||||
mu := g.parsed.MihomoByAlias[alias]
|
||||
if mu == nil {
|
||||
proxy.MihomoJSONError(w, "mihomo_not_configured", "mihomo is not configured for this server")
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write(proxy.MihomoMetaJSON(mu.Base))
|
||||
return
|
||||
}
|
||||
mh.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
g.proxies[alias].ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (g *Gateway) serveLiveEvents(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user