mirror of
https://github.com/openwrt/luci.git
synced 2026-04-15 10:51:51 +00:00
luci-app-banip: release 1.8.1-1
* sync with base package Signed-off-by: Dirk Brenken <dev@brenken.org>
This commit is contained in:
@@ -180,7 +180,7 @@ return view.extend({
|
||||
]),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Last Run')),
|
||||
E('div', { 'class': 'cbi-value-field', 'id': 'last', 'style': 'margin - bottom:- 5px; color:#37c; ' }, ' - ')
|
||||
E('div', { 'class': 'cbi-value-field', 'id': 'last', 'style': 'margin-bottom:- 5px; color:#37c; ' }, ' - ')
|
||||
]),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('System Info')),
|
||||
|
||||
@@ -6,8 +6,8 @@ include $(TOPDIR)/rules.mk
|
||||
LUCI_TITLE:=LuCI support for banIP
|
||||
LUCI_DEPENDS:=+luci-base +banip
|
||||
|
||||
PKG_VERSION:=1.8.0
|
||||
PKG_RELEASE:=3
|
||||
PKG_VERSION:=1.8.1
|
||||
PKG_RELEASE:=1
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
'require ui';
|
||||
|
||||
const localFile = '/etc/banip/banip.allowlist';
|
||||
let notMsg = false, errMsg = false;
|
||||
const maxSize = 100000;
|
||||
let notMsg = false;
|
||||
|
||||
const resetScroll = () => {
|
||||
document.body.scrollTop = document.documentElement.scrollTop = 0;
|
||||
@@ -12,19 +13,24 @@ const resetScroll = () => {
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return L.resolveDefault(fs.stat(localFile), "")
|
||||
return L.resolveDefault(fs.stat(localFile), null)
|
||||
.then(function (stat) {
|
||||
if (!stat) {
|
||||
return fs.write(localFile, "");
|
||||
}
|
||||
return Promise.all([
|
||||
L.resolveDefault(fs.stat(localFile), ""),
|
||||
L.resolveDefault(fs.read_direct(localFile), "")
|
||||
]);
|
||||
});
|
||||
if (!stat) {
|
||||
return fs.write(localFile, "").then(() => [{ size: 0 }, ""]);
|
||||
}
|
||||
return Promise.all([
|
||||
Promise.resolve(stat),
|
||||
L.resolveDefault(fs.read_direct(localFile), "")
|
||||
]);
|
||||
});
|
||||
},
|
||||
|
||||
render: function (allowlist) {
|
||||
if (allowlist[0] && allowlist[0].size >= 100000) {
|
||||
const size = allowlist[0] ? allowlist[0].size : 0;
|
||||
const content = allowlist[1] != null ? allowlist[1] : '';
|
||||
const tooBig = size >= maxSize;
|
||||
|
||||
if (tooBig) {
|
||||
resetScroll();
|
||||
ui.addNotification(null, E('p', _('The allowlist is too big, unable to save modifications.')), 'error');
|
||||
}
|
||||
@@ -35,28 +41,29 @@ return view.extend({
|
||||
'style': 'width: 100% !important; padding: 5px; font-family: monospace; margin-top: .4em',
|
||||
'spellcheck': 'false',
|
||||
'wrap': 'off',
|
||||
'rows': 25
|
||||
}, [allowlist[1] != null ? allowlist[1] : ''])
|
||||
'rows': 25,
|
||||
'readonly': tooBig ? 'readonly' : null,
|
||||
'input': function () { notMsg = false; }
|
||||
}, [content])
|
||||
]);
|
||||
},
|
||||
handleSave: function (ev) {
|
||||
let value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/\r\n?/g, '\n'));
|
||||
|
||||
handleSave: function (_ev) {
|
||||
const 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) {
|
||||
.then(function () {
|
||||
document.querySelector('textarea').value = value + "\n";
|
||||
resetScroll();
|
||||
if (!notMsg) {
|
||||
notMsg = true;
|
||||
ui.addNotification(null, E('p', _('Allowlist modifications have been saved, reload banIP that changes take effect.')), 'info');
|
||||
}
|
||||
}).catch(function (e) {
|
||||
resetScroll();
|
||||
ui.addNotification(null, E('p', _('Unable to save modifications: %s').format(e.message)), 'error');
|
||||
errMsg = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleReset: null
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,8 @@
|
||||
'require ui';
|
||||
|
||||
const localFile = '/etc/banip/banip.blocklist';
|
||||
let notMsg = false, errMsg = false;
|
||||
const maxSize = 100000;
|
||||
let notMsg = false;
|
||||
|
||||
const resetScroll = () => {
|
||||
document.body.scrollTop = document.documentElement.scrollTop = 0;
|
||||
@@ -12,19 +13,24 @@ const resetScroll = () => {
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return L.resolveDefault(fs.stat(localFile), "")
|
||||
return L.resolveDefault(fs.stat(localFile), null)
|
||||
.then(function (stat) {
|
||||
if (!stat) {
|
||||
return fs.write(localFile, "");
|
||||
}
|
||||
return Promise.all([
|
||||
L.resolveDefault(fs.stat(localFile), ""),
|
||||
L.resolveDefault(fs.read_direct(localFile), "")
|
||||
]);
|
||||
});
|
||||
if (!stat) {
|
||||
return fs.write(localFile, "").then(() => [{ size: 0 }, ""]);
|
||||
}
|
||||
return Promise.all([
|
||||
Promise.resolve(stat),
|
||||
L.resolveDefault(fs.read_direct(localFile), "")
|
||||
]);
|
||||
});
|
||||
},
|
||||
|
||||
render: function (blocklist) {
|
||||
if (blocklist[0] && blocklist[0].size >= 100000) {
|
||||
const size = blocklist[0] ? blocklist[0].size : 0;
|
||||
const content = blocklist[1] != null ? blocklist[1] : '';
|
||||
const tooBig = size >= maxSize;
|
||||
|
||||
if (tooBig) {
|
||||
resetScroll();
|
||||
ui.addNotification(null, E('p', _('The blocklist is too big, unable to save modifications.')), 'error');
|
||||
}
|
||||
@@ -35,28 +41,29 @@ return view.extend({
|
||||
'style': 'width: 100% !important; padding: 5px; font-family: monospace; margin-top: .4em',
|
||||
'spellcheck': 'false',
|
||||
'wrap': 'off',
|
||||
'rows': 25
|
||||
}, [blocklist[1] != null ? blocklist[1] : ''])
|
||||
'rows': 25,
|
||||
'readonly': tooBig ? 'readonly' : null,
|
||||
'input': function () { notMsg = false; }
|
||||
}, [content])
|
||||
]);
|
||||
},
|
||||
handleSave: function (ev) {
|
||||
let value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/\r\n?/g, '\n'));
|
||||
|
||||
handleSave: function (_ev) {
|
||||
const 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) {
|
||||
.then(function () {
|
||||
document.querySelector('textarea').value = value + "\n";
|
||||
resetScroll();
|
||||
if (!notMsg) {
|
||||
notMsg = true;
|
||||
ui.addNotification(null, E('p', _('Blocklist modifications have been saved, reload banIP that changes take effect.')), 'info');
|
||||
}
|
||||
}).catch(function (e) {
|
||||
resetScroll();
|
||||
ui.addNotification(null, E('p', _('Unable to save modifications: %s').format(e.message)), 'error');
|
||||
errMsg = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleReset: null
|
||||
});
|
||||
});
|
||||
@@ -13,6 +13,26 @@ document.querySelector('head').appendChild(E('link', {
|
||||
'href': L.resource('view/banip/custom.css')
|
||||
}));
|
||||
|
||||
/*
|
||||
module-level file size set during render, read by observer
|
||||
*/
|
||||
let fileSize = 0;
|
||||
|
||||
/*
|
||||
button state helper
|
||||
*/
|
||||
function updateButtons() {
|
||||
const buttons = document.querySelectorAll('#btnClear, #btnCreate, #btnSave, #btnUpload, #btnDownload');
|
||||
if (fileSize === 0) {
|
||||
if (buttons[1]) buttons[1].removeAttribute('disabled');
|
||||
if (buttons[2]) buttons[2].removeAttribute('disabled');
|
||||
} else {
|
||||
if (buttons[0]) buttons[0].removeAttribute('disabled');
|
||||
if (buttons[3]) buttons[3].removeAttribute('disabled');
|
||||
if (buttons[4]) buttons[4].removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
observe DOM changes
|
||||
*/
|
||||
@@ -29,17 +49,7 @@ const observer = new MutationObserver(function (mutations) {
|
||||
labels.forEach(function (label) {
|
||||
label.setAttribute("style", "font-weight: bold !important; color: #595 !important;");
|
||||
})
|
||||
L.resolveDefault(fs.stat('/etc/banip/banip.custom.feeds'), '').then(function (stat) {
|
||||
const buttons = document.querySelectorAll('#btnClear, #btnCreate, #btnSave, #btnUpload, #btnDownload');
|
||||
if (buttons[1] && buttons[2] && stat.size === 0) {
|
||||
buttons[1].removeAttribute('disabled');
|
||||
buttons[2].removeAttribute('disabled');
|
||||
} else if (buttons[0] && buttons[3] && buttons[4] && stat.size > 0) {
|
||||
buttons[0].removeAttribute('disabled');
|
||||
buttons[3].removeAttribute('disabled');
|
||||
buttons[4].removeAttribute('disabled');
|
||||
}
|
||||
});
|
||||
updateButtons();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -59,29 +69,20 @@ function handleEdit(ev) {
|
||||
if (ev === 'upload') {
|
||||
return ui.uploadFile('/etc/banip/banip.custom.feeds').then(function () {
|
||||
L.resolveDefault(fs.read_direct('/etc/banip/banip.custom.feeds', 'json'), "").then(function (data) {
|
||||
if (data) {
|
||||
let dataLength = Object.keys(data).length || 0;
|
||||
if (dataLength > 0) {
|
||||
for (let i = 0; i < dataLength; i++) {
|
||||
let feed = Object.keys(data)[i];
|
||||
let descr = data[feed].descr;
|
||||
if (feed && descr) {
|
||||
continue;
|
||||
}
|
||||
fs.write('/etc/banip/banip.custom.feeds', null).then(function () {
|
||||
ui.addNotification(null, E('p', _('Upload of the custom feed file failed.')), 'error');
|
||||
});
|
||||
return;
|
||||
if (data && Object.keys(data).length > 0) {
|
||||
for (let i = 0; i < Object.keys(data).length; i++) {
|
||||
let feed = Object.keys(data)[i];
|
||||
let descr = data[feed].descr;
|
||||
if (feed && descr) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
fs.write('/etc/banip/banip.custom.feeds', null).then(function () {
|
||||
return fs.write('/etc/banip/banip.custom.feeds', null).then(function () {
|
||||
ui.addNotification(null, E('p', _('Upload of the custom feed file failed.')), 'error');
|
||||
});
|
||||
return;
|
||||
}
|
||||
location.reload();
|
||||
} else {
|
||||
fs.write('/etc/banip/banip.custom.feeds', null).then(function () {
|
||||
return fs.write('/etc/banip/banip.custom.feeds', null).then(function () {
|
||||
ui.addNotification(null, E('p', _('Upload of the custom feed file failed.')), 'error');
|
||||
});
|
||||
}
|
||||
@@ -120,9 +121,9 @@ function handleEdit(ev) {
|
||||
}
|
||||
}
|
||||
/*
|
||||
gather all input data
|
||||
gather all input data and fall through from 'save'
|
||||
*/
|
||||
let sumSubElements = [];
|
||||
const exportObj = {};
|
||||
const nodeKeys = document.querySelectorAll('[id^="widget.cbid.json"][id$="name"]');
|
||||
for (const keyNode of nodeKeys) {
|
||||
const keyValue = keyNode.value?.trim();
|
||||
@@ -144,40 +145,39 @@ function handleEdit(ev) {
|
||||
sub[key] = value;
|
||||
}
|
||||
}
|
||||
if (sub.descr) {
|
||||
sumSubElements.push(keyValue, sub);
|
||||
/* require at least descr and rule to produce a valid feed entry */
|
||||
if (sub.descr && sub.rule && (sub.url_4 || sub.url_6)) {
|
||||
exportObj[keyValue] = sub;
|
||||
}
|
||||
}
|
||||
/*
|
||||
construct json object
|
||||
*/
|
||||
let exportObj = {};
|
||||
for (let i = 0; i < sumSubElements.length; i += 2) {
|
||||
const key = sumSubElements[i];
|
||||
const value = sumSubElements[i + 1];
|
||||
exportObj[key] = value;
|
||||
}
|
||||
const exportJson = JSON.stringify(exportObj, null, 4);
|
||||
/*
|
||||
save to file and reload
|
||||
*/
|
||||
const exportJson = JSON.stringify(exportObj, null, 4);
|
||||
return fs.write('/etc/banip/banip.custom.feeds', exportJson)
|
||||
.then(() => location.reload());
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return L.resolveDefault(fs.stat('/etc/banip/banip.custom.feeds'), "")
|
||||
return L.resolveDefault(fs.stat('/etc/banip/banip.custom.feeds'), null)
|
||||
.then(function (stat) {
|
||||
if (!stat) {
|
||||
return fs.write('/etc/banip/banip.custom.feeds', "");
|
||||
return fs.write('/etc/banip/banip.custom.feeds', "").then(function () {
|
||||
return { size: 0, data: null };
|
||||
});
|
||||
}
|
||||
return L.resolveDefault(fs.read_direct('/etc/banip/banip.custom.feeds', 'json'), "");
|
||||
})
|
||||
return L.resolveDefault(fs.read_direct('/etc/banip/banip.custom.feeds', 'json'), "")
|
||||
.then(function (data) {
|
||||
return { size: stat.size, data: data };
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
render: function (data) {
|
||||
render: function (result) {
|
||||
let m, s, o, feed, url_4, url_6, rule, chain, descr, flag;
|
||||
const data = result.data;
|
||||
fileSize = result.size;
|
||||
|
||||
m = new form.JSONMap(data, null, _('With this editor you can upload your local custom feed file or fill up an initial one (a 1:1 copy of the version shipped with the package). \
|
||||
The file is located at \'/etc/banip/banip.custom.feeds\'. \
|
||||
@@ -213,7 +213,7 @@ return view.extend({
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
if (!value.match(/^(http:\/\/|https:\/\/)[A-Za-z0-9\/\.\-\?\&\+_@%=:~#]+$/)) {
|
||||
if (!value.match(/^https?:\/\/[A-Za-z0-9\[\]\/.\-?&+_@%=:~#]+$/)) {
|
||||
return _('Protocol/URL format not supported');
|
||||
}
|
||||
return true;
|
||||
@@ -224,7 +224,7 @@ return view.extend({
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
if (!value.match(/^(http:\/\/|https:\/\/)[A-Za-z0-9\/\.\-\?\&\+_@%=:~#]+$/)) {
|
||||
if (!value.match(/^https?:\/\/[A-Za-z0-9\[\]\/.\-?&+_@%=:~#]+$/)) {
|
||||
return _('Protocol/URL format not supported');
|
||||
}
|
||||
return true;
|
||||
@@ -320,4 +320,4 @@ return view.extend({
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
});
|
||||
@@ -12,37 +12,39 @@ function Logview(logtag, name) {
|
||||
return L.view.extend({
|
||||
load: () => Promise.resolve(),
|
||||
|
||||
render: () => {
|
||||
L.Poll.add(() => {
|
||||
render: function () {
|
||||
const pollFn = () => {
|
||||
return callLogRead(1000, false, true).then(res => {
|
||||
const logEl = document.getElementById('logfile');
|
||||
if (!logEl) return;
|
||||
|
||||
const filtered = (res?.log ?? [])
|
||||
.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'
|
||||
.filter(entry => !logtag || entry.msg.includes(logtag))
|
||||
.map(entry => {
|
||||
const d = new Date(entry.time * 1000);
|
||||
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}`;
|
||||
});
|
||||
const time = d.toLocaleTimeString([], {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false
|
||||
});
|
||||
return `[${date}-${time}] ${entry.msg}`;
|
||||
});
|
||||
if (filtered.length > 0) {
|
||||
logEl.value = filtered.join('\n');
|
||||
} else {
|
||||
logEl.value = _('No %s related logs yet!').format(name);
|
||||
}
|
||||
|
||||
logEl.value = filtered.length > 0
|
||||
? filtered.join('\n')
|
||||
: _('No %s related logs yet!').format(name);
|
||||
logEl.scrollTop = logEl.scrollHeight;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this._pollFn = pollFn;
|
||||
L.Poll.add(pollFn);
|
||||
|
||||
return E('div', { class: 'cbi-map' }, [
|
||||
E('div', { class: 'cbi-section' }, [
|
||||
@@ -58,10 +60,17 @@ function Logview(logtag, name) {
|
||||
]);
|
||||
},
|
||||
|
||||
unload: function () {
|
||||
if (this._pollFn) {
|
||||
L.Poll.remove(this._pollFn);
|
||||
this._pollFn = null;
|
||||
}
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
}
|
||||
|
||||
return L.Class.extend({ Logview });
|
||||
return L.Class.extend({ Logview });
|
||||
@@ -60,56 +60,69 @@ return view.extend({
|
||||
/*
|
||||
poll runtime information
|
||||
*/
|
||||
let parseErrCount = 0;
|
||||
poll.add(function () {
|
||||
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 = '-';
|
||||
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');
|
||||
return L.resolveDefault(fs.stat('/var/run/banip_runtime.json'), null).then(function (stat) {
|
||||
if (!stat) {
|
||||
return;
|
||||
}
|
||||
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 {
|
||||
if (status.classList.contains("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');
|
||||
let info = null;
|
||||
try {
|
||||
info = JSON.parse(res);
|
||||
parseErrCount = 0;
|
||||
} catch (e) {
|
||||
info = null;
|
||||
parseErrCount++;
|
||||
if (status) {
|
||||
status.textContent = '-';
|
||||
buttons.forEach(function (btn) {
|
||||
btn.disabled = false;
|
||||
})
|
||||
status.classList.remove("spinning");
|
||||
});
|
||||
status.classList.remove('spinning');
|
||||
if (parseErrCount >= 3) {
|
||||
ui.addNotification(null, E('p', _('Unable to parse the banIP runtime information!')), 'error');
|
||||
poll.stop();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (status && info) {
|
||||
status.textContent = `${info.status || '-'} (frontend: ${info.frontend_ver || '-'} / backend: ${info.backend_ver || '-'})`;
|
||||
if (info.status === "processing") {
|
||||
buttons.forEach(function (btn) {
|
||||
btn.disabled = true;
|
||||
btn.blur();
|
||||
});
|
||||
if (!status.classList.contains("spinning")) {
|
||||
status.classList.add("spinning");
|
||||
}
|
||||
} else {
|
||||
if (status.classList.contains("spinning")) {
|
||||
status.classList.remove("spinning");
|
||||
buttons.forEach(function (btn) {
|
||||
btn.disabled = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info) {
|
||||
setText('elements', info.element_count);
|
||||
setText('feeds', info.active_feeds?.join(', '));
|
||||
setText('devices', `wan-dev: ${info.wan_devices?.join(', ') || '-'} /
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -232,7 +245,7 @@ return view.extend({
|
||||
o.optional = true;
|
||||
o.retain = true;
|
||||
|
||||
o = s.taboption('general', form.Value, 'ban_fetchparm', _('Download Parameters'), _('Override the pre-configured download options for the selected download utility.'))
|
||||
o = s.taboption('general', form.Value, 'ban_fetchparm', _('Download Parameters'), _('Override the pre-configured download options for the selected download utility.'));
|
||||
o.depends('ban_autodetect', '0');
|
||||
o.optional = true;
|
||||
o.retain = true;
|
||||
@@ -331,7 +344,7 @@ return view.extend({
|
||||
o.rmempty = true;
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'ban_deduplicate', _('Deduplicate IPs'), _('Deduplicate IP addresses across all active Sets and tidy up the local blocklist.'));
|
||||
o.default = 1
|
||||
o.default = 1;
|
||||
o.rmempty = false;
|
||||
|
||||
/*
|
||||
@@ -507,7 +520,7 @@ return view.extend({
|
||||
o.optional = true;
|
||||
o.rmempty = true;
|
||||
|
||||
o = s.taboption('adv_set', form.MultiValue, 'ban_feedfreset', _('Feed Flag Reset'), _('Override the default feed configuration and remove existing port/protocol limitations.'));
|
||||
o = s.taboption('adv_set', form.MultiValue, 'ban_feedreset', _('Feed Flag Reset'), _('Override the default feed configuration and remove existing port/protocol limitations.'));
|
||||
o.value('allowlist', _('local allowlist'));
|
||||
o.value('blocklist', _('local blocklist'));
|
||||
feedKeys.forEach(f => o.value(f.trim()));
|
||||
@@ -592,7 +605,7 @@ return view.extend({
|
||||
o.rmempty = true;
|
||||
|
||||
o = s.taboption('adv_log', form.Flag, 'ban_remotelog', _('Enable Remote Logging'), _('Enable the cgi interface to receive remote logging events.'));
|
||||
o.default = 0
|
||||
o.default = 0;
|
||||
o.optional = true;
|
||||
o.rmempty = true;
|
||||
|
||||
@@ -607,7 +620,7 @@ return view.extend({
|
||||
return _('Invalid characters');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
o.optional = true;
|
||||
o.rmempty = true;
|
||||
|
||||
@@ -616,7 +629,7 @@ return view.extend({
|
||||
*/
|
||||
o = s.taboption('adv_email', form.DummyValue, '_sub');
|
||||
o.rawhtml = true;
|
||||
o.default = '<em style="color:#37c;font-weight:bold;">' + _('To enable email notifications, set up the \'msmtp\' package and specify a vaild E-Mail receiver address.') + '</em>'
|
||||
o.default = '<em style="color:#37c;font-weight:bold;">' + _('To enable email notifications, set up the \'msmtp\' package and specify a valid E-Mail receiver address.') + '</em>'
|
||||
+ '<hr style="width: 200px; height: 1px;" />';
|
||||
|
||||
o = s.taboption('adv_email', form.Flag, 'ban_mailnotification', _('E-Mail Notification'), _('Receive E-Mail notifications with every banIP run.'));
|
||||
@@ -661,7 +674,7 @@ return view.extend({
|
||||
o.rmempty = true;
|
||||
}
|
||||
|
||||
o = s.taboption('feeds', form.DummyValue, '_feeds');
|
||||
o = s.taboption('feeds', form.DummyValue, '_feeds1');
|
||||
o.rawhtml = true;
|
||||
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('Country Selection') + '</em>';
|
||||
|
||||
@@ -703,7 +716,7 @@ return view.extend({
|
||||
o = s.taboption('feeds', form.Flag, 'ban_countrysplit', _('Split Country Set'), _('The selected Countries are stored in separate Sets.'));
|
||||
o.rmempty = true;
|
||||
|
||||
o = s.taboption('feeds', form.DummyValue, '_feeds');
|
||||
o = s.taboption('feeds', form.DummyValue, '_feeds2');
|
||||
o.rawhtml = true;
|
||||
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('ASN Selection') + '</em>';
|
||||
|
||||
@@ -715,7 +728,7 @@ return view.extend({
|
||||
o = s.taboption('feeds', form.Flag, 'ban_asnsplit', _('Split ASN Set'), _('The selected ASNs are stored in separate Sets.'));
|
||||
o.rmempty = true;
|
||||
|
||||
o = s.taboption('feeds', form.DummyValue, '_feeds');
|
||||
o = s.taboption('feeds', form.DummyValue, '_feeds3');
|
||||
o.rawhtml = true;
|
||||
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('External Allowlist Feeds') + '</em>';
|
||||
|
||||
@@ -743,15 +756,15 @@ return view.extend({
|
||||
return _('Invalid URL format');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
o = s.taboption('feeds', form.DummyValue, '_feeds');
|
||||
o = s.taboption('feeds', form.DummyValue, '_feeds4');
|
||||
o.rawhtml = true;
|
||||
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('Local Feed Settings') + '</em>';
|
||||
|
||||
o = s.taboption('feeds', form.Flag, 'ban_autoallowlist', _('Auto Allowlist'), _('Automatically add resolved domains and uplink IPs to the local banIP allowlist.'));
|
||||
o.default = 1
|
||||
o.default = 1;
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('feeds', form.ListValue, 'ban_autoallowuplink', _('Auto Allow Uplink'), _('Limit the uplink autoallow function.'));
|
||||
@@ -766,11 +779,11 @@ return view.extend({
|
||||
o.rmempty = true;
|
||||
|
||||
o = s.taboption('feeds', form.Flag, 'ban_autoblocklist', _('Auto Blocklist'), _('Automatically add resolved domains and suspicious IPs to the local banIP blocklist.'));
|
||||
o.default = 1
|
||||
o.default = 1;
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('feeds', form.Flag, 'ban_autoblocksubnet', _('Auto Block Subnet'), _('Automatically add entire subnets to the blocklist Set based on an additional RDAP request with the suspicious IP.'));
|
||||
o.default = 0
|
||||
o.default = 0;
|
||||
o.optional = true;
|
||||
o.rmempty = true;
|
||||
|
||||
@@ -791,8 +804,8 @@ return view.extend({
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
if (!value.match(/^[1-9][0-9]*(ms|s|m|h|d|w)$/)) {
|
||||
return _('Invalid expiry format');
|
||||
if (!value.match(/^([1-9][0-9]*(ms|s|m|h|d|w))+$/)) {
|
||||
return _('Invalid expiry format, e.g. 5m, 2h, 1d or 1h30m');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
@@ -824,7 +837,7 @@ return view.extend({
|
||||
return handleAction('restart');
|
||||
})
|
||||
}, [_('Save & Restart')])
|
||||
])
|
||||
]);
|
||||
});
|
||||
return m.render();
|
||||
},
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
/*
|
||||
button handling
|
||||
*/
|
||||
let errMsg = false;
|
||||
|
||||
function handleAction(report, ev) {
|
||||
if (ev === 'search') {
|
||||
ui.showModal(_('IP Search'), [
|
||||
@@ -60,7 +62,7 @@ function handleAction(report, ev) {
|
||||
document.getElementById('search').focus();
|
||||
}
|
||||
if (ev === 'content') {
|
||||
let content, selectOption, errMsg;
|
||||
let content, selectOption;
|
||||
|
||||
if (report[1]) {
|
||||
try {
|
||||
@@ -77,11 +79,11 @@ function handleAction(report, ev) {
|
||||
}
|
||||
selectOption = [E('option', { value: '' }, [_('-- Set Selection --')])];
|
||||
Object.keys(content.nftables)
|
||||
.filter(key => content.nftables[key].set?.name && content.nftables[key].set.table === 'banIP')
|
||||
.sort((a, b) => content.nftables[a].set.name.localeCompare(content.nftables[b].set.name))
|
||||
.forEach(key => {
|
||||
selectOption.push(E('option', { 'value': content.nftables[key].set.name }, content.nftables[key].set.name));
|
||||
})
|
||||
.filter(key => content.nftables[key].set?.name && content.nftables[key].set.table === 'banIP')
|
||||
.sort((a, b) => content.nftables[a].set.name.localeCompare(content.nftables[b].set.name))
|
||||
.forEach(key => {
|
||||
selectOption.push(E('option', { 'value': content.nftables[key].set.name }, content.nftables[key].set.name));
|
||||
})
|
||||
ui.showModal(_('Set Content'), [
|
||||
E('p', _('List the elements of a specific banIP-related Set.')),
|
||||
E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [
|
||||
@@ -126,7 +128,7 @@ function handleAction(report, ev) {
|
||||
'class': 'btn cbi-button-action',
|
||||
'click': ui.createHandlerFn(this, function (ev) {
|
||||
const checkbox = document.getElementById('chkFilter');
|
||||
const isChecked = checkbox.checked;
|
||||
const isChecked = checkbox.checked ? 'true' : 'false';
|
||||
let set = document.getElementById('set').value;
|
||||
if (set) {
|
||||
document.getElementById('result').textContent = 'Collecting Set content, please wait...';
|
||||
@@ -151,8 +153,10 @@ function handleAction(report, ev) {
|
||||
}
|
||||
if (ev === 'map') {
|
||||
const modal = ui.showModal(null, [
|
||||
E('div', { id: 'mapModal',
|
||||
style: 'position: relative;' }, [
|
||||
E('div', {
|
||||
id: 'mapModal',
|
||||
style: 'position: relative;'
|
||||
}, [
|
||||
E('iframe', {
|
||||
id: 'mapFrame',
|
||||
src: L.resource('view/banip/map.html'),
|
||||
@@ -193,7 +197,7 @@ return view.extend({
|
||||
},
|
||||
|
||||
render: function (report) {
|
||||
let content=[], rowSets, tblSets, notMsg, errMsg;
|
||||
let content = [], rowSets, tblSets, notMsg;
|
||||
|
||||
if (report) {
|
||||
try {
|
||||
@@ -228,7 +232,7 @@ return view.extend({
|
||||
E('em', content[0].sets[key].inbound + cnt1),
|
||||
E('em', content[0].sets[key].outbound + cnt2),
|
||||
E('em', content[0].sets[key].port),
|
||||
E('em', content[0].sets[key].set_elements.join(", "))
|
||||
E('em', content[0].sets[key].set_elements.join(", "))
|
||||
]);
|
||||
});
|
||||
rowSets.push([
|
||||
@@ -249,41 +253,41 @@ return view.extend({
|
||||
You can also display the specific content of Sets, search for suspicious IPs and finally, these IPs can also be displayed on a map.')),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;width:230px;font-weight:bold;' }, _('Timestamp')),
|
||||
E('div', { 'class': 'cbi-value-title', 'id': 'start', 'style': 'margin-bottom:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.timestamp || '-')
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.timestamp || '-')
|
||||
]),
|
||||
E('hr'),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;width:230px;font-weight:bold;' }, _('blocked syn-flood packets')),
|
||||
E('div', { 'class': 'cbi-value-title', 'id': 'start', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_synflood || '-')
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_synflood || '-')
|
||||
]),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;width:230px;font-weight:bold;' }, _('blocked udp-flood packets')),
|
||||
E('div', { 'class': 'cbi-value-title', 'id': 'start', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_udpflood || '-')
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_udpflood || '-')
|
||||
]),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;width:230px;font-weight:bold;' }, _('blocked icmp-flood packets')),
|
||||
E('div', { 'class': 'cbi-value-title', 'id': 'start', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_icmpflood || '-')
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_icmpflood || '-')
|
||||
]),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;width:230px;font-weight:bold;' }, _('blocked invalid ct packets')),
|
||||
E('div', { 'class': 'cbi-value-title', 'id': 'start', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_ctinvalid || '-')
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_ctinvalid || '-')
|
||||
]),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;width:230px;font-weight:bold;' }, _('blocked invalid tcp packets')),
|
||||
E('div', { 'class': 'cbi-value-title', 'id': 'start', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_tcpinvalid || '-')
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_tcpinvalid || '-')
|
||||
]),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;width:230px;font-weight:bold;' }, _('blocked bcp38 packets')),
|
||||
E('div', { 'class': 'cbi-value-title', 'id': 'start', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_bcp38 || '-')
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.sum_bcp38 || '-')
|
||||
]),
|
||||
E('hr'),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;width:230px;font-weight:bold;' }, _('auto-added IPs to allowlist')),
|
||||
E('div', { 'class': 'cbi-value-title', 'id': 'start', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.autoadd_allow || '-')
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.autoadd_allow || '-')
|
||||
]),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;width:230px;font-weight:bold;' }, _('auto-added IPs to blocklist')),
|
||||
E('div', { 'class': 'cbi-value-title', 'id': 'start', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.autoadd_block || '-')
|
||||
E('div', { 'class': 'cbi-value-title', 'style': 'margin-top:-5px;color:#37c;font-weight:bold;' }, content?.[0]?.autoadd_block || '-')
|
||||
])
|
||||
]),
|
||||
E('br'),
|
||||
@@ -300,11 +304,10 @@ return view.extend({
|
||||
'id': 'btnMap',
|
||||
'disabled': 'disabled',
|
||||
'click': ui.createHandlerFn(this, function () {
|
||||
if (content[1] && content[1].length > 1) {
|
||||
if (Array.isArray(content[1]) && content[1].length > 1) {
|
||||
sessionStorage.setItem('mapData', JSON.stringify(content[1]));
|
||||
return handleAction(report, 'map');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (!notMsg) {
|
||||
notMsg = true;
|
||||
return ui.addNotification(null, E('p', _('No GeoIP Map data!')), 'info');
|
||||
@@ -330,15 +333,24 @@ return view.extend({
|
||||
'class': 'btn cbi-button cbi-button-positive important',
|
||||
'style': 'float:none',
|
||||
'click': function () {
|
||||
document.querySelectorAll('.cbi-page-actions button').forEach(function(btn) {
|
||||
btn.disabled = true;
|
||||
})
|
||||
this.blur();
|
||||
this.classList.add('spinning');
|
||||
const btn = this;
|
||||
document.querySelectorAll('.cbi-page-actions button').forEach(function (b) {
|
||||
b.disabled = true;
|
||||
});
|
||||
btn.blur();
|
||||
btn.classList.add('spinning');
|
||||
L.resolveDefault(fs.exec_direct('/etc/init.d/banip', ['report', 'gen']))
|
||||
.then(function () {
|
||||
location.reload();
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res !== null) {
|
||||
location.reload();
|
||||
} else {
|
||||
btn.classList.remove('spinning');
|
||||
document.querySelectorAll('.cbi-page-actions button').forEach(function (b) {
|
||||
b.disabled = false;
|
||||
});
|
||||
ui.addNotification(null, E('p', _('Failed to generate banIP report!')), 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [_('Refresh')])
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user