When no wireguard interface is configured, the wireguard status page will simply display a blank page only containing a heading. Print a short notice stating that no interfaces are configured. Also use actual table elements for table markup while we're at it. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
228 lines
5.7 KiB
JavaScript
228 lines
5.7 KiB
JavaScript
'use strict';
|
|
'require view';
|
|
'require rpc';
|
|
'require form';
|
|
'require poll';
|
|
|
|
|
|
var callGetWgInstances = rpc.declare({
|
|
object: 'luci.wireguard',
|
|
method: 'getWgInstances'
|
|
});
|
|
|
|
function timestampToStr(timestamp) {
|
|
if (timestamp < 1) {
|
|
return _('Never');
|
|
}
|
|
var now = new Date();
|
|
var seconds = (now.getTime() / 1000) - timestamp;
|
|
var ago = '';
|
|
if (seconds < 60) {
|
|
ago = _('%ds ago').format(parseInt(seconds));
|
|
} else if (seconds < 3600) {
|
|
ago = _('%dm ago').format(parseInt(seconds / 60));
|
|
} else if (seconds < 86401) {
|
|
ago = _('%dh ago').format(parseInt(seconds / 3600));
|
|
} else {
|
|
ago = _('over a day ago');
|
|
}
|
|
var t = new Date(timestamp * 1000);
|
|
return t.toUTCString() + ' (' + ago + ')';
|
|
}
|
|
|
|
function generatePeerOption(key, title, value) {
|
|
return E('div', { 'class': 'cbi-value', 'style': 'padding: 0;' }, [
|
|
E('label', {
|
|
'class': 'cbi-value-title', 'style': 'font-weight: bold;'
|
|
}, title),
|
|
E('input', {
|
|
'class': 'cbi-input-text',
|
|
'data-name': key,
|
|
'style': 'border: none; float: left; width: 50%;',
|
|
'disabled': '',
|
|
'value': value
|
|
})
|
|
]);
|
|
}
|
|
|
|
function generatePeerTable(options, iconSrc) {
|
|
return E('div', { 'class': 'table cbi-section-table' }, [
|
|
E('div', { 'class': 'td' },
|
|
E('img', { 'src': iconSrc, 'class': 'tunnel-icon' })
|
|
),
|
|
E('div', { 'class': 'td peer-options' },
|
|
options.filter(function (option) {
|
|
return option[2] != null;
|
|
}).map(function (option) {
|
|
return generatePeerOption.apply(null, option);
|
|
})
|
|
)
|
|
]);
|
|
}
|
|
|
|
function getTunnelIcon(latestHandshake) {
|
|
var img = (new Date().getTime() / 1000 - latestHandshake) < 140 ?
|
|
'tunnel' : 'tunnel_disabled';
|
|
|
|
return L.resource('icons', img + '.png');
|
|
}
|
|
|
|
function generatePeerRows(peers) {
|
|
var peerRows = [];
|
|
|
|
peers.forEach(function (peer) {
|
|
var peerData = parsePeerData(peer);
|
|
var iconSrc = getTunnelIcon(peer.latest_handshake);
|
|
|
|
peerRows.push(E('tr', {
|
|
'class': 'tr cbi-section-table-row'
|
|
}, [
|
|
E('td', {
|
|
'class': 'td peer-name',
|
|
'style': 'width: 25%; font-size: 0.9rem;'
|
|
}, peer.name),
|
|
E('td', { 'class': 'td', 'data-section-id': peer.name },
|
|
generatePeerTable(peerData, iconSrc)
|
|
)
|
|
]));
|
|
});
|
|
|
|
if (!peerRows.length) {
|
|
peerRows.push(
|
|
E('tr', { 'class': 'tr placeholder' },
|
|
E('td', { 'class': 'td' },
|
|
E('em', _('No peer information available')))));
|
|
}
|
|
|
|
return peerRows;
|
|
}
|
|
|
|
function parseIfaceData(iface) {
|
|
return [
|
|
['public_key', _('Public Key'),
|
|
iface.public_key != '(none)' ? iface.public_key : null],
|
|
['listen_port', _('Listen Port'),
|
|
iface.listen_port > 0 ? iface.listen_port : null],
|
|
['fwmark', _('Firewall Mark'),
|
|
iface.fwmark != 'off' ? iface.fwmark : null]
|
|
];
|
|
}
|
|
|
|
function parsePeerData(peer) {
|
|
return [
|
|
['public_key', _('Public Key'),
|
|
peer.public_key],
|
|
['endpoint', _('Endpoint'),
|
|
peer.endpoint == '(none)' ? null : peer.endpoint],
|
|
['allowed_ips', _('Allowed IPs'),
|
|
peer.allowed_ips.length == 0 ? null : peer.allowed_ips.join('\n')],
|
|
['persistent_keepalive', _('Persistent Keepalive'),
|
|
peer.persistent_keepalive == 'off' ? null : peer.persistent_keepalive + 's'],
|
|
['latest_handshake', _('Latest Handshake'),
|
|
timestampToStr(peer.latest_handshake)],
|
|
['transfer_rx', _('Data Received'),
|
|
'%1024mB'.format(peer.transfer_rx)],
|
|
['transfer_tx', _('Data Transmitted'),
|
|
'%1024mB'.format(peer.transfer_tx)]
|
|
];
|
|
}
|
|
|
|
return view.extend({
|
|
load: function () {
|
|
return callGetWgInstances();
|
|
},
|
|
|
|
poll_status: function (nodes, ifaces) {
|
|
Object.keys(ifaces).forEach(function (ifaceName) {
|
|
var iface = ifaces[ifaceName];
|
|
|
|
var section = nodes.querySelector(
|
|
'[data-section-id="%q"]'.format(ifaceName)
|
|
);
|
|
|
|
parseIfaceData(iface).forEach(function (option) {
|
|
if (option[2] != null) {
|
|
var optionEl = section.querySelector(
|
|
'[data-name="%q"]'.format(option[0])
|
|
);
|
|
var inputEl = optionEl.querySelector('input');
|
|
|
|
inputEl.value = option[2];
|
|
}
|
|
});
|
|
|
|
iface.peers.forEach(function (peer) {
|
|
var peerData = parsePeerData(peer);
|
|
var iconSrc = getTunnelIcon(peer.latest_handshake);
|
|
|
|
var peerSection = section.querySelector(
|
|
'[data-section-id="%q"]'.format(peer.name)
|
|
);
|
|
var iconEl = peerSection.querySelector('.tunnel-icon');
|
|
iconEl.src = iconSrc;
|
|
|
|
peerData.forEach(function (option) {
|
|
if (option[2]) {
|
|
var inputEl = peerSection.querySelector(
|
|
'[data-name="%q"]'.format(option[0])
|
|
);
|
|
inputEl.value = option[2];
|
|
}
|
|
})
|
|
});
|
|
});
|
|
},
|
|
|
|
render: function (ifaces) {
|
|
var m, s, o, ss;
|
|
|
|
m = new form.JSONMap(ifaces, _('WireGuard Status'));
|
|
m.tabbed = true;
|
|
|
|
var ifaceNames = Object.keys(ifaces);
|
|
|
|
for (var i = ifaceNames.length - 1; i >= 0; i--) {
|
|
var ifaceName = ifaceNames[i];
|
|
var iface = ifaces[ifaceName];
|
|
|
|
s = m.section(form.TypedSection, ifaceName);
|
|
s.tabbed = true;
|
|
s.anonymous = true;
|
|
|
|
var ifaceData = parseIfaceData(iface);
|
|
ifaceData.forEach(function (option) {
|
|
if (option[2] != null) {
|
|
o = s.option(form.Value, option[0], option[1]);
|
|
o.readonly = true;
|
|
}
|
|
});
|
|
|
|
o = s.option(form.SectionValue, 'peers', form.TypedSection, 'peers');
|
|
ss = o.subsection;
|
|
|
|
ss.render = L.bind(function (view, section_id) {
|
|
return E('div', { 'class': 'cbi-section' }, [
|
|
E('h3', _('Peers')),
|
|
E('table', { 'class': 'table cbi-section-table' },
|
|
generatePeerRows(this.peers))
|
|
]);
|
|
}, iface, this);
|
|
}
|
|
|
|
return m.render().then(L.bind(function (m, nodes) {
|
|
if (!ifaceNames.length)
|
|
nodes.appendChild(E('p', {}, E('em', _('No WireGuard interfaces configured.'))));
|
|
|
|
poll.add(L.bind(function () {
|
|
return callGetWgInstances().then(
|
|
L.bind(this.poll_status, this, nodes)
|
|
);
|
|
}, this), 5);
|
|
return nodes;
|
|
}, this, m));
|
|
},
|
|
|
|
handleReset: null,
|
|
handleSaveApply: null,
|
|
handleSave: null
|
|
});
|