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