Per user decision 2026-10-21 (reverses the earlier keep call in PANEL-LITE-SPEC.md, now amended): the lite panel never runs a local core, so measuring/probing from the panel host is meaningless. Backend: - delete service/outbound probe implementation (probe_http.go and its tests) and the TestOutbound/TestOutbounds batch prober; outbound traffic accounting (AddTraffic/GetOutboundsTraffic/Reset) unchanged - drop POST /panel/api/xray/testOutbound + testOutbounds routes and the outboundTestUrl setting (get/update plumbing removed; stored rows in existing DBs are inert) - drop POST /panel/api/server/restartXrayService + stopXrayService routes/handlers and the node-sync allowlist entry; the service-level Stop/RestartXrayService stay for internal import/restore flows - node-sync allowlist contract test updated to the new inventory Frontend: - remove test buttons/mode switch/test-all from outbounds tab, card list and table columns; delete TestResultPopover and CountryPill; drop the egress/country/latency columns fed by probe results - remove outboundTestUrl state and save plumbing from useXraySetting and the Basics tab field; drop OutboundTestResult schemas - delete the 15 now-dead i18n keys from all 13 locales (incl. the restart/stop keys orphaned since P6/P7 — i18n-dead-keys test green) - tests: drop the test-URL hook test, re-probe the loopback index mapping through Move-to-top, regenerate API types (gen)
79 lines
2.9 KiB
TypeScript
79 lines
2.9 KiB
TypeScript
import { OutboundProtocols as Protocols } from '@/schemas/primitives';
|
|
import type { 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 showSecurity(security?: string): boolean {
|
|
return security === 'tls' || security === 'reality';
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|