瀏覽代碼

fix(xray): do not revive a manually stopped Xray on a background restart

RestartXray cleared isManuallyStopped unconditionally at its top, so the @30s
pending-config cron (and warp/ldap/outbound reconcile jobs) that call
RestartXray(false) resurrected an Xray the admin had deliberately stopped —
unlike the crash-detector, which honors the manual-stop flag. Skip a non-forced
restart while the stop flag is set; only an explicit forced restart clears it.
MHSanaei 1 天之前
父節點
當前提交
116ef900d5
共有 2 個文件被更改,包括 29 次插入0 次删除
  1. 6 0
      internal/web/service/xray.go
  2. 23 0
      internal/web/service/xray_restart_test.go

+ 6 - 0
internal/web/service/xray.go

@@ -986,6 +986,12 @@ func (s *XrayService) RestartXray(isForce bool) error {
 	lock.Lock()
 	defer lock.Unlock()
 	logger.Debug("restart Xray, force:", isForce)
+	// A background reconcile (a pending config-change flag, warp/ldap/outbound
+	// jobs) must not revive an Xray the admin deliberately stopped; only an
+	// explicit forced restart clears the manual-stop state.
+	if !isForce && isManuallyStopped.Load() {
+		return nil
+	}
 	isManuallyStopped.Store(false)
 
 	xrayConfig, err := s.GetXrayConfig()

+ 23 - 0
internal/web/service/xray_restart_test.go

@@ -0,0 +1,23 @@
+package service
+
+import (
+	"testing"
+)
+
+// A background (non-forced) restart — the pending-config-change cron, warp/ldap/
+// outbound reconcile jobs — must not revive an Xray the admin deliberately
+// stopped. Only an explicit forced restart clears the manual-stop state.
+func TestRestartXrayRespectsManualStop(t *testing.T) {
+	setupSettingTestDB(t)
+	if err := (&SettingService{}).saveSetting("xrayTemplateConfig", "{ not valid json"); err != nil {
+		t.Fatalf("seed template: %v", err)
+	}
+	t.Cleanup(func() { isManuallyStopped.Store(false) })
+
+	isManuallyStopped.Store(true)
+	_ = (&XrayService{}).RestartXray(false)
+
+	if !isManuallyStopped.Load() {
+		t.Fatal("a non-forced restart cleared a deliberate manual stop and would revive xray")
+	}
+}