Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • firmware/gluon
  • 0x4A6F/gluon
  • patrick/gluon
3 results
Select Git revision
Loading items
Show changes
Showing
with 457 additions and 144 deletions
#!/bin/busybox sh
# Show the current ntp state using gluon-state-check.
#
# In case of an unsynced ntp session the file has_lost_ntp_sync is created.
# Its atime will be bumped upon further failed synchronizations.
# As long as there is no sync, the file has_ntp_sync is absent.
#
# Vice versa, the file has_ntp_sync is created upon stratum-level <16.
# As long as the sync persists, only the files atime is updated.
# Furthermore, if there is an ntp sync, the file has_lost_ntp_sync is deleted.
#
# This allows the following code to emit the current ntp status and how long its been going on.
# These two variables must not contain spaces in order for the awk splitting to work.
HAS_LOST_NTP_SYNC="/var/gluon/state/has_lost_ntp_sync"
HAS_NTP_SYNC="/var/gluon/state/has_ntp_sync"
get_duration() {
local flag_file
local use_atime
local mtime
local time_epoch
local current_time
flag_file=$1
use_atime=$2
if [ "$use_atime" = true ]; then
ls_option="-u"
fi
# shellcheck disable=SC2012 # stat is unavailable and busybox find does not emit the necessary fields.
mtime=$(ls -l "$ls_option" "$flag_file" --full-time | awk '{print $6, $7}') 2>/dev/null
time_epoch=$(date -d "$mtime" +%s)
current_time=$(date +%s)
echo $((current_time - time_epoch))
}
if [ -f "$HAS_NTP_SYNC" ]; then
duration=$(get_duration "$HAS_NTP_SYNC")
last_checked=$(get_duration "$HAS_NTP_SYNC" true)
echo "NTP has been synced for $duration seconds (last checked $last_checked seconds ago)."
elif [ -f "$HAS_LOST_NTP_SYNC" ]; then
duration=$(get_duration "$HAS_LOST_NTP_SYNC")
last_checked=$(get_duration "$HAS_LOST_NTP_SYNC" true)
echo "NTP has been unsynced for $duration seconds (last checked $last_checked seconds ago)."
else
echo "NTP state is unknown, ntpd hotplugs might not have been invoked, yet."
fi
* * * * * /usr/sbin/gluon-state-check
#!/usr/bin/lua
local util = require 'gluon.util'
local unistd = require 'posix.unistd'
local state_dir = "/var/gluon/state/"
local check_dir = "/lib/gluon/state/check.d/"
local function set_flag(stateflag, state)
if state then
-- this does not modify atime
local flaghandle = io.open(stateflag, "w")
flaghandle:close()
else
os.remove(stateflag)
end
end
local function exec_check(checkpath)
local checkname = string.sub(checkpath, #check_dir+1)
local ret = os.execute(checkpath)
local flagfile = state_dir..checkname
set_flag(flagfile, 0==ret)
end
local function run_executable_checks()
for _, v in ipairs(util.glob(check_dir..'*')) do
if unistd.access(v, 'x') then
exec_check(v)
end
end
end
-- ensure state path exists
if not unistd.access(state_dir) then
os.execute("mkdir -p "..state_dir)
end
run_executable_checks()
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-status-page-mesh-batman-adv
PKG_VERSION:=1
include ../gluon.mk
......
......@@ -54,9 +54,6 @@ static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg)
BATADV_ARRAY_SIZE(parse_orig_list_mandatory)))
return NL_OK;
if (!attrs[BATADV_ATTR_FLAG_BEST])
return NL_OK;
orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]);
dest = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]);
tq = nla_get_u8(attrs[BATADV_ATTR_TQ]);
......@@ -78,6 +75,7 @@ static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg)
json_object_object_add(neigh, "tq", json_object_new_int(tq * 100 / 255));
json_object_object_add(neigh, "ifname", json_object_new_string(ifname));
json_object_object_add(neigh, "best", json_object_new_boolean(attrs[BATADV_ATTR_FLAG_BEST]));
json_object_object_add(opts->obj, mac1, neigh);
......
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-status-page
PKG_VERSION:=3
include ../gluon.mk
......@@ -10,12 +9,20 @@ define Package/gluon-status-page
DEPENDS:=+gluon-web +gluon-neighbour-info +gluon-respondd +uhttpd +sse-multiplex +libiwinfo +libjson-c +libnl-tiny +libubus-lua
endef
PKG_CONFIG_DEPENDS += CONFIG_GLUON_MINIFY
define Package/gluon-status-page/install
$(Gluon/Build/Install)
$(INSTALL_DIR) $(1)/lib/gluon/status-page/providers
$(INSTALL_BIN) $(PKG_BUILD_DIR)/stations $(1)/lib/gluon/status-page/providers/
ifdef CONFIG_GLUON_MINIFY
$(INSTALL_DATA) ./javascript/status-page.min.js $(1)/lib/gluon/status-page/www/static/status-page.js
else
$(INSTALL_DATA) ./javascript/status-page.js $(1)/lib/gluon/status-page/www/static/status-page.js
endif
$(INSTALL_DIR) $(1)/lib/gluon/status-page/view/
$(LN) /lib/gluon/web/i18n $(1)/lib/gluon/status-page/
$(LN) /lib/gluon/web/view/error $(1)/lib/gluon/status-page/view/
......
<%-
http:prepare_content("application/xhtml+xml")
http:prepare_content("text/html")
-%>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<html lang="">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title><%:Error%></title>
<link rel="stylesheet" href="/static/status-page.css" type="text/css" />
<link rel="stylesheet" href="/static/status-page.css" type="text/css">
</head>
<body>
<header>
......
<%-
local iwinfo = require 'iwinfo'
local ubus = require 'ubus'
local unistd = require 'posix.unistd'
local util = require 'gluon.util'
local wireless = require 'gluon.wireless'
local uci = require('simple-uci').cursor()
local translations = {}
local site_i18n = i18n 'gluon-site'
local function _(v)
translations[v] = translate(v)
......@@ -28,17 +33,31 @@
local mesh = get_mesh()
local function get_interfaces()
local uconn = ubus.connect()
if not uconn then
error('failed to connect to ubus')
end
local function get_interfaces(uconn)
local interfaces = util.get_mesh_devices(uconn)
ubus.close(uconn)
table.sort(interfaces)
return interfaces
end
local function get_radios()
local ret = {}
wireless.foreach_radio(uci, function(radio)
local channel = iwinfo.nl80211.channel(wireless.find_phy(radio))
if channel then
table.insert(ret, {
name = radio['.name'],
channel = channel,
})
end
end)
table.sort(ret, function(a, b)
return a.name < b.name
end)
return ret
end
local function is_wireless(iface)
while true do
local pattern = '/sys/class/net/' .. iface .. '/lower_*'
......@@ -48,10 +67,19 @@
iface = lower:sub(pattern:len())
end
return unistd.access('/sys/class/net/' .. iface .. '/wireless') ~= nil
return unistd.access('/sys/class/net/' .. iface .. '/phy80211') ~= nil
end
local interfaces = get_interfaces()
local uconn = ubus.connect()
if not uconn then
error('failed to connect to ubus')
end
local interfaces = get_interfaces(uconn)
ubus.close(uconn)
local radios = get_radios()
local function sorted(t)
t = {unpack(t)}
......@@ -63,29 +91,45 @@
return v and translate('enabled') or translate('disabled')
end
local function formatBits(bits)
local units = {[0]='', 'k', 'M', 'G'}
local unit = 0
for i = 1, #units do
if math.abs(bits) < 1000 then
break
end
unit = i
bits = bits / 1000
end
return string.format('%g %sbit', bits, units[unit])
end
local function statistics(key, format)
return string.format('<span data-statistics="%s" data-format="%s"></span>', pcdata(key), pcdata(format or 'id'))
end
local function statisticsTraffic(key)
return string.format('%s<br />%s<br />%s',
return string.format('%s<br>%s<br>%s',
statistics(key .. '/packets', 'packetsDiff'),
statistics(key .. '/bytes', 'bytesDiff'),
statistics(key .. '/bytes', 'bytes')
)
end
http:prepare_content("application/xhtml+xml")
http:prepare_content("text/html")
-%>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<html lang="">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title><%| nodeinfo.hostname %> - <%:Status%></title>
<link rel="stylesheet" href="/static/status-page.css" type="text/css" />
<link rel="stylesheet" href="/static/status-page.css" type="text/css">
</head>
<body data-node-address="<%| http:getenv('SERVER_ADDR') %>"<%=
attr('data-translations', translations) ..
......@@ -103,12 +147,48 @@
<% if nodeinfo.owner and nodeinfo.owner.contact then -%>
<dt><%:Contact%></dt><dd><%| nodeinfo.owner.contact %></dd>
<%- end %>
<% if nodeinfo.location then -%>
<dt><%:Location%></dt>
<dd><a href="geo:<%| nodeinfo.location.latitude %>,<%| nodeinfo.location.longitude %>">
<%| nodeinfo.location.latitude %>, <%| nodeinfo.location.longitude %>
</a></dd>
<%- end %>
<dt><%:Model%></dt><dd><%| nodeinfo.hardware.model %></dd>
<dt><%:Primary MAC address%></dt><dd><%| nodeinfo.network.mac %></dd>
<dt><%:IP address%></dt><dd><%= pcdata(table.concat(sorted(nodeinfo.network.addresses), '\n')):gsub('\n', '<br />') %></dd>
<dt><%:IP address%></dt><dd><%= pcdata(table.concat(sorted(nodeinfo.network.addresses), '\n')):gsub('\n', '<br>') %></dd>
<dt><%:Firmware%></dt><dd><%| nodeinfo.software.firmware.release %></dd>
<% if nodeinfo.software.fastd then -%>
<dt><%:Mesh VPN%></dt><dd><%| enabled(nodeinfo.software.fastd.enabled) %></dd>
<% if nodeinfo.network.mesh_vpn then -%>
<dt><%:Mesh VPN%></dt>
<dd>
<%| enabled(nodeinfo.network.mesh_vpn.enabled) %>
<% if nodeinfo.network.mesh_vpn.provider then -%>
<br><%| nodeinfo.network.mesh_vpn.provider %>
<%- end %>
</dd>
<% if nodeinfo.network.mesh_vpn.bandwidth_limit.enabled then -%>
<dt><%:Bandwidth limit%></dt>
<dd>
<% if nodeinfo.network.mesh_vpn.bandwidth_limit.ingress then -%>
<%| formatBits(nodeinfo.network.mesh_vpn.bandwidth_limit.ingress*1000) %>/s <%:downstream%><br>
<%- end %>
<% if nodeinfo.network.mesh_vpn.bandwidth_limit.egress then -%>
<%| formatBits(nodeinfo.network.mesh_vpn.bandwidth_limit.egress*1000) %>/s <%:upstream%>
<%- end %>
</dd>
<%- end %>
<%- end %>
<dt><%:Site%></dt><dd><%| site.site_name() %></dd>
<% if nodeinfo.system.domain_code then -%>
<dt><%:Domain%></dt>
<dd>
<%| site.domain_names[nodeinfo.system.domain_code]() %>
<% if nodeinfo.system.domain_code ~= nodeinfo.system.primary_domain_code then %>
(<%| site.domain_names[nodeinfo.system.primary_domain_code]() %>)
<%- end %>
</dd>
<%- end %>
<% if nodeinfo.system.role then -%>
<dt><%:Role%></dt><dd><%| site_i18n._translate('gluon-web-node-role:role:' .. nodeinfo.system.role) %></dd>
<%- end %>
<% if nodeinfo.software.autoupdater then -%>
<dt><%:Automatic updates%></dt><dd><%| enabled(nodeinfo.software.autoupdater.enabled) %><%|
......@@ -116,33 +196,65 @@
string.format(' (%s)', nodeinfo.software.autoupdater.branch)
%></dd>
<%- end %>
<% if nodeinfo.software['batman-adv'] then -%>
<dt><%:Mesh protocol%></dt>
<% if nodeinfo.software['batman-adv'] then -%>
<dd>batman-adv <%| nodeinfo.software['batman-adv'].version %> (compat<%| nodeinfo.software['batman-adv'].compat %>)</dd>
<%- end %>
<%- end %>
</dl>
</div>
<div class="frame">
<h2><%:Monitoring%></h2>
<table>
<tbody>
<tr><th><%:Uptime%></th><td><%= statistics('uptime', 'time') %></td></tr>
<tr><th><%:Load average%></th><td><%= statistics('loadavg', 'decimal') %></td></tr>
<tr><th><%:RAM%></th><td><%= statistics('memory', 'memory') %></td></tr>
<tr><th><%:Filesystem%></th><td><%= statistics('rootfs_usage', 'percent') %></td></tr>
<tr><th><%:Gateway%></th><td><%= statistics('gateway') %></td></tr>
<tr><th><%:Clients%></th><td><%= statistics('clients/total') %></td></tr>
<tr><th><%:Gateway%></th><td><%= statistics('gateway') %> (TQ: <%= statistics('gateway_tq', 'tq') %>)<br><%= statistics('gateway_nexthop', 'neighbour') %></td></tr>
</tbody>
</table>
<h3><%:Clients%></h3>
<table>
<tbody>
<tr><th><%:Total%></th><td><%= statistics('clients/total') %></td></tr>
<tr><th><%:Wireless 2.4 GHz%></th><td><%= statistics('clients/wifi24') %></td></tr>
<tr><th><%:Wireless 5 GHz%></th><td><%= statistics('clients/wifi5') %></td></tr>
</tbody>
</table>
<% if radios[1] then -%>
<h3><%:Radios%></h3>
<table>
<tbody>
<% for _, radio in ipairs(radios) do -%>
<tr>
<th><%| radio.name %></th>
<td><%| translatef('Channel %u', radio.channel) %></td>
</tr>
<%- end %>
</tbody>
</table>
<%- end %>
<h3><%:Traffic%></h3>
<table>
<tbody>
<tr><th><%:Transmitted%></th><td><%= statisticsTraffic('traffic/tx') %></td></tr>
<tr><th><%:Received%></th><td><%= statisticsTraffic('traffic/rx') %></td></tr>
<tr><th><%:Forwarded%></th><td><%= statisticsTraffic('traffic/forward') %></td></tr>
</tbody>
</table>
<div id="mesh-vpn" style="display: none">
<h3><%:Mesh VPN%></h3>
<table id="mesh-vpn-peers">
<table>
<tbody id="mesh-vpn-peers"></tbody>
</table>
</div>
</div>
<div class="frame">
<div class="frame frame-wide">
<h2><%:Neighbors%></h2>
<%
......@@ -154,17 +266,19 @@
<h3><%| iface %></h3>
<div data-interface="<%| iface %>" data-interface-address="<%| util.trim(address) %>"<%= attr('data-interface-wireless', wireless) %>>
<table class="datatable">
<tbody>
<tr>
<th><%:Node%></th>
<% for i, v in ipairs(mesh.attrs or {}) do %>
<th<%= attr('data-key', v[1]) .. attr('data-suffix', v[3]) %>><%| v[2] %></th>
<th<%= attr('class', 'row-' .. v[1] ) .. attr('data-key', v[1]) .. attr('data-suffix', v[3]) %>><%| v[2] %></th>
<% end %>
<% if wireless then %>
<th>dBm</th>
<th><%:Distance%></th>
<th><%:Last seen%></th>
<th class="row-signal">dBm</th>
<th class="row-distance"><%:Distance%></th>
<th class="row-inactive"><%:Last seen%></th>
<% end %>
</tr>
</tbody>
</table>
</div>
<%
......
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<html lang="">
<head>
<meta http-equiv="refresh" content="0; URL=/cgi-bin/status" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta http-equiv="refresh" content="0; URL=/cgi-bin/status">
<meta name="robots" content="noindex,nofollow">
</head>
<body>
</body>
......
User-agent: *
Disallow: /
html,body,div,span,h1,h2,h3,dl,dt,dd,canvas,header,table,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{background:rgba(0,0,0,0.12);font-family:Roboto, Lucida Grande, sans, Arial;color:rgba(0,0,0,0.87);font-size:14px;line-height:1}a{color:rgba(220,0,103,0.87);text-decoration:none;margin:0;padding:0;font-size:100%;vertical-align:baseline;background:transparent}a:hover{text-decoration:underline}h1{font-weight:bold}h2{font-size:16px;margin-bottom:16px;color:rgba(0,0,0,0.54)}h3{font-size:15px;margin-top:16px;margin-bottom:8px;color:rgba(0,0,0,0.54)}header{display:flex;padding:0 14px;background:#dc0067;color:rgba(255,255,255,0.98);position:absolute;top:0;width:100%;box-sizing:border-box;height:20vh;z-index:-1;box-shadow:0px 5px 6px rgba(0,0,0,0.16),0px 1.5px 3px rgba(0,0,0,0.23);white-space:nowrap}header h1{font-size:24px;margin:10px 0;padding:6px 0;text-overflow:ellipsis;overflow:hidden;flex:1}.container{display:flex;max-width:90vw;margin:64px auto 24px auto;background:#fdfdfd;box-shadow:0px 5px 20px rgba(0,0,0,0.19),0px 3px 6px rgba(0,0,0,0.23)}.container>.frame{flex:1;border-style:solid;border-color:rgba(0,0,0,0.12);box-sizing:border-box;padding:16px}.container>.frame+.frame{border-width:0 0 0 1px}dt,th{font-weight:bold;color:rgba(0,0,0,0.87)}dt{margin-bottom:4px}th,td{text-align:left;padding:4px 16px 4px 0}th:last-child,td:last-child{padding-right:0}dd,td{font-weight:normal;font-size:0.9em;color:rgba(0,0,0,0.54)}dd{margin-bottom:16px}table{border-collapse:collapse;border-spacing:0}table.datatable{width:100%}table.datatable th,table.datatable td{font-size:1em;white-space:nowrap}table.datatable th:last-child,table.datatable td:last-child{width:100%}table.datatable tr.inactive{opacity:0.33}table.datatable tr.highlight{background:rgba(255,180,0,0.25)}canvas.signalgraph{margin-top:8px;width:100%}@media only screen and (max-width: 1250px){.container{max-width:none;margin:56px 0 0}header{height:56px;z-index:1;position:fixed}}@media only screen and (max-width: 700px){.container{display:block}.container>.frame+.frame{border-width:1px 0 0 0}}
html,body,div,span,h1,h2,h3,dl,dt,dd,canvas,header,table,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{background:rgba(0,0,0,0.12);font-family:Roboto, Lucida Grande, sans, Arial;color:rgba(0,0,0,0.87);font-size:14px;line-height:1}a{color:rgba(220,0,103,0.87);text-decoration:none;margin:0;padding:0;font-size:100%;vertical-align:baseline;background:transparent}a:hover{text-decoration:underline}h1{font-weight:bold}h2{font-size:16px;margin-bottom:16px;color:rgba(0,0,0,0.54)}h3{font-size:15px;margin-top:16px;margin-bottom:8px;color:rgba(0,0,0,0.54)}header{display:flex;padding:0 14px;background:#dc0067;color:rgba(255,255,255,0.98);position:absolute;top:0;width:100%;box-sizing:border-box;height:20vh;z-index:-1;box-shadow:0px 5px 6px rgba(0,0,0,0.16),0px 1.5px 3px rgba(0,0,0,0.23);white-space:nowrap}header h1{font-size:24px;margin:10px 0;padding:6px 0;text-overflow:ellipsis;overflow:hidden;flex:1}.container{display:flex;max-width:90vw;margin:64px auto 24px auto;background:#fdfdfd;box-shadow:0px 5px 20px rgba(0,0,0,0.19),0px 3px 6px rgba(0,0,0,0.23)}.container>.frame{flex:1;border-style:solid;border-color:rgba(0,0,0,0.12);box-sizing:border-box;padding:16px}.container>.frame+.frame{border-width:0 0 0 1px}.container>.frame-wide{flex:2}dt,th,td::before{font-weight:bold;color:rgba(0,0,0,0.87)}dt{margin-bottom:4px}th,td{text-align:left;padding:4px 16px 4px 0}th:last-child,td:last-child{padding-right:0}dd,td{font-weight:normal;font-size:0.9em;color:rgba(0,0,0,0.54)}dd{margin-bottom:16px}table{border-collapse:collapse;border-spacing:0}table.datatable{width:100%;table-layout:fixed}table.datatable th,table.datatable td{font-size:1em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.datatable th.row-tq{width:45px}table.datatable th.row-signal{width:36px}table.datatable th.row-distance{width:90px}table.datatable th.row-inactive{width:130px}table.datatable tr.inactive{opacity:0.33}table.datatable tr.highlight{background:rgba(255,180,0,0.25)}canvas.signalgraph{margin-top:8px;width:100%}@media only screen and (max-width: 1250px){.container{max-width:none;margin:56px 0 0}header{height:56px;z-index:1;position:fixed}.datatable tr{display:block;margin-bottom:15px}.datatable tr:first-child{margin-bottom:0}.datatable th{display:none}.datatable td{display:block;position:relative;padding-left:150px;max-width:calc(100% - 150px)}.datatable td::before{position:absolute;left:5px;content:attr(data-label)}}@media only screen and (max-width: 700px){.container{display:block}.container>.frame+.frame{border-width:1px 0 0 0}}
"use strict";!function(){var a=JSON.parse(document.body.getAttribute("data-translations"));function i(t,e){return t.toFixed(e).replace(/\./,a["."])}function o(t,e){e--;for(var n=t;10<=n&&0<e;n/=10)e--;return i(t,e)}function r(t){return function(t,e,n){var r=0;if(void 0===n)return"- ";for(;e<n&&r<t.length-1;)n/=e,r++;return(n=o(n,3))+" "+t[r]}(["","K","M","G","T"],1024,t)}String.prototype.sprintf=function(){var t=0,e=arguments;return this.replace(/%s/g,function(){return e[t++]})};var u={id:function(t){return t},decimal:function(t){return i(t,2)},percent:function(t){return a["%s used"].sprintf(o(100*t,3)+"%")},memory:function(t){var e=1-(t.free+t.buffers+t.cached)/t.total;return u.percent(e)},time:function(t){var e=Math.round(t/60),n=Math.floor(e/1440),r=Math.floor(e%1440/60);e=Math.floor(e%60);var i="";return 1===n?i+=a["1 day"]+", ":1<n&&(i+=a["%s days"].sprintf(n)+", "),i+=r+":",e<10&&(i+="0"),i+=e},packetsDiff:function(t,e,n){if(0<n)return r=(t-e)/n,a["%s packets/s"].sprintf(i(r,0));var r},bytesDiff:function(t,e,n){if(0<n)return r(8*((t-e)/n))+"bps"},bytes:function(t){return r(t)+"B"}};function f(e,t){return t.split("/").forEach(function(t){e&&(e=e[t])}),e}function l(t,n){var e=new EventSource(t),r={};e.onmessage=function(t){var e=JSON.parse(t.data);n(e,r),r=e},e.onerror=function(){e.close(),window.setTimeout(function(){l(t,n)},3e3)}}var y,C=document.body.getAttribute("data-node-address");try{y=JSON.parse(document.body.getAttribute("data-node-location"))}catch(t){}var t=document.querySelectorAll("[data-statistics]");l("/cgi-bin/dyn/statistics",function(o,c){var s=o.uptime-c.uptime;t.forEach(function(t){var e=t.getAttribute("data-statistics"),n=t.getAttribute("data-format"),r=f(c,e),i=f(o,e);try{var a=u[n](i,r,s);void 0!==a&&(t.textContent=a)}catch(t){console.error(t)}});try{!function(t){var e=document.getElementById("mesh-vpn");if(t){e.style.display="";for(var i=document.getElementById("mesh-vpn-peers");i.lastChild;)i.removeChild(i.lastChild);var n=function e(n,r){return Object.keys(r.peers||{}).forEach(function(t){n.push([t,r.peers[t]])}),Object.keys(r.groups||{}).forEach(function(t){e(n,r.groups[t])}),n}([],t);n.sort(),n.forEach(function(t){var e=document.createElement("tr"),n=document.createElement("th");n.textContent=t[0],e.appendChild(n);var r=document.createElement("td");t[1]?r.textContent=a.connected+" ("+u.time(t[1].established)+")":r.textContent=a["not connected"],e.appendChild(r),i.appendChild(e)})}else e.style.display="none"}(o.mesh_vpn)}catch(t){console.error(t)}});var c={};function w(a){var o=document.createElement("canvas"),c=o.getContext("2d"),s=null,u=1.2;return{canvas:o,highlight:!1,resize:function(t,e){try{c.getImageData(0,0,t,e)}catch(t){}o.width=t,o.height=e},draw:function(t,e){var n,r,i=e(s);c.clearRect(t,0,5,o.height),i&&(n=t,r=i,c.beginPath(),c.fillStyle=a,c.arc(n,r,u,0,2*Math.PI,!1),c.closePath(),c.fill())},set:function(t){s=t}}}function h(){var s=-100,u=0,n=0,r=[],f=document.createElement("canvas");f.className="signalgraph",f.height=200;var l=f.getContext("2d");function t(){f.width=f.clientWidth,r.forEach(function(t){t.resize(f.width,f.height)})}function i(){if(0!==f.clientWidth){f.width!==f.clientWidth&&t(),l.clearRect(0,0,f.width,f.height);var e=!1;r.forEach(function(t){t.highlight&&(e=!0)}),l.save(),r.forEach(function(t){e&&(l.globalAlpha=.2),t.highlight&&(l.globalAlpha=1),t.draw(n,function(t){return e=t,n=s,r=u,i=f.height,(1-(e-n)/(r-n))*i;var e,n,r,i}),l.drawImage(t.canvas,0,0)}),l.restore(),l.save(),l.beginPath(),l.strokeStyle="rgba(255, 180, 0, 0.15)",l.lineWidth=5,l.moveTo(n+2.5,0),l.lineTo(n+2.5,f.height),l.stroke(),function(){var t,e,n,r,i=Math.floor(f.height/40);l.save(),l.lineWidth=.5,l.strokeStyle="rgba(0, 0, 0, 0.25)",l.fillStyle="rgba(0, 0, 0, 0.5)",l.textAlign="end",l.textBaseline="bottom",l.beginPath();for(var a=0;a<i;a++){var o=f.height-40*a;l.moveTo(0,o-.5),l.lineTo(f.width,o-.5);var c=Math.round((t=o,e=s,n=u,r=f.height,(e*t+n*(r-t))/r))+" dBm";l.save(),l.strokeStyle="rgba(255, 255, 255, 0.9)",l.lineWidth=4,l.miterLimit=2,l.strokeText(c,f.width-5,o-2.5),l.fillText(c,f.width-5,o-2.5),l.restore()}l.stroke(),l.strokeStyle="rgba(0, 0, 0, 0.83)",l.lineWidth=1.5,l.strokeRect(.5,.5,f.width-1,f.height-1),l.restore()}()}}t(),window.addEventListener("resize",i);var a=0;return window.requestAnimationFrame(function t(e){40<e-a&&(i(),n=(n+1)%f.width,a=e),window.requestAnimationFrame(t)}),{el:f,addSignal:function(t){r.push(t),t.resize(f.width,f.height)},removeSignal:function(t){r.splice(r.indexOf(t),1)}}}function d(t,e,n,r){var i=t.table.firstElementChild,a=t.table.insertRow(),o=a.insertCell();if(t.wireless){var c=document.createElement("span");c.textContent="",c.style.color=n,o.appendChild(c)}var h=document.createElement("span");h.textContent=e,o.appendChild(h);var s,d,u,f,l,v={};function g(t){var e=t.getAttribute("data-key");if(e){var n=t.getAttribute("data-suffix")||"",r=a.insertCell();r.textContent="-",v[e]={td:r,suffix:n}}}for(var m=0;m<i.children.length;m++)g(i.children[m]);function p(){l&&window.clearTimeout(l),l=window.setTimeout(function(){f&&t.signalgraph.removeSignal(f),a.parentNode.removeChild(a),r()},6e4)}function b(t){var e=function(t){"::"==t.slice(0,2)&&(t="0"+t),"::"==t.slice(-2)&&(t+="0");var e=t.split(":"),n=e.length,r=[];return e.forEach(function(t,e){if(""===t)for(;n++<=8;)r.push(0);else{if(!/^[a-f0-9]{1,4}$/i.test(t))return;r.push(parseInt(t,16))}}),r}(t);if(e){var n="";return e.forEach(function(t){n+=("0000000000000000"+t.toString(2)).slice(-16)}),n}}return t.wireless&&((s=a.insertCell()).textContent="-",(d=a.insertCell()).textContent="-",(u=a.insertCell()).textContent="-",f=w(n),t.signalgraph.addSignal(f)),a.onmouseenter=function(){a.classList.add("highlight"),f&&(f.highlight=!0)},a.onmouseleave=function(){a.classList.remove("highlight"),f&&(f.highlight=!1)},p(),{update_nodeinfo:function(t){var e,n,r,i,a,o,c,s,u=function(t){var r=b(C);if(t&&t[0]){(t=t.map(function(t){var e=b(t);if(!e)return[-1];var n=0;return r&&(n=function(t,e){var n;for(n=0;n<t.length&&n<e.length&&t[n]===e[n];n++);return n}(r,e)),[n,e,t]})).sort(function(t,e){return t[0]<e[0]?1:t[0]>e[0]?-1:t[1]<e[1]?-1:t[1]>e[1]?1:0});var e=t[0][2];return e&&!/^fe80:/i.test(e)?e:void 0}}(t.network.addresses);if(u){if("span"===h.nodeName.toLowerCase()){var f=h;h=document.createElement("a"),f.parentNode.replaceChild(h,f)}h.href="http://["+u+"]/"}if(h.textContent=t.hostname,y&&t.location){var l=(e=y.latitude,n=y.longitude,r=t.location.latitude,i=t.location.longitude,a=Math.PI/180,o=(r*=a)-(e*=a),c=(i*=a)-(n*=a),s=Math.sin(o/2)*Math.sin(o/2)+Math.sin(c/2)*Math.sin(c/2)*Math.cos(e)*Math.cos(r),2*Math.asin(Math.sqrt(s))*6372.8);d.textContent=Math.round(1e3*l)+" m"}p()},update_mesh:function(n){Object.keys(v).forEach(function(t){var e=v[t];e.td.textContent=n[t]+e.suffix}),p()},update_wifi:function(t){s.textContent=t.signal,u.textContent=Math.round(t.inactive/1e3)+" s",a.classList.toggle("inactive",200<t.inactive),f.set(200<t.inactive?null:t.signal),p()}}}function s(t,e,n){var r,a={};n&&(r=h(),t.appendChild(r.el));var i={table:t.firstElementChild,signalgraph:r,ifname:e,wireless:n},o=!1,c={},s=[];function u(){if(!o){o=!0;var t=new EventSource("/cgi-bin/dyn/neighbours-nodeinfo?"+encodeURIComponent(e));t.addEventListener("neighbour",function(t){try{var n=JSON.parse(t.data);(e=n,r=[],i=e.network.mesh,Object.keys(i).forEach(function(t){var e=i[t].interfaces;Object.keys(e).forEach(function(t){e[t].forEach(function(t){r.push(t)})})}),r).forEach(function(t){var e=a[t];if(e){delete c[t];try{e.update_nodeinfo(n)}catch(t){console.error(t)}}})}catch(t){console.error(t)}var e,r,i},!1),t.onerror=function(){t.close(),o=!1,Object.keys(c).forEach(function(t){0<c[t]&&(c[t]--,u())})}}}function f(t){var e=a[t];return e||(c[t]=3,e=a[t]=d(i,t,(s[0]||(s=["#396AB1","#DA7C30","#3E9651","#CC2529","#535154","#6B4C9A","#922428","#948B3D"]),s.shift()),function(){delete c[t],delete a[t]}),u()),e}return n&&l("/cgi-bin/dyn/stations?"+encodeURIComponent(e),function(n){Object.keys(n).forEach(function(t){var e=n[t];f(t).update_wifi(e)})}),{get_neigh:f}}document.querySelectorAll("[data-interface]").forEach(function(t){var e=t.getAttribute("data-interface"),n=(t.getAttribute("data-interface-address"),!!t.getAttribute("data-interface-wireless"));c[e]=s(t,e,n)});var e=document.body.getAttribute("data-mesh-provider");e&&l(e,function(r){Object.keys(r).forEach(function(t){var e=r[t],n=c[e.ifname];n&&n.get_neigh(t).update_mesh(e)})})}();
\ No newline at end of file
#!/bin/sh
# shellcheck source=package/gluon-autoupdater/files/lib/gluon/autoupdater/lib.sh
. /lib/gluon/autoupdater/lib.sh
......
#!/bin/sh
# shellcheck source=package/gluon-autoupdater/files/lib/gluon/autoupdater/lib.sh
. /lib/gluon/autoupdater/lib.sh
......
......@@ -28,6 +28,12 @@ msgstr "1 Tag"
msgid "Automatic updates"
msgstr "Automatische Updates"
msgid "Bandwidth limit"
msgstr "Bandbreitenlimit"
msgid "Channel %u"
msgstr "Kanal %u"
msgid "Clients"
msgstr "Clients"
......@@ -37,6 +43,12 @@ msgstr "Kontakt"
msgid "Distance"
msgstr "Entfernung"
msgid "Domain"
msgstr "Domäne"
msgid "downstream"
msgstr "Downstream"
msgid "Error"
msgstr "Fehler"
......@@ -61,6 +73,12 @@ msgstr "Zuletzt gesehen"
msgid "Load average"
msgstr "Systemlast"
msgid "Location"
msgstr "Position"
msgid "Mesh protocol"
msgstr "Mesh-Protokoll"
msgid "Mesh VPN"
msgstr "Mesh-VPN"
......@@ -85,24 +103,45 @@ msgstr "Übersicht"
msgid "Primary MAC address"
msgstr "Primäre MAC-Adresse"
msgid "Radios"
msgstr ""
msgid "RAM"
msgstr "RAM"
msgid "Received"
msgstr "Empfangen"
msgid "Role"
msgstr "Rolle"
msgid "Site"
msgstr "Site"
msgid "Status"
msgstr "Status"
msgid "Total"
msgstr "Gesamt"
msgid "Traffic"
msgstr ""
msgid "Transmitted"
msgstr "Gesendet"
msgid "upstream"
msgstr "Upstream"
msgid "Uptime"
msgstr "Laufzeit"
msgid "Wireless 2.4 GHz"
msgstr ""
msgid "Wireless 5 GHz"
msgstr ""
msgid "connected"
msgstr "verbunden"
......@@ -114,3 +153,6 @@ msgstr "aktiviert"
msgid "not connected"
msgstr "nicht verbunden"
#~ msgid "Gateway Nexthop"
#~ msgstr "Gateway Nexthop"
Draft for french translation, taken from gluon-config-mode-mesh-vpn
msgid ""
msgstr ""
msgid "downstream"
msgstr "débit déscendant"
msgid "upstream"
msgstr "débit ascendant"
......@@ -19,6 +19,12 @@ msgstr ""
msgid "Automatic updates"
msgstr ""
msgid "Bandwidth limit"
msgstr ""
msgid "Channel %u"
msgstr ""
msgid "Clients"
msgstr ""
......@@ -28,6 +34,12 @@ msgstr ""
msgid "Distance"
msgstr ""
msgid "Domain"
msgstr ""
msgid "downstream"
msgstr ""
msgid "Error"
msgstr ""
......@@ -52,6 +64,12 @@ msgstr ""
msgid "Load average"
msgstr ""
msgid "Location"
msgstr ""
msgid "Mesh protocol"
msgstr ""
msgid "Mesh VPN"
msgstr ""
......@@ -76,24 +94,45 @@ msgstr ""
msgid "Primary MAC address"
msgstr ""
msgid "Radios"
msgstr ""
msgid "RAM"
msgstr ""
msgid "Received"
msgstr ""
msgid "Role"
msgstr ""
msgid "Site"
msgstr ""
msgid "Status"
msgstr ""
msgid "Total"
msgstr ""
msgid "Traffic"
msgstr ""
msgid "Transmitted"
msgstr ""
msgid "upstream"
msgstr ""
msgid "Uptime"
msgstr ""
msgid "Wireless 2.4 GHz"
msgstr ""
msgid "Wireless 5 GHz"
msgstr ""
msgid "connected"
msgstr ""
......
THe previous version of the status page had a Russian translation;
if we ever add Russion to gluon-web, the following strings can be reused:
if we ever add Russian to gluon-web, the following strings can be reused:
"Node": "Узел",
"Distance": "Дальность",
......@@ -10,14 +10,17 @@ if we ever add Russion to gluon-web, the following strings can be reused:
"Primary MAC": "Основной MAC",
"IP Address": "IP Адрес",
"Automatic updates": "Автоматические обновления",
"Bandwidth limit": "Ограничение пропускной способности",
"Overview": "Обзор",
"used": "используется",
"Uptime": "Время работы",
"Load average": "Загрузка системы",
"Gateway": "Шлюз",
"Channel": "Канал",
"Clients": "Клиенты",
"Transmitted": "Передано",
"Received": "Получено",
"Role": "Роль",
"Forwarded": "Переправленно",
"Day": "День",
"Days": "Дней",
......@@ -26,6 +29,7 @@ if we ever add Russion to gluon-web, the following strings can be reused:
"Packets/s": "Пакетов/c",
"Statistic": "Статистика",
"Traffic": "Трафик",
"Mesh protocol": "Ячеистый протокол"
"Neighbors": "Соседи",
"Firmware": "Прошивка",
"Branch": "Ветка"