From 40c56ddd7797f9e916abe5443784b21ed9ba51cf Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Thu, 30 Jan 2020 12:08:35 +0100 Subject: [PATCH] luci-app-vnstat2: fully convert to client side rendering This converts the graph rendering to client side JavaScript and replaces the route registration with declarative JSON. Signed-off-by: Jo-Philipp Wich --- .../resources/view/vnstat2/graphs.js | 73 +++++++++++++++++++ .../luasrc/controller/vnstat2.lua | 50 ------------- .../luasrc/view/vnstat2/graphs.htm | 55 -------------- .../share/luci/menu.d/luci-app-vnstat2.json | 33 +++++++++ .../share/rpcd/acl.d/luci-app-vnstat2.json | 7 ++ 5 files changed, 113 insertions(+), 105 deletions(-) create mode 100644 applications/luci-app-vnstat2/htdocs/luci-static/resources/view/vnstat2/graphs.js delete mode 100644 applications/luci-app-vnstat2/luasrc/controller/vnstat2.lua delete mode 100644 applications/luci-app-vnstat2/luasrc/view/vnstat2/graphs.htm create mode 100644 applications/luci-app-vnstat2/root/usr/share/luci/menu.d/luci-app-vnstat2.json diff --git a/applications/luci-app-vnstat2/htdocs/luci-static/resources/view/vnstat2/graphs.js b/applications/luci-app-vnstat2/htdocs/luci-static/resources/view/vnstat2/graphs.js new file mode 100644 index 000000000..770884655 --- /dev/null +++ b/applications/luci-app-vnstat2/htdocs/luci-static/resources/view/vnstat2/graphs.js @@ -0,0 +1,73 @@ +// This is free software, licensed under the Apache License, Version 2.0 + +'use strict'; +'require fs'; +'require ui'; + +return L.view.extend({ + renderTab: function(ifaces, style, title) { + var tab = E('div', { + 'class': 'cbi-section', + 'data-tab': style, + 'data-tab-title': title + }, [ + E('p', {}, E('em', { 'class': 'spinning' }, [ _('Loading graphs…') ])) + ]); + + ifaces.forEach(function(iface) { + tab.appendChild(E('p', {}, E('img', { 'data-iface': iface, 'style': 'display:none' }))); + fs.exec_direct('/usr/bin/vnstati', [ '-'+style, '-i', iface, '-o', '-' ], 'blob').then(function(res) { + var img = tab.querySelector('img[data-iface="%s"]'.format(iface)); + img.src = URL.createObjectURL(res); + img.style.display = ''; + tab.firstElementChild.style.display = 'none'; + }); + }); + + return tab; + }, + + load: function() { + return fs.exec_direct('/usr/bin/vnstat', [ '--json', 'f', '1' ], 'text').then(function(res) { + var json = null; + + try { + json = JSON.parse(res) + } + catch(e) { + throw new Error(res.replace(/^Error: /, '')); + } + + return (L.isObject(json) ? L.toArray(json.interfaces) : []).map(function(iface) { + return iface.name; + }).sort(); + }).catch(function(err) { + ui.addNotification(null, E('p', { 'style': 'white-space:pre' }, [err])); + return []; + }); + }, + + render: function(ifaces) { + var view = E([], [ + E('h2', [_('vnStat Graphs')]), + E('div', ifaces.length ? [ + this.renderTab(ifaces, 's', _('Summary')), + this.renderTab(ifaces, 't', _('Top')), + this.renderTab(ifaces, '5', _('5 Minute')), + this.renderTab(ifaces, 'h', _('Hourly')), + this.renderTab(ifaces, 'd', _('Daily')), + this.renderTab(ifaces, 'm', _('Monthly')), + this.renderTab(ifaces, 'y', _('Yearly')) + ] : [ E('em', [_('No monitored interfaces have been found. Go to the configuration to enable monitoring for one or more interfaces.')]) ]) + ]); + + if (ifaces.length) + ui.tabs.initTabGroup(view.lastElementChild.childNodes); + + return view; + }, + + handleSave: null, + handleSaveApply: null, + handleReset: null +}); diff --git a/applications/luci-app-vnstat2/luasrc/controller/vnstat2.lua b/applications/luci-app-vnstat2/luasrc/controller/vnstat2.lua deleted file mode 100644 index 139c1f499..000000000 --- a/applications/luci-app-vnstat2/luasrc/controller/vnstat2.lua +++ /dev/null @@ -1,50 +0,0 @@ -module("luci.controller.vnstat2", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/vnstat") then - return - end - - entry({"admin", "status", "vnstat2"}, alias("admin", "status", "vnstat2", "graphs"), _("vnStat Traffic Monitor"), 90) - entry({"admin", "status", "vnstat2", "graphs"}, template("vnstat2/graphs"), _("Graphs"), 1) - entry({"admin", "status", "vnstat2", "config"}, view("vnstat2/config"), _("Configuration"), 2) - entry({"admin", "status", "vnstat2", "graph"}, call("action_graph"), nil, 3) -end - -function action_graph() - local util = require "luci.util" - - local param = luci.http.formvalue - - local iface = param("iface") - local style = param("style") - - if not iface or not style then - luci.http.status(404, "Not Found") - return - end - - local style_valid = false - for _, v in ipairs({"s", "t", "5", "h", "d", "m", "y"}) do - if v == style then - style_valid = true - break - end - end - - if not style_valid then - luci.http.status(404, "Not Found") - return - end - - luci.http.prepare_content("image/png") - - local cmd = "vnstati -i %s -%s -o -" % { - util.shellquote(iface), - util.shellquote(style) - } - - local image = io.popen(cmd) - luci.http.write(image:read("*a")) - image:close() -end diff --git a/applications/luci-app-vnstat2/luasrc/view/vnstat2/graphs.htm b/applications/luci-app-vnstat2/luasrc/view/vnstat2/graphs.htm deleted file mode 100644 index 318611a9d..000000000 --- a/applications/luci-app-vnstat2/luasrc/view/vnstat2/graphs.htm +++ /dev/null @@ -1,55 +0,0 @@ -<%# - This is free software, licensed under the Apache License, Version 2.0 --%> - -<%- - -local util = require "luci.util" -local json = require "luci.jsonc" - - -local ifaces = {} - -local data = util.exec("vnstat --json f 1 2>/dev/null") -local content = json.parse(data) -if type(content) == "table" then - for _, iface in pairs(content["interfaces"]) do - table.insert(ifaces, iface["name"]) - end -end - - -local function render_section(style, title) - %>
<% - - for _, iface in ipairs(ifaces) do - %>

