Enhance error handling in gateway and configuration
Publish telemt-api gateway Docker image / test (push) Successful in 24s
Publish telemt-api gateway Docker image / build-and-push (push) Successful in 2m6s

- Added `expose_upstream_errors` option to the configuration, allowing detailed error messages in JSON responses for 502 errors.
- Implemented `writeBadGatewayJSON` function to streamline JSON error responses, including upstream error details when enabled.
- Updated documentation to reflect changes in configuration and error handling behavior for improved diagnostics.
This commit is contained in:
Denozordec
2026-03-31 14:30:15 +07:00
parent deb12e72c0
commit 8bf4bb7f35
4 changed files with 36 additions and 14 deletions
+16 -12
View File
@@ -36,6 +36,19 @@ type Gateway struct {
webUI http.Handler
}
func writeBadGatewayJSON(w http.ResponseWriter, expose bool, code, message string, upstreamErr error) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusBadGateway)
errObj := map[string]any{"code": code, "message": message}
if expose && upstreamErr != nil {
errObj["detail"] = upstreamErr.Error()
}
_ = json.NewEncoder(w).Encode(map[string]any{
"ok": false,
"error": errObj,
})
}
// NewGateway builds handlers and reverse proxies from parsed config.
func NewGateway(p *config.Parsed, log *slog.Logger, geo *geoip.Service) (*Gateway, error) {
t := proxy.DirectTransport()
@@ -54,6 +67,7 @@ func NewGateway(p *config.Parsed, log *slog.Logger, geo *geoip.Service) (*Gatewa
transport: t,
promHandler: promhttp.Handler(),
}
exposeErr := p.Config.ExposeUpstreamErrors
for i := range p.Config.Servers {
s := &p.Config.Servers[i]
u, err := url.Parse(s.BaseURL)
@@ -64,12 +78,7 @@ func NewGateway(p *config.Parsed, log *slog.Logger, geo *geoip.Service) (*Gatewa
strip := "/api/" + s.Alias
g.proxies[s.Alias] = proxy.NewAliasForward(u, strip, s.PathPrefix, auth, t, func(w http.ResponseWriter, r *http.Request, err error) {
log.Error("upstream error", "alias", s.Alias, "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": "upstream unreachable"},
})
writeBadGatewayJSON(w, exposeErr, "bad_gateway", "upstream unreachable", err)
})
}
for alias, m := range p.MihomoByAlias {
@@ -77,12 +86,7 @@ func NewGateway(p *config.Parsed, log *slog.Logger, geo *geoip.Service) (*Gatewa
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"},
})
writeBadGatewayJSON(w, exposeErr, "bad_gateway", "mihomo upstream unreachable", err)
})
}
var aggCacheTTL time.Duration