From c3f0378e686461f6b3a0763f09278ca3f2a3aa20 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Wed, 15 Jul 2026 00:14:00 +0200 Subject: [PATCH] fix(inbound): request an xray restart when toggling a routed MTProto inbound AddInbound, DelInbound and UpdateInbound all flag needRestart when an inbound routes MTProto through xray, so the egress SOCKS bridge is regenerated. Only SetInboundEnable's local path omitted it, so toggling a routed MTProto inbound off then on left the bridge out of the running config while the sidecar dialed its loopback port, blackholing that inbound until an unrelated restart. Flag the restart on the local enable path too. --- internal/web/service/inbound.go | 4 +++ .../service/inbound_mtproto_txfail_test.go | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/internal/web/service/inbound.go b/internal/web/service/inbound.go index e2346318d..991295172 100644 --- a/internal/web/service/inbound.go +++ b/internal/web/service/inbound.go @@ -1103,6 +1103,10 @@ func (s *InboundService) SetInboundEnable(id int, enable bool) (bool, error) { return false, nil } + if mtprotoRoutesThroughXray(inbound) { + needRestart = true + } + if !push { return true, nil } diff --git a/internal/web/service/inbound_mtproto_txfail_test.go b/internal/web/service/inbound_mtproto_txfail_test.go index 190c4251c..c4fa6599b 100644 --- a/internal/web/service/inbound_mtproto_txfail_test.go +++ b/internal/web/service/inbound_mtproto_txfail_test.go @@ -51,3 +51,30 @@ func TestUpdateInboundLocalMtprotoDefersPushUntilCommit(t *testing.T) { t.Fatalf("the MTProto sidecar push ran %d time(s) inside the failed transaction; it must be deferred until the commit succeeds", n) } } + +// Re-enabling a routed MTProto inbound must request an xray restart: the egress +// SOCKS bridge is only injected for enabled inbounds, so the running config +// needs regenerating or the sidecar dials a bridge that is not there. +func TestSetInboundEnableRoutedMtprotoRequestsRestart(t *testing.T) { + setupConflictDB(t) + + mgr := runtime.NewManager(runtime.LocalDeps{APIPort: func() int { return 0 }}) + mgr.SetLocalRuntimeOverride(&fakeNodeRuntime{}) + runtime.SetManager(mgr) + t.Cleanup(func() { runtime.SetManager(nil) }) + + seedInboundConflict(t, "mt-route", "", 46160, model.MTProto, "", + `{"clients":[{"email":"mtr","secret":"`+mtprotoTestSecretA+`","enable":true}],"routeThroughXray":true,"routeXrayPort":12345}`) + seeded := loadInboundByTag(t, "mt-route") + if err := database.GetDB().Model(&model.Inbound{}).Where("id = ?", seeded.Id).Update("enable", false).Error; err != nil { + t.Fatalf("force disable: %v", err) + } + + needRestart, err := (&InboundService{}).SetInboundEnable(seeded.Id, true) + if err != nil { + t.Fatalf("SetInboundEnable: %v", err) + } + if !needRestart { + t.Fatal("re-enabling a routed MTProto inbound must request an xray restart to re-inject the egress bridge") + } +}