diff options
author | Jo-Philipp Wich <jo@mein.io> | 2022-11-22 10:27:15 +0100 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2022-11-22 10:56:36 +0100 |
commit | 6e6fce3eb43ea6899eedc91bebd5713a5d4c773b (patch) | |
tree | 281b09a0e3977ec940896416341f1bb8b3008399 /protocols/luci-proto-wireguard | |
parent | 90a2b1eaeb58b7169b31fdc097c5bfe6f557c778 (diff) |
luci-proto-wireguard: merge status page functionality
Merge status page functionality from the separate `luci-app-wireguard`
package into the `luci-proto-wirguard` protocol backend.
Also rewrite the status page markup to be more compact while we're at it.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'protocols/luci-proto-wireguard')
4 files changed, 233 insertions, 0 deletions
diff --git a/protocols/luci-proto-wireguard/Makefile b/protocols/luci-proto-wireguard/Makefile index 7d2614a0a8..12137fb19b 100644 --- a/protocols/luci-proto-wireguard/Makefile +++ b/protocols/luci-proto-wireguard/Makefile @@ -10,6 +10,8 @@ LUCI_TITLE:=Support for WireGuard VPN LUCI_DEPENDS:=+wireguard-tools +ucode LUCI_PKGARCH:=all +PKG_PROVIDES:=luci-app-wireguard + include ../../luci.mk # call BuildPackage - OpenWrt buildroot signature diff --git a/protocols/luci-proto-wireguard/htdocs/luci-static/resources/view/wireguard/status.js b/protocols/luci-proto-wireguard/htdocs/luci-static/resources/view/wireguard/status.js new file mode 100644 index 0000000000..3b7fa13b25 --- /dev/null +++ b/protocols/luci-proto-wireguard/htdocs/luci-static/resources/view/wireguard/status.js @@ -0,0 +1,212 @@ +'use strict'; +'require view'; +'require rpc'; +'require poll'; +'require dom'; +'require ui'; + + +var callGetWgInstances = rpc.declare({ + object: 'luci.wireguard', + method: 'getWgInstances' +}); + +function timestampToStr(timestamp) { + if (timestamp < 1) + return _('Never', 'No WireGuard peer handshake yet'); + + var seconds = (Date.now() / 1000) - timestamp; + var ago; + + if (seconds < 60) + ago = _('%ds ago').format(seconds); + else if (seconds < 3600) + ago = _('%dm ago').format(seconds / 60); + else if (seconds < 86401) + ago = _('%dh ago').format(seconds / 3600); + else + ago = _('over a day ago'); + + return (new Date(timestamp * 1000)).toUTCString() + ' (' + ago + ')'; +} + +/* +{ + "jow": { + "public_key": "o4iLoC1pl8+vEUshV7eUyKrryo7cr0WJkjS/Dlixxy8=", + "name": "jow", + "fwmark": "off", + "listen_port": "51821", + "peers": [ + { + "endpoint": "45.13.105.118:51821", + "public_key": "672KLy/R4miKXdvw2unuf9jQzVEmNnqen5kF+zVjMX0=", + "name": "m300", + "latest_handshake": "1668680574", + "persistent_keepalive": "off", + "allowed_ips": [ + "192.168.220.10/32" + ], + "transfer_tx": "308", + "transfer_rx": "220" + }, + { + "endpoint": "171.22.3.161:51821", + "public_key": "yblNj1s41F8m1MdXhxD2U+Aew6ZR6Miy8OcNK/fkAks=", + "name": "ac2", + "latest_handshake": "0", + "persistent_keepalive": "off", + "allowed_ips": [ + "192.168.220.11/32" + ], + "transfer_tx": "2960", + "transfer_rx": "0" + } + ] + } +} +*/ + +function handleInterfaceDetails(iface) { + ui.showModal(_('Instance Details'), [ + ui.itemlist(E([]), [ + _('Name'), iface.name, + _('Public Key'), E('code', [ iface.public_key ]), + _('Listen Port'), iface.listen_port, + _('Firewall Mark'), iface.fwmark != 'off' ? iface.fwmark : E('em', _('none')) + ]), + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'btn cbi-button', + 'click': ui.hideModal + }, [ _('Dismiss') ]) + ]) + ]); +} + +function handlePeerDetails(peer) { + ui.showModal(_('Peer Details'), [ + ui.itemlist(E([]), [ + _('Description'), peer.name, + _('Public Key'), E('code', [ peer.public_key ]), + _('Endpoint'), peer.endpoint, + _('Allowed IPs'), (Array.isArray(peer.allowed_ips) && peer.allowed_ips.length) ? peer.allowed_ips.join(', ') : E('em', _('none')), + _('Received Data'), '%1024mB'.format(peer.transfer_rx), + _('Transmitted Data'), '%1024mB'.format(peer.transfer_tx), + _('Latest Handshake'), timestampToStr(+peer.latest_handshake), + _('Keep-Alive'), (peer.persistent_keepalive != 'off') ? _('every %ds', 'WireGuard keep alive interval').format(+peer.persistent_keepalive) : E('em', _('none')), + ]), + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'btn cbi-button', + 'click': ui.hideModal + }, [ _('Dismiss') ]) + ]) + ]); +} + +function renderPeerTable(instanceName, peers) { + var t = new L.ui.Table( + [ + _('Peer'), + _('Endpoint'), + _('Data Received'), + _('Data Transmitted'), + _('Latest Handshake') + ], + { + id: 'peers-' + instanceName + }, + E('em', [ + _('No peers connected') + ]) + ); + + t.update(peers.map(function(peer) { + return [ + [ + peer.name || '', + E('div', { + 'style': 'cursor:pointer', + 'click': ui.createHandlerFn(this, handlePeerDetails, peer) + }, [ + E('p', [ + peer.name ? E('span', [ peer.name ]) : E('em', [ _('Untitled peer') ]) + ]), + E('span', { + 'class': 'ifacebadge hide-sm', + 'data-tooltip': _('Public key: %h', 'Tooltip displaying full WireGuard peer public key').format(peer.public_key) + }, [ + E('code', [ peer.public_key.replace(/^(.{5}).+(.{6})$/, '$1…$2') ]) + ]) + ]) + ], + peer.endpoint, + [ +peer.transfer_rx, '%1024mB'.format(+peer.transfer_rx) ], + [ +peer.transfer_tx, '%1024mB'.format(+peer.transfer_tx) ], + [ +peer.latest_handshake, timestampToStr(+peer.latest_handshake) ] + ]; + })); + + return t.render(); +} + +return view.extend({ + renderIfaces: function(ifaces) { + var res = [ + E('h2', [ _('WireGuard Status') ]) + ]; + + for (var instanceName in ifaces) { + res.push( + E('h3', [ _('Instance "%h"', 'WireGuard instance heading').format(instanceName) ]), + E('p', { + 'style': 'cursor:pointer', + 'click': ui.createHandlerFn(this, handleInterfaceDetails, ifaces[instanceName]) + }, [ + E('span', { 'class': 'ifacebadge' }, [ + E('img', { 'src': L.resource('icons', 'tunnel.png') }), + '\xa0', + instanceName + ]), + E('span', { 'style': 'opacity:.8' }, [ + ' · ', + _('Port %d', 'WireGuard listen port').format(ifaces[instanceName].listen_port), + ' · ', + E('code', { 'click': '' }, [ ifaces[instanceName].public_key ]) + ]) + ]), + renderPeerTable(instanceName, ifaces[instanceName].peers) + ); + } + + if (res.length == 1) + res.push(E('p', { 'class': 'center', 'style': 'margin-top:5em' }, [ + E('em', [ _('No WireGuard interfaces configured.') ]) + ])); + + return E([], res); + }, + + render: function() { + poll.add(L.bind(function () { + return callGetWgInstances().then(L.bind(function(ifaces) { + dom.content( + document.querySelector('#view'), + this.renderIfaces(ifaces) + ); + }, this)); + }, this), 5); + + return E([], [ + E('h2', [ _('WireGuard Status') ]), + E('p', { 'class': 'center', 'style': 'margin-top:5em' }, [ + E('em', [ _('Loading data…') ]) + ]) + ]); + }, + + handleReset: null, + handleSaveApply: null, + handleSave: null +}); diff --git a/protocols/luci-proto-wireguard/root/usr/share/luci/menu.d/luci-proto-wireguard.json b/protocols/luci-proto-wireguard/root/usr/share/luci/menu.d/luci-proto-wireguard.json new file mode 100644 index 0000000000..06940ee7ef --- /dev/null +++ b/protocols/luci-proto-wireguard/root/usr/share/luci/menu.d/luci-proto-wireguard.json @@ -0,0 +1,14 @@ +{ + "admin/status/wireguard": { + "title": "WireGuard", + "order": 92, + "action": { + "type": "view", + "path": "wireguard/status" + }, + "depends": { + "acl": [ "luci-proto-wireguard" ], + "uci": { "network": true } + } + } +} diff --git a/protocols/luci-proto-wireguard/root/usr/share/rpcd/acl.d/luci-wireguard.json b/protocols/luci-proto-wireguard/root/usr/share/rpcd/acl.d/luci-wireguard.json index 674deb6c7d..e7187c0e4f 100644 --- a/protocols/luci-proto-wireguard/root/usr/share/rpcd/acl.d/luci-wireguard.json +++ b/protocols/luci-proto-wireguard/root/usr/share/rpcd/acl.d/luci-wireguard.json @@ -5,6 +5,11 @@ "file": { "/usr/bin/qrencode --inline --8bit --type=SVG --output=- -- *": [ "exec" ] }, + "ubus": { + "luci.wireguard": [ + "getWgInstances" + ] + }, "uci": [ "ddns", "system" ] }, "write": { |