fix(wireguard-dashboard): Fix RPC expect unwrapping, wizard interface naming and key persistence

- Change generateConfig/generateQR RPC declarations to use empty expect
  so error responses are not silently discarded by LuCI's RPC unwrapper
- Simplify handleShowQR to always check backend for stored key first
- Auto-detect next available interface name in wizard (wg1 if wg0 exists)
- Pass private key to addPeer in wizard's createPeers for QR persistence

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-02-03 17:35:44 +01:00
parent 75e3f59207
commit 2ed00d1967
3 changed files with 30 additions and 35 deletions

View File

@ -609,44 +609,27 @@ return view.extend({
var ifaceObj = interfaces.find(function(i) { return i.name === peer.interface; }) || {}; var ifaceObj = interfaces.find(function(i) { return i.name === peer.interface; }) || {};
if (privateKey) { if (privateKey) {
// Have key in session - go straight to endpoint prompt
this.promptForEndpointAndShowQR(peer, ifaceObj, privateKey); this.promptForEndpointAndShowQR(peer, ifaceObj, privateKey);
return; return;
} }
// Try backend with empty private key - it will look up the stored key // Check if backend has the stored key via a quick generateConfig test
var savedEndpoint = sessionStorage.getItem('wg_server_endpoint') || ''; API.generateConfig(peer.interface, peer.public_key, '', 'test').then(function(result) {
if (savedEndpoint) { if (result && result.config && !result.error) {
API.generateQR(peer.interface, peer.public_key, '', savedEndpoint).then(function(result) { // Backend has the key - prompt for endpoint, backend will handle the rest
if (result && result.qrcode && !result.error) { self.promptForEndpointAndShowQR(peer, ifaceObj, '');
self.displayQRModal(peer, result.qrcode, result.config, false); } else {
} else { // No stored key - ask user manually
self.showPrivateKeyPrompt(peer, ifaceObj, function(key) {
self.promptForEndpointAndShowQR(peer, ifaceObj, key);
});
}
}).catch(function() {
self.showPrivateKeyPrompt(peer, ifaceObj, function(key) { self.showPrivateKeyPrompt(peer, ifaceObj, function(key) {
self.promptForEndpointAndShowQR(peer, ifaceObj, key); self.promptForEndpointAndShowQR(peer, ifaceObj, key);
}); });
}
}).catch(function() {
self.showPrivateKeyPrompt(peer, ifaceObj, function(key) {
self.promptForEndpointAndShowQR(peer, ifaceObj, key);
}); });
} else { });
// No saved endpoint yet - need to prompt for endpoint first
// Try a test call to see if backend has the key
API.generateConfig(peer.interface, peer.public_key, '', 'test').then(function(result) {
if (result && result.config && !result.error) {
// Backend has the key, proceed with endpoint prompt
self.promptForEndpointAndShowQR(peer, ifaceObj, '');
} else {
self.showPrivateKeyPrompt(peer, ifaceObj, function(key) {
self.promptForEndpointAndShowQR(peer, ifaceObj, key);
});
}
}).catch(function() {
self.showPrivateKeyPrompt(peer, ifaceObj, function(key) {
self.promptForEndpointAndShowQR(peer, ifaceObj, key);
});
});
}
}, },
handleDownloadConfig: function(peer, interfaces, ev) { handleDownloadConfig: function(peer, interfaces, ev) {

View File

@ -135,6 +135,16 @@ return view.extend({
}); });
}, },
getNextInterfaceName: function(interfaces) {
var existing = interfaces.map(function(i) { return i.name; });
for (var i = 0; i < 100; i++) {
var name = 'wg' + i;
if (existing.indexOf(name) === -1)
return name;
}
return 'wg0';
},
render: function(data) { render: function(data) {
var self = this; var self = this;
// Handle RPC expect unwrapping - results may be array or object // Handle RPC expect unwrapping - results may be array or object
@ -145,6 +155,7 @@ return view.extend({
this.wizardData.publicIP = publicIP; this.wizardData.publicIP = publicIP;
this.wizardData.existingInterfaces = interfaces; this.wizardData.existingInterfaces = interfaces;
this.wizardData.nextIfaceName = this.getNextInterfaceName(interfaces);
var view = E('div', { 'class': 'wg-wizard' }, [ var view = E('div', { 'class': 'wg-wizard' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('wireguard-dashboard/dashboard.css') }), E('link', { 'rel': 'stylesheet', 'href': L.resource('wireguard-dashboard/dashboard.css') }),
@ -246,7 +257,7 @@ return view.extend({
'type': 'text', 'type': 'text',
'id': 'cfg-iface-name', 'id': 'cfg-iface-name',
'class': 'wg-input', 'class': 'wg-input',
'value': 'wg0', 'value': this.wizardData.nextIfaceName || 'wg0',
'placeholder': 'wg0' 'placeholder': 'wg0'
}), }),
E('small', {}, _('Name for the WireGuard interface')) E('small', {}, _('Name for the WireGuard interface'))
@ -633,7 +644,7 @@ return view.extend({
results.push(peerData); results.push(peerData);
// Add peer to interface // Add peer to interface (include private key for QR persistence)
return api.addPeer( return api.addPeer(
data.ifaceName, data.ifaceName,
zone.name + '_' + (idx + 1), zone.name + '_' + (idx + 1),
@ -641,7 +652,8 @@ return view.extend({
keys.public_key, keys.public_key,
keys.preshared_key, keys.preshared_key,
'', '',
zone.keepalive.toString() zone.keepalive.toString(),
keys.private_key
); );
}) })
); );

View File

@ -65,14 +65,14 @@ var callGenerateConfig = rpc.declare({
object: 'luci.wireguard-dashboard', object: 'luci.wireguard-dashboard',
method: 'generate_config', method: 'generate_config',
params: ['interface', 'peer', 'private_key', 'endpoint'], params: ['interface', 'peer', 'private_key', 'endpoint'],
expect: { config: '' } expect: { }
}); });
var callGenerateQR = rpc.declare({ var callGenerateQR = rpc.declare({
object: 'luci.wireguard-dashboard', object: 'luci.wireguard-dashboard',
method: 'generate_qr', method: 'generate_qr',
params: ['interface', 'peer', 'private_key', 'endpoint'], params: ['interface', 'peer', 'private_key', 'endpoint'],
expect: { qrcode: '' } expect: { }
}); });
var callGetTraffic = rpc.declare({ var callGetTraffic = rpc.declare({