| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- import { getMessage } from '@/utils/messageBus';
- import { ColorUtils, ClipboardManager, FileManager } from '@/utils';
- import { Protocols } from '@/schemas/primitives';
- import { coerceInboundJsonField } from '@/models/dbinbound';
- import {
- canEnableTlsFlow,
- isSS2022 as isSS2022Helper,
- isSSMultiUser as isSSMultiUserHelper,
- } from '@/lib/xray/protocol-capabilities';
- import type { ClientSetting, ClientStats, DBInboundLike, InboundInfo } from './types';
- const LINK_PROTOCOLS: ReadonlySet<string> = new Set([
- Protocols.VMESS,
- Protocols.VLESS,
- Protocols.TROJAN,
- Protocols.SHADOWSOCKS,
- Protocols.HYSTERIA,
- ]);
- export function hasShareLink(protocol: string): boolean {
- return LINK_PROTOCOLS.has(protocol);
- }
- function readHeader(headers: unknown, name: string): string {
- const needle = name.toLowerCase();
- if (Array.isArray(headers)) {
- for (const h of headers) {
- if (h && typeof h === 'object' && String((h as { name?: string }).name ?? '').toLowerCase() === needle) {
- return String((h as { value?: unknown }).value ?? '');
- }
- }
- return '';
- }
- if (headers && typeof headers === 'object') {
- for (const [k, v] of Object.entries(headers as Record<string, unknown>)) {
- if (k.toLowerCase() === needle) {
- return Array.isArray(v) ? String(v[0] ?? '') : String(v ?? '');
- }
- }
- }
- return '';
- }
- function readNetworkHost(stream: Record<string, unknown>, network: string): string | null {
- switch (network) {
- case 'tcp': {
- const tcp = stream.tcpSettings as { header?: { request?: { headers?: unknown } } } | undefined;
- return readHeader(tcp?.header?.request?.headers, 'host');
- }
- case 'ws': {
- const ws = stream.wsSettings as { host?: string; headers?: unknown } | undefined;
- return (ws?.host && ws.host.length > 0) ? ws.host : readHeader(ws?.headers, 'host');
- }
- case 'httpupgrade': {
- const hu = stream.httpupgradeSettings as { host?: string; headers?: unknown } | undefined;
- return (hu?.host && hu.host.length > 0) ? hu.host : readHeader(hu?.headers, 'host');
- }
- case 'xhttp': {
- const xh = stream.xhttpSettings as { host?: string; headers?: unknown } | undefined;
- return (xh?.host && xh.host.length > 0) ? xh.host : readHeader(xh?.headers, 'host');
- }
- default:
- return null;
- }
- }
- function readNetworkPath(stream: Record<string, unknown>, network: string): string | null {
- switch (network) {
- case 'tcp': {
- const tcp = stream.tcpSettings as { header?: { request?: { path?: string[] } } } | undefined;
- return tcp?.header?.request?.path?.[0] ?? null;
- }
- case 'ws':
- return (stream.wsSettings as { path?: string } | undefined)?.path ?? null;
- case 'httpupgrade':
- return (stream.httpupgradeSettings as { path?: string } | undefined)?.path ?? null;
- case 'xhttp':
- return (stream.xhttpSettings as { path?: string } | undefined)?.path ?? null;
- default:
- return null;
- }
- }
- export function buildInboundInfo(dbInbound: DBInboundLike): InboundInfo {
- const settings = coerceInboundJsonField(dbInbound.settings) as Record<string, unknown>;
- const stream = coerceInboundJsonField(dbInbound.streamSettings) as Record<string, unknown>;
- const network = (stream.network as string | undefined) ?? '';
- const security = (stream.security as string | undefined) ?? 'none';
- const clients = Array.isArray(settings.clients) ? (settings.clients as ClientSetting[]) : [];
- const xhttpSettings = stream.xhttpSettings as { mode?: string } | undefined;
- const grpcSettings = stream.grpcSettings as { multiMode?: boolean; serviceName?: string } | undefined;
- let serverName = '';
- if (security === 'tls') {
- const tls = stream.tlsSettings as { sni?: string; serverName?: string } | undefined;
- serverName = tls?.sni ?? tls?.serverName ?? '';
- } else if (security === 'reality') {
- const reality = stream.realitySettings as { serverNames?: string[]; serverName?: string } | undefined;
- if (Array.isArray(reality?.serverNames)) {
- serverName = reality.serverNames.join(', ');
- } else if (reality?.serverName) {
- serverName = reality.serverName;
- }
- }
- return {
- protocol: dbInbound.protocol,
- clients,
- settings,
- isTcp: network === 'tcp',
- isWs: network === 'ws',
- isHttpupgrade: network === 'httpupgrade',
- isXHTTP: network === 'xhttp',
- isGrpc: network === 'grpc',
- isSSMultiUser: isSSMultiUserHelper({
- protocol: dbInbound.protocol,
- settings: settings as { method?: string },
- }),
- isSS2022: isSS2022Helper({
- protocol: dbInbound.protocol,
- settings: settings as { method?: string },
- }),
- isVlessTlsFlow: canEnableTlsFlow({
- protocol: dbInbound.protocol,
- streamSettings: { network, security },
- }),
- host: readNetworkHost(stream, network),
- path: readNetworkPath(stream, network),
- serviceName: grpcSettings?.serviceName ?? '',
- serverName,
- stream: {
- network,
- security,
- xhttp: xhttpSettings ? { mode: xhttpSettings.mode } : undefined,
- grpc: grpcSettings ? { multiMode: grpcSettings.multiMode } : undefined,
- },
- };
- }
- export function copyText(value: unknown, t: (k: string) => string) {
- ClipboardManager.copyText(String(value ?? '')).then((ok) => {
- if (ok) getMessage().success(t('copied'));
- });
- }
- export function downloadText(content: string, filename: string) {
- FileManager.downloadTextFile(content, filename);
- }
- export function statsColor(stats: ClientStats, trafficDiff: number) {
- return ColorUtils.usageColor(stats.up + stats.down, trafficDiff, stats.total);
- }
- export function formatIpInfo(record: unknown) {
- if (record == null) return '';
- if (typeof record === 'string' || typeof record === 'number') return String(record);
- const r = record as { ip?: string; IP?: string; timestamp?: number | string; Timestamp?: number | string };
- const ip = r.ip || r.IP || '';
- const ts = r.timestamp || r.Timestamp || 0;
- if (!ip) return String(record);
- if (!ts) return String(ip);
- const date = new Date(Number(ts) * 1000);
- const timeStr = date
- .toLocaleString('en-GB', {
- year: 'numeric', month: '2-digit', day: '2-digit',
- hour: '2-digit', minute: '2-digit', second: '2-digit',
- hour12: false,
- })
- .replace(',', '');
- return `${ip} (${timeStr})`;
- }
|