feat(wireguard): client config UX, collapsible config card, configurable DNS

Land the WireGuard client-config UX work on main (the upstream PR #5642
branch could not be pushed to).

- Reusable collapsible ConfigBlock (copy/download/QR, actions aligned right)
  for the client .conf, used by client info and the public sub page.
- Correct .conf: canonical PresharedKey casing and DNS sourced from the inbound
  (configurable per-inbound, default 1.1.1.1, 1.0.0.1).
- Configurable per-inbound DNS for WireGuard (schema + form + backend hint via
  InboundOption.WgDns); inert at the Xray layer.
- Public sub page now shows the WireGuard config, rebuilt from the share link;
  the Go wireguard:// link carries dns/presharedkey/keepalive for completeness.
- QR enabled for the wireguard:// link; link rows are compact like other protocols.
- Client information order is subscription, copy URL, WireGuard config; the
  redundant config tab is removed from the add/edit client modal.
- Drop the Inbound Information and QR Code row actions for WireGuard inbounds.
This commit is contained in:
MHSanaei
2026-06-29 00:50:34 +02:00
parent 60c54827aa
commit a329882e0e
26 changed files with 509 additions and 57 deletions
+61 -1
View File
@@ -828,7 +828,7 @@ export function genWireguardConfig(input: GenWireguardLinkInput): string {
let txt = `[Interface]\n`;
txt += `PrivateKey = ${peer.privateKey ?? ''}\n`;
txt += `Address = ${peer.allowedIPs[0] ?? ''}\n`;
txt += `DNS = 1.1.1.1, 1.0.0.1\n`;
txt += `DNS = ${settings.dns || '1.1.1.1, 1.0.0.1'}\n`;
if (typeof settings.mtu === 'number' && settings.mtu > 0) {
txt += `MTU = ${settings.mtu}\n`;
}
@@ -846,6 +846,66 @@ export function genWireguardConfig(input: GenWireguardLinkInput): string {
return txt;
}
export function wireguardConfigFromLink(link: string, fallbackRemark = ''): string {
let url: URL;
try {
url = new URL(link);
} catch {
return '';
}
const scheme = url.protocol.replace(/:$/, '');
if (scheme !== 'wireguard' && scheme !== 'wg') return '';
const params = url.searchParams;
const pick = (...keys: string[]): string => {
for (const k of keys) {
const v = params.get(k);
if (v) return v;
}
return '';
};
let privateKey: string;
try {
privateKey = decodeURIComponent(url.username);
} catch {
privateKey = url.username;
}
const host = url.hostname;
const endpoint = host ? (url.port ? `${host}:${url.port}` : host) : '';
const address = pick('address', 'ip') || '10.0.0.2/32';
const publicKey = pick('publickey', 'publicKey', 'public_key', 'peerPublicKey');
const dns = pick('dns') || '1.1.1.1, 1.0.0.1';
const mtu = pick('mtu');
const psk = pick('presharedkey', 'preshared_key', 'pre-shared-key', 'psk');
const keepAlive = pick('keepalive', 'persistentkeepalive', 'persistent_keepalive');
const allowedIPs = pick('allowedips', 'allowed_ips') || '0.0.0.0/0, ::/0';
let remark = fallbackRemark;
try {
const decoded = decodeURIComponent(url.hash.replace(/^#/, ''));
if (decoded) remark = decoded;
} catch {
const raw = url.hash.replace(/^#/, '');
if (raw) remark = raw;
}
const lines = [
'[Interface]',
`PrivateKey = ${privateKey}`,
`Address = ${address}`,
`DNS = ${dns}`,
];
if (mtu && Number(mtu) > 0) lines.push(`MTU = ${mtu}`);
lines.push('');
if (remark) lines.push(`# ${remark}`);
lines.push('[Peer]', `PublicKey = ${publicKey}`);
if (psk) lines.push(`PresharedKey = ${psk}`);
lines.push(`AllowedIPs = ${allowedIPs}`, `Endpoint = ${endpoint}`);
if (keepAlive && Number(keepAlive) > 0) lines.push(`PersistentKeepalive = ${keepAlive}`);
return lines.join('\n');
}
export type { WireguardInboundPeer };
function isUnixSocketListen(listen: string): boolean {