Files
3x-ui/frontend/src/pages/inbounds/list/helpers.ts
T
MHSanaei 61e8bed3e0 refactor(inbounds): remove column sorter from inbound list
Drop the table header sorter on the inbounds page: the sortKey/sortOrder
state, the sortedInbounds memo and onChange handler, the per-column
sorterFor spreads, the SORT_FNS comparator map, and the now-unused
SortKey/SortOrder types. The list renders in DB order.
2026-05-31 22:01:10 +02:00

90 lines
3.3 KiB
TypeScript

import { isSSMultiUser } from '@/lib/xray/protocol-capabilities';
import { coerceInboundJsonField } from '@/models/dbinbound';
import type { DBInboundRecord, StreamHints } from './types';
export function readStreamHints(streamSettings: unknown): StreamHints {
const stream = coerceInboundJsonField(streamSettings) as { network?: string; security?: string };
return {
network: stream.network ?? '',
isTls: stream.security === 'tls',
isReality: stream.security === 'reality',
};
}
// Display label for a network value. All known transports render in
// upper-case for visual consistency with the TCP/UDP/TLS/Reality tags
// already shown alongside; compound names (`httpupgrade`, `splithttp`,
// `xhttp`) get a tiny touch of casing so they don't read as one word.
export function networkLabel(network: string): string {
const n = (network || '').toLowerCase();
if (!n) return 'TCP';
switch (n) {
case 'httpupgrade': return 'HTTPUpgrade';
case 'splithttp': return 'SplitHTTP';
case 'xhttp': return 'XHTTP';
}
return n.toUpperCase();
}
// Returns the underlying L4 protocol for transports whose name isn't
// already TCP/UDP. `kcp` and `quic` both ride on UDP; everything else
// (`ws`, `grpc`, `http`, `httpupgrade`, `xhttp`) is TCP-based and gets
// no extra tag (the transport name implies TCP).
export function networkL4(network: string): 'UDP' | '' {
const n = (network || '').toLowerCase();
if (n === 'kcp' || n === 'quic') return 'UDP';
return '';
}
// Shadowsocks settings.network ("tcp" / "udp" / "tcp,udp") and Tunnel
// settings.allowedNetwork (same shape, different field name) both carry
// the L4 transport list independent of streamSettings. Returns a
// comma-separated label.
export function commaNetworkLabel(raw: string): string {
const parts = (raw || 'tcp').toLowerCase().split(',').map((p) => p.trim()).filter(Boolean);
if (parts.length === 0) return 'TCP';
return parts.map(networkLabel).join(',');
}
export function shadowsocksNetworkLabel(settings: unknown): string {
return commaNetworkLabel(readSettings(settings).network || '');
}
export function tunnelNetworkLabel(settings: unknown): string {
return commaNetworkLabel(readSettings(settings).allowedNetwork || '');
}
// Mixed (socks+http combo) is always TCP at L4; settings.udp=true adds
// UDP-associate support on the same port (SOCKS5 UDP).
export function mixedNetworkLabel(settings: unknown): string {
const st = coerceInboundJsonField(settings) as { udp?: boolean };
return st.udp ? 'TCP,UDP' : 'TCP';
}
export function readSettings(settings: unknown): { method?: string; network?: string; allowedNetwork?: string } {
return coerceInboundJsonField(settings) as { method?: string; network?: string; allowedNetwork?: string };
}
export function isInboundMultiUser(record: { protocol: string; settings: unknown }): boolean {
switch (record.protocol) {
case 'vmess':
case 'vless':
case 'trojan':
case 'hysteria':
return true;
case 'shadowsocks':
return isSSMultiUser({ protocol: 'shadowsocks', settings: readSettings(record.settings) });
default:
return false;
}
}
export function showQrCodeMenu(dbInbound: DBInboundRecord): boolean {
if (dbInbound.isWireguard) return true;
if (dbInbound.isSS) {
return !isSSMultiUser({ protocol: 'shadowsocks', settings: readSettings(dbInbound.settings) });
}
return false;
}