mirror of
https://github.com/sirpdboy/sirpdboy-package.git
synced 2025-01-07 03:17:03 +08:00
up luci-app-wrtbwmon
This commit is contained in:
parent
30201e2f0e
commit
996b47a6ae
22
luci-app-wrtbwmon/Makefile
Normal file
22
luci-app-wrtbwmon/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
#
|
||||
# Copyright (C) 2019 Openwrt.org
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-wrtbwmon
|
||||
PKG_VERSION:=1.6.3
|
||||
PKG_RELEASE:=2
|
||||
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
PKG_MAINTAINER:=
|
||||
|
||||
LUCI_TITLE:=A Luci module that uses wrtbwmon to track bandwidth usage
|
||||
LUCI_DEPENDS:=+wrtbwmon
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
15
luci-app-wrtbwmon/luasrc/controller/wrtbwmon.lua
Normal file
15
luci-app-wrtbwmon/luasrc/controller/wrtbwmon.lua
Normal file
@ -0,0 +1,15 @@
|
||||
module("luci.controller.wrtbwmon", package.seeall)
|
||||
|
||||
function index()
|
||||
if not nixio.fs.access("/etc/config/wrtbwmon") then
|
||||
return
|
||||
end
|
||||
entry({"admin","status","online"},alias("admin","status","online","onlinuser"),_("Online User"), 10)
|
||||
entry({"admin","status","online","onlinuser"},template("onlinuser"),_("User online")).leaf=true
|
||||
entry({"admin", "nlbw", "usage"},alias("admin", "nlbw", "usage", "details"),_("Usage"), 1)
|
||||
entry({"admin", "nlbw", "usage", "details"},template("wrtbwmon"),_("Details"), 10)
|
||||
entry({"admin", "nlbw", "usage", "config"},arcombine(cbi("wrtbwmon/config")),_("Configuration"), 20)
|
||||
entry({"admin", "nlbw", "usage", "custom"},form("wrtbwmon/custom"),_("User file"), 30)
|
||||
|
||||
end
|
||||
|
48
luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/config.lua
Normal file
48
luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/config.lua
Normal file
@ -0,0 +1,48 @@
|
||||
local lastvalue
|
||||
local cursor = luci.model.uci.cursor()
|
||||
local m, s, o
|
||||
m = Map("wrtbwmon", translate("Traffic Configuration"))
|
||||
|
||||
s = m:section(NamedSection, "general", "wrtbwmon", translate("General settings"))
|
||||
|
||||
o = s:option(Flag, "enabled", translate("Enabled"))
|
||||
o.rmempty= true
|
||||
|
||||
o = s:option(Value, "path", translate("Database Path"),
|
||||
translate("This box is used to select the Database path, "
|
||||
.. "which is /tmp/usage.db by default."))
|
||||
o:value("/tmp/usage.db")
|
||||
o:value("/etc/usage.db")
|
||||
o.rmempty= false
|
||||
|
||||
function m.on_parse(self)
|
||||
lastvalue = cursor:get("wrtbwmon", "general", "path")
|
||||
end
|
||||
|
||||
function o.write(self,section,value)
|
||||
local fpath = nixio.fs.dirname(value) .. "/"
|
||||
|
||||
if not nixio.fs.access(fpath) then
|
||||
if not nixio.fs.mkdirr(fpath) then
|
||||
return Value.write(self, section, lastvalue)
|
||||
end
|
||||
end
|
||||
|
||||
io.popen("/etc/init.d/wrtbwmon stop")
|
||||
io.popen("mv -f " .. fileRename(lastvalue, "*") .. " ".. fpath)
|
||||
Value.write(self,section,value)
|
||||
io.popen("/etc/init.d/wrtbwmon start")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function fileRename(fileName, tag)
|
||||
local idx = fileName:match(".+()%.%w+$")
|
||||
if(idx) then
|
||||
return fileName:sub(1, idx-1) .. tag .. fileName:sub(idx, -1)
|
||||
else
|
||||
return fileName .. tag
|
||||
end
|
||||
end
|
||||
|
||||
return m
|
26
luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/custom.lua
Normal file
26
luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/custom.lua
Normal file
@ -0,0 +1,26 @@
|
||||
local USER_FILE_PATH = "/etc/wrtbwmon.user"
|
||||
|
||||
local fs = require "nixio.fs"
|
||||
|
||||
local f = SimpleForm("wrtbwmon",
|
||||
translate("Usage - Custom User File"),
|
||||
translate("This file is used to match users with MAC addresses. "
|
||||
.. "Each line must have the following format: <b><font color=\"red\">\"00:aa:bb:cc:ee:ff,username\"</font></b>."))
|
||||
|
||||
local o = f:field(Value, "_custom")
|
||||
|
||||
o.template = "cbi/tvalue"
|
||||
o.rows = 20
|
||||
|
||||
function o.cfgvalue(self, section)
|
||||
return fs.readfile(USER_FILE_PATH)
|
||||
end
|
||||
|
||||
function o.write(self, section, value)
|
||||
value = value:gsub("\r\n?", "\n")
|
||||
fs.writefile(USER_FILE_PATH, value)
|
||||
end
|
||||
|
||||
f.submit = translate("Submit")
|
||||
|
||||
return f
|
98
luci-app-wrtbwmon/luasrc/view/onlinuser.htm
Normal file
98
luci-app-wrtbwmon/luasrc/view/onlinuser.htm
Normal file
@ -0,0 +1,98 @@
|
||||
<%#
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%
|
||||
local util = require "luci.util"
|
||||
local function online_data()
|
||||
local rv = { }
|
||||
local fd = util.execi('/usr/bin/awk \'BEGIN{while ((getline < "/tmp/dhcp.leases") > 0){a[$2]=$4;}while ((getline < "/proc/net/arp") > 0){if (!a[$4]){a[$4]="\?";}if (match($3,"0x[26]")){"ping -q -c 1 "$1" &";if (b[$4]){b[$4]=b[$4]"/"$1;}else{b[$4]=$1;}c[$4]=$6;}}while (("ip -6 neighbor show | grep -v fe80" | getline) > 0){if (b[$5]) {"ping -q -c 1 "$1" &";b[$5]=b[$5]"/"$1;}}for (mac in b){print(a[mac],b[mac],mac,c[mac]);}}\' ')
|
||||
while true do
|
||||
local ln = fd()
|
||||
if ln == nil then break end
|
||||
local name,ip,mac,dev = ln:match("^(%S+) (%S+) (%S+) (%S+)")
|
||||
|
||||
if mac and ip and name and dev then
|
||||
rv[#rv+1] = {
|
||||
hostname = name,
|
||||
device = dev,
|
||||
macaddr = mac,
|
||||
ipaddr = ip
|
||||
}
|
||||
end
|
||||
end
|
||||
return rv
|
||||
end
|
||||
|
||||
|
||||
if luci.http.formvalue("status") == "1" then
|
||||
local rv = {
|
||||
onlines = online_data()
|
||||
}
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(rv)
|
||||
return
|
||||
end
|
||||
-%>
|
||||
|
||||
<%+header%>
|
||||
|
||||
<script type="text/javascript" src="<%=resource%>/cbi.js?v=git-19.271.33176-b099749"></script>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
var npoll = 1;
|
||||
|
||||
XHR.poll(5, '<%=REQUEST_URI%>', { status: 1 },
|
||||
function(x, info)
|
||||
{
|
||||
var ls = document.getElementById('online_status_table');
|
||||
if (ls)
|
||||
{
|
||||
/* clear all rows */
|
||||
while( ls.rows.length > 1 )
|
||||
ls.rows[0].parentNode.deleteRow(1);
|
||||
|
||||
for( var i = 0; i < info.onlines.length; i++ )
|
||||
{
|
||||
|
||||
var tr = ls.rows[0].parentNode.insertRow(-1);
|
||||
tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
|
||||
|
||||
tr.insertCell(-1).innerHTML = info.onlines[i].hostname ? info.onlines[i].hostname : '?';
|
||||
tr.insertCell(-1).innerHTML = info.onlines[i].ipaddr.split("/").join("<br>");
|
||||
tr.insertCell(-1).innerHTML = info.onlines[i].macaddr;
|
||||
tr.insertCell(-1).innerHTML = info.onlines[i].device;
|
||||
}
|
||||
|
||||
if( ls.rows.length == 1 )
|
||||
{
|
||||
var tr = ls.rows[0].parentNode.insertRow(-1);
|
||||
tr.className = 'cbi-section-table-row';
|
||||
|
||||
var td = tr.insertCell(-1);
|
||||
td.colSpan = 4;
|
||||
td.innerHTML = '<em><br /><%:There is no one online now.%></em>';
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
//]]></script>
|
||||
|
||||
<fieldset class="cbi-section">
|
||||
<legend><%:User online%></legend>
|
||||
|
||||
<table class="cbi-section-table" id="online_status_table">
|
||||
<tr class="cbi-section-table-titles">
|
||||
<th class="cbi-section-table-cell"><%:Hostname%></th>
|
||||
<th class="cbi-section-table-cell"><%:IPv4-Address%></th>
|
||||
<th class="cbi-section-table-cell"><%:MAC-Address%></th>
|
||||
<th class="cbi-section-table-cell"><%:Interface%></th>
|
||||
</tr>
|
||||
<tr class="cbi-section-table-row">
|
||||
<td colspan="4"><em><br /><%:Collecting data...%></em></td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<%+footer%>
|
581
luci-app-wrtbwmon/luasrc/view/wrtbwmon.htm
Normal file
581
luci-app-wrtbwmon/luasrc/view/wrtbwmon.htm
Normal file
@ -0,0 +1,581 @@
|
||||
<%#
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%
|
||||
local util = require "luci.util"
|
||||
local stat = require "luci.tools.status"
|
||||
local fs = require "nixio.fs"
|
||||
local dba = luci.model.uci.cursor():get("wrtbwmon", "general", "path")
|
||||
-- Function to generate table from string.
|
||||
local function strToTable(str)
|
||||
local tb = {}
|
||||
local cmd = nil
|
||||
setmetatable(tb, {__index = table.insert})
|
||||
str:gsub("[^%s,]+", tb)
|
||||
tb.__index = nil
|
||||
return tb
|
||||
end
|
||||
|
||||
-- Function to update the mac-hostname table.
|
||||
local function getmactable(family)
|
||||
local mactable = {}
|
||||
local leases = (family == 4 and {stat.dhcp_leases()} or {stat.dhcp6_leases()})[1]
|
||||
|
||||
if fs.access("/etc/wrtbwmon.user") then
|
||||
for line in io.lines("/etc/wrtbwmon.user") do
|
||||
local macpair = strToTable(line)
|
||||
mactable[macpair[1]:lower()] = macpair[2]
|
||||
end
|
||||
end
|
||||
|
||||
for _, line in pairs(leases) do
|
||||
if line.macaddr and not mactable[line.macaddr:lower()] then
|
||||
mactable[line.macaddr:lower()] = line.hostname
|
||||
end
|
||||
end
|
||||
|
||||
return mactable
|
||||
end
|
||||
|
||||
-- Rename the db file for ipv6.
|
||||
local function fileRename(fn, tag)
|
||||
local idx = fn:match(".+()%.%w+$")
|
||||
if(idx) then
|
||||
return fn:sub(1, idx-1) .. tag .. fn:sub(idx, -1)
|
||||
else
|
||||
return fn .. tag
|
||||
end
|
||||
end
|
||||
|
||||
local function procressData(db, family)
|
||||
local dbc = (family == 6 and {fileRename(db, ".6")} or {db})[1]
|
||||
local cmd_setup = "/etc/init.d/wrtbwmon restart"
|
||||
local cmd_update = "wrtbwmon -" .. family .. " -f " .. db .. " >>/dev/null 2>&1"
|
||||
local data, total, mactable, firstline = {}, {0.0, 0.0, 0.0, 0.0, 0.0}, getmactable(family), true
|
||||
local isshow = luci.http.formvalue("isShow")
|
||||
|
||||
-- Setup the background update process.
|
||||
if not fs.access("/var/run/wrtbwmon.pid") then
|
||||
io.popen(cmd_setup)
|
||||
else
|
||||
io.popen(cmd_update)
|
||||
end
|
||||
|
||||
-- Process the database.
|
||||
for line in io.lines(dbc) do
|
||||
if firstline then
|
||||
firstline = false
|
||||
else
|
||||
local tbl = strToTable(line)
|
||||
if isshow == "1" or tbl[8] ~= "0" then
|
||||
tbl[1] = tbl[1]:lower()
|
||||
|
||||
if mactable[tbl[1]] then
|
||||
tbl[3] = mactable[tbl[1]]
|
||||
else
|
||||
tbl[3] = tbl[1]
|
||||
end
|
||||
|
||||
for i = 1,#total do
|
||||
total[i] = total[i] + (tbl[i+3] .. ".0")
|
||||
end
|
||||
|
||||
data[#data+1] = {tbl[3], tbl[1], unpack(tbl, 4)}
|
||||
table.insert(data[#data], tbl[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Transfer the database to js.
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({data, total})
|
||||
return
|
||||
end
|
||||
|
||||
if luci.http.formvalue("proto") == "ipv4" then
|
||||
procressData(dba, 4)
|
||||
return
|
||||
elseif luci.http.formvalue("proto") == "ipv6" then
|
||||
procressData(dba, 6)
|
||||
return
|
||||
end
|
||||
|
||||
if luci.http.formvalue("reset") == "1" then
|
||||
os.execute("ip -4 neigh flush dev br-lan && ip -6 neigh flush dev br-lan")
|
||||
os.execute("rm -f " .. fileRename(dba, "*") .. " && wrtbwmon -46 -f " .. dba .. " >>/dev/null 2>&1")
|
||||
luci.http.status(200, "OK")
|
||||
return
|
||||
end
|
||||
-%>
|
||||
|
||||
<%+header%>
|
||||
|
||||
<style type="text/css">
|
||||
.showMore.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tr.table-totals {
|
||||
background: #eee !important;
|
||||
}
|
||||
|
||||
.cbi-progressbar {
|
||||
position: relative;
|
||||
height: 18px;
|
||||
margin: 3px 0;
|
||||
border: thin solid #999;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.cbi-progressbar > div {
|
||||
width: 0;
|
||||
height: 100%;
|
||||
transition: width .1s ease-in;
|
||||
}
|
||||
|
||||
.cbi-progressbar::after {
|
||||
line-height: normal;
|
||||
position: absolute;
|
||||
bottom 0;
|
||||
left 0;
|
||||
right 0;
|
||||
top 0;
|
||||
overflow: hidden;
|
||||
content: attr(title);
|
||||
text-align: center;
|
||||
white-space: pre;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="cbi-map">
|
||||
<h2><%:Usage - Details%></h2>
|
||||
|
||||
<div class="cbi-section">
|
||||
<div class="table">
|
||||
<div class="tr">
|
||||
<div class="td left" style="width:10%"><label><%:protocol:%></label></div>
|
||||
<div class="td left" style="width:30%">
|
||||
<select id="Select46" style="width:auto">
|
||||
<option value="ipv4" selected="selected">ipv4</option>
|
||||
<option value="ipv6">ipv6</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="td left" style="width:10%"><label for="isShow"><%:Show Zeros:%></label></div>
|
||||
<div class="td left" style="width:30%">
|
||||
<input class="cbi-input-checkbox" type="checkbox" id="isShow"/>
|
||||
</div>
|
||||
<div class="td right">
|
||||
<input type="button" id="resetDatabase" class="cbi-button" value="<%:Reset Database%>"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tr">
|
||||
<div class="td left" style="width:10%"><label><%:bandwidth:%></label></div>
|
||||
<div class="td left" style="width:30%">
|
||||
<input type="text" id="setBD" value="1000M" /><label id="checkBD"></label>
|
||||
</div>
|
||||
<div class="td left" style="width:10%">
|
||||
<label for="showMore"><%:Show More:%></label>
|
||||
</div>
|
||||
<div class="td left" style="width:30%">
|
||||
<input class="cbi-input-checkbox" type="checkbox" id="showMore"/>
|
||||
</div>
|
||||
<div class="td"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cbi-section">
|
||||
<div class="table">
|
||||
<div class="tr">
|
||||
<div class="td left" style="width:70%">
|
||||
<small><div id="updated" style="display:inline"></div><div id="updating" style="display:inline"></div></small>
|
||||
</div>
|
||||
<div class="td right" style="width:30%">
|
||||
<label for="intervalSelect"><%:Auto update every%></label>
|
||||
<select id="intervalSelect">
|
||||
<option value="-1"><%:Disabled%></option>
|
||||
<option value="1"><%:1 second%></option>
|
||||
<option value="2" selected="selected"><%:2 seconds%></option>
|
||||
<option value="5"><%:5 seconds%></option>
|
||||
<option value="10"><%:10 seconds%></option>
|
||||
<option value="20"><%:20 seconds%></option>
|
||||
<option value="30"><%:30 seconds%></option>
|
||||
<option value="60"><%:60 seconds%></option>
|
||||
<option value="120"><%:2 minutes%></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table">
|
||||
<div class="tr"><div class="td left" width="10%"><%:upflow:%></div><div class="td left" style="width:90%"><div id="upflow" class="cbi-progressbar" title="-"><div></div></div></div></div>
|
||||
<div class="tr"><div class="td left" width="10%"><%:downflow:%></div><div class="td left" style="width:90%"><div id="downflow" class="cbi-progressbar" title="-"><div></div></div></div></div>
|
||||
</div>
|
||||
|
||||
<div class="table" id="traffic">
|
||||
<div class="tr table-titles">
|
||||
<div class="th" id="thClient" style="width:17%"><%:Clients%></div>
|
||||
<div class="th showMore hide" id="thMAC" style="width:10%"><%:MAC%></div>
|
||||
<div class="th" id="thDownload" style="width:8%"><%:Download%></div>
|
||||
<div class="th" id="thUpload" style="width:8%"><%:Upload%></div>
|
||||
<div class="th" id="thTotalDown" style="width:9%"><%:Total Down%></div>
|
||||
<div class="th" id="thTotalUp" style="width:9%"><%:Total Up%></div>
|
||||
<div class="th" id="thTotal" style="width:9%"><%:Total%></div>
|
||||
<div class="th showMore hide" id="thFirstSeen" style="width:15%"><%:First Seen%></div>
|
||||
<div class="th showMore hide" id="thLastSeen" style="width:15%"><%:Last Seen%></div>
|
||||
</div>
|
||||
<div class="tr placeholder">
|
||||
<div class="td"><em><%:Collecting data...%></em></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
(function () {
|
||||
var sortedId = "thTotal", sortedBy = "desc";
|
||||
var cachedData = null;
|
||||
|
||||
function parseSize(size){
|
||||
var num = parseFloat((size).match(/^[0-9]+\.?[0-9]*/g));
|
||||
var base = (size).match(/[KMGTPEZ]/i).toString();
|
||||
var unit = ["" , "K", "M", "G", "T", "P", "E", "Z"];
|
||||
var ex = unit.indexOf(base);
|
||||
|
||||
return Math.round((num ? num : 1) * (ex != "-1" ? 1024 ** ex : 1));
|
||||
}
|
||||
|
||||
function padstr(str) {
|
||||
return str < 10 ? '0' + str : str;
|
||||
}
|
||||
|
||||
function dateToString(date) {
|
||||
var d = new Date((/\W/g).test(date) ? date : date * 1000);
|
||||
var Y = d.getFullYear(), M = d.getMonth() + 1, D = d.getDate();
|
||||
var hh = d.getHours(), mm = d.getMinutes(), ss = d.getSeconds();
|
||||
return Y + '/' + padstr(M) + '/' + padstr(D) + ' ' + padstr(hh) + ':' + padstr(mm) + ':' + padstr(ss);
|
||||
}
|
||||
|
||||
function isArray(obj) {
|
||||
return obj instanceof Array;
|
||||
}
|
||||
|
||||
function handleError() {
|
||||
// TODO handle errors
|
||||
// var message = 'Something went wrong...';
|
||||
}
|
||||
|
||||
function displayTable(eltid) {
|
||||
var tb = $('traffic'), bdw = parseSize($('setBD').value);
|
||||
var col = setSortColumn(eltid);
|
||||
|
||||
cachedData[0].sort(function(x, y) {
|
||||
var byCol = x[col] == y[col] ? 1 : col;
|
||||
var n1 = x[byCol], n2 = y[byCol];
|
||||
var flag = sortedBy =="desc" ? 1 : -1;
|
||||
return sortingFunction(n1, n2, byCol) * flag;
|
||||
});
|
||||
|
||||
// display data
|
||||
// console.time('update_table');
|
||||
updateTable(tb, cachedData, '<em><%:Loading...%></em>');
|
||||
// console.timeEnd('update_table');
|
||||
|
||||
progressbar('downflow', cachedData[1][0], bdw, true);
|
||||
progressbar('upflow', cachedData[1][1], bdw, true);
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
function updateTable(tb, values, placeholder) {
|
||||
|
||||
var dom = document.createDocumentFragment(), nodeLen = tb.childElementCount - 2;
|
||||
var tbData = values[0], shadowNode, newNode, childTD, tabTitle = tb.firstElementChild;
|
||||
var showMore = $('showMore').checked;
|
||||
// Create the shadow node, which will be used in the following.
|
||||
if (tbData.length > nodeLen) {
|
||||
if (tb.childElementCount > 2) {
|
||||
shadowNode = tabTitle.nextElementSibling.cloneNode(true);
|
||||
} else {
|
||||
shadowNode = document.createElement('div');
|
||||
childTD = document.createElement('div');
|
||||
childTD.appendChild(document.createTextNode(''));
|
||||
for (var j = 0; j < tabTitle.children.length; j++) {
|
||||
childTD.className = 'td' + (!showMore && '178'.indexOf(j) != -1 ? ' hide showMore' : '');
|
||||
childTD.setAttribute('data-title', tabTitle.children[j].innerHTML);
|
||||
shadowNode.appendChild(childTD.cloneNode(true));
|
||||
}
|
||||
shadowNode.firstElementChild.appendChild(document.createElement('br'));
|
||||
shadowNode.firstElementChild.appendChild(document.createTextNode(''));
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < tbData.length; i++) {
|
||||
if (i < nodeLen) {
|
||||
newNode = tabTitle.nextElementSibling;
|
||||
} else {
|
||||
newNode = shadowNode.cloneNode(true);
|
||||
newNode.className = 'tr cbi-rowstyle-%d'.format(i % 2 ? 2 : 1);
|
||||
}
|
||||
childTD = newNode.firstElementChild;
|
||||
childTD.title = tbData[i][1].toUpperCase() ;
|
||||
|
||||
childTD.lastChild.nodeValue = tbData[i].slice(-1);
|
||||
if (childTD.lastChild.nodeValue == 'NA')tbData[i][0]='LAN-WAN';
|
||||
for (var j = 0; j < tabTitle.childElementCount; j++, childTD = childTD.nextElementSibling){
|
||||
childTD.firstChild.nodeValue = ('23456'.indexOf(j) != -1 ?
|
||||
'%1024.2mB' + ('23'.indexOf(j) != -1 ? '/s' : '') :
|
||||
'%s').format('78'.indexOf(j) != -1 ? dateToString(tbData[i][j]) : tbData[i][j]);
|
||||
}
|
||||
dom.appendChild(newNode);
|
||||
}
|
||||
// Remove the table data which has been deleted from the database.
|
||||
while (tb.childElementCount > 2) {
|
||||
tb.removeChild(tabTitle.nextElementSibling);
|
||||
}
|
||||
//Append the totals or placeholder row.
|
||||
dom.appendChild(tb.lastElementChild);
|
||||
newNode = dom.lastElementChild;
|
||||
if (newNode.classList.contains('table-totals')) {
|
||||
if (tbData.length == 0) {
|
||||
while (newNode.firstElementChild.firstChild.nextSibling) {
|
||||
newNode.removeChild(newNode.lastElementChild);
|
||||
};
|
||||
newNode.className = 'tr placeholder';
|
||||
newNode.firstChild.innerHTML = placeholder;
|
||||
}
|
||||
} else {
|
||||
if (tbData.length > 0) {
|
||||
dom.replaceChild(shadowNode.cloneNode(true), newNode);
|
||||
newNode = dom.lastElementChild;
|
||||
newNode.className = 'tr table-totals';
|
||||
while (newNode.firstElementChild.firstChild.nextSibling) {
|
||||
newNode.firstElementChild.removeChild(newNode.firstElementChild.lastChild);
|
||||
};
|
||||
newNode.firstElementChild.style.fontWeight = 'bold';
|
||||
newNode.firstElementChild.nextSibling.style.fontWeight = 'bold';
|
||||
}
|
||||
}
|
||||
|
||||
if (newNode.classList.contains('table-totals')) {
|
||||
newNode.firstElementChild.firstChild.nodeValue = !showMore ? '<%:TOTAL%>: ' + tbData.length : '<%:TOTAL%>:';
|
||||
newNode.firstElementChild.nextSibling.firstChild.nodeValue = !showMore ? '' : tbData.length + ' <%:Clients%>';
|
||||
for (var j = 0; j < values[1].length; j++) {
|
||||
newNode.children[j + 2].firstChild.nodeValue = '%1024.2mB'.format(values[1][j]) + (j < 2 ? '/s' : '');
|
||||
}
|
||||
}
|
||||
tb.appendChild(dom);
|
||||
}
|
||||
|
||||
function sortingFunction(x, y, byCol) {
|
||||
var toHex = false;
|
||||
var a = x.split(/[^\w\d]/g), b = y.split(/[^\w\d]/g);
|
||||
|
||||
if ( byCol == "9" ) {
|
||||
var ipCk1 = cbi_validators.ipaddr.apply(x) ? 1 : 0;
|
||||
var ipCk2 = cbi_validators.ipaddr.apply(y) ? 1 : 0;
|
||||
|
||||
if (ipCk1 * ipCk2 == 0) {
|
||||
return ipCk2 - ipCk1;
|
||||
} else {
|
||||
a = cbi_validators.ip6addr.apply(x) ? (toHex = true, IPv6(x)) : a;
|
||||
b = cbi_validators.ip6addr.apply(y) ? (toHex = true, IPv6(y)) : b;
|
||||
}
|
||||
}
|
||||
|
||||
var len = (a.length <= b.length ? a.length : b.length);
|
||||
var num1, num2;
|
||||
|
||||
for (var i = 0 ; i < len ; i++ ) {
|
||||
if (byCol == "1" || (byCol == "9" && toHex)) {
|
||||
num1 = parseInt(a[i], 16);
|
||||
num2 = parseInt(b[i], 16);
|
||||
} else if (a[i].match(/[a-z]/ig) || b[i].match(/[a-z]/ig)){
|
||||
num1 = a[i].toLowerCase();
|
||||
num2 = b[i].toLowerCase();
|
||||
} else {
|
||||
num1 = parseInt(a[i]);
|
||||
num2 = parseInt(b[i]);
|
||||
}
|
||||
|
||||
if (num1 != num2) return num2 - num1;
|
||||
}
|
||||
return "1";
|
||||
}
|
||||
|
||||
function progressbar(query, v, m, byte) {
|
||||
var pg = $(query),
|
||||
vn = parseInt(v) || 0,
|
||||
mn = parseInt(m) || 100,
|
||||
fv = byte ? String.format('%1024.2mB', v) : v,
|
||||
pc = ((100 / mn) * vn).toFixed(2),
|
||||
wt = Math.floor(pc > 100 ? 100 : pc),
|
||||
bgc = (pc >= 95 ? "red" : (pc >= 80 ? "magenta" : (pc >= 60 ? "yellow" : "lime")));
|
||||
|
||||
if (pg) {
|
||||
pg.firstElementChild.style.width = wt + '%';
|
||||
pg.firstElementChild.style.background = bgc;
|
||||
pg.setAttribute('title', '%s/s (%d%%)'.format(fv, pc));
|
||||
}
|
||||
}
|
||||
|
||||
function $(tid) {
|
||||
return document.getElementById(tid);
|
||||
}
|
||||
|
||||
function registerTableEventHandlers() {
|
||||
$('xhr_poll_status').onclick = function() {
|
||||
var e = $('intervalSelect');
|
||||
XHR.running() ? (XHR.halt(), e.value = -1) : (XHR.run(), e.value = XHR._q[0].interval);
|
||||
}
|
||||
|
||||
$('traffic').querySelectorAll('.th').forEach( function(e) {
|
||||
if (e) {
|
||||
e.addEventListener('click', function () {
|
||||
displayTable(this.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('intervalSelect').addEventListener('change', function () {
|
||||
if (this.value > 0) {
|
||||
XHR._q[0].interval = this.value;
|
||||
if (!XHR.running()) XHR.run();
|
||||
} else {
|
||||
XHR.halt();
|
||||
setUpdateMessage('');
|
||||
}
|
||||
});
|
||||
|
||||
$('resetDatabase').addEventListener('click', function () {
|
||||
if (confirm('<%:This will delete the database file. Are you sure?%>')) {
|
||||
(new XHR()).post('<%=REQUEST_URI%>', {reset: 1}, function(xhr) {
|
||||
document.location.reload();
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
$('setBD').addEventListener('input', function () {
|
||||
var strTest = (/^[0-9]+\.?[0-9]*[\s]*[KMGTP]?B?(\/s)?$/g).test(this.value);
|
||||
$("checkBD").innerHTML = strTest ? '\u2714' : '\u2716';
|
||||
});
|
||||
|
||||
$('setBD').addEventListener('focusout', function () {
|
||||
if ($("checkBD").innerHTML == '\u2716') {
|
||||
alert('Error! Bandwidth reset!!!');
|
||||
this.value = '1M';
|
||||
}
|
||||
$("checkBD").innerHTML = "";
|
||||
});
|
||||
|
||||
$('Select46').addEventListener('change', function () {
|
||||
XHR._q[0].data["proto"] = this.value;
|
||||
});
|
||||
|
||||
$('isShow').addEventListener('click', function () {
|
||||
XHR._q[0].data["isShow"] = this.checked ? 1 : 0;
|
||||
});
|
||||
|
||||
$('showMore').addEventListener('click', function () {
|
||||
var tot = document.querySelector('.tr.table-totals').firstElementChild;
|
||||
var showMore = this.checked;
|
||||
tot.firstChild.nodeValue = '<%:TOTAL%>:' + (showMore ? '' : ' ' + $('traffic').childElementCount - 2);
|
||||
tot.nextElementSibling.firstChild.nodeValue = showMore ? $('traffic').childElementCount - 2 + ' <%:Clients%>' : '';
|
||||
document.querySelectorAll('.showMore').forEach(function(e) {
|
||||
if(e) {
|
||||
showMore ? e.classList.remove('hide') :e.classList.add('hide');
|
||||
}
|
||||
});
|
||||
if (!showMore && ["thMAC", "thFirstSeen", "thLastSeen"].indexOf(sortedId)!= -1) displayTable("thTotal");
|
||||
});
|
||||
}
|
||||
|
||||
function updateData() {
|
||||
var interval = $('intervalSelect').value;
|
||||
var status = {
|
||||
proto: $('Select46').value,
|
||||
isShow: 0
|
||||
}
|
||||
|
||||
XHR.poll(interval, '<%=REQUEST_URI%>', status,
|
||||
function(x, info) {
|
||||
// console.time('start');
|
||||
if (!info) {
|
||||
handleError();
|
||||
} else {
|
||||
cachedData = info;
|
||||
|
||||
// Display the sorted values.
|
||||
displayTable(null);
|
||||
|
||||
$('updated').innerHTML = '<%:Last updated %>' + dateToString(Math.round(Date.now() / 1000)) +'<%:.%> ';
|
||||
}
|
||||
// console.timeEnd('start');
|
||||
});
|
||||
updatePerSec();
|
||||
}
|
||||
|
||||
function updatePerSec() {
|
||||
var post;
|
||||
XHR.poll(1, "", "", function(x, data) {
|
||||
var itv = XHR._q[0].interval;
|
||||
var sec = XHR._t % itv ? itv - XHR._t % itv : 0;
|
||||
setUpdateMessage('<%:Updating again in%> <b>' + sec + '</b> <%:seconds.%>');
|
||||
if(sec == 0) {
|
||||
setTimeout(function() {
|
||||
setUpdateMessage('<%:Updating again in%> <b>' + XHR._q[0].interval + '</b> <%:seconds.%>');
|
||||
}, 50);
|
||||
}
|
||||
}, post);
|
||||
|
||||
(XHR._q.slice(-1)[0]).xhr[post ? 'post' : 'get'] = function(url, data, callback, timeout) {
|
||||
callback("", "");
|
||||
}
|
||||
}
|
||||
|
||||
function setUpdateMessage(msg) {
|
||||
$('updating').innerHTML = msg;
|
||||
}
|
||||
|
||||
function setSortColumn(eltid) {
|
||||
var label = ["", "thMAC", "thDownload", "thUpload", "thTotalDown", "thTotalUp", "thTotal", "thFirstSeen", "thLastSeen", "thClient"];
|
||||
|
||||
// Remove the old sorted sign.
|
||||
var e = $(sortedId);
|
||||
if (e) {
|
||||
e.innerHTML = e.innerHTML.replace(/\u25B2|\u25BC/, "");
|
||||
}
|
||||
|
||||
// Toggle the sort direction.
|
||||
if (eltid) {
|
||||
if ( eltid == sortedId ) {
|
||||
sortedBy = (sortedBy == "desc") ? "asc" : "desc";
|
||||
} else {
|
||||
sortedBy = "desc";
|
||||
sortedId = eltid;
|
||||
}
|
||||
}
|
||||
|
||||
e = $(sortedId);
|
||||
if (e) {
|
||||
e.innerHTML += (sortedBy == "asc" ? "\u25B2" : "\u25BC");
|
||||
}
|
||||
|
||||
return label.indexOf(sortedId)
|
||||
}
|
||||
|
||||
if (<%=luci.http.write_json(io.popen("opkg status wrtbwmon") == "")%>) {
|
||||
alert("<%:wrtbwmon is not installed!%>");
|
||||
} else {
|
||||
registerTableEventHandlers();
|
||||
updateData();
|
||||
}
|
||||
|
||||
return 0;
|
||||
})();
|
||||
//]]></script>
|
||||
|
||||
<%+footer%>
|
162
luci-app-wrtbwmon/po/zh-cn/wrtbwmon.po
Normal file
162
luci-app-wrtbwmon/po/zh-cn/wrtbwmon.po
Normal file
@ -0,0 +1,162 @@
|
||||
msgid "Usage"
|
||||
msgstr "统计"
|
||||
|
||||
msgid "Traffic Configuration"
|
||||
msgstr "流量设置"
|
||||
|
||||
msgid "Details"
|
||||
msgstr "流量信息"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "设置"
|
||||
|
||||
msgid "User file"
|
||||
msgstr "用户文件"
|
||||
|
||||
msgid "General settings"
|
||||
msgstr "通用设置"
|
||||
|
||||
|
||||
msgid "This box is used to select the Database path, which is /tmp/usage.db by default."
|
||||
msgstr "该选项用于选择数据存储路径,默认路径为/tmp/usage.db。"
|
||||
|
||||
msgid "This box is used to set the total bandwidth (Byte/s), which is 1000000Byte/s by default."
|
||||
msgstr "该选项用于选设置总的带宽(Byte/s),默认带宽值1000000Byte/s。"
|
||||
|
||||
msgid "Usage - Custom User File"
|
||||
msgstr "用户文件配置"
|
||||
|
||||
msgid "This file is used to match users with MAC addresses. Each line must have the following format: <b><font color=\"red\">\"00:aa:bb:cc:ee:ff,username\"</font></b>."
|
||||
msgstr "该文件用来进行MAC和主机进行匹配。每行必须满足<b><font color=\"red\">\"00:aa:bb:cc:ee:ff,用户名\"</font></b>的格式"
|
||||
|
||||
msgid "Usage - Details"
|
||||
msgstr "流量详情"
|
||||
|
||||
msgid "downflow:"
|
||||
msgstr "下行:"
|
||||
|
||||
msgid "upflow:"
|
||||
msgstr "上行:"
|
||||
|
||||
msgid "protocol:"
|
||||
msgstr "协议:"
|
||||
|
||||
msgid "Reset Database"
|
||||
msgstr "重置"
|
||||
|
||||
msgid "Auto update every"
|
||||
msgstr "自动刷新"
|
||||
|
||||
msgid "Database Path"
|
||||
msgstr "数据存储位置"
|
||||
|
||||
msgid "Show Zeros:"
|
||||
msgstr "显示0流量:"
|
||||
|
||||
msgid "Show More:"
|
||||
msgstr "显示更多:"
|
||||
|
||||
msgid "bandwidth:"
|
||||
msgstr "带宽:"
|
||||
|
||||
msgid "Calculate per host totals"
|
||||
msgstr "单主机总流量统计"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "禁用"
|
||||
|
||||
msgid "1 second"
|
||||
msgstr "1秒"
|
||||
|
||||
msgid "2 seconds"
|
||||
msgstr "2秒"
|
||||
|
||||
msgid "5 seconds"
|
||||
msgstr "5秒"
|
||||
|
||||
msgid "10 seconds"
|
||||
msgstr "10秒"
|
||||
|
||||
msgid "20 seconds"
|
||||
msgstr "20秒"
|
||||
|
||||
msgid "30 seconds"
|
||||
msgstr "30秒"
|
||||
|
||||
msgid "40 seconds"
|
||||
msgstr "40秒"
|
||||
|
||||
msgid "50 seconds"
|
||||
msgstr "50秒"
|
||||
|
||||
msgid "60 seconds"
|
||||
msgstr "60秒"
|
||||
|
||||
msgid "2 minutes"
|
||||
msgstr "2分"
|
||||
|
||||
msgid "3 minutes"
|
||||
msgstr "3分"
|
||||
|
||||
msgid "Loading..."
|
||||
msgstr "数据载入中"
|
||||
|
||||
msgid "Clients"
|
||||
msgstr "客户端"
|
||||
|
||||
msgid "MAC Address"
|
||||
msgstr "MAC地址"
|
||||
|
||||
msgid "Download"
|
||||
msgstr "下载"
|
||||
|
||||
msgid "Upload"
|
||||
msgstr "上传"
|
||||
|
||||
msgid "Total Down"
|
||||
msgstr "总下载"
|
||||
|
||||
msgid "Total Up"
|
||||
msgstr "总上传"
|
||||
|
||||
msgid "Total"
|
||||
msgstr "总计"
|
||||
|
||||
msgid "First Seen"
|
||||
msgstr "初次记录"
|
||||
|
||||
msgid "Last Seen"
|
||||
msgstr "最后记录"
|
||||
|
||||
msgid "TOTAL"
|
||||
msgstr "总共"
|
||||
|
||||
msgid "Last updated"
|
||||
msgstr "最后更新于"
|
||||
|
||||
msgid "This will delete the database file. Are you sure?"
|
||||
msgstr "该操作将删除数据统计文件,确定执行该操作?"
|
||||
|
||||
msgid "Updating again in"
|
||||
msgstr "下次更新将于"
|
||||
|
||||
msgid "seconds."
|
||||
msgstr "秒以后。"
|
||||
|
||||
msgid "wrtbwmon is not installed!"
|
||||
msgstr "wrtbwmon插件尚未安装"
|
||||
|
||||
msgid "Real time speed"
|
||||
msgstr "实时速度"
|
||||
|
||||
msgid "Online User"
|
||||
msgstr "在线用户"
|
||||
|
||||
msgid "Speed monitor"
|
||||
msgstr "实时监控"
|
||||
|
||||
msgid "User online"
|
||||
msgstr "在线用户列表"
|
||||
|
||||
msgid "There is no one online now."
|
||||
msgstr "当前无任何设备在线。"
|
10
luci-app-wrtbwmon/root/etc/uci-defaults/luci-wrtbwmon
Normal file
10
luci-app-wrtbwmon/root/etc/uci-defaults/luci-wrtbwmon
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
delete ucitrack.@wrtbwmon[-1]
|
||||
add ucitrack wrtbwmon
|
||||
set ucitrack.@wrtbwmon[-1].init=wrtbwmon
|
||||
commit ucitrack
|
||||
EOF
|
||||
|
||||
rm -f /tmp/luci-indexcache
|
||||
exit 0
|
Loading…
Reference in New Issue
Block a user