luci-app-wireguard: fix btn style
[project/luci.git] / applications / luci-app-wireguard / luasrc / view / wireguard.htm
1 <%#
2 Copyright 2016-2017 Dan Luedtke <mail@danrl.com>
3 Licensed to the public under the Apache License 2.0.
4 -%>
5
6 <%
7 local data = { }
8 local last_device = ""
9 local qr_pubkey = { }
10
11 local function qr_clean(qr_type, value)
12 if not value or value == "" or value == "(none)" then
13 return ""
14 end
15 if qr_type == "privkey" then
16 return "PrivateKey = " ..value
17 elseif qr_type == "pubkey" then
18 return "PublicKey = " ..value
19 end
20 end
21
22 local wg_dump = io.popen("wg show all dump 2>/dev/null")
23 if wg_dump then
24 local line
25 for line in wg_dump:lines() do
26 local line = string.split(line, "\t")
27 if not (last_device == line[1]) then
28 last_device = line[1]
29 data[line[1]] = {
30 name = line[1],
31 public_key = line[3],
32 listen_port = line[4],
33 fwmark = line[5],
34 peers = { }
35 }
36 qr_pubkey[line[1]] = qr_clean("pubkey", line[3])
37 else
38 local peer = {
39 public_key = line[2],
40 endpoint = line[4],
41 allowed_ips = { },
42 latest_handshake = line[6],
43 transfer_rx = line[7],
44 transfer_tx = line[8],
45 persistent_keepalive = line[9]
46 }
47 if not (line[4] == '(none)') then
48 local ipkey, ipvalue
49 for ipkey, ipvalue in pairs(string.split(line[5], ",")) do
50 if #ipvalue > 0 then
51 table.insert(peer['allowed_ips'], ipvalue)
52 end
53 end
54 end
55 table.insert(data[line[1]].peers, peer)
56 end
57 end
58 end
59
60 if luci.http.formvalue("status") == "1" then
61 luci.http.prepare_content("application/json")
62 luci.http.write_json(data)
63 return
64 end
65 -%>
66
67 <%+header%>
68
69 <script type="text/javascript">//<![CDATA[
70
71 function bytes_to_str(bytes) {
72 bytes = parseFloat(bytes);
73 if (bytes < 1) { return "0 B"; }
74 var sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];
75 var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
76 return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
77 };
78
79 function timestamp_to_str(timestamp) {
80 if (timestamp < 1) {
81 return '<%:Never%>';
82 }
83 var now = new Date();
84 var seconds = (now.getTime() / 1000) - timestamp;
85 var ago = "";
86 if (seconds < 60) {
87 ago = parseInt(seconds) + '<%:s ago%>';
88 } else if (seconds < 3600) {
89 ago = parseInt(seconds / 60) + '<%:m ago%>';
90 } else if (seconds < 86401) {
91 ago = parseInt(seconds / 3600) + '<%:h ago%>';
92 } else {
93 ago = '<%:over a day ago%>';
94 }
95 var t = new Date(timestamp * 1000);
96 return t.toUTCString() + ' (' + ago + ')';
97 }
98
99 function toggle_qrcode(iface) {
100 var view = document.getElementById(iface.name);
101 if (view.style.display === "none") {
102 view.style.display = "block";
103 } else {
104 view.style.display = "none";
105 }
106 }
107
108 XHR.poll(-1, '<%=REQUEST_URI%>', { status: 1 },
109 function(x, data) {
110 for (var key in data) {
111 if (!data.hasOwnProperty(key)) { continue; }
112 var ifname = key;
113 var iface = data[key];
114 var s = "";
115 if (iface.public_key == '(none)') {
116 s += '<em><%:Interface does not have a public key!%></em>';
117 } else {
118 s += String.format(
119 '<strong><%:Public Key%>: </strong>%s',
120 iface.public_key
121 );
122 }
123 if (iface.listen_port > 0) {
124 s += String.format(
125 '<br /><strong><%:Listen Port%>: </strong>%s',
126 iface.listen_port
127 );
128 }
129 if (iface.fwmark != 'off') {
130 s += String.format(
131 '<br /><strong><%:Firewall Mark%>: </strong>%s',
132 iface.fwmark
133 );
134 }
135 document.getElementById(ifname + "_info").innerHTML = s;
136 for (var i = 0, ilen = iface.peers.length; i < ilen; i++) {
137 var peer = iface.peers[i];
138 var s = String.format(
139 '<strong><%:Public Key%>: </strong>%s',
140 peer.public_key
141 );
142 if (peer.endpoint != '(none)') {
143 s += String.format(
144 '<br /><strong><%:Endpoint%>: </strong>%s',
145 peer.endpoint
146 );
147 }
148 if (peer.allowed_ips.length > 0) {
149 s += '<br /><strong><%:Allowed IPs%>:</strong>';
150 for (var k = 0, klen = peer.allowed_ips.length; k < klen; k++) {
151 s += '<br />&#160;&#160;&#8226;&#160;' + peer.allowed_ips[k];
152 }
153 }
154 if (peer.persistent_keepalive != 'off') {
155 s += String.format(
156 '<br /><strong><%:Persistent Keepalive%>: </strong>%ss',
157 peer.persistent_keepalive
158 );
159 }
160 var icon = '<img src="<%=resource%>/icons/tunnel_disabled.png" />';
161 var now = new Date();
162 if (((now.getTime() / 1000) - peer.latest_handshake) < 140) {
163 icon = '<img src="<%=resource%>/icons/tunnel.png" />';
164 }
165 s += String.format(
166 '<br /><strong><%:Latest Handshake%>: </strong>%s',
167 timestamp_to_str(peer.latest_handshake)
168 );
169 s += String.format(
170 '<br /><strong><%:Data Received%>: </strong>%s' +
171 '<br /><strong><%:Data Transmitted%>: </strong>%s',
172 bytes_to_str(peer.transfer_rx),
173 bytes_to_str(peer.transfer_tx),
174 );
175 document.getElementById(ifname + "_" + peer.public_key + "_icon").innerHTML = icon;
176 document.getElementById(ifname + "_" + peer.public_key + "_info").innerHTML = s;
177 }
178 }
179 });
180 //]]></script>
181
182 <h2><%:WireGuard Status%></h2>
183
184 <div class="cbi-section">
185
186 <% if next(data) == nil then %>
187 <div class="table cbi-section-table">
188 <div class="tr cbi-section-table-row">
189 <p>
190 <em><%:This section contains no values yet%></em>
191 </p>
192 </div>
193 </div>
194 <% end %>
195
196 <%-
197 local ikey, iface
198 for ikey, iface in pairs(data) do
199 -%>
200 <h3><%:Interface%> <%=ikey%></h3>
201 <div class="cbi-value" id="button" style="padding: 5px">
202 <input class="btn cbi-button cbi-button-apply" type="button" name="qrcode_<%=ikey%>" value="<%:Show/Hide QR-Code%>" onclick="toggle_qrcode(this)" />
203 </div>
204 <%-
205 local qr_enc
206 local qr_code
207 local qr_privkey
208 if fs.access("/usr/bin/qrencode") then
209 qr_privkey = qr_clean("privkey", luci.sys.exec("wg genkey 2>/dev/null"))
210 if qr_pubkey[ikey] and qr_privkey then
211 qr_enc = "[Interface]\n" ..qr_privkey.. "\n[Peer]\n" ..qr_pubkey[ikey].. "\nAllowedIPs = 0.0.0.0/0, ::/0"
212 qr_code = luci.sys.exec("/usr/bin/qrencode --inline --8bit --type=SVG --output=- '" ..qr_enc.. "' 2>/dev/null")
213 else
214 qr_code = "<em>The QR-Code could not be generated, the wg interface setup is incomplete!</em>"
215 end
216 else
217 qr_code = "<em>For QR-Code support please install the package 'qrencode'!</em>"
218 end
219 -%>
220 <div class="cbi-section-node">
221 <span class="cbi-value" style="display: none" id="qrcode_<%=ikey%>">
222 <%:The QR-Code works per wg interface, it will be refreshed with every manual page reload and transfers the following information:%><br />
223 &#8226;&#160;<%:[Interface] A random, on the fly generated 'PrivateKey', the key will not be saved on the router%><br />
224 &#8226;&#160;<%:[Peer] The 'PublicKey' of that wg interface and the 'AllowedIPs' with the default of '0.0.0.0/0, ::/0' to allow sending traffic to any IPv4 and IPv6 address%><br />
225 <hr /><%=qr_code%><br />
226 </span>
227 </div>
228 <div class="cbi-section-node">
229 <div class="table cbi-section-table">
230 <div class="tr cbi-section-table-row" style="text-align: left;">
231 <div class="td" style="text-align: left; vertical-align:top"><%:Configuration%></div>
232 <div class="td" style="flex: 0 1 90%; text-align: left;">
233 <div class="table cbi-section-table" style="border: 0px;">
234 <div class="tr cbi-section-table-row" style="text-align: left; border: 0px;">
235 <div class="td" id="<%=ikey%>_icon" style="width: 22px; text-align: left; border-top: 0px; padding: 3px;">&#160;</div>
236 <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>
237 </div>
238 </div>
239 </div>
240 </div>
241 <%-
242 local cur = uci.cursor()
243 local pkey, peer
244 for pkey, peer in pairs(iface.peers) do
245 local desc
246 cur:foreach("network", "wireguard_" .. ikey, function(s)
247 local key, value, tmp_desc, pub_key
248 for key, value in pairs(s) do
249 if key == "description" then
250 tmp_desc = value
251 end
252 if value == peer.public_key then
253 pub_key = value
254 end
255 if pub_key and tmp_desc then
256 desc = ': ' ..tmp_desc
257 end
258 end
259 end)
260 -%>
261 <div class="tr cbi-section-table-row" style="text-align: left;">
262 <div class="td" style="text-align: left; vertical-align:top"><%:Peer%><%=desc%></div>
263 <div class="td" style="flex: 0 1 90%; text-align: left;">
264 <div class="table cbi-section-table" style="border: 0px">
265 <div class="tr cbi-section-table-row" style="border: 0px;">
266 <div class="td" id="<%=ikey%>_<%=peer.public_key%>_icon" style="width:16px; text-align: left; padding: 3px;border-top: 0px;">
267 <img src="<%=resource%>/icons/tunnel_disabled.png" />
268 <small>?</small>
269 </div>
270 <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>
271 </div>
272 </div>
273 </div>
274 </div>
275 <%-
276 end
277 -%>
278 </div>
279 </div>
280 <%-
281 end
282 -%>
283 </div>
284
285 <%+footer%>