소스 검색

feat(frontend): OutboundFormModal.new.tsx sockopt + mux sections

- Sockopts: Switch toggles streamSettings.sockopt between undefined and
  a populated default object (17 fields with sane bbr/UseIP defaults).
  Only the 8 most-used fields are rendered (dialer proxy, domain
  strategy, keep alive interval, TFO, MPTCP, penetrate, mark, interface).
  The remaining sockopt knobs (acceptProxyProtocol, tcpUserTimeout,
  tcpcongestion, tcpKeepAliveIdle, tcpMaxSeg, tcpWindowClamp, V6Only,
  trustedXForwardedFor, tproxy) are still in the wire payload — edit
  them via the JSON tab.

- Mux: gated by isMuxAllowed(protocol, flow, network) — VMess/VLESS/
  Trojan/SS/HTTP/SOCKS, no flow set, no xhttp transport. Sub-fields
  (concurrency / xudpConcurrency / xudpProxyUDP443) only render when
  enabled is true.

- Sockopt section visible only when streamAllowed AND network is set —
  non-stream protocols (freedom/blackhole/dns/loopback) still edit
  sockopt via the JSON tab.
MHSanaei 9 시간 전
부모
커밋
7765fb39fe
1개의 변경된 파일168개의 추가작업 그리고 0개의 파일을 삭제
  1. 168 0
      frontend/src/pages/xray/OutboundFormModal.new.tsx

+ 168 - 0
frontend/src/pages/xray/OutboundFormModal.new.tsx

