mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 12:44:22 +00:00
refactor(frontend): extract OutboundFormModal constants & stream helpers
OutboundFormModal.tsx 2238 -> 2080. Moved the pure option arrays/sets and the stream-slice helpers (newStreamSlice, hysteriaStreamSlice, isMuxAllowed, buildAddModeValues) into outbound-form-constants.ts and outbound-form-helpers.ts. Per-protocol render snapshots unchanged -> verified no behavior change. typecheck/lint/build green.
This commit is contained in:
parent
c24b7d9da3
commit
1ca0e10151
3 changed files with 146 additions and 117 deletions
|
|
@ -36,18 +36,11 @@ import {
|
|||
type OutboundFormValues,
|
||||
} from '@/schemas/forms/outbound-form';
|
||||
import {
|
||||
ALPN_OPTION,
|
||||
Address_Port_Strategy,
|
||||
DNSRuleActions,
|
||||
DOMAIN_STRATEGY_OPTION,
|
||||
MODE_OPTION,
|
||||
OutboundDomainStrategies,
|
||||
OutboundProtocols as Protocols,
|
||||
SNIFFING_OPTION,
|
||||
TCP_CONGESTION_OPTION,
|
||||
TLS_FLOW_CONTROL,
|
||||
USERS_SECURITY,
|
||||
UTLS_FINGERPRINT,
|
||||
WireguardDomainStrategy,
|
||||
} from '@/schemas/primitives';
|
||||
import {
|
||||
|
|
@ -62,6 +55,26 @@ import {
|
|||
} from '@/lib/xray/protocol-capabilities';
|
||||
import { SSMethodSchema } from '@/schemas/protocols/shared/shadowsocks';
|
||||
import { antdRule } from '@/utils/zodForm';
|
||||
|
||||
import {
|
||||
ADDRESS_PORT_STRATEGY_OPTIONS,
|
||||
ALPN_OPTIONS,
|
||||
FLOW_OPTIONS,
|
||||
HYSTERIA_NETWORK_OPTION,
|
||||
MODE_OPTIONS,
|
||||
NETWORK_OPTIONS,
|
||||
PROTOCOL_OPTIONS,
|
||||
SECURITY_OPTIONS,
|
||||
SERVER_PROTOCOLS,
|
||||
SS_METHOD_OPTIONS,
|
||||
UTLS_OPTIONS,
|
||||
} from './outbound-form-constants';
|
||||
import {
|
||||
buildAddModeValues,
|
||||
hysteriaStreamSlice,
|
||||
isMuxAllowed,
|
||||
newStreamSlice,
|
||||
} from './outbound-form-helpers';
|
||||
import './OutboundFormModal.css';
|
||||
|
||||
// Pattern A rewrite of OutboundFormModal. Built as a sibling `.new.tsx`
|
||||
|
|
@ -77,116 +90,6 @@ interface OutboundFormModalProps {
|
|||
onConfirm: (outbound: Record<string, unknown>) => void;
|
||||
}
|
||||
|
||||
const PROTOCOL_OPTIONS = Object.values(Protocols).map((p) => ({ value: p, label: p }));
|
||||
const SECURITY_OPTIONS = Object.values(USERS_SECURITY).map((v) => ({ value: v, label: v }));
|
||||
const FLOW_OPTIONS = Object.values(TLS_FLOW_CONTROL).map((v) => ({ value: v, label: v }));
|
||||
const SS_METHOD_OPTIONS = SSMethodSchema.options.map((v) => ({ value: v, label: v }));
|
||||
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: 'RAW' },
|
||||
{ value: 'kcp', label: 'mKCP' },
|
||||
{ value: 'ws', label: 'WebSocket' },
|
||||
{ value: 'grpc', label: 'gRPC' },
|
||||
{ value: 'httpupgrade', label: 'HTTPUpgrade' },
|
||||
{ value: 'xhttp', label: 'XHTTP' },
|
||||
];
|
||||
|
||||
// The hysteria protocol is locked to its own QUIC transport: the selector
|
||||
// shows only this option when the parent protocol is hysteria.
|
||||
const HYSTERIA_NETWORK_OPTION = { value: 'hysteria', label: 'Hysteria' };
|
||||
|
||||
// Per-network bootstrap. Mirrors the legacy class constructors so the
|
||||
// initial state for each transport matches what xray-core expects.
|
||||
function newStreamSlice(network: string): Record<string, unknown> {
|
||||
switch (network) {
|
||||
case 'tcp':
|
||||
return { network: 'tcp', tcpSettings: { header: { type: 'none' } } };
|
||||
case 'kcp':
|
||||
return {
|
||||
network: 'kcp',
|
||||
kcpSettings: {
|
||||
mtu: 1350, tti: 20, uplinkCapacity: 5, downlinkCapacity: 20,
|
||||
cwndMultiplier: 1, maxSendingWindow: 2097152,
|
||||
},
|
||||
};
|
||||
case 'ws':
|
||||
return {
|
||||
network: 'ws',
|
||||
wsSettings: { path: '/', host: '', headers: {}, heartbeatPeriod: 0 },
|
||||
};
|
||||
case 'grpc':
|
||||
return {
|
||||
network: 'grpc',
|
||||
grpcSettings: { serviceName: '', authority: '', multiMode: false },
|
||||
};
|
||||
case 'httpupgrade':
|
||||
return {
|
||||
network: 'httpupgrade',
|
||||
httpupgradeSettings: { path: '/', host: '', headers: {} },
|
||||
};
|
||||
case 'xhttp':
|
||||
return {
|
||||
network: 'xhttp',
|
||||
xhttpSettings: {
|
||||
path: '/', host: '', mode: '', headers: [],
|
||||
xPaddingBytes: '100-1000', scMaxEachPostBytes: '1000000',
|
||||
},
|
||||
};
|
||||
case 'hysteria':
|
||||
return {
|
||||
network: 'hysteria',
|
||||
hysteriaSettings: {
|
||||
version: 2,
|
||||
auth: '',
|
||||
udpIdleTimeout: 60,
|
||||
},
|
||||
};
|
||||
default:
|
||||
return { network: 'tcp', tcpSettings: { header: { type: 'none' } } };
|
||||
}
|
||||
}
|
||||
|
||||
// Hysteria2 always rides its own QUIC transport with TLS — the panel never
|
||||
// offers another transport or 'none' security for it.
|
||||
function hysteriaStreamSlice(): Record<string, unknown> {
|
||||
return {
|
||||
...newStreamSlice('hysteria'),
|
||||
security: 'tls',
|
||||
tlsSettings: {
|
||||
serverName: '', alpn: ['h3'], fingerprint: '',
|
||||
echConfigList: '', verifyPeerCertByName: '', pinnedPeerCertSha256: '',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Protocols whose form schema carries a flat connect target — these all
|
||||
// get the shared "server" sub-block (address + port) at the top of the
|
||||
// protocol section. Wireguard has an address but no port. DNS/freedom/
|
||||
// blackhole/loopback have no connect target.
|
||||
const SERVER_PROTOCOLS = new Set<string>([
|
||||
'vmess', 'vless', 'trojan', 'shadowsocks', 'socks', 'http', 'hysteria',
|
||||
]);
|
||||
|
||||
function buildAddModeValues(): OutboundFormValues {
|
||||
return rawOutboundToFormValues({});
|
||||
}
|
||||
|
||||
export default function OutboundFormModal({
|
||||
open,
|
||||
|
|
|
|||
47
frontend/src/pages/xray/outbounds/outbound-form-constants.ts
Normal file
47
frontend/src/pages/xray/outbounds/outbound-form-constants.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import {
|
||||
ALPN_OPTION,
|
||||
Address_Port_Strategy,
|
||||
MODE_OPTION,
|
||||
OutboundProtocols as Protocols,
|
||||
TLS_FLOW_CONTROL,
|
||||
USERS_SECURITY,
|
||||
UTLS_FINGERPRINT,
|
||||
} from '@/schemas/primitives';
|
||||
import { SSMethodSchema } from '@/schemas/protocols/shared/shadowsocks';
|
||||
|
||||
export const PROTOCOL_OPTIONS = Object.values(Protocols).map((p) => ({ value: p, label: p }));
|
||||
export const SECURITY_OPTIONS = Object.values(USERS_SECURITY).map((v) => ({ value: v, label: v }));
|
||||
export const FLOW_OPTIONS = Object.values(TLS_FLOW_CONTROL).map((v) => ({ value: v, label: v }));
|
||||
export const SS_METHOD_OPTIONS = SSMethodSchema.options.map((v) => ({ value: v, label: v }));
|
||||
export const MODE_OPTIONS = Object.values(MODE_OPTION).map((v) => ({ value: v, label: v }));
|
||||
export const UTLS_OPTIONS = Object.values(UTLS_FINGERPRINT).map((v) => ({ value: v, label: v }));
|
||||
export const ALPN_OPTIONS = Object.values(ALPN_OPTION).map((v) => ({ value: v, label: v }));
|
||||
export 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.
|
||||
export const MUX_PROTOCOLS = new Set<string>(['vmess', 'vless', 'trojan', 'shadowsocks', 'http', 'socks']);
|
||||
|
||||
export const NETWORK_OPTIONS: { value: string; label: string }[] = [
|
||||
{ value: 'tcp', label: 'RAW' },
|
||||
{ value: 'kcp', label: 'mKCP' },
|
||||
{ value: 'ws', label: 'WebSocket' },
|
||||
{ value: 'grpc', label: 'gRPC' },
|
||||
{ value: 'httpupgrade', label: 'HTTPUpgrade' },
|
||||
{ value: 'xhttp', label: 'XHTTP' },
|
||||
];
|
||||
|
||||
// The hysteria protocol is locked to its own QUIC transport: the selector
|
||||
// shows only this option when the parent protocol is hysteria.
|
||||
export const HYSTERIA_NETWORK_OPTION = { value: 'hysteria', label: 'Hysteria' };
|
||||
|
||||
// Protocols whose form schema carries a flat connect target — these all
|
||||
// get the shared "server" sub-block (address + port) at the top of the
|
||||
// protocol section. Wireguard has an address but no port. DNS/freedom/
|
||||
// blackhole/loopback have no connect target.
|
||||
export const SERVER_PROTOCOLS = new Set<string>([
|
||||
'vmess', 'vless', 'trojan', 'shadowsocks', 'socks', 'http', 'hysteria',
|
||||
]);
|
||||
79
frontend/src/pages/xray/outbounds/outbound-form-helpers.ts
Normal file
79
frontend/src/pages/xray/outbounds/outbound-form-helpers.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import { rawOutboundToFormValues } from '@/lib/xray/outbound-form-adapter';
|
||||
import type { OutboundFormValues } from '@/schemas/forms/outbound-form';
|
||||
|
||||
import { MUX_PROTOCOLS } from './outbound-form-constants';
|
||||
|
||||
export 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;
|
||||
}
|
||||
|
||||
// Per-network bootstrap. Mirrors the legacy class constructors so the
|
||||
// initial state for each transport matches what xray-core expects.
|
||||
export function newStreamSlice(network: string): Record<string, unknown> {
|
||||
switch (network) {
|
||||
case 'tcp':
|
||||
return { network: 'tcp', tcpSettings: { header: { type: 'none' } } };
|
||||
case 'kcp':
|
||||
return {
|
||||
network: 'kcp',
|
||||
kcpSettings: {
|
||||
mtu: 1350, tti: 20, uplinkCapacity: 5, downlinkCapacity: 20,
|
||||
cwndMultiplier: 1, maxSendingWindow: 2097152,
|
||||
},
|
||||
};
|
||||
case 'ws':
|
||||
return {
|
||||
network: 'ws',
|
||||
wsSettings: { path: '/', host: '', headers: {}, heartbeatPeriod: 0 },
|
||||
};
|
||||
case 'grpc':
|
||||
return {
|
||||
network: 'grpc',
|
||||
grpcSettings: { serviceName: '', authority: '', multiMode: false },
|
||||
};
|
||||
case 'httpupgrade':
|
||||
return {
|
||||
network: 'httpupgrade',
|
||||
httpupgradeSettings: { path: '/', host: '', headers: {} },
|
||||
};
|
||||
case 'xhttp':
|
||||
return {
|
||||
network: 'xhttp',
|
||||
xhttpSettings: {
|
||||
path: '/', host: '', mode: '', headers: [],
|
||||
xPaddingBytes: '100-1000', scMaxEachPostBytes: '1000000',
|
||||
},
|
||||
};
|
||||
case 'hysteria':
|
||||
return {
|
||||
network: 'hysteria',
|
||||
hysteriaSettings: {
|
||||
version: 2,
|
||||
auth: '',
|
||||
udpIdleTimeout: 60,
|
||||
},
|
||||
};
|
||||
default:
|
||||
return { network: 'tcp', tcpSettings: { header: { type: 'none' } } };
|
||||
}
|
||||
}
|
||||
|
||||
// Hysteria2 always rides its own QUIC transport with TLS — the panel never
|
||||
// offers another transport or 'none' security for it.
|
||||
export function hysteriaStreamSlice(): Record<string, unknown> {
|
||||
return {
|
||||
...newStreamSlice('hysteria'),
|
||||
security: 'tls',
|
||||
tlsSettings: {
|
||||
serverName: '', alpn: ['h3'], fingerprint: '',
|
||||
echConfigList: '', verifyPeerCertByName: '', pinnedPeerCertSha256: '',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function buildAddModeValues(): OutboundFormValues {
|
||||
return rawOutboundToFormValues({});
|
||||
}
|
||||
Loading…
Reference in a new issue