瀏覽代碼

fix(frontend): seed full Zod-schema defaults for stream slices + QUIC params (B17)

XHTTP showed blank Selects for Session Placement / Sequence Placement /
Padding Method / Uplink HTTP Method (and several other knobs). Those
fields have a literal "" (empty string) value in the schema, which the
Select renders as "Default (path)" / "Default (repeat-x)" / etc.
The form field was `undefined`, not `""`, so the Select showed blank
instead of the labelled default option.

newStreamSlice in InboundFormModal hand-rolled per-network seed
objects with only a handful of fields. Replaced with
{Tcp,Kcp,Ws,Grpc,HttpUpgrade,XHttp}StreamSettingsSchema.parse({}) so
every default declared in the schema populates the form on network
switch. Same change in buildAddModeValues for the initial TCP state.

QUIC Params (FinalMaskForm) had the same shape on a smaller scale —
defaultQuicParams() only seeded congestion + debug + udpHop. The
schema's other fields are .optional() (no Zod default) so a schema
parse won't help. Hard-coded the xray-core / hysteria recommended
values (maxIdleTimeout 30, keepAlivePeriod 10, brutalUp/Down 0,
maxIncomingStreams 1024, four window sizes) so the InputNumber
controls render with usable starting values instead of blank.
MHSanaei 1 天之前
父節點
當前提交
a3dfafadb1
共有 2 個文件被更改,包括 37 次插入22 次删除
  1. 16 0
      frontend/src/components/FinalMaskForm.tsx
  2. 21 22
      frontend/src/pages/inbounds/InboundFormModal.tsx

+ 16 - 0
frontend/src/components/FinalMaskForm.tsx

@@ -80,10 +80,26 @@ function defaultNoiseItem(): Record<string, unknown> {
 }
 
 function defaultQuicParams(): Record<string, unknown> {
+  // Seeded with the xray-core / hysteria recommended defaults so the QUIC
+  // Params sub-form doesn't show blank InputNumber fields when first
+  // enabled. The schema declares these as .optional() (no Zod default)
+  // because the wire shape omits them when xray's built-in default
+  // applies — but the panel needs values to render the controls.
   return {
     congestion: 'bbr',
     debug: false,
+    brutalUp: 0,
+    brutalDown: 0,
+    hasUdpHop: false,
     udpHop: { ports: '20000-50000', interval: 5 },
+    maxIdleTimeout: 30,
+    keepAlivePeriod: 10,
+    disablePathMTUDiscovery: false,
+    maxIncomingStreams: 1024,
+    initStreamReceiveWindow: 8388608,
+    maxStreamReceiveWindow: 8388608,
+    initConnectionReceiveWindow: 20971520,
+    maxConnectionReceiveWindow: 20971520,
   };
 }
 

+ 21 - 22
frontend/src/pages/inbounds/InboundFormModal.tsx

@@ -64,6 +64,12 @@ import { SockoptStreamSettingsSchema } from '@/schemas/protocols/stream/sockopt'
 import { TlsStreamSettingsSchema } from '@/schemas/protocols/security/tls';
 import { RealityStreamSettingsSchema } from '@/schemas/protocols/security/reality';
 import { SniffingSchema } from '@/schemas/primitives/sniffing';
+import { TcpStreamSettingsSchema } from '@/schemas/protocols/stream/tcp';
+import { KcpStreamSettingsSchema } from '@/schemas/protocols/stream/kcp';
+import { WsStreamSettingsSchema } from '@/schemas/protocols/stream/ws';
+import { GrpcStreamSettingsSchema } from '@/schemas/protocols/stream/grpc';
+import { HttpUpgradeStreamSettingsSchema } from '@/schemas/protocols/stream/httpupgrade';
+import { XHttpStreamSettingsSchema } from '@/schemas/protocols/stream/xhttp';
 import DateTimePicker from '@/components/DateTimePicker';
 import FinalMaskForm from '@/components/FinalMaskForm';
 import HeaderMapEditor from '@/components/HeaderMapEditor';
@@ -264,7 +270,7 @@ function buildAddModeValues(): InboundFormValues {
     streamSettings: {
       network: 'tcp',
       security: 'none',
-      tcpSettings: { header: { type: 'none' } },
+      tcpSettings: TcpStreamSettingsSchema.parse({ header: { type: 'none' } }),
     },
     sniffing: SniffingSchema.parse({}),
     port: RandomUtil.randomInteger(10000, 60000),
@@ -1305,29 +1311,22 @@ export default function InboundFormModal({
   // network's blob and seed the new one with the schema defaults so the
   // Form.Items inside it have valid initial values (KCP needs MTU=1350
   // etc., not empty strings).
+  // Seed each network's settings blob with its Zod schema defaults so
+  // every Form.Item inside the network sub-form has a defined starting
+  // value. XHTTP in particular has ~20 fields (sessionPlacement,
+  // seqPlacement, xPaddingMethod, uplinkHTTPMethod, ...) whose value
+  // is the literal "" sentinel meaning "let xray-core pick its
+  // default". Without seeding "", the Form.Item reads `undefined` and
+  // the Select shows blank instead of the "Default (path)" option.
   const newStreamSlice = (n: string): Record<string, unknown> => {
     switch (n) {
-      case 'tcp':
-        return { header: { type: 'none' } };
-      case 'kcp':
-        return {
-          mtu: 1350, tti: 20,
-          uplinkCapacity: 5, downlinkCapacity: 20,
-          cwndMultiplier: 1, maxSendingWindow: 2097152,
-        };
-      case 'ws':
-        return { path: '/', host: '', headers: {}, heartbeatPeriod: 0 };
-      case 'grpc':
-        return { serviceName: '', authority: '', multiMode: false };
-      case 'httpupgrade':
-        return { path: '/', host: '', headers: {} };
-      case 'xhttp':
-        return {
-          path: '/', host: '', mode: 'auto', headers: {},
-          xPaddingBytes: '100-1000', scMaxEachPostBytes: '1000000',
-        };
-      default:
-        return {};
+      case 'tcp':         return TcpStreamSettingsSchema.parse({ header: { type: 'none' } });
+      case 'kcp':         return KcpStreamSettingsSchema.parse({});
+      case 'ws':          return WsStreamSettingsSchema.parse({});
+      case 'grpc':        return GrpcStreamSettingsSchema.parse({});
+      case 'httpupgrade': return HttpUpgradeStreamSettingsSchema.parse({});
+      case 'xhttp':       return XHttpStreamSettingsSchema.parse({});
+      default:            return {};
     }
   };
   const onNetworkChange = (next: string) => {