@@ -32,6 +32,7 @@ import {
 } from '@/schemas/forms/outbound-form';
 import {
   ALPN_OPTION,
+  Address_Port_Strategy,
   DNSRuleActions,
   MODE_OPTION,
   OutboundDomainStrategies,
@@ -72,6 +73,20 @@ const SS_METHOD_OPTIONS = SSMethodSchema.options.map((v) => ({ value: v, label:
 const MODE_OPTIONS = Object.values(MODE_OPTION).map((v) => ({ value: v, label: v }));
 const UTLS_OPTIONS = Object.values(UTLS_FINGERPRINT).map((v) => ({ value: v, label: v }));
 const ALPN_OPTIONS = Object.values(ALPN_OPTION).map((v) => ({ value: v, label: v }));
+const ADDRESS_PORT_STRATEGY_OPTIONS = Object.values(Address_Port_Strategy).map((v) => ({
+  value: v,
+  label: v,
+}));
+
+// canEnableMux mirrors the adapter's helper but lives here so the modal
+// can show/hide the Mux section without going through the adapter.
+const MUX_PROTOCOLS = new Set<string>(['vmess', 'vless', 'trojan', 'shadowsocks', 'http', 'socks']);
+function isMuxAllowed(protocol: string, flow: string, network: string): boolean {
+  if (!MUX_PROTOCOLS.has(protocol)) return false;
+  if (protocol === 'vless' && flow) return false;
+  if (network === 'xhttp') return false;
+  return true;
+}
 
 const NETWORK_OPTIONS: { value: string; label: string }[] = [
   { value: 'tcp', label: 'TCP (RAW)' },
@@ -1289,6 +1304,159 @@ export default function OutboundFormModalNew({
                         </Form.Item>
                       </>
                     )}
+
+                    {streamAllowed && network && (
+                      <Form.Item shouldUpdate noStyle>
+                        {() => {
+                          const hasSockopt = !!form.getFieldValue([
+                            'streamSettings',
+                            'sockopt',
+                          ]);
+                          return (
+                            <>
+                              <Form.Item label="Sockopts">
+                                <Switch
+                                  checked={hasSockopt}
+                                  onChange={(checked) => {
+                                    form.setFieldValue(
+                                      ['streamSettings', 'sockopt'],
+                                      checked
+                                        ? {
+                                            acceptProxyProtocol: false,
+                                            tcpFastOpen: false,
+                                            mark: 0,
+                                            tproxy: 'off',
+                                            tcpMptcp: false,
+                                            penetrate: false,
+                                            domainStrategy: 'UseIP',
+                                            tcpMaxSeg: 1440,
+                                            dialerProxy: '',
+                                            tcpKeepAliveInterval: 0,
+                                            tcpKeepAliveIdle: 300,
+                                            tcpUserTimeout: 10000,
+                                            tcpcongestion: 'bbr',
+                                            V6Only: false,
+                                            tcpWindowClamp: 600,
+                                            interfaceName: '',
+                                            trustedXForwardedFor: [],
+                                          }
+                                        : undefined,
+                                    );
+                                  }}
+                                />
+                              </Form.Item>
+                              {hasSockopt && (
+                                <>
+                                  <Form.Item
+                                    label="Dialer proxy"
+                                    name={['streamSettings', 'sockopt', 'dialerProxy']}
+                                  >
+                                    <Input />
+                                  </Form.Item>
+                                  <Form.Item
+                                    label="Domain strategy"
+                                    name={['streamSettings', 'sockopt', 'domainStrategy']}
+                                  >
+                                    <Select
+                                      options={ADDRESS_PORT_STRATEGY_OPTIONS}
+                                    />
+                                  </Form.Item>
+                                  <Form.Item
+                                    label="Keep alive interval"
+                                    name={['streamSettings', 'sockopt', 'tcpKeepAliveInterval']}
+                                  >
+                                    <InputNumber min={0} />
+                                  </Form.Item>
+                                  <Form.Item
+                                    label="TCP Fast Open"
+                                    name={['streamSettings', 'sockopt', 'tcpFastOpen']}
+                                    valuePropName="checked"
+                                  >
+                                    <Switch />
+                                  </Form.Item>
+                                  <Form.Item
+                                    label="Multipath TCP"
+                                    name={['streamSettings', 'sockopt', 'tcpMptcp']}
+                                    valuePropName="checked"
+                                  >
+                                    <Switch />
+                                  </Form.Item>
+                                  <Form.Item
+                                    label="Penetrate"
+                                    name={['streamSettings', 'sockopt', 'penetrate']}
+                                    valuePropName="checked"
+                                  >
+                                    <Switch />
+                                  </Form.Item>
+                                  <Form.Item
+                                    label="Mark (fwmark)"
+                                    name={['streamSettings', 'sockopt', 'mark']}
+                                  >
+                                    <InputNumber min={0} />
+                                  </Form.Item>
+                                  <Form.Item
+                                    label="Interface"
+                                    name={['streamSettings', 'sockopt', 'interfaceName']}
+                                  >
+                                    <Input />
+                                  </Form.Item>
+                                </>
+                              )}
+                            </>
+                          );
+                        }}
+                      </Form.Item>
+                    )}
+
+                    {(() => {
+                      const flow = (form.getFieldValue(['settings', 'flow']) ?? '') as string;
+                      if (!isMuxAllowed(protocol, flow, network)) return null;
+                      return (
+                        <Form.Item shouldUpdate noStyle>
+                          {() => {
+                            const muxEnabled = !!form.getFieldValue(['mux', 'enabled']);
+                            return (
+                              <>
+                                <Form.Item
+                                  label={t('pages.settings.mux')}
+                                  name={['mux', 'enabled']}
+                                  valuePropName="checked"
+                                >
+                                  <Switch />
+                                </Form.Item>
+                                {muxEnabled && (
+                                  <>
+                                    <Form.Item
+                                      label="Concurrency"
+                                      name={['mux', 'concurrency']}
+                                    >
+                                      <InputNumber min={-1} max={1024} />
+                                    </Form.Item>
+                                    <Form.Item
+                                      label="xudp concurrency"
+                                      name={['mux', 'xudpConcurrency']}
+                                    >
+                                      <InputNumber min={-1} max={1024} />
+                                    </Form.Item>
+                                    <Form.Item
+                                      label="xudp UDP 443"
+                                      name={['mux', 'xudpProxyUDP443']}
+                                    >
+                                      <Select
+                                        options={['reject', 'allow', 'skip'].map((v) => ({
+                                          value: v,
+                                          label: v,
+                                        }))}
+                                      />
+                                    </Form.Item>
+                                  </>
+                                )}
+                              </>
+                            );
+                          }}
+                        </Form.Item>
+                      );
+                    })()}
                   </>
                 ),
               },