status.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. 'use strict';
  2. 'require view';
  3. 'require rpc';
  4. 'require poll';
  5. 'require dom';
  6. 'require ui';
  7. var callGetWgInstances = rpc.declare({
  8. object: 'luci.wireguard',
  9. method: 'getWgInstances'
  10. });
  11. function timestampToStr(timestamp) {
  12. if (timestamp < 1)
  13. return _('Never', 'No WireGuard peer handshake yet');
  14. var seconds = (Date.now() / 1000) - timestamp;
  15. var ago;
  16. if (seconds < 60)
  17. ago = _('%ds ago').format(seconds);
  18. else if (seconds < 3600)
  19. ago = _('%dm ago').format(seconds / 60);
  20. else if (seconds < 86401)
  21. ago = _('%dh ago').format(seconds / 3600);
  22. else
  23. ago = _('over a day ago');
  24. return (new Date(timestamp * 1000)).toUTCString() + ' (' + ago + ')';
  25. }
  26. function handleInterfaceDetails(iface) {
  27. ui.showModal(_('Instance Details'), [
  28. ui.itemlist(E([]), [
  29. _('Name'), iface.name,
  30. _('Public Key'), E('code', [ iface.public_key ]),
  31. _('Listen Port'), iface.listen_port,
  32. _('Firewall Mark'), iface.fwmark != 'off' ? iface.fwmark : E('em', _('none'))
  33. ]),
  34. E('div', { 'class': 'right' }, [
  35. E('button', {
  36. 'class': 'btn cbi-button',
  37. 'click': ui.hideModal
  38. }, [ _('Dismiss') ])
  39. ])
  40. ]);
  41. }
  42. function handlePeerDetails(peer) {
  43. ui.showModal(_('Peer Details'), [
  44. ui.itemlist(E([]), [
  45. _('Description'), peer.name,
  46. _('Public Key'), E('code', [ peer.public_key ]),
  47. _('Endpoint'), peer.endpoint,
  48. _('Allowed IPs'), (Array.isArray(peer.allowed_ips) && peer.allowed_ips.length) ? peer.allowed_ips.join(', ') : E('em', _('none')),
  49. _('Received Data'), '%1024mB'.format(peer.transfer_rx),
  50. _('Transmitted Data'), '%1024mB'.format(peer.transfer_tx),
  51. _('Latest Handshake'), timestampToStr(+peer.latest_handshake),
  52. _('Keep-Alive'), (peer.persistent_keepalive != 'off') ? _('every %ds', 'WireGuard keep alive interval').format(+peer.persistent_keepalive) : E('em', _('none')),
  53. ]),
  54. E('div', { 'class': 'right' }, [
  55. E('button', {
  56. 'class': 'btn cbi-button',
  57. 'click': ui.hideModal
  58. }, [ _('Dismiss') ])
  59. ])
  60. ]);
  61. }
  62. function renderPeerTable(instanceName, peers) {
  63. var t = new L.ui.Table(
  64. [
  65. _('Peer'),
  66. _('Endpoint'),
  67. _('Data Received'),
  68. _('Data Transmitted'),
  69. _('Latest Handshake')
  70. ],
  71. {
  72. id: 'peers-' + instanceName
  73. },
  74. E('em', [
  75. _('No peers connected')
  76. ])
  77. );
  78. t.update(peers.map(function(peer) {
  79. return [
  80. [
  81. peer.name || '',
  82. E('div', {
  83. 'style': 'cursor:pointer',
  84. 'click': ui.createHandlerFn(this, handlePeerDetails, peer)
  85. }, [
  86. E('p', [
  87. peer.name ? E('span', [ peer.name ]) : E('em', [ _('Untitled peer') ])
  88. ]),
  89. E('span', {
  90. 'class': 'ifacebadge hide-sm',
  91. 'data-tooltip': _('Public key: %h', 'Tooltip displaying full WireGuard peer public key').format(peer.public_key)
  92. }, [
  93. E('code', [ peer.public_key.replace(/^(.{5}).+(.{6})$/, '$1…$2') ])
  94. ])
  95. ])
  96. ],
  97. peer.endpoint,
  98. [ +peer.transfer_rx, '%1024mB'.format(+peer.transfer_rx) ],
  99. [ +peer.transfer_tx, '%1024mB'.format(+peer.transfer_tx) ],
  100. [ +peer.latest_handshake, timestampToStr(+peer.latest_handshake) ]
  101. ];
  102. }));
  103. return t.render();
  104. }
  105. return view.extend({
  106. renderIfaces: function(ifaces) {
  107. var res = [
  108. E('h2', [ _('WireGuard Status') ])
  109. ];
  110. for (var instanceName in ifaces) {
  111. res.push(
  112. E('h3', [ _('Instance "%h"', 'WireGuard instance heading').format(instanceName) ]),
  113. E('p', {
  114. 'style': 'cursor:pointer',
  115. 'click': ui.createHandlerFn(this, handleInterfaceDetails, ifaces[instanceName])
  116. }, [
  117. E('span', { 'class': 'ifacebadge' }, [
  118. E('img', { 'src': L.resource('icons', 'tunnel.png') }),
  119. '\xa0',
  120. instanceName
  121. ]),
  122. E('span', { 'style': 'opacity:.8' }, [
  123. ' · ',
  124. _('Port %d', 'WireGuard listen port').format(ifaces[instanceName].listen_port),
  125. ' · ',
  126. E('code', { 'click': '' }, [ ifaces[instanceName].public_key ])
  127. ])
  128. ]),
  129. renderPeerTable(instanceName, ifaces[instanceName].peers)
  130. );
  131. }
  132. if (res.length == 1)
  133. res.push(E('p', { 'class': 'center', 'style': 'margin-top:5em' }, [
  134. E('em', [ _('No WireGuard interfaces configured.') ])
  135. ]));
  136. return E([], res);
  137. },
  138. render: function() {
  139. poll.add(L.bind(function () {
  140. return callGetWgInstances().then(L.bind(function(ifaces) {
  141. dom.content(
  142. document.querySelector('#view'),
  143. this.renderIfaces(ifaces)
  144. );
  145. }, this));
  146. }, this), 5);
  147. return E([], [
  148. E('h2', [ _('WireGuard Status') ]),
  149. E('p', { 'class': 'center', 'style': 'margin-top:5em' }, [
  150. E('em', [ _('Loading data…') ])
  151. ])
  152. ]);
  153. },
  154. handleReset: null,
  155. handleSaveApply: null,
  156. handleSave: null
  157. });