From: Dirk Brenken Date: Thu, 4 Dec 2025 19:28:42 +0000 (+0100) Subject: luci-app-banip: sync with banIP-1.6.0-1 X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=bf0ce9c571a51947ebafc0b02d4594073b18f218;p=project%2Fluci.git luci-app-banip: sync with banIP-1.6.0-1 Signed-off-by: Dirk Brenken --- diff --git a/applications/luci-app-banip/Makefile b/applications/luci-app-banip/Makefile index 6b00a4cc02..ce20f7740e 100644 --- a/applications/luci-app-banip/Makefile +++ b/applications/luci-app-banip/Makefile @@ -6,6 +6,8 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI support for banIP LUCI_DEPENDS:=+luci-base +banip +PKG_VERSION:=1.6.0 +PKG_RELEASE:=1 PKG_LICENSE:=Apache-2.0 PKG_MAINTAINER:=Dirk Brenken diff --git a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/allowlist.js b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/allowlist.js index 24d9438b76..43f62cd8db 100644 --- a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/allowlist.js +++ b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/allowlist.js @@ -3,25 +3,29 @@ 'require fs'; 'require ui'; -let localFile = '/etc/banip/banip.allowlist'; -let notMsg, errMsg; +const localFile = '/etc/banip/banip.allowlist'; +let notMsg = false, errMsg = false; + +const resetScroll = () => { + document.body.scrollTop = document.documentElement.scrollTop = 0; +}; return view.extend({ load: function () { return L.resolveDefault(fs.stat(localFile), "") - .then(function (stat) { - if (!stat) { - return fs.write(localFile, ""); - } - return Promise.all([ - L.resolveDefault(fs.stat(localFile), ""), - L.resolveDefault(fs.read_direct(localFile), "") - ]); - }); + .then(function (stat) { + if (!stat) { + return fs.write(localFile, ""); + } + return Promise.all([ + L.resolveDefault(fs.stat(localFile), ""), + L.resolveDefault(fs.read_direct(localFile), "") + ]); + }); }, render: function (allowlist) { if (allowlist[0].size >= 100000) { - document.body.scrollTop = document.documentElement.scrollTop = 0; + resetScroll(); ui.addNotification(null, E('p', _('The allowlist is too big, unable to save modifications.')), 'error'); } return E('div', { 'class': 'cbi-section cbi-section-descr' }, [ @@ -36,22 +40,22 @@ return view.extend({ ]); }, handleSave: function (ev) { - let value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/\r\n/g, '\n')) + '\n'; - return fs.write(localFile, value) - .then(function () { - document.querySelector('textarea').value = value; - document.body.scrollTop = document.documentElement.scrollTop = 0; - if (!notMsg) { - ui.addNotification(null, E('p', _('Allowlist modifications have been saved, reload banIP that changes take effect.')), 'info'); - notMsg = true; - } - }).catch(function (e) { - document.body.scrollTop = document.documentElement.scrollTop = 0; - if (!errMsg) { - ui.addNotification(null, E('p', _('Unable to save modifications: %s').format(e.message)), 'error'); - errMsg = true; - } - }); + let value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/\r\n?/g, '\n')); + return fs.write(localFile, value + "\n") + .then(function () { + document.querySelector('textarea').value = value; + resetScroll(); + if (!notMsg) { + ui.addNotification(null, E('p', _('Allowlist modifications have been saved, reload banIP that changes take effect.')), 'info'); + notMsg = true; + } + }).catch(function (e) { + resetScroll(); + if (!errMsg) { + ui.addNotification(null, E('p', _('Unable to save modifications: %s').format(e.message)), 'error'); + errMsg = true; + } + }); }, handleSaveApply: null, handleReset: null diff --git a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/blocklist.js b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/blocklist.js index 3a9c478da8..a676bc8cc5 100644 --- a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/blocklist.js +++ b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/blocklist.js @@ -3,25 +3,29 @@ 'require fs'; 'require ui'; -let localFile = '/etc/banip/banip.blocklist'; -let notMsg, errMsg; +const localFile = '/etc/banip/banip.blocklist'; +let notMsg = false, errMsg = false; + +const resetScroll = () => { + document.body.scrollTop = document.documentElement.scrollTop = 0; +}; return view.extend({ load: function () { return L.resolveDefault(fs.stat(localFile), "") - .then(function (stat) { - if (!stat) { - return fs.write(localFile, ""); - } - return Promise.all([ - L.resolveDefault(fs.stat(localFile), ""), - L.resolveDefault(fs.read_direct(localFile), "") - ]); - }); + .then(function (stat) { + if (!stat) { + return fs.write(localFile, ""); + } + return Promise.all([ + L.resolveDefault(fs.stat(localFile), ""), + L.resolveDefault(fs.read_direct(localFile), "") + ]); + }); }, render: function (blocklist) { if (blocklist[0].size >= 100000) { - document.body.scrollTop = document.documentElement.scrollTop = 0; + resetScroll(); ui.addNotification(null, E('p', _('The blocklist is too big, unable to save modifications.')), 'error'); } return E('div', { 'class': 'cbi-section cbi-section-descr' }, [ @@ -36,22 +40,22 @@ return view.extend({ ]); }, handleSave: function (ev) { - let value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/\r\n/g, '\n')) + '\n'; - return fs.write(localFile, value) - .then(function () { - document.querySelector('textarea').value = value; - document.body.scrollTop = document.documentElement.scrollTop = 0; - if (!notMsg) { - ui.addNotification(null, E('p', _('Blocklist modifications have been saved, reload banIP that changes take effect.')), 'info'); - notMsg = true; - } - }).catch(function (e) { - document.body.scrollTop = document.documentElement.scrollTop = 0; - if (!errMsg) { - ui.addNotification(null, E('p', _('Unable to save modifications: %s').format(e.message)), 'error'); - errMsg = true; - } - }); + let value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/\r\n?/g, '\n')); + return fs.write(localFile, value + "\n") + .then(function () { + document.querySelector('textarea').value = value; + resetScroll(); + if (!notMsg) { + ui.addNotification(null, E('p', _('Blocklist modifications have been saved, reload banIP that changes take effect.')), 'info'); + notMsg = true; + } + }).catch(function (e) { + resetScroll(); + if (!errMsg) { + ui.addNotification(null, E('p', _('Unable to save modifications: %s').format(e.message)), 'error'); + errMsg = true; + } + }); }, handleSaveApply: null, handleReset: null diff --git a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/custom.css b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/custom.css index d79e6f987b..8895f27dfa 100644 --- a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/custom.css +++ b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/custom.css @@ -3,7 +3,6 @@ margin-bottom: -5px; padding-top: 0rem; } - .cbi-input-select { margin-bottom: -5px; padding-top: 0rem; diff --git a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/firewall_log.js b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/firewall_log.js index 7ed6580bc0..5f54db7deb 100644 --- a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/firewall_log.js +++ b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/firewall_log.js @@ -1,4 +1,4 @@ 'use strict'; 'require view.banip.logtemplate as LogTemplate'; -return LogTemplate.Logview('banIP/', 'banIP'); +return LogTemplate.Logview(' banIP/', 'banIP'); diff --git a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/logtemplate.js b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/logtemplate.js index 23a8fbebe7..10c552721f 100644 --- a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/logtemplate.js +++ b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/logtemplate.js @@ -1,33 +1,54 @@ 'use strict'; -'require fs'; +'require rpc'; + +const callLogRead = rpc.declare({ + object: 'log', + method: 'read', + params: ['lines', 'stream', 'oneshot'], + expect: {} +}); function Logview(logtag, name) { return L.view.extend({ - load: function() { - return Promise.all([ - L.resolveDefault(fs.stat('/sbin/logread'), null), - L.resolveDefault(fs.stat('/usr/sbin/logread'), null) - ]); - }, + load: () => Promise.resolve(), - render: function(stat) { - let logger = stat[0]?.path || stat[1]?.path || null; + render: () => { + L.Poll.add(() => { + return callLogRead(1000, false, true).then(res => { + const logEl = document.getElementById('logfile'); + if (!logEl) return; - if (!logger) { - return E('div', { class: 'error' }, _('logread not found on system.')); - } - L.Poll.add(function() { - return L.resolveDefault(fs.exec_direct(logger, ['-e', logtag])).then(function(res) { - var log = document.getElementById('logfile'); - if (log) { - log.value = res ? res.trim() : _('No %s related logs yet!').format(name); - log.scrollTop = log.scrollHeight; + const entries = res?.log ?? []; + if (entries.length > 0) { + const filtered = entries + .filter(entry => !logtag || entry.msg.includes(logtag)) + .map(entry => { + const d = new Date(entry.time); + const date = d.toLocaleDateString([], { + year: 'numeric', + month: '2-digit', + day: '2-digit' + }); + const time = d.toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false + }); + return `[${date}-${time}] ${entry.msg}`; + }); + logEl.value = filtered.join('\n'); + } else { + logEl.value = _('No %s related logs yet!').format(name); } + logEl.scrollTop = logEl.scrollHeight; }); }); + return E('div', { class: 'cbi-map' }, [ E('div', { class: 'cbi-section' }, [ - E('div', { class: 'cbi-section-descr' }, _('The syslog output, pre-filtered for messages related to: %s').format(name)), + E('div', { class: 'cbi-section-descr' }, + _('The syslog output, pre-filtered for messages related to: %s').format(name)), E('textarea', { id: 'logfile', style: 'min-height: 500px; max-height: 90vh; width: 100%; padding: 5px; font-family: monospace; resize: vertical;', @@ -37,12 +58,11 @@ function Logview(logtag, name) { ]) ]); }, + handleSaveApply: null, handleSave: null, handleReset: null }); } -return L.Class.extend({ - Logview: Logview -}); +return L.Class.extend({ Logview }); diff --git a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/overview.js b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/overview.js index 27707da340..642f82ee6d 100644 --- a/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/overview.js +++ b/applications/luci-app-banip/htdocs/luci-static/resources/view/banip/overview.js @@ -41,116 +41,77 @@ return view.extend({ For further information please check the online documentation')); /* - poll runtime information + set text content helper function */ - let buttons, rtRes, infStat, infVer, infElements, infFeeds, infDevices, infUplink, infSys, infNft, infRun, infFlags, infLast + const setText = (id, value) => { + const el = document.getElementById(id); + if (el) { + el.textContent = value || '-'; + } + }; + /* + poll runtime information + */ pollData: poll.add(function () { - return L.resolveDefault(fs.stat('/var/run/banip.lock')).then(function (stat) { - buttons = document.querySelectorAll('.cbi-button'); - infStat = document.getElementById('status'); - if (stat) { - for (let i = 0; i < buttons.length; i++) { - buttons[i].setAttribute('disabled', 'true'); - } - if (infStat && !infStat.classList.contains('spinning')) { - infStat.classList.add('spinning'); - } - } else { - for (let i = 0; i < buttons.length; i++) { - buttons[i].removeAttribute('disabled'); - } - if (infStat && infStat.classList.contains('spinning')) { - infStat.classList.remove('spinning'); + return L.resolveDefault(fs.read_direct('/var/run/banip_runtime.json'), 'null').then(function (res) { + const status = document.getElementById('status'); + const buttons = document.querySelectorAll('.cbi-page-actions button'); + try { + var info = JSON.parse(res); + } catch (e) { + status.textContent = '-'; + poll.stop(); + if (status.classList.contains('spinning')) { + buttons.forEach(function (btn) { + btn.disabled = false; + }) + status.classList.remove('spinning'); } + ui.addNotification(null, E('p', _('Unable to parse the runtime information!')), 'error'); } - L.resolveDefault(fs.exec_direct('/etc/init.d/banip', ['status'])).then(function (result) { - if (result) { - rtRes = result.trim().split('\n'); - if (rtRes) { - for (let i = 0; i < rtRes.length; i++) { - if (rtRes[i].match(/^\s+\+\sstatus\s+\:\s+(.*)$/)) { - rtRes.status = rtRes[i].match(/^\s+\+\sstatus\s+\:\s+(.*)$/)[1]; - } else if (rtRes[i].match(/^\s+\+\sversion\s+\:\s+(.*)$/)) { - rtRes.version = rtRes[i].match(/^\s+\+\sversion\s+\:\s+(.*)$/)[1]; - } else if (rtRes[i].match(/^\s+\+\selement_count\s+\:\s+(.*)$/)) { - rtRes.elementCount = rtRes[i].match(/^\s+\+\selement_count\s+\:\s+(.*)$/)[1]; - } else if (rtRes[i].match(/^\s+\+\sactive_feeds\s+\:\s+(.*)$/)) { - rtRes.activeFeeds = rtRes[i].match(/^\s+\+\sactive_feeds\s+\:\s+(.*)$/)[1]; - } else if (rtRes[i].match(/^\s+\+\sactive_devices\s+\:\s+(.*)$/)) { - rtRes.activeDevices = rtRes[i].match(/^\s+\+\sactive_devices\s+\:\s+(.*)$/)[1]; - } else if (rtRes[i].match(/^\s+\+\sactive_uplink\s+\:\s+(.*)$/)) { - rtRes.activeUplink = rtRes[i].match(/^\s+\+\sactive_uplink\s+\:\s+(.*)$/)[1]; - } else if (rtRes[i].match(/^\s+\+\snft_info\s+\:\s+(.*)$/)) { - rtRes.nftInfo = rtRes[i].match(/^\s+\+\snft_info\s+\:\s+(.*)$/)[1]; - } else if (rtRes[i].match(/^\s+\+\srun_info\s+\:\s+(.*)$/)) { - rtRes.runInfo = rtRes[i].match(/^\s+\+\srun_info\s+\:\s+(.*)$/)[1]; - } else if (rtRes[i].match(/^\s+\+\srun_flags\s+\:\s+(.*)$/)) { - rtRes.runFlags = rtRes[i].match(/^\s+\+\srun_flags\s+\:\s+(.*)$/)[1]; - } else if (rtRes[i].match(/^\s+\+\slast_run\s+\:\s+(.*)$/)) { - rtRes.lastRun = rtRes[i].match(/^\s+\+\slast_run\s+\:\s+(.*)$/)[1]; - } else if (rtRes[i].match(/^\s+\+\ssystem_info\s+\:\s+(.*)$/)) { - rtRes.sysInfo = rtRes[i].match(/^\s+\+\ssystem_info\s+\:\s+(.*)$/)[1]; - } - } - } - if (rtRes) { - infStat = document.getElementById('status'); - if (infStat) { - infStat.textContent = rtRes.status || '-'; - } - infVer = document.getElementById('version'); - if (infVer) { - infVer.textContent = rtRes.version || '-'; - } - infElements = document.getElementById('elements'); - if (infElements) { - infElements.textContent = rtRes.elementCount || '-'; - } - infFeeds = document.getElementById('feeds'); - if (infFeeds) { - infFeeds.textContent = rtRes.activeFeeds || '-'; - } - infDevices = document.getElementById('devices'); - if (infDevices) { - infDevices.textContent = rtRes.activeDevices || '-'; - } - infUplink = document.getElementById('uplink'); - if (infUplink) { - infUplink.textContent = rtRes.activeUplink || '-'; - } - infNft = document.getElementById('nft'); - if (infNft) { - infNft.textContent = rtRes.nftInfo || '-'; - } - infRun = document.getElementById('run'); - if (infRun) { - infRun.textContent = rtRes.runInfo || '-'; - } - infFlags = document.getElementById('flags'); - if (infFlags) { - infFlags.textContent = rtRes.runFlags || '-'; - } - infLast = document.getElementById('last'); - if (infLast) { - infLast.textContent = rtRes.lastRun || '-'; - } - infSys = document.getElementById('sys'); - if (infSys) { - infSys.textContent = rtRes.sysInfo || '-'; - } + if (status && info) { + status.textContent = `${info.status || '-'} (frontend: ${info.frontend_ver || '-'} / backend: ${info.backend_ver || '-'})`; + if (info.status === "processing") { + if (!status.classList.contains("spinning")) { + status.classList.add("spinning"); } + buttons.forEach(function (btn) { + btn.disabled = true; + btn.blur(); + }) } else { - infStat = document.getElementById('status'); - if (infStat) { - infStat.textContent = '-'; - poll.stop(); - if (infStat.classList.contains('spinning')) { - infStat.classList.remove('spinning'); - } + if (status.classList.contains("spinning")) { + buttons.forEach(function (btn) { + btn.disabled = false; + }) + status.classList.remove("spinning"); } } - }); + } else if (status) { + status.textContent = '-'; + poll.stop(); + if (status.classList.contains('spinning')) { + buttons.forEach(function (btn) { + btn.disabled = false; + }) + status.classList.remove('spinning'); + } + } + if (info) { + setText('elements', info.element_count); + setText('feeds', info.active_feeds?.join(', ')); + setText('devices', `wan-dev: ${info.wan_devices?.join(', ') || '-'} / + wan-if: ${info.wan_interfaces?.join(', ') || '-'} / + vlan-allow: ${info.vlan_allow?.join(', ') || '-'} / + vlan-block: ${info.vlan_block?.join(', ') || '-'}`); + setText('uplink', info.active_uplink?.join(', ') || '-'); + setText('nft', info.nft_info); + setText('run', info.run_info); + setText('flags', info.run_flags); + setText('last', info.last_run); + setText('sys', info.system_info); + } }); }, 2); @@ -165,10 +126,6 @@ return view.extend({ E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Status')), E('div', { 'class': 'cbi-value-field spinning', 'id': 'status', 'style': 'margin-bottom:-5px;color:#37c;' }, '\xa0') ]), - E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Version')), - E('div', { 'class': 'cbi-value-field', 'id': 'version', 'style': 'margin-bottom:-5px;color:#37c;' }, '-') - ]), E('div', { 'class': 'cbi-value' }, [ E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Element Count')), E('div', { 'class': 'cbi-value-field', 'id': 'elements', 'style': 'margin-bottom:-5px;color:#37c;' }, '-') diff --git a/applications/luci-app-banip/root/usr/share/rpcd/acl.d/luci-app-banip.json b/applications/luci-app-banip/root/usr/share/rpcd/acl.d/luci-app-banip.json index 51a1d63222..b74902cdf0 100644 --- a/applications/luci-app-banip/root/usr/share/rpcd/acl.d/luci-app-banip.json +++ b/applications/luci-app-banip/root/usr/share/rpcd/acl.d/luci-app-banip.json @@ -3,76 +3,30 @@ "description": "Grant access to LuCI app banIP", "write": { "file": { - "/etc/banip/*": [ - "read", - "write" - ], - "/etc/banip/banip.allowlist": [ - "write" - ], - "/etc/banip/banip.blocklist": [ - "write" - ], - "/etc/banip/banip.custom.feeds": [ - "read", - "write" - ] + "/etc/banip/*": [ "read", "write" ], + "/etc/banip/banip.allowlist": [ "write" ], + "/etc/banip/banip.blocklist": [ "write" ], + "/etc/banip/banip.custom.feeds": [ "read", "write" ] }, - "uci": [ - "banip" - ] + "uci": [ "banip" ] }, "read": { - "cgi-io": [ - "exec" - ], + "cgi-io": [ "exec" ], "file": { - "/var/run/banip.lock": [ - "read" - ], - "/sbin/logread -e banIP-": [ - "exec" - ], - "/usr/sbin/logread -e banIP-": [ - "exec" - ], - "/sbin/logread -e banIP/": [ - "exec" - ], - "/usr/sbin/logread -e banIP/": [ - "exec" - ], - "/usr/sbin/nft -tj list sets": [ - "exec" - ], - "/etc/init.d/banip stop": [ - "exec" - ], - "/etc/init.d/banip reload": [ - "exec" - ], - "/etc/init.d/banip restart": [ - "exec" - ], - "/etc/init.d/banip report json": [ - "exec" - ], - "/etc/init.d/banip report gen": [ - "exec" - ], - "/etc/init.d/banip search [A-Za-z0-9:.]*": [ - "exec" - ], - "/etc/init.d/banip content [A-Za-z0-9]* *": [ - "exec" - ], - "/etc/init.d/banip status": [ - "exec" - ] + "/var/run/banip.lock": [ "read" ], + "/var/run/banip_runtime.json": [ "read" ], + "/usr/sbin/nft -tj list sets": [ "exec" ], + "/etc/init.d/banip stop": [ "exec" ], + "/etc/init.d/banip reload": [ "exec" ], + "/etc/init.d/banip restart": [ "exec" ], + "/etc/init.d/banip report json": [ "exec" ], + "/etc/init.d/banip report gen": [ "exec" ], + "/etc/init.d/banip search [A-Za-z0-9:.]*": [ "exec" ], + "/etc/init.d/banip content [A-Za-z0-9]* *": [ "exec" ], + "/etc/init.d/banip status": [ "exec" ] }, - "uci": [ - "banip" - ] + "uci": [ "banip"], + "log": [ "read" ] } } } \ No newline at end of file