luci-app-travelmate: enhancements & fixes
[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
10   local wg_dump = io.popen("wg show all dump")
11   if wg_dump then
12     local line
13     for line in wg_dump:lines() do
14       local line = string.split(line, "\t")
15       if not (last_device == line[1]) then
16         last_device = line[1]
17         data[line[1]] = {
18           name                 = line[1],
19           public_key           = line[3],
20           listen_port          = line[5],
21           fwmark               = line[6],
22           peers                = { }
23         }
24       else
25         local peer = {
26           public_key           = line[2],
27           endpoint             = line[3],
28           allowed_ips          = { },
29           latest_handshake     = line[5],
30           transfer_rx          = line[6],
31           transfer_tx          = line[7],
32           persistent_keepalive = line[8]
33         }
34         if not (line[4] == '(none)') then
35           for ipkey, ipvalue in pairs(string.split(line[4], ",")) do
36             if #ipvalue > 0 then
37               table.insert(peer['allowed_ips'], ipvalue)
38             end
39           end
40         end
41         table.insert(data[line[1]].peers, peer)
42       end
43     end
44   end
45
46   if luci.http.formvalue("status") == "1" then
47     luci.http.prepare_content("application/json")
48     luci.http.write_json(data)
49     return
50   end
51 -%>
52
53 <%+header%>
54
55 <script type="text/javascript" src="<%=resource%>/cbi.js"></script>
56 <script type="text/javascript">//<![CDATA[
57
58   function bytes_to_str(bytes) {
59     bytes = parseFloat(bytes);
60     if (bytes < 1) { return "0 B"; }
61     var sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];
62     var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
63     return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
64   };
65
66   function timestamp_to_str(timestamp) {
67     if (timestamp < 1) {
68       return '<%:Never%>';
69     }
70     var now = new Date();
71     var seconds = (now.getTime() / 1000) - timestamp;
72     var ago = "";
73     if (seconds < 60) {
74       ago = parseInt(seconds) + '<%:s ago%>';
75     } else if (seconds < 3600) {
76       ago = parseInt(seconds / 60) + '<%:m ago%>';
77     } else if (seconds < 86401) {
78       ago = parseInt(seconds / 3600) + '<%:h ago%>';
79     } else {
80       ago = '<%:over a day ago%>';
81     }
82     var t = new Date(timestamp * 1000);
83     return t.toUTCString() + ' (' + ago + ')';
84   }
85
86   XHR.poll(5, '<%=REQUEST_URI%>', { status: 1 },
87    function(x, data) {
88     for (var key in data) {
89       if (!data.hasOwnProperty(key)) { continue; }
90       var ifname = key;
91       var iface = data[key];
92       var s = "";
93       if (iface.public_key == '(none)') {
94         s += '<em><%:Interface does not have a public key!%></em>';
95       } else {
96         s += String.format(
97           '<strong><%:Public Key%>: </strong>%s',
98           iface.public_key
99         );
100       }
101       if (iface.listen_port > 0) {
102         s += String.format(
103           '<br /><strong><%:Listen Port%>: </strong>%s',
104           iface.listen_port
105         );
106       }
107       if (iface.fwmark != 'off') {
108         s += String.format(
109           '<br /><strong><%:Firewall Mark%>: </strong>%s',
110           iface.fwmark
111         );
112       }
113       document.getElementById(ifname + "_info").innerHTML = s;
114       for (var i = 0, ilen = iface.peers.length; i < ilen; i++) {
115         var peer = iface.peers[i];
116         var s = String.format(
117           '<strong><%:Public Key%>: </strong>%s',
118           peer.public_key
119         );
120         if (peer.endpoint != '(none)') {
121           s += String.format(
122             '<br /><strong><%:Endpoint%>: </strong>%s',
123             peer.endpoint
124           );
125         }
126         if (peer.allowed_ips.length > 0) {
127           s += '<br /><strong><%:Allowed IPs%>:</strong>';
128           for (var k = 0, klen = peer.allowed_ips.length; k < klen; k++) {
129             s += '<br />&nbsp;&nbsp;&bull;&nbsp;' + peer.allowed_ips[k];
130           }
131         }
132         if (peer.persistent_keepalive != 'off') {
133           s += String.format(
134             '<br /><strong><%:Persistent Keepalive%>: </strong>%ss',
135             peer.persistent_keepalive
136           );
137         }
138         var icon = '<img src="<%=resource%>/icons/tunnel_disabled.png" />';
139         var now = new Date();
140         if (((now.getTime() / 1000) - peer.latest_handshake) < 140) {
141           icon = '<img src="<%=resource%>/icons/tunnel.png" />';
142         }
143         s += String.format(
144           '<br /><strong><%:Latest Handshake%>: </strong>%s',
145           timestamp_to_str(peer.latest_handshake)
146         );
147         s += String.format(
148           '<br /><strong><%:Data Received%>: </strong>%s' +
149           '<br /><strong><%:Data Transmitted%>: </strong>%s',
150           bytes_to_str(peer.transfer_rx),
151           bytes_to_str(peer.transfer_tx)
152         );
153         document.getElementById(ifname + "_" + peer.public_key + "_icon").innerHTML = icon;
154         document.getElementById(ifname + "_" + peer.public_key + "_info").innerHTML = s;
155       }
156     }
157   });
158 //]]></script>
159
160 <h2>WireGuard Status</h2>
161
162 <fieldset class="cbi-section">
163 <%-
164 for ikey, iface in pairs(data) do
165   -%>
166   <legend><%:Interface%> <%=ikey%></legend>
167   <table width="100%" cellspacing="10">
168     <tr>
169       <td width="33%" style="vertical-align:top"><%:Configuration%></td>
170       <td>
171         <table>
172           <tr>
173             <td id="<%=ikey%>_icon" style="width:16px; text-align:center; padding:3px">
174               &nbsp;
175             </td>
176             <td id="<%=ikey%>_info" style="vertical-align:middle; padding: 3px">
177               <em><%:Collecting data...%></em>
178             </td>
179         </tr></table>
180       </td>
181     </tr>
182   <%-
183   for pkey, peer in pairs(iface.peers) do
184     -%>
185     <tr>
186       <td width="33%" style="vertical-align:top"><%:Peer%></td>
187       <td>
188         <table>
189           <tr>
190             <td id="<%=ikey%>_<%=peer.public_key%>_icon" style="width:16px; text-align:center; padding:3px">
191               <img src="<%=resource%>/icons/tunnel_disabled.png" /><br />
192               <small>?</small>
193             </td>
194             <td id="<%=ikey%>_<%=peer.public_key%>_info" style="vertical-align:middle; padding: 3px">
195               <em><%:Collecting data...%></em>
196             </td>
197         </tr></table>
198       </td>
199     </tr>
200     <%-
201   end
202   -%>
203   </table>
204   <%-
205 end
206 -%>
207 </fieldset>
208
209 <%+footer%>