Files
l-ui/frontend/src/lib/xray/forms/outbound/outbound-form-helpers.ts
T
Muse 277838d7ca ITEM6 B4: frontend reshaping — xray page retired, stats switches to settings, template read paths closed
- Delete the xray settings page (routes/menu/title), geodata UI/service,
  XraySettingController/Service and routing-sync write paths; the stored
  xrayTemplateConfig row loses its last readers (GetXrayConfigTemplate now
  returns the embedded factory template; B1 already deleted the row).
- Settings GeneralTab gains the four trafficStatsSwitches switches
  (settings.trafficStatsSwitches, B1) with the dual warning copy; entity
  AllSetting carries the key end to end (TestTrafficStatsSwitchesPlumbing).
- Tag-directory consumers (panelOutbound, mtproto egress, node/host
  outbound pickers) move from POST /xray/ to GET /server/outboundDirectory.
- Shared Mux/Sockopt forms and balancer helpers relocated to lib/xray;
  converter preserves sniffing ips/domains exclusions into the panel
  sidecar instead of dropping them (B2 gap closed by retention).
- Freedom final-rules DB fix seeders retired; getDefaultJsonConfig
  endpoint removed (no in-repo consumer); locales pruned to referenced
  keys (pages.xray 331 -> 68) plus the five new settings keys.
2026-10-22 23:40:54 +00:00

126 lines
4.0 KiB
TypeScript

import { rawOutboundToFormValues } from '@/lib/xray/outbound-form-adapter';
import { canEnableReality, canEnableTls } from '@/lib/xray/protocol-capabilities';
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',
},
};
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: '',
},
};
}
// Network change cascade: swap the per-network sub-key (tcpSettings,
// wsSettings, etc.) so the DU branch matches. Carry over the security mode
// and its settings (tlsSettings/realitySettings, including SNI serverName)
// when the new network still supports it; otherwise fall back to 'none'.
// Dropping tlsSettings here silently wiped the spoofed SNI on save (#4791).
export function applyNetworkChange(
protocol: string,
prevStream: Record<string, unknown> | undefined,
next: string,
): Record<string, unknown> {
if (next === 'hysteria') return hysteriaStreamSlice();
const stream = prevStream ?? {};
const currentSecurity = (stream.security as string) ?? 'none';
const stillTls = canEnableTls({
protocol,
streamSettings: { network: next, security: currentSecurity },
});
const stillReality = canEnableReality({
protocol,
streamSettings: { network: next, security: currentSecurity },
});
const newSecurity =
currentSecurity === 'tls' && !stillTls
? 'none'
: currentSecurity === 'reality' && !stillReality
? 'none'
: currentSecurity;
const newStream: Record<string, unknown> = { ...newStreamSlice(next), security: newSecurity };
if (newSecurity === 'tls' && stream.tlsSettings) newStream.tlsSettings = stream.tlsSettings;
else if (newSecurity === 'reality' && stream.realitySettings)
newStream.realitySettings = stream.realitySettings;
return newStream;
}
export function buildAddModeValues(): OutboundFormValues {
return rawOutboundToFormValues({});
}