mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-03 19:55:04 +03:00
47964afbc5
When a client belongs to multiple AmneziaWG or WireGuard inbounds (e.g. across remote nodes), findAmneziaWGInbounds and findWireguardInbounds only returned the first matching inbound. Consequently, ClientInfoModal and ClientQrModal rendered only one config block, making other inbounds' configs unreachable. - Add findAmneziaWGInbounds and findWireguardInbounds returning all matching inbounds - Add formatTunnelConfigMeta helper to unify label, fileName, and qrRemark resolution - Support addressOverride in buildWireguardClientConfig from tunnelAllowedIPs - Render all tunnel configs in ClientInfoModal and ClientQrModal with node remarks - Distinguish download filenames with inbound remark suffix to avoid collisions - Add component integration tests covering multi-inbound modal rendering
57 lines
2.0 KiB
TypeScript
57 lines
2.0 KiB
TypeScript
import { formatInboundLabel } from '@/lib/inbounds/label';
|
|
import { preferPublicHost, resolveShareHost } from '@/lib/xray/inbound-link';
|
|
import type { ClientRecord, InboundOption } from '@/hooks/useClients';
|
|
|
|
export function isWireguardClient(client: ClientRecord | null | undefined): boolean {
|
|
if (!client) return false;
|
|
return !!(
|
|
client.privateKey ||
|
|
client.publicKey ||
|
|
client.allowedIPs ||
|
|
client.preSharedKey ||
|
|
client.keepAlive
|
|
);
|
|
}
|
|
|
|
export function findWireguardInbounds(
|
|
client: ClientRecord | null | undefined,
|
|
inboundsById: Record<number, InboundOption>,
|
|
): InboundOption[] {
|
|
return (client?.inboundIds || [])
|
|
.map((id) => inboundsById?.[id])
|
|
.filter((ib): ib is InboundOption => ib?.protocol === 'wireguard');
|
|
}
|
|
|
|
export function buildWireguardClientConfig(
|
|
client: ClientRecord,
|
|
inbound: InboundOption | undefined,
|
|
host = window.location.hostname,
|
|
publicHost = '',
|
|
addressOverride = '',
|
|
): string {
|
|
const endpointHost = resolveShareHost(
|
|
inbound ?? {},
|
|
inbound?.nodeAddress ?? '',
|
|
preferPublicHost(host, publicHost),
|
|
);
|
|
const address = addressOverride || client.allowedIPs || '10.0.0.2/32';
|
|
const endpoint = `${endpointHost}:${inbound?.port || ''}`;
|
|
const inboundName = inbound ? formatInboundLabel(inbound.tag, inbound.remark) : '';
|
|
const remark = [inboundName, client.email, client.comment].filter(Boolean).join(' - ');
|
|
const lines = [
|
|
'[Interface]',
|
|
`PrivateKey = ${client.privateKey || client.password || ''}`,
|
|
`Address = ${address}`,
|
|
`DNS = ${inbound?.wgDns || '1.1.1.1, 1.0.0.1'}`,
|
|
];
|
|
if (inbound?.wgMtu && inbound.wgMtu > 0) lines.push(`MTU = ${inbound.wgMtu}`);
|
|
lines.push('');
|
|
if (remark) lines.push(`# ${remark}`);
|
|
lines.push('[Peer]', `PublicKey = ${inbound?.wgPublicKey || ''}`);
|
|
if (client.preSharedKey) lines.push(`PresharedKey = ${client.preSharedKey}`);
|
|
lines.push('AllowedIPs = 0.0.0.0/0, ::/0', `Endpoint = ${endpoint}`);
|
|
if (client.keepAlive && client.keepAlive > 0)
|
|
lines.push(`PersistentKeepalive = ${client.keepAlive}`);
|
|
return lines.join('\n');
|
|
}
|