feat(wireguard-dashboard): Persist peer private keys in UCI for QR code generation
Store the client private key in UCI config (_client_private_key) when a peer is created, so QR codes and config files can be generated after page refresh without prompting the user to manually re-enter the key. Old peers without stored keys still get the manual entry fallback. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2d810a2e95
commit
2ab0965917
@ -358,7 +358,7 @@ return view.extend({
|
|||||||
|
|
||||||
var privkey = document.getElementById('peer-privkey').value;
|
var privkey = document.getElementById('peer-privkey').value;
|
||||||
|
|
||||||
API.addPeer(iface, name, allowed_ips, pubkey, psk, endpoint, keepalive).then(function(result) {
|
API.addPeer(iface, name, allowed_ips, pubkey, psk, endpoint, keepalive, privkey).then(function(result) {
|
||||||
ui.hideModal();
|
ui.hideModal();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// Store private key for QR generation
|
// Store private key for QR generation
|
||||||
@ -460,9 +460,9 @@ return view.extend({
|
|||||||
generateAndShowQR: function(peer, ifaceObj, privateKey, serverEndpoint) {
|
generateAndShowQR: function(peer, ifaceObj, privateKey, serverEndpoint) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// Build WireGuard client config
|
var buildLocalConfig = function(privKey) {
|
||||||
var config = '[Interface]\n' +
|
return '[Interface]\n' +
|
||||||
'PrivateKey = ' + privateKey + '\n' +
|
'PrivateKey = ' + privKey + '\n' +
|
||||||
'Address = ' + (peer.allowed_ips || '10.0.0.2/32') + '\n' +
|
'Address = ' + (peer.allowed_ips || '10.0.0.2/32') + '\n' +
|
||||||
'DNS = 1.1.1.1, 1.0.0.1\n\n' +
|
'DNS = 1.1.1.1, 1.0.0.1\n\n' +
|
||||||
'[Peer]\n' +
|
'[Peer]\n' +
|
||||||
@ -470,28 +470,38 @@ return view.extend({
|
|||||||
'Endpoint = ' + serverEndpoint + ':' + (ifaceObj.listen_port || 51820) + '\n' +
|
'Endpoint = ' + serverEndpoint + ':' + (ifaceObj.listen_port || 51820) + '\n' +
|
||||||
'AllowedIPs = 0.0.0.0/0, ::/0\n' +
|
'AllowedIPs = 0.0.0.0/0, ::/0\n' +
|
||||||
'PersistentKeepalive = 25';
|
'PersistentKeepalive = 25';
|
||||||
|
};
|
||||||
|
|
||||||
// First try backend QR generation
|
// Try backend QR generation (it will look up stored key if privateKey is empty)
|
||||||
API.generateQR(peer.interface, peer.public_key, privateKey, serverEndpoint).then(function(result) {
|
API.generateQR(peer.interface, peer.public_key, privateKey || '', serverEndpoint).then(function(result) {
|
||||||
if (result && result.qrcode && !result.error) {
|
if (result && result.qrcode && !result.error) {
|
||||||
|
var config = result.config || buildLocalConfig(privateKey);
|
||||||
self.displayQRModal(peer, result.qrcode, config, false);
|
self.displayQRModal(peer, result.qrcode, config, false);
|
||||||
} else {
|
} else if (privateKey) {
|
||||||
// Fall back to JavaScript QR generation
|
// Backend failed but we have a key - fall back to JavaScript QR generation
|
||||||
|
var config = buildLocalConfig(privateKey);
|
||||||
var svg = qrcode.generateSVG(config, 250);
|
var svg = qrcode.generateSVG(config, 250);
|
||||||
if (svg) {
|
if (svg) {
|
||||||
self.displayQRModal(peer, svg, config, true);
|
self.displayQRModal(peer, svg, config, true);
|
||||||
} else {
|
} else {
|
||||||
ui.addNotification(null, E('p', _('Failed to generate QR code')), 'error');
|
ui.addNotification(null, E('p', _('Failed to generate QR code')), 'error');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ui.addNotification(null, E('p', result.error || _('Failed to generate QR code')), 'error');
|
||||||
}
|
}
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
|
if (privateKey) {
|
||||||
// Fall back to JavaScript QR generation
|
// Fall back to JavaScript QR generation
|
||||||
|
var config = buildLocalConfig(privateKey);
|
||||||
var svg = qrcode.generateSVG(config, 250);
|
var svg = qrcode.generateSVG(config, 250);
|
||||||
if (svg) {
|
if (svg) {
|
||||||
self.displayQRModal(peer, svg, config, true);
|
self.displayQRModal(peer, svg, config, true);
|
||||||
} else {
|
} else {
|
||||||
ui.addNotification(null, E('p', _('Failed to generate QR code')), 'error');
|
ui.addNotification(null, E('p', _('Failed to generate QR code')), 'error');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ui.addNotification(null, E('p', _('Error: %s').format(err.message || err)), 'error');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -552,17 +562,12 @@ return view.extend({
|
|||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleShowQR: function(peer, interfaces, ev) {
|
showPrivateKeyPrompt: function(peer, ifaceObj, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var privateKey = this.getStoredPrivateKey(peer.public_key);
|
|
||||||
var ifaceObj = interfaces.find(function(i) { return i.name === peer.interface; }) || {};
|
|
||||||
|
|
||||||
if (!privateKey) {
|
|
||||||
// Private key not stored - ask user to input it
|
|
||||||
ui.showModal(_('Private Key Required'), [
|
ui.showModal(_('Private Key Required'), [
|
||||||
E('p', {}, _('To generate a QR code, the peer\'s private key is needed.')),
|
E('p', {}, _('To generate a QR code, the peer\'s private key is needed.')),
|
||||||
E('p', { 'style': 'color: #666; font-size: 0.9em;' },
|
E('p', { 'style': 'color: #666; font-size: 0.9em;' },
|
||||||
_('Private keys are only stored in your browser session immediately after peer creation. If you closed or refreshed the page, you\'ll need to enter it manually.')),
|
_('The private key was not found on the server. This can happen for peers created before key persistence was enabled.')),
|
||||||
E('div', { 'class': 'cbi-value' }, [
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
E('label', { 'class': 'cbi-value-title' }, _('Private Key')),
|
E('label', { 'class': 'cbi-value-title' }, _('Private Key')),
|
||||||
E('div', { 'class': 'cbi-value-field' }, [
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
@ -589,18 +594,59 @@ return view.extend({
|
|||||||
ui.addNotification(null, E('p', _('Please enter a valid private key (44 characters)')), 'error');
|
ui.addNotification(null, E('p', _('Please enter a valid private key (44 characters)')), 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Store for future use
|
|
||||||
self.storePrivateKey(peer.public_key, key);
|
self.storePrivateKey(peer.public_key, key);
|
||||||
ui.hideModal();
|
ui.hideModal();
|
||||||
self.promptForEndpointAndShowQR(peer, ifaceObj, key);
|
callback(key);
|
||||||
}
|
}
|
||||||
}, _('Continue'))
|
}, _('Continue'))
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleShowQR: function(peer, interfaces, ev) {
|
||||||
|
var self = this;
|
||||||
|
var privateKey = this.getStoredPrivateKey(peer.public_key);
|
||||||
|
var ifaceObj = interfaces.find(function(i) { return i.name === peer.interface; }) || {};
|
||||||
|
|
||||||
|
if (privateKey) {
|
||||||
|
this.promptForEndpointAndShowQR(peer, ifaceObj, privateKey);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.promptForEndpointAndShowQR(peer, ifaceObj, privateKey);
|
// Try backend with empty private key - it will look up the stored key
|
||||||
|
var savedEndpoint = sessionStorage.getItem('wg_server_endpoint') || '';
|
||||||
|
if (savedEndpoint) {
|
||||||
|
API.generateQR(peer.interface, peer.public_key, '', savedEndpoint).then(function(result) {
|
||||||
|
if (result && result.qrcode && !result.error) {
|
||||||
|
self.displayQRModal(peer, result.qrcode, result.config, false);
|
||||||
|
} 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} 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) {
|
||||||
@ -608,6 +654,17 @@ return view.extend({
|
|||||||
var privateKey = this.getStoredPrivateKey(peer.public_key);
|
var privateKey = this.getStoredPrivateKey(peer.public_key);
|
||||||
var ifaceObj = interfaces.find(function(i) { return i.name === peer.interface; }) || {};
|
var ifaceObj = interfaces.find(function(i) { return i.name === peer.interface; }) || {};
|
||||||
|
|
||||||
|
var downloadConfig = function(config) {
|
||||||
|
var blob = new Blob([config], { type: 'text/plain' });
|
||||||
|
var url = URL.createObjectURL(blob);
|
||||||
|
var a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = peer.interface + '-' + (peer.short_key || 'peer') + '.conf';
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
ui.addNotification(null, E('p', _('Configuration file downloaded')), 'info');
|
||||||
|
};
|
||||||
|
|
||||||
var showConfigModal = function(privKey) {
|
var showConfigModal = function(privKey) {
|
||||||
var savedEndpoint = sessionStorage.getItem('wg_server_endpoint') || '';
|
var savedEndpoint = sessionStorage.getItem('wg_server_endpoint') || '';
|
||||||
|
|
||||||
@ -640,7 +697,9 @@ return view.extend({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sessionStorage.setItem('wg_server_endpoint', endpoint);
|
sessionStorage.setItem('wg_server_endpoint', endpoint);
|
||||||
|
ui.hideModal();
|
||||||
|
|
||||||
|
if (privKey) {
|
||||||
var config = '[Interface]\n' +
|
var config = '[Interface]\n' +
|
||||||
'PrivateKey = ' + privKey + '\n' +
|
'PrivateKey = ' + privKey + '\n' +
|
||||||
'Address = ' + (peer.allowed_ips || '10.0.0.2/32') + '\n' +
|
'Address = ' + (peer.allowed_ips || '10.0.0.2/32') + '\n' +
|
||||||
@ -650,17 +709,19 @@ return view.extend({
|
|||||||
'Endpoint = ' + endpoint + ':' + (ifaceObj.listen_port || 51820) + '\n' +
|
'Endpoint = ' + endpoint + ':' + (ifaceObj.listen_port || 51820) + '\n' +
|
||||||
'AllowedIPs = 0.0.0.0/0, ::/0\n' +
|
'AllowedIPs = 0.0.0.0/0, ::/0\n' +
|
||||||
'PersistentKeepalive = 25';
|
'PersistentKeepalive = 25';
|
||||||
|
downloadConfig(config);
|
||||||
var blob = new Blob([config], { type: 'text/plain' });
|
} else {
|
||||||
var url = URL.createObjectURL(blob);
|
// Use backend to generate config (it has the stored key)
|
||||||
var a = document.createElement('a');
|
API.generateConfig(peer.interface, peer.public_key, '', endpoint).then(function(result) {
|
||||||
a.href = url;
|
if (result && result.config && !result.error) {
|
||||||
a.download = peer.interface + '-' + (peer.short_key || 'peer') + '.conf';
|
downloadConfig(result.config);
|
||||||
a.click();
|
} else {
|
||||||
URL.revokeObjectURL(url);
|
ui.addNotification(null, E('p', result.error || _('Failed to generate config')), 'error');
|
||||||
|
}
|
||||||
ui.hideModal();
|
}).catch(function(err) {
|
||||||
ui.addNotification(null, E('p', _('Configuration file downloaded')), 'info');
|
ui.addNotification(null, E('p', _('Error: %s').format(err.message || err)), 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, _('Download'))
|
}, _('Download'))
|
||||||
])
|
])
|
||||||
@ -668,42 +729,22 @@ return view.extend({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!privateKey) {
|
if (!privateKey) {
|
||||||
// Private key not stored - ask user to input it
|
// Try backend first - it may have the stored key
|
||||||
ui.showModal(_('Private Key Required'), [
|
API.generateConfig(peer.interface, peer.public_key, '', 'test').then(function(result) {
|
||||||
E('p', {}, _('Enter the peer\'s private key to generate the configuration file:')),
|
if (result && result.config && !result.error) {
|
||||||
E('div', { 'class': 'cbi-value' }, [
|
// Backend has the key, show config modal with backend-generated config
|
||||||
E('label', { 'class': 'cbi-value-title' }, _('Private Key')),
|
showConfigModal('');
|
||||||
E('div', { 'class': 'cbi-value-field' }, [
|
} else {
|
||||||
E('input', {
|
// Fallback to manual prompt
|
||||||
'type': 'text',
|
self.showPrivateKeyPrompt(peer, ifaceObj, function(key) {
|
||||||
'id': 'cfg-private-key',
|
|
||||||
'class': 'cbi-input-text',
|
|
||||||
'placeholder': 'Base64 private key (44 characters)',
|
|
||||||
'style': 'font-family: monospace;'
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
E('div', { 'class': 'right', 'style': 'margin-top: 1em;' }, [
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': ui.hideModal
|
|
||||||
}, _('Cancel')),
|
|
||||||
' ',
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn cbi-button-action',
|
|
||||||
'click': function() {
|
|
||||||
var key = document.getElementById('cfg-private-key').value.trim();
|
|
||||||
if (!key || key.length !== 44) {
|
|
||||||
ui.addNotification(null, E('p', _('Please enter a valid private key (44 characters)')), 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.storePrivateKey(peer.public_key, key);
|
|
||||||
ui.hideModal();
|
|
||||||
showConfigModal(key);
|
showConfigModal(key);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, _('Continue'))
|
}).catch(function() {
|
||||||
])
|
self.showPrivateKeyPrompt(peer, ifaceObj, function(key) {
|
||||||
]);
|
showConfigModal(key);
|
||||||
|
});
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,14 +34,11 @@ return view.extend({
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
generateQRForPeer: function(iface, peer, serverEndpoint) {
|
showPrivateKeyPrompt: function(iface, peer, serverEndpoint) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var privateKey = this.getStoredPrivateKey(peer.public_key);
|
|
||||||
|
|
||||||
if (!privateKey) {
|
|
||||||
ui.showModal(_('Private Key Required'), [
|
ui.showModal(_('Private Key Required'), [
|
||||||
E('p', {}, _('To generate a QR code, you need the peer\'s private key.')),
|
E('p', {}, _('To generate a QR code, you need the peer\'s private key.')),
|
||||||
E('p', {}, _('Private keys are only available immediately after peer creation for security reasons.')),
|
E('p', {}, _('The private key was not found on the server. This can happen for peers created before key persistence was enabled.')),
|
||||||
E('div', { 'class': 'wg-form-group' }, [
|
E('div', { 'class': 'wg-form-group' }, [
|
||||||
E('label', {}, _('Enter Private Key:')),
|
E('label', {}, _('Enter Private Key:')),
|
||||||
E('input', {
|
E('input', {
|
||||||
@ -73,10 +70,29 @@ return view.extend({
|
|||||||
}, _('Generate QR'))
|
}, _('Generate QR'))
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
generateQRForPeer: function(iface, peer, serverEndpoint) {
|
||||||
|
var self = this;
|
||||||
|
var privateKey = this.getStoredPrivateKey(peer.public_key);
|
||||||
|
|
||||||
|
if (privateKey) {
|
||||||
|
this.showQRCode(iface, peer, privateKey, serverEndpoint);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showQRCode(iface, peer, privateKey, serverEndpoint);
|
// Try backend first with empty private key - it will look up the stored key
|
||||||
|
api.generateQR(iface.name, peer.public_key, '', serverEndpoint).then(function(result) {
|
||||||
|
if (result && result.qrcode && !result.error) {
|
||||||
|
// Backend found the stored key and generated QR
|
||||||
|
self.displayQRModal(iface, peer, result.qrcode, result.config);
|
||||||
|
} else {
|
||||||
|
// Backend doesn't have the key - prompt user
|
||||||
|
self.showPrivateKeyPrompt(iface, peer, serverEndpoint);
|
||||||
|
}
|
||||||
|
}).catch(function() {
|
||||||
|
self.showPrivateKeyPrompt(iface, peer, serverEndpoint);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
showQRCode: function(iface, peer, privateKey, serverEndpoint) {
|
showQRCode: function(iface, peer, privateKey, serverEndpoint) {
|
||||||
|
|||||||
@ -44,7 +44,7 @@ var callCreateInterface = rpc.declare({
|
|||||||
var callAddPeer = rpc.declare({
|
var callAddPeer = rpc.declare({
|
||||||
object: 'luci.wireguard-dashboard',
|
object: 'luci.wireguard-dashboard',
|
||||||
method: 'add_peer',
|
method: 'add_peer',
|
||||||
params: ['interface', 'name', 'allowed_ips', 'public_key', 'preshared_key', 'endpoint', 'persistent_keepalive'],
|
params: ['interface', 'name', 'allowed_ips', 'public_key', 'preshared_key', 'endpoint', 'persistent_keepalive', 'private_key'],
|
||||||
expect: { }
|
expect: { }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -506,6 +506,7 @@ add_peer() {
|
|||||||
json_get_var psk preshared_key
|
json_get_var psk preshared_key
|
||||||
json_get_var endpoint endpoint
|
json_get_var endpoint endpoint
|
||||||
json_get_var keepalive persistent_keepalive
|
json_get_var keepalive persistent_keepalive
|
||||||
|
json_get_var client_privkey private_key
|
||||||
|
|
||||||
json_init
|
json_init
|
||||||
|
|
||||||
@ -572,6 +573,11 @@ add_peer() {
|
|||||||
uci set network.$section_name.persistent_keepalive="$keepalive"
|
uci set network.$section_name.persistent_keepalive="$keepalive"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Store client private key for QR/config generation
|
||||||
|
if [ -n "$client_privkey" ]; then
|
||||||
|
uci set network.$section_name._client_private_key="$client_privkey"
|
||||||
|
fi
|
||||||
|
|
||||||
# Route allowed IPs
|
# Route allowed IPs
|
||||||
uci set network.$section_name.route_allowed_ips="1"
|
uci set network.$section_name.route_allowed_ips="1"
|
||||||
|
|
||||||
@ -655,6 +661,24 @@ generate_config() {
|
|||||||
|
|
||||||
json_init
|
json_init
|
||||||
|
|
||||||
|
# If private key not provided, look it up from UCI
|
||||||
|
if [ -z "$peer_privkey" ]; then
|
||||||
|
local sections=$(uci show network 2>/dev/null | grep "=wireguard_$iface$" | cut -d'.' -f2 | cut -d'=' -f1)
|
||||||
|
for section in $sections; do
|
||||||
|
local key=$(uci -q get network.$section.public_key)
|
||||||
|
if [ "$key" = "$peer_key" ]; then
|
||||||
|
peer_privkey=$(uci -q get network.$section._client_private_key)
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$peer_privkey" ]; then
|
||||||
|
json_add_string "error" "Private key not available. Please provide it manually."
|
||||||
|
json_dump
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
# Get interface details
|
# Get interface details
|
||||||
local server_pubkey=$($WG_CMD show $iface public-key 2>/dev/null)
|
local server_pubkey=$($WG_CMD show $iface public-key 2>/dev/null)
|
||||||
local server_port=$($WG_CMD show $iface listen-port 2>/dev/null)
|
local server_port=$($WG_CMD show $iface listen-port 2>/dev/null)
|
||||||
@ -709,6 +733,24 @@ generate_qr() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# If private key not provided, look it up from UCI
|
||||||
|
if [ -z "$peer_privkey" ]; then
|
||||||
|
local sections=$(uci show network 2>/dev/null | grep "=wireguard_$iface$" | cut -d'.' -f2 | cut -d'=' -f1)
|
||||||
|
for section in $sections; do
|
||||||
|
local key=$(uci -q get network.$section.public_key)
|
||||||
|
if [ "$key" = "$peer_key" ]; then
|
||||||
|
peer_privkey=$(uci -q get network.$section._client_private_key)
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$peer_privkey" ]; then
|
||||||
|
json_add_string "error" "Private key not available. Please provide it manually."
|
||||||
|
json_dump
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
# Get interface details
|
# Get interface details
|
||||||
local server_pubkey=$($WG_CMD show $iface public-key 2>/dev/null)
|
local server_pubkey=$($WG_CMD show $iface public-key 2>/dev/null)
|
||||||
local server_port=$($WG_CMD show $iface listen-port 2>/dev/null)
|
local server_port=$($WG_CMD show $iface listen-port 2>/dev/null)
|
||||||
@ -1008,7 +1050,7 @@ get_bandwidth_rates() {
|
|||||||
# Main dispatcher
|
# Main dispatcher
|
||||||
case "$1" in
|
case "$1" in
|
||||||
list)
|
list)
|
||||||
echo '{"status":{},"interfaces":{},"peers":{},"traffic":{},"config":{},"generate_keys":{},"create_interface":{"name":"str","private_key":"str","listen_port":"str","addresses":"str","mtu":"str"},"add_peer":{"interface":"str","name":"str","allowed_ips":"str","public_key":"str","preshared_key":"str","endpoint":"str","persistent_keepalive":"str"},"remove_peer":{"interface":"str","public_key":"str"},"generate_config":{"interface":"str","peer":"str","private_key":"str","endpoint":"str"},"generate_qr":{"interface":"str","peer":"str","private_key":"str","endpoint":"str"},"bandwidth_history":{},"endpoint_info":{"endpoint":"str"},"ping_peer":{"ip":"str"},"interface_control":{"interface":"str","action":"str"},"peer_descriptions":{},"bandwidth_rates":{}}'
|
echo '{"status":{},"interfaces":{},"peers":{},"traffic":{},"config":{},"generate_keys":{},"create_interface":{"name":"str","private_key":"str","listen_port":"str","addresses":"str","mtu":"str"},"add_peer":{"interface":"str","name":"str","allowed_ips":"str","public_key":"str","preshared_key":"str","endpoint":"str","persistent_keepalive":"str","private_key":"str"},"remove_peer":{"interface":"str","public_key":"str"},"generate_config":{"interface":"str","peer":"str","private_key":"str","endpoint":"str"},"generate_qr":{"interface":"str","peer":"str","private_key":"str","endpoint":"str"},"bandwidth_history":{},"endpoint_info":{"endpoint":"str"},"ping_peer":{"ip":"str"},"interface_control":{"interface":"str","action":"str"},"peer_descriptions":{},"bandwidth_rates":{}}'
|
||||||
;;
|
;;
|
||||||
call)
|
call)
|
||||||
case "$2" in
|
case "$2" in
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user