?iface=<%=iface%>&style=<%=style%>" alt="" style="max-width:100%" />

<% - end - - %>
<% -end - - --%> - -<%+header%> - -

<%:vnStat Graphs%>

- -
- <% - if #ifaces == 0 then - %>

<%:No monitored interfaces have been found. Go to the configuration to enable monitoring for one or more interfaces.%>

<% - else - render_section("s", translate("Summary")) - render_section("t", translate("Top")) - render_section("5", translate("5 Minute")) - render_section("h", translate("Hourly")) - render_section("d", translate("Daily")) - render_section("m", translate("Monthly")) - render_section("y", translate("Yearly")) - end - %> -
- -<%+footer%> diff --git a/applications/luci-app-vnstat2/root/usr/share/luci/menu.d/luci-app-vnstat2.json b/applications/luci-app-vnstat2/root/usr/share/luci/menu.d/luci-app-vnstat2.json new file mode 100644 index 000000000..0b3303c36 --- /dev/null +++ b/applications/luci-app-vnstat2/root/usr/share/luci/menu.d/luci-app-vnstat2.json @@ -0,0 +1,33 @@ +{ + "admin/status/vnstat2": { + "title": "vnStat Traffic Monitor", + "order": 90, + "action": { + "type": "firstchild" + }, + "depends": { + "fs": { + "/usr/bin/vnstat": "executable", + "/usr/bin/vnstati": "executable" + } + } + }, + + "admin/status/vnstat2/graphs": { + "title": "Graphs", + "order": 1, + "action": { + "type": "view", + "path": "vnstat2/graphs" + } + }, + + "admin/status/vnstat2/config": { + "title": "Configuration", + "order": 2, + "action": { + "type": "view", + "path": "vnstat2/config" + } + } +} diff --git a/applications/luci-app-vnstat2/root/usr/share/rpcd/acl.d/luci-app-vnstat2.json b/applications/luci-app-vnstat2/root/usr/share/rpcd/acl.d/luci-app-vnstat2.json index 58a2aa50c..fb16f09c9 100644 --- a/applications/luci-app-vnstat2/root/usr/share/rpcd/acl.d/luci-app-vnstat2.json +++ b/applications/luci-app-vnstat2/root/usr/share/rpcd/acl.d/luci-app-vnstat2.json @@ -1,6 +1,13 @@ { "luci-app-vnstat2": { "description": "Grant access to LuCI app vnstat2", + "read": { + "cgi-io": [ "exec" ], + "file": { + "/usr/bin/vnstat --json f 1": [ "exec" ], + "/usr/bin/vnstati -[5dhmsty] -i * -o -": [ "exec" ] + } + }, "write": { "file": { "/usr/bin/vnstat": [ "exec" ] -- 2.25.1