| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- import type { TFunction } from 'i18next';
- import { OutboundProtocols as Protocols } from '@/schemas/primitives';
- import { isUdpOutbound } from '@/hooks/useXraySetting';
- import type { OutboundTestMode, OutboundTestState, OutboundTrafficRow } from '@/hooks/useXraySetting';
- import type { OutboundRow } from './outbounds-tab-types';
- /**
- * Translate a table row's positional index into that outbound's index in the
- * full, unfiltered outbounds array. The table hides balancer-loopback outbounds
- * but keeps each visible row's original index in `key`, so any handler that
- * mutates the outbounds array (or probes an outbound by index) must map the
- * positional index back through `key` or it operates on the wrong outbound once
- * a hidden loopback precedes it.
- */
- export function originalOutboundIndex(rows: OutboundRow[], positionalIndex: number): number {
- const row = rows[positionalIndex];
- return row ? row.key : positionalIndex;
- }
- export function outboundAddresses(o: OutboundRow): string[] {
- const settings = o.settings as Record<string, unknown> | undefined;
- switch (o.protocol) {
- case Protocols.VMess: {
- const serverObj = settings?.vnext as Array<{ address: string; port: number }> | undefined;
- return serverObj ? serverObj.map((s) => `${s.address}:${s.port}`) : [];
- }
- case Protocols.VLESS:
- return [`${settings?.address || ''}:${settings?.port || ''}`];
- case Protocols.HTTP:
- case Protocols.Socks:
- case Protocols.Shadowsocks:
- case Protocols.Trojan: {
- const serverObj = settings?.servers as Array<{ address: string; port: number }> | undefined;
- return serverObj ? serverObj.map((s) => `${s.address}:${s.port}`) : [];
- }
- case Protocols.DNS: {
- const addr = (settings?.rewriteAddress as string) || (settings?.address as string) || '';
- const port = (settings?.rewritePort as string | number) || (settings?.port as string | number) || '';
- return addr || port ? [`${addr}:${port}`] : [];
- }
- case Protocols.Wireguard:
- return (((settings?.peers as Array<{ endpoint?: string }>) || []).map((p) => p.endpoint || '').filter(Boolean));
- default:
- return [];
- }
- }
- export function isUntestable(o: OutboundRow): boolean {
- if (!o) return true;
- if (o.protocol === Protocols.Blackhole || o.protocol === Protocols.Loopback || o.tag === 'blocked') return true;
- // freedom ("direct") and dns aren't proxies — a TCP dial has no endpoint and
- // an HTTP probe would only measure the host's own direct reachability, so
- // they're untestable in every mode.
- if (o.protocol === Protocols.Freedom || o.protocol === Protocols.DNS) return true;
- return false;
- }
- export function showSecurity(security?: string): boolean {
- return security === 'tls' || security === 'reality';
- }
- export function effectiveTestMode(o: unknown, mode: OutboundTestMode): OutboundTestMode {
- return mode === 'tcp' && isUdpOutbound(o) ? 'http' : mode;
- }
- export function testModeLabel(mode: string, t: TFunction): string {
- return mode === 'real' ? t('pages.xray.outbound.modeRealDelay') : mode.toUpperCase();
- }
- export function trafficFor(outboundsTraffic: OutboundTrafficRow[], o: OutboundRow): { up: number; down: number } {
- const tr = outboundsTraffic.find((x) => x.tag === o.tag);
- return { up: tr?.up || 0, down: tr?.down || 0 };
- }
- export function countryFlag(country?: string): string {
- const code = (country || '').trim().toUpperCase();
- if (!/^[A-Z]{2}$/.test(code)) return '';
- return String.fromCodePoint(...[...code].map((ch) => 0x1f1e6 + ch.charCodeAt(0) - 65));
- }
- export function countryName(country?: string, locale?: string): string {
- const code = (country || '').trim().toUpperCase();
- if (!/^[A-Z]{2}$/.test(code)) return '';
- try {
- return new Intl.DisplayNames(locale ? [locale] : undefined, { type: 'region' }).of(code) || code;
- } catch {
- return code;
- }
- }
- export function isTesting<K extends string | number>(states: Record<K, OutboundTestState>, idx: K): boolean {
- return !!states?.[idx]?.testing;
- }
- export function testResult<K extends string | number>(states: Record<K, OutboundTestState>, idx: K) {
- return states?.[idx]?.result || null;
- }
|