mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-13 01:14:59 +03:00
c5e1e54c75
xray-core opens an XDRIVE stream's storage only when the inbound starts listening, so a Google Drive inbound saved with the form's defaults (no folder, no secrets), a local one without a folder, or a template one whose template xray cannot use stopped xray and every other inbound with it. validateXDriveStorage applies the core's storage rules at save, on inbound add/update and on the xray template's inbounds and outbounds: the service must be local, Google Drive or template; local and Google Drive need a remoteFolder; Google Drive needs its three non-empty secrets; a template must pass newTemplateStorage's checks (put, get, list and delete urls, and a list namesRegex with one capture group). A VLESS client kept its xtls-rprx-vision flow when its inbound moved to a transport that cannot carry it, and GetXrayConfig only honoured DisableFlow, so the server demanded Vision there and refused every client. GetXrayConfig and the outbound form now apply the Vision gate, and that gate now follows the core in the backend, the subscription service and the frontend: TLS/REALITY on raw TCP, or VLESS encryption on any transport (xray runs Vision inside encryption.CommonConn whatever the transport, not only on XHTTP). The outbound modal passes the VLESS encryption into the gate and gates Mux on the effective flow.
107 lines
4.5 KiB
TypeScript
107 lines
4.5 KiB
TypeScript
// Pure-function ports of the legacy Inbound class capability predicates
|
|
// (canEnableTls, canEnableReality, canEnableTlsFlow, canEnableStream,
|
|
// canEnableVisionSeed, isSS2022). Each accepts the minimal
|
|
// slice of an InboundFormValues it needs, so the same predicate can be
|
|
// called against a partial-row, a full form value, or a hand-built test
|
|
// fixture without the caller projecting a whole object.
|
|
|
|
const TLS_ELIGIBLE_PROTOCOLS = ['vmess', 'vless', 'trojan', 'shadowsocks'];
|
|
const TLS_NETWORKS = ['tcp', 'ws', 'http', 'grpc', 'httpupgrade', 'xhttp'];
|
|
const REALITY_ELIGIBLE_PROTOCOLS = ['vless', 'trojan'];
|
|
const REALITY_NETWORKS = ['tcp', 'http', 'grpc', 'xhttp'];
|
|
const STREAM_PROTOCOLS = [
|
|
'vmess',
|
|
'vless',
|
|
'trojan',
|
|
'shadowsocks',
|
|
'hysteria',
|
|
'wireguard',
|
|
'tunnel',
|
|
'masque',
|
|
];
|
|
const VISION_FLOW = 'xtls-rprx-vision';
|
|
const SS_2022_PREFIX = '2022';
|
|
|
|
export interface CapabilityProtocolSlice {
|
|
protocol: string;
|
|
settings?: { encryption?: string; decryption?: string };
|
|
streamSettings?: { network?: string; security?: string };
|
|
}
|
|
|
|
export interface CapabilityVlessSlice extends CapabilityProtocolSlice {
|
|
settings?: { encryption?: string; decryption?: string; clients?: { flow?: string }[] };
|
|
}
|
|
|
|
export interface CapabilityShadowsocksSlice extends CapabilityProtocolSlice {
|
|
settings?: { encryption?: string; method?: string };
|
|
}
|
|
|
|
export function canEnableTls(values: CapabilityProtocolSlice): boolean {
|
|
if (values.protocol === 'hysteria' || values.protocol === 'masque') return true;
|
|
if (!TLS_ELIGIBLE_PROTOCOLS.includes(values.protocol)) return false;
|
|
return TLS_NETWORKS.includes(values.streamSettings?.network ?? '');
|
|
}
|
|
|
|
// An XDRIVE outbound's tlsSettings shape only its HTTPS calls to the storage API (SNI, uTLS
|
|
// fingerprint); xray-core 26.10.10 reads them on the dial side, never on a listener.
|
|
export function canEnableOutboundTls(values: CapabilityProtocolSlice): boolean {
|
|
if (canEnableTls(values)) return true;
|
|
return (
|
|
TLS_ELIGIBLE_PROTOCOLS.includes(values.protocol) && values.streamSettings?.network === 'xdrive'
|
|
);
|
|
}
|
|
|
|
export function canEnableReality(values: CapabilityProtocolSlice): boolean {
|
|
if (!REALITY_ELIGIBLE_PROTOCOLS.includes(values.protocol)) return false;
|
|
return REALITY_NETWORKS.includes(values.streamSettings?.network ?? '');
|
|
}
|
|
|
|
// VLESS encryption (vlessenc / ML-KEM) is on when encryption or decryption holds
|
|
// a generated value (e.g. "mlkem768x25519plus.native.0rtt.<key>") rather than
|
|
// the "none"/"" sentinel. The value is never the literal "vlessenc" (that is the
|
|
// `xray vlessenc` subcommand). decryption is the server-side value; encryption is
|
|
// stored for link generation — either being set means it is on.
|
|
function hasVlessEncryption(settings: CapabilityProtocolSlice['settings']): boolean {
|
|
const isSet = (v?: string) => v != null && v !== '' && v !== 'none';
|
|
return isSet(settings?.encryption) || isSet(settings?.decryption);
|
|
}
|
|
|
|
export function canEnableTlsFlow(values: CapabilityProtocolSlice): boolean {
|
|
if (values.protocol !== 'vless') return false;
|
|
const network = values.streamSettings?.network;
|
|
const security = values.streamSettings?.security;
|
|
|
|
// Classic XTLS Vision: raw TCP carried over TLS or REALITY.
|
|
if (network === 'tcp' && (security === 'tls' || security === 'reality')) return true;
|
|
|
|
// xray-core runs Vision inside VLESS encryption (encryption.CommonConn) on any transport.
|
|
return hasVlessEncryption(values.settings);
|
|
}
|
|
|
|
export function canEnableStream(values: { protocol: string }): boolean {
|
|
return STREAM_PROTOCOLS.includes(values.protocol);
|
|
}
|
|
|
|
// mtproto and amneziawg are served by an external process/interface, not
|
|
// Xray, so the Xray sniffing block does not apply to either. Every other
|
|
// inbound supports sniffing.
|
|
export function canEnableSniffing(values: { protocol: string }): boolean {
|
|
return (
|
|
values.protocol !== 'mtproto' && values.protocol !== 'amneziawg' && values.protocol !== 'tuic'
|
|
);
|
|
}
|
|
|
|
// Vision seed applies only when XTLS Vision (TCP/TLS) flow is selected
|
|
// AND at least one VLESS client uses the vision flow. Excludes UDP variant.
|
|
export function canEnableVisionSeed(values: CapabilityVlessSlice): boolean {
|
|
if (!canEnableTlsFlow(values)) return false;
|
|
const clients = values.settings?.clients;
|
|
if (!Array.isArray(clients)) return false;
|
|
return clients.some((c) => c?.flow === VISION_FLOW);
|
|
}
|
|
|
|
export function isSS2022(values: CapabilityShadowsocksSlice): boolean {
|
|
const method = values.protocol === 'shadowsocks' ? (values.settings?.method ?? '') : '';
|
|
return method.substring(0, 4) === SS_2022_PREFIX;
|
|
}
|