Procházet zdrojové kódy

fix(frontend): tolerate a malformed happyEyeballs value in the Xray Basics tab

BasicsTab derived directHappyEyeballs by calling HappyEyeballsSchema.parse
during render, guarding only against null/non-object. A wrong-typed field
(e.g. happyEyeballs.tryDelayMs as a string) or any other shape mismatch —
reachable via the Complete Template JSON editor or an imported config — threw
straight out of render, white-screening the default Xray landing tab.

Use safeParse and fall back to null so a bad value degrades to "no override"
instead of crashing the page.
MHSanaei před 1 dnem
rodič
revize
980c023287

+ 2 - 1
frontend/src/pages/xray/basics/BasicsTab.tsx

@@ -115,7 +115,8 @@ export default function BasicsTab({
       ?.sockopt;
     const raw = sockopt?.happyEyeballs;
     if (raw == null || typeof raw !== 'object') return null;
-    return HappyEyeballsSchema.parse(raw);
+    const parsed = HappyEyeballsSchema.safeParse(raw);
+    return parsed.success ? parsed.data : null;
   })();
 
   const setDirectHappyEyeballs = useCallback(

+ 36 - 0
frontend/src/test/basics-happy-eyeballs.test.tsx

@@ -0,0 +1,36 @@
+import { describe, it, expect, vi } from 'vitest';
+
+import BasicsTab from '@/pages/xray/basics/BasicsTab';
+import type { XraySettingsValue } from '@/hooks/useXraySetting';
+
+import { renderWithProviders } from './test-utils';
+
+function settingsWithMalformedHappyEyeballs(): XraySettingsValue {
+  return {
+    outbounds: [
+      {
+        protocol: 'freedom',
+        tag: 'direct',
+        streamSettings: { sockopt: { happyEyeballs: { tryDelayMs: 'fast' } } },
+      },
+    ],
+  } as unknown as XraySettingsValue;
+}
+
+describe('BasicsTab malformed happyEyeballs', () => {
+  it('renders instead of white-screening on a wrong-typed happyEyeballs value', () => {
+    const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
+    expect(() =>
+      renderWithProviders(
+        <BasicsTab
+          templateSettings={settingsWithMalformedHappyEyeballs()}
+          setTemplateSettings={vi.fn()}
+          outboundTestUrl=""
+          onChangeOutboundTestUrl={vi.fn()}
+          onResetDefault={vi.fn()}
+        />,
+      ),
+    ).not.toThrow();
+    errorSpy.mockRestore();
+  });
+});