diff options
author | Dirk Brenken <dev@brenken.org> | 2018-10-01 08:09:12 +0200 |
---|---|---|
committer | Dirk Brenken <dev@brenken.org> | 2018-10-01 11:57:38 +0200 |
commit | ca76ead1ba0c2fdcdb8fc202588700e3116d3966 (patch) | |
tree | e27c6f57260d1c4820dab261b01a8a57c177d2ee | |
parent | 24cf28b5ec148bc29d2d150e94f8928d96c1d1e6 (diff) |
luci-app-wireguard: add QR Code support plus fixes
* add optional QR Code support to status page (per wg Interface),
submitted information via QR code are:
- [Interface] (list of) Address / PrivateKey
- [Peer] Endpoint / PublicKey / (list of) AllowedIPs
- support multiple interfaces and peers
* fix XHTML rendering errors with OpenWrt standard theme
* fix logical glitch with empty descriptions, remove needless variables
* left align the output for better viewing esp. with material theme
* freshen up design a little bit
* switch space indentation to tabs
Signed-off-by: Dirk Brenken <dev@brenken.org>
-rw-r--r-- | applications/luci-app-wireguard/luasrc/controller/wireguard.lua | 2 | ||||
-rw-r--r-- | applications/luci-app-wireguard/luasrc/view/wireguard.htm | 440 |
2 files changed, 241 insertions, 201 deletions
diff --git a/applications/luci-app-wireguard/luasrc/controller/wireguard.lua b/applications/luci-app-wireguard/luasrc/controller/wireguard.lua index 68a82fe5c..5a9196097 100644 --- a/applications/luci-app-wireguard/luasrc/controller/wireguard.lua +++ b/applications/luci-app-wireguard/luasrc/controller/wireguard.lua @@ -4,5 +4,5 @@ module("luci.controller.wireguard", package.seeall) function index() - entry({"admin", "status", "wireguard"}, template("wireguard"), _("WireGuard Status"), 92) + entry({"admin", "status", "wireguard"}, template("wireguard"), _("WireGuard Status"), 92) end diff --git a/applications/luci-app-wireguard/luasrc/view/wireguard.htm b/applications/luci-app-wireguard/luasrc/view/wireguard.htm index c25ef85ca..444533275 100644 --- a/applications/luci-app-wireguard/luasrc/view/wireguard.htm +++ b/applications/luci-app-wireguard/luasrc/view/wireguard.htm @@ -1,224 +1,264 @@ <%# - Copyright 2016-2017 Dan Luedtke <mail@danrl.com> - Licensed to the public under the Apache License 2.0. + Copyright 2016-2017 Dan Luedtke <mail@danrl.com> + Licensed to the public under the Apache License 2.0. -%> <% - local data = { } - local last_device = "" + local uci = uci.cursor() + local data = { } + local last_device = "" + local enc = { } - local wg_dump = io.popen("wg show all dump") - if wg_dump then - local line - for line in wg_dump:lines() do - local line = string.split(line, "\t") - if not (last_device == line[1]) then - last_device = line[1] - data[line[1]] = { - name = line[1], - public_key = line[3], - listen_port = line[4], - fwmark = line[5], - peers = { } - } - else - local peer = { - public_key = line[2], - endpoint = line[4], - allowed_ips = { }, - latest_handshake = line[6], - transfer_rx = line[7], - transfer_tx = line[8], - persistent_keepalive = line[9] - } - if not (line[4] == '(none)') then - for ipkey, ipvalue in pairs(string.split(line[5], ",")) do - if #ipvalue > 0 then - table.insert(peer['allowed_ips'], ipvalue) - end - end - end - table.insert(data[line[1]].peers, peer) - end - end - end + local wg_dump = io.popen("wg show all dump") + if wg_dump then + local line + for line in wg_dump:lines() do + local line = string.split(line, "\t") + if not (last_device == line[1]) then + last_device = line[1] + data[line[1]] = { + name = line[1], + public_key = line[3], + listen_port = line[4], + fwmark = line[5], + peers = { } + } + local s = uci:get_list("network", line[1], "addresses") + local address = "" + local key, value + for key, value in pairs(s) do + if address ~= "" then + address = address.. ", " ..value + else + address = value + end + end + enc[line[1]] = "[Interface]\nPrivateKey = " ..line[2].. "\nAddress = " ..address + else + local peer = { + public_key = line[2], + endpoint = line[4], + allowed_ips = { }, + latest_handshake = line[6], + transfer_rx = line[7], + transfer_tx = line[8], + persistent_keepalive = line[9] + } + if not (line[4] == '(none)') then + local ipkey, ipvalue + for ipkey, ipvalue in pairs(string.split(line[5], ",")) do + if #ipvalue > 0 then + table.insert(peer['allowed_ips'], ipvalue) + end + end + end + table.insert(data[line[1]].peers, peer) + enc[line[1]] = enc[line[1]].. "\n\n[Peer]\nEndpoint = " ..line[4].. "\nPublicKey = " ..line[2].. "\nAllowedIPs = " ..line[5] + end + end + end - if luci.http.formvalue("status") == "1" then - luci.http.prepare_content("application/json") - luci.http.write_json(data) - return - end + if luci.http.formvalue("status") == "1" then + luci.http.prepare_content("application/json") + luci.http.write_json(data) + return + end -%> <%+header%> <script type="text/javascript">//<![CDATA[ - function bytes_to_str(bytes) { - bytes = parseFloat(bytes); - if (bytes < 1) { return "0 B"; } - var sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']; - var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); - return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]; - }; + function bytes_to_str(bytes) { + bytes = parseFloat(bytes); + if (bytes < 1) { return "0 B"; } + var sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']; + var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); + return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]; + }; - function timestamp_to_str(timestamp) { - if (timestamp < 1) { - return '<%:Never%>'; - } - var now = new Date(); - var seconds = (now.getTime() / 1000) - timestamp; - var ago = ""; - if (seconds < 60) { - ago = parseInt(seconds) + '<%:s ago%>'; - } else if (seconds < 3600) { - ago = parseInt(seconds / 60) + '<%:m ago%>'; - } else if (seconds < 86401) { - ago = parseInt(seconds / 3600) + '<%:h ago%>'; - } else { - ago = '<%:over a day ago%>'; - } - var t = new Date(timestamp * 1000); - return t.toUTCString() + ' (' + ago + ')'; - } + function timestamp_to_str(timestamp) { + if (timestamp < 1) { + return '<%:Never%>'; + } + var now = new Date(); + var seconds = (now.getTime() / 1000) - timestamp; + var ago = ""; + if (seconds < 60) { + ago = parseInt(seconds) + '<%:s ago%>'; + } else if (seconds < 3600) { + ago = parseInt(seconds / 60) + '<%:m ago%>'; + } else if (seconds < 86401) { + ago = parseInt(seconds / 3600) + '<%:h ago%>'; + } else { + ago = '<%:over a day ago%>'; + } + var t = new Date(timestamp * 1000); + return t.toUTCString() + ' (' + ago + ')'; + } - XHR.poll(5, '<%=REQUEST_URI%>', { status: 1 }, - function(x, data) { - for (var key in data) { - if (!data.hasOwnProperty(key)) { continue; } - var ifname = key; - var iface = data[key]; - var s = ""; - if (iface.public_key == '(none)') { - s += '<em><%:Interface does not have a public key!%></em>'; - } else { - s += String.format( - '<strong><%:Public Key%>: </strong>%s', - iface.public_key - ); - } - if (iface.listen_port > 0) { - s += String.format( - '<br /><strong><%:Listen Port%>: </strong>%s', - iface.listen_port - ); - } - if (iface.fwmark != 'off') { - s += String.format( - '<br /><strong><%:Firewall Mark%>: </strong>%s', - iface.fwmark - ); - } - document.getElementById(ifname + "_info").innerHTML = s; - for (var i = 0, ilen = iface.peers.length; i < ilen; i++) { - var peer = iface.peers[i]; - var s = String.format( - '<strong><%:Public Key%>: </strong>%s', - peer.public_key - ); - if (peer.endpoint != '(none)') { - s += String.format( - '<br /><strong><%:Endpoint%>: </strong>%s', - peer.endpoint - ); - } - if (peer.allowed_ips.length > 0) { - s += '<br /><strong><%:Allowed IPs%>:</strong>'; - for (var k = 0, klen = peer.allowed_ips.length; k < klen; k++) { - s += '<br /> • ' + peer.allowed_ips[k]; - } - } - if (peer.persistent_keepalive != 'off') { - s += String.format( - '<br /><strong><%:Persistent Keepalive%>: </strong>%ss', - peer.persistent_keepalive - ); - } - var icon = '<img src="<%=resource%>/icons/tunnel_disabled.png" />'; - var now = new Date(); - if (((now.getTime() / 1000) - peer.latest_handshake) < 140) { - icon = '<img src="<%=resource%>/icons/tunnel.png" />'; - } - s += String.format( - '<br /><strong><%:Latest Handshake%>: </strong>%s', - timestamp_to_str(peer.latest_handshake) - ); - s += String.format( - '<br /><strong><%:Data Received%>: </strong>%s' + - '<br /><strong><%:Data Transmitted%>: </strong>%s', - bytes_to_str(peer.transfer_rx), - bytes_to_str(peer.transfer_tx) - ); - document.getElementById(ifname + "_" + peer.public_key + "_icon").innerHTML = icon; - document.getElementById(ifname + "_" + peer.public_key + "_info").innerHTML = s; - } - } - }); + function toggle_qrcode(iface) { + var view = document.getElementById(iface.name); + if (view.style.display === "none") { + view.style.display = "block"; + } else { + view.style.display = "none"; + } + } + + XHR.poll(5, '<%=REQUEST_URI%>', { status: 1 }, + function(x, data) { + for (var key in data) { + if (!data.hasOwnProperty(key)) { continue; } + var ifname = key; + var iface = data[key]; + var s = ""; + if (iface.public_key == '(none)') { + s += '<em><%:Interface does not have a public key!%></em>'; + } else { + s += String.format( + '<strong><%:Public Key%>: </strong>%s', + iface.public_key + ); + } + if (iface.listen_port > 0) { + s += String.format( + '<br /><strong><%:Listen Port%>: </strong>%s', + iface.listen_port + ); + } + if (iface.fwmark != 'off') { + s += String.format( + '<br /><strong><%:Firewall Mark%>: </strong>%s', + iface.fwmark + ); + } + document.getElementById(ifname + "_info").innerHTML = s; + for (var i = 0, ilen = iface.peers.length; i < ilen; i++) { + var peer = iface.peers[i]; + var s = String.format( + '<strong><%:Public Key%>: </strong>%s', + peer.public_key + ); + if (peer.endpoint != '(none)') { + s += String.format( + '<br /><strong><%:Endpoint%>: </strong>%s', + peer.endpoint + ); + } + if (peer.allowed_ips.length > 0) { + s += '<br /><strong><%:Allowed IPs%>:</strong>'; + for (var k = 0, klen = peer.allowed_ips.length; k < klen; k++) { + s += '<br />  • ' + peer.allowed_ips[k]; + } + } + if (peer.persistent_keepalive != 'off') { + s += String.format( + '<br /><strong><%:Persistent Keepalive%>: </strong>%ss', + peer.persistent_keepalive + ); + } + var icon = '<img src="<%=resource%>/icons/tunnel_disabled.png" />'; + var now = new Date(); + if (((now.getTime() / 1000) - peer.latest_handshake) < 140) { + icon = '<img src="<%=resource%>/icons/tunnel.png" />'; + } + s += String.format( + '<br /><strong><%:Latest Handshake%>: </strong>%s', + timestamp_to_str(peer.latest_handshake) + ); + s += String.format( + '<br /><strong><%:Data Received%>: </strong>%s' + + '<br /><strong><%:Data Transmitted%>: </strong>%s', + bytes_to_str(peer.transfer_rx), + bytes_to_str(peer.transfer_tx), + ); + document.getElementById(ifname + "_" + peer.public_key + "_icon").innerHTML = icon; + document.getElementById(ifname + "_" + peer.public_key + "_info").innerHTML = s; + } + } + }); //]]></script> <h2>WireGuard Status</h2> -<fieldset class="cbi-section"> +<div class="cbi-section"> <%- +local ikey, iface for ikey, iface in pairs(data) do - -%> - <legend><%:Interface%> <%=ikey%></legend> - <div class="table" width="100%" cellspacing="10"> - <div class="tr"> - <div class="td" width="33%" style="vertical-align:top"><%:Configuration%></div> - <div class="td"> - <div class="table"> - <div class="tr"> - <div class="td" id="<%=ikey%>_icon" style="width:16px; text-align:center; padding:3px"> - - </div> - <div class="td" id="<%=ikey%>_info" style="vertical-align:middle; padding: 3px"> - <em><%:Collecting data...%></em> - </div> - </div></div> - </div> - </div> - <%- - local cur = uci.cursor() - for pkey, peer in pairs(iface.peers) do - local desc, tmp_desc, pub_key = "", "", "" - cur:foreach("network", "wireguard_" .. ikey, function(s) - local tmp_desc, pub_key = "", "" - for key, value in pairs(s) do - if key == "description" then - tmp_desc = value - end - if value == peer.public_key then - pub_key = value - end - if pub_key == peer.public_key and tmp_desc ~= "" then - desc = ': ' .. tmp_desc - end - end - end) - -%> - <div class="tr"> - <div class="td" width="33%" style="vertical-align:top"><%:Peer%><%=desc%></div> - <div class="td"> - <div class="table"> - <div class="tr"> - <div class="td" id="<%=ikey%>_<%=peer.public_key%>_icon" style="width:16px; text-align:center; padding:3px"> - <img src="<%=resource%>/icons/tunnel_disabled.png" /><br /> - <small>?</small> - </div> - <div class="td" id="<%=ikey%>_<%=peer.public_key%>_info" style="vertical-align:middle; padding: 3px"> - <em><%:Collecting data...%></em> - </div> - </div></div> - </div> - </div> - <%- - end - -%> - </div> - <%- +-%> + <h3><%:Interface%> <%=ikey%></h3> + <div class="cbi-value" id="button" style="padding: 5px"> + <input class="cbi-button cbi-button-apply" type="button" name="qrcode_<%=ikey%>" value="<%:Show/Hide QR-Code%>" onclick="toggle_qrcode(this)" /> + </div> +<%- + local qrcode + if fs.access("/usr/bin/qrencode") then + if enc[ikey]:sub(26,31) ~= "(none)" then + qrcode = luci.sys.exec("/usr/bin/qrencode --inline --8bit --type=SVG --output=- '" ..enc[ikey].. "'") + end + else + qrcode = "<em>For QR-Code support please install package 'qrencode'!</em>" + end +-%> + <div class="cbi-value-title"> + <span class="cbi-value" style="display: none" id="qrcode_<%=ikey%>"><%=qrcode%></span> + </div> + <div class="cbi-section-node"> + <div class="table cbi-section-table"> + <div class="tr cbi-section-table-row" style="text-align: left;"> + <div class="td" style="text-align: left; vertical-align:top"><%:Configuration%></div> + <div class="td" style="flex: 0 1 90%; text-align: left;"> + <div class="table cbi-section-table" style="border: 0px;"> + <div class="tr cbi-section-table-row" style="text-align: left; border: 0px;"> + <div class="td" id="<%=ikey%>_icon" style="width: 22px; text-align: left; border-top: 0px; padding: 3px;"> </div> + <div class="td" id="<%=ikey%>_info" style="flex: 0 1 90%; text-align: left; vertical-align:middle; padding: 3px; border-top: 0px;"><em><%:Collecting data...%></em></div> + </div> + </div> + </div> + </div> + <%- + local cur = uci.cursor() + local pkey, peer + for pkey, peer in pairs(iface.peers) do + local desc + cur:foreach("network", "wireguard_" .. ikey, function(s) + local key, value, tmp_desc, pub_key + for key, value in pairs(s) do + if key == "description" then + tmp_desc = value + end + if value == peer.public_key then + pub_key = value + end + if pub_key and tmp_desc then + desc = ': ' ..tmp_desc + end + end + end) + -%> + <div class="tr cbi-section-table-row" style="text-align: left;"> + <div class="td" style="text-align: left; vertical-align:top"><%:Peer%><%=desc%></div> + <div class="td" style="flex: 0 1 90%; text-align: left;"> + <div class="table cbi-section-table" style="border: 0px"> + <div class="tr cbi-section-table-row" style="border: 0px;"> + <div class="td" id="<%=ikey%>_<%=peer.public_key%>_icon" style="width:16px; text-align: left; padding: 3px;border-top: 0px;"> + <img src="<%=resource%>/icons/tunnel_disabled.png" /> + <small>?</small> + </div> + <div class="td" id="<%=ikey%>_<%=peer.public_key%>_info" style="flex: 0 1 90%; text-align: left; vertical-align:middle; padding: 3px;border-top: 0px;"><em><%:Collecting data...%></em></div> + </div> + </div> + </div> + </div> + <%- + end + -%> + </div> + </div> + <%- end -%> -</fieldset> +</div> <%+footer%> |