feat(wireguard): per-peer comments for identifying devices (#5168)

WG peers were only identifiable by their keys. Add an optional panel-side
comment per peer: editable in the inbound form (echoed next to "Peer N"
in the section header), stored in the settings JSON alongside the
panel-only privateKey (xray-core ignores unknown peer fields), and
appended to the share link / .conf remark so the device is identifiable
in client apps too.
This commit is contained in:
MHSanaei
2026-06-12 09:10:57 +02:00
parent d1a13844b2
commit d04cb10971
3 changed files with 24 additions and 4 deletions
+11 -4
View File
@@ -1079,11 +1079,11 @@ export function genWireguardLinks(input: GenWireguardFanoutInput): string {
const addr = resolveAddr(inbound, hostOverride, fallbackHostname);
const sep = remarkModel.charAt(0);
return inbound.settings.peers
.map((_p, i) => genWireguardLink({
.map((p, i) => genWireguardLink({
settings: inbound.settings as WireguardInboundSettings,
address: addr,
port: inbound.port,
remark: `${remark}${sep}${i + 1}`,
remark: `${remark}${sep}${i + 1}${wgPeerCommentSuffix(p)}`,
peerIndex: i,
}))
.join('\r\n');
@@ -1095,16 +1095,23 @@ export function genWireguardConfigs(input: GenWireguardFanoutInput): string {
const addr = resolveAddr(inbound, hostOverride, fallbackHostname);
const sep = remarkModel.charAt(0);
return inbound.settings.peers
.map((_p, i) => genWireguardConfig({
.map((p, i) => genWireguardConfig({
settings: inbound.settings as WireguardInboundSettings,
address: addr,
port: inbound.port,
remark: `${remark}${sep}${i + 1}`,
remark: `${remark}${sep}${i + 1}${wgPeerCommentSuffix(p)}`,
peerIndex: i,
}))
.join('\r\n');
}
// Peer comments (#5168) are panel-side annotations; when present they ride
// along in the share remark so the device is identifiable in client apps.
function wgPeerCommentSuffix(peer: unknown): string {
const comment = (peer as { comment?: unknown })?.comment;
return typeof comment === 'string' && comment.trim() !== '' ? ` (${comment.trim()})` : '';
}
export function isPostQuantumLink(link: string): boolean {
if (/[?&]pqv=/.test(link)) return true;
if (link.includes('mlkem768') || link.includes('mldsa65')) return true;
@@ -102,6 +102,12 @@ export default function WireguardFields({ wgPubKey, regenInboundWg, regenWgPeerK
<Divider titlePlacement="center">
<Space>
<span>{t('pages.inbounds.info.peerNumber', { n: idx + 1 })}</span>
<Form.Item noStyle shouldUpdate>
{() => {
const comment = form.getFieldValue(['settings', 'peers', field.name, 'comment']) as string | undefined;
return comment ? <span style={{ opacity: 0.65 }}> {comment}</span> : null;
}}
</Form.Item>
{fields.length > 1 && (
<Button
size="small"
@@ -112,6 +118,9 @@ export default function WireguardFields({ wgPubKey, regenInboundWg, regenWgPeerK
)}
</Space>
</Divider>
<Form.Item name={[field.name, 'comment']} label={t('comment')}>
<Input placeholder="e.g. Alice's laptop" />
</Form.Item>
<Form.Item label={t('pages.xray.wireguard.secretKey')}>
<Space.Compact block>
<Form.Item name={[field.name, 'privateKey']} noStyle>
@@ -26,6 +26,10 @@ export const WireguardInboundPeerSchema = z.object({
preSharedKey: z.string().optional(),
allowedIPs: z.array(z.string()).default([]),
keepAlive: optionalClearedInt(z.number().int().min(0)),
// Panel-only annotation (#5168): which client/device this peer belongs to.
// Rides along in the settings JSON like privateKey does; xray-core ignores
// unknown peer fields.
comment: z.string().optional(),
});
export type WireguardInboundPeer = z.infer<typeof WireguardInboundPeerSchema>;