From dc712d53f487ac628fe9bf5eb7aee5a8d3126c4a Mon Sep 17 00:00:00 2001 From: Michael Gray Date: Sat, 15 Jan 2022 19:55:11 +1100 Subject: [PATCH] gargoyle: Allow quota usage to be modified by the user Can be used to reset a quota back to zero, prematurely expire a quota, or set it anywhere in between --- package/gargoyle/files/www/js/common.js | 32 +++- package/gargoyle/files/www/js/quota_usage.js | 158 +++++++++++++++++- .../files/www/templates/set_quotas_template | 41 +++++ .../files/www/i18n/English-EN/quotas.js | 6 +- 4 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 package/gargoyle/files/www/templates/set_quotas_template diff --git a/package/gargoyle/files/www/js/common.js b/package/gargoyle/files/www/js/common.js index ff28ea4031..3e4e4747bf 100644 --- a/package/gargoyle/files/www/js/common.js +++ b/package/gargoyle/files/www/js/common.js @@ -1057,6 +1057,28 @@ function parseBytes(bytes, units, abbr, dDgt) return parsed; } +function toBytes(val, unit) +{ + var byteVal = val; + if(unit == "KB") + { + byteVal = byteVal * 1024; + } + else if(unit == "MB") + { + byteVal = byteVal * 1024 * 1024; + } + else if(unit == "GB") + { + byteVal = byteVal * 1024 * 1024 * 1024; + } + else if(unit == "TB") + { + byteVal = byteVal * 1024 * 1024 * 1024 * 1024; + } + return byteVal; +} + function parseKbytesPerSecond(kbytes, units) { var parsed; @@ -2965,8 +2987,6 @@ function checkForPortConflict(port, proto, ignorePorts) return portConflict; } - - function query(queryHeader, queryText, buttonNameList, continueFunction ) { document.getElementById("wait_icon").style.display="none" @@ -3014,6 +3034,10 @@ function query(queryHeader, queryText, buttonNameList, continueFunction ) } } +function getKeyByValue(object, value) { + return Object.keys(object).find(key => object[key] === value); +} + //apparently these var fbS=new Object(); //part of i18n -> objects do not have a length (it is undefined) function ObjLen(an_obj) { var len=0; @@ -3351,6 +3375,10 @@ function modalPrepare(modalID, title, elements, buttons) { inputEl.innerText = element.innertext; } + if(element.checked !== undefined) + { + inputEl.checked = element.checked; + } if(element.disable !== undefined) { inputEl.disabled = element.disable; diff --git a/package/gargoyle/files/www/js/quota_usage.js b/package/gargoyle/files/www/js/quota_usage.js index 21fba9ad35..68e878e1a4 100644 --- a/package/gargoyle/files/www/js/quota_usage.js +++ b/package/gargoyle/files/www/js/quota_usage.js @@ -16,11 +16,11 @@ var allQuotaUsed; var allQuotaLimits; var allQuotaPercents; - var idToSection = []; var idToIpStr = []; var idToTimeParams = []; +var refreshInterval; function resetData() { @@ -48,7 +48,7 @@ function resetData() } refreshTableData(document.getElementById("data_display").value); - setInterval("updateTableData()", 1500); + refreshInterval = setInterval("updateTableData()", 1500); } function getIpFromUci(srcUci, quotaSection) { @@ -229,7 +229,7 @@ function refreshTableData(scheme) hostList = getHostList(ipList); if (!hide) { - quotaTableData.push( [ textListToSpanElement(hostList, true, document), timeParamsToTableSpan(timeParameters), total, down, up ] ); + quotaTableData.push( [ textListToSpanElement(hostList, true, document), timeParamsToTableSpan(timeParameters), total, down, up, createSetButton(id,ipList) ] ); } } } @@ -339,3 +339,155 @@ function weekly_i18n(weekly_schd, direction) { //this is part of i18n; TODO: bes } return joiner.join(" "); } + +function createSetButton(id,iplist) +{ + setButton = createInput("button"); + setButton.textContent = quotasStr.SetQuota; + setButton.className = "btn btn-default btn-edit"; + setButton.onclick = function(){setQuotaModal(id,iplist);}; + + setElementEnabled(setButton, true); + + return setButton; +} + +function doSetQuota(id,iplist) +{ + var createCmdStr = function(id,quotaId,ipList,quotaVal) { + // Beware escaping things properly here + var cmdLines = []; + cmdLines.push("bw_get -i " + quotaId + " -f /tmp/" + quotaId + ".quota"); + cmdLines.push("sed -i 's/\\(.*\t" + (id == "ALL_OTHERS_INDIVIDUAL" ? ipList : "0.0.0.0") + "\\)[\t ].*/\\1\t" + quotaVal + "/' /tmp/" + quotaId + ".quota"); + cmdLines.push("bw_set -i " + quotaId + " -f /tmp/" + quotaId + ".quota"); + cmdLines.push("rm /tmp/" + quotaId + ".quota"); + + return cmdLines.join("\n"); + }; + var setUpCheck = byId("use_set_up").checked; + var setDownCheck = byId("use_set_down").checked; + var setCombinedCheck = byId("use_set_combined").checked; + + var cmd = []; + if(setUpCheck) + { + var quotaID = id + "_egress"; + var setVal = byId("set_up").value; + var setUnit = getSelectedValue("set_up_unit"); + var quotaVal = toBytes(setVal,setUnit); + cmd.push(createCmdStr(id,quotaID,iplist,quotaVal)); + } + if(setDownCheck) + { + var quotaID = id + "_ingress"; + var setVal = byId("set_down").value; + var setUnit = getSelectedValue("set_down_unit"); + var quotaVal = toBytes(setVal,setUnit); + cmd.push(createCmdStr(id,quotaID,iplist,quotaVal)); + } + if(setCombinedCheck) + { + var quotaID = id + "_combined"; + var setVal = byId("set_combined").value; + var setUnit = getSelectedValue("set_combined_unit"); + var quotaVal = toBytes(setVal,setUnit); + cmd.push(createCmdStr(id,quotaID,iplist,quotaVal)); + } + + if(cmd.length > 0) + { + var command = cmd.join("\n"); + var param = getParameterDefinition("commands", command) + "&" + getParameterDefinition("hash", document.cookie.replace(/^.*hash=/,"").replace(/[\t ;]+.*$/, "")); + + var stateChangeFunction = function(req) + { + if(req.readyState == 4) + { + var text = req.responseText.split(/[\r\n]+/); + } + } + runAjax("POST", "utility/run_commands.sh", param, stateChangeFunction); + } + + closeQuotaModal(); +} + +function closeQuotaModal() +{ + refreshInterval = setInterval("updateTableData()", 1500); // restart GUI updates + closeModalWindow('quotas_modal'); +} + +function setQuotaModal(id,iplist) +{ + var unitOpts = {}; + unitOpts['KB'] = UI.KBy; + unitOpts['MB'] = UI.MBy; + unitOpts['GB'] = UI.GBy; + unitOpts['TB'] = UI.TBy; + joinIpList = iplist.join(","); + + upCurrent = allQuotaUsed[id][joinIpList][2]; + downCurrent = allQuotaUsed[id][joinIpList][1]; + combinedCurrent = allQuotaUsed[id][joinIpList][0]; + upCurrent = (upCurrent == -1 ? " " : parseBytes(upCurrent)).split(" "); + downCurrent = (downCurrent == -1 ? " " : parseBytes(downCurrent)).split(" "); + combinedCurrent = (combinedCurrent == -1 ? " " : parseBytes(combinedCurrent)).split(" "); + upCurrentUnit = getKeyByValue(unitOpts,upCurrent[1]); + upCurrent = upCurrent[0]; + downCurrentUnit = getKeyByValue(unitOpts,downCurrent[1]); + downCurrent = downCurrent[0]; + combinedCurrentUnit = getKeyByValue(unitOpts,combinedCurrent[1]); + combinedCurrent = combinedCurrent[0]; + + upLimit = allQuotaLimits[id][joinIpList][2]; + downLimit = allQuotaLimits[id][joinIpList][1]; + combinedLimit = allQuotaLimits[id][joinIpList][0]; + upLimit = upLimit == -1 ? "N/A" : parseBytes(upLimit); + downLimit = downLimit == -1 ? "N/A" : parseBytes(downLimit); + combinedLimit = combinedLimit == -1 ? "N/A" : parseBytes(combinedLimit); + + upDisabled = (upCurrent == ""); + downDisabled = (downCurrent == ""); + combinedDisabled = (combinedCurrent == ""); + + enableBothQuotaFields = function(triggerEl,fieldName,data,unit) { + enableAssociatedField(triggerEl,fieldName,data); + enableAssociatedField(triggerEl,fieldName+'_unit',unit); + }; + + byId("use_set_up").onclick = function() { + enableBothQuotaFields(this,'set_up',upCurrent,upCurrentUnit); + }; + byId("use_set_down").onclick = function() { + enableBothQuotaFields(this,'set_down',downCurrent,downCurrentUnit); + }; + byId("use_set_combined").onclick = function() { + enableBothQuotaFields(this,'set_combined',combinedCurrent,combinedCurrentUnit); + }; + + clearInterval(refreshInterval); // pause GUI updates + + modalButtons = [ + {"title" : UI.CApplyChanges, "classes" : "btn btn-primary", "function" : function(){doSetQuota(id,joinIpList);}}, + {"title" : UI.CDiscardChanges, "classes" : "btn btn-warning", "function" : function(){closeQuotaModal();}} + ]; + + modalElements = [ + {"id" : "set_up", "value" : upCurrent, "disable" : true}, + {"id" : "set_down", "value" : downCurrent, "disable" : true}, + {"id" : "set_combined", "value" : combinedCurrent, "disable" : true}, + {"id" : "use_set_up", "checked" : false, "disable" : upDisabled}, + {"id" : "use_set_down", "checked" : false, "disable" : downDisabled}, + {"id" : "use_set_combined", "checked" : false, "disable" : combinedDisabled}, + {"id" : "set_up_unit", "options" : unitOpts, "value" : upCurrentUnit, "disable" : true}, + {"id" : "set_down_unit", "options" : unitOpts, "value" : downCurrentUnit, "disable" : true}, + {"id" : "set_combined_unit", "options" : unitOpts, "value" : combinedCurrentUnit, "disable" : true}, + {"id" : "set_up_limit", "innertext" : upLimit}, + {"id" : "set_down_limit", "innertext" : downLimit}, + {"id" : "set_combined_limit", "innertext" : combinedLimit} + ]; + + modalPrepare('quotas_modal', quotasStr.SetQuota, modalElements, modalButtons); + openModalWindow('quotas_modal'); +} diff --git a/package/gargoyle/files/www/templates/set_quotas_template b/package/gargoyle/files/www/templates/set_quotas_template new file mode 100644 index 0000000000..889f7394cf --- /dev/null +++ b/package/gargoyle/files/www/templates/set_quotas_template @@ -0,0 +1,41 @@ +
+ + + + + + + + + + + Limit: +
+ +
+ + + + + + + + + + + Limit: +
+ +
+ + + + + + + + + + + Limit: +
\ No newline at end of file diff --git a/package/plugin-gargoyle-i18n-English-EN/files/www/i18n/English-EN/quotas.js b/package/plugin-gargoyle-i18n-English-EN/files/www/i18n/English-EN/quotas.js index 8641da3a2a..34c775a4ca 100644 --- a/package/plugin-gargoyle-i18n-English-EN/files/www/i18n/English-EN/quotas.js +++ b/package/plugin-gargoyle-i18n-English-EN/files/www/i18n/English-EN/quotas.js @@ -69,4 +69,8 @@ quotasStr.ESection="Edit Quota"; quotasStr.USect="Bandwidth Quota Usage"; quotasStr.All="All"; quotasStr.Alws="Always"; -quotasStr.ColNms=["Host(s)", "Active", "Total", "Down", "Up" ]; +quotasStr.ColNms=["Host(s)", "Active", "Total", "Down", "Up", "" ]; +quotasStr.SetQuota="Set Quota Usage"; +quotasStr.Up="Up"; +quotasStr.Down="Down"; +quotasStr.Total="Total";