luci-app: Minor fixes and improvements

This commit is contained in:
gSpot
2021-11-04 18:57:08 +03:00
parent 87abb0e334
commit 50a934702e
27 changed files with 1659 additions and 1593 deletions
+4 -4
View File
@@ -11,8 +11,8 @@ LUCI_APP=1
OWRT_VERSION="19.07"
RUAB_VERSION="0.9.0-2"
RUAB_MOD_LUA_VERSION="0.9.0-2"
RUAB_LUCI_APP_VERSION="0.9.0-6"
BASE_URL="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master"
RUAB_LUCI_APP_VERSION="0.9.0-7"
BASE_URL="https://github.com/gSpotx2f/ruantiblock_openwrt/raw/master"
PKG_DIR="/tmp"
if [ -n "$1" ]; then
@@ -29,8 +29,8 @@ URL_LUCI_APP_RU_PKG="${BASE_URL}/packages/${OWRT_VERSION}/luci-i18n-ruantiblock-
### tor
URL_TORRC="${BASE_URL}/tor/etc/tor/torrc"
### ruantiblock-mod-lua
URL_LUA_IPTOOL="https://raw.githubusercontent.com/gSpotx2f/iptool-lua/master/5.1/iptool.lua"
URL_LUA_IDN="https://raw.githubusercontent.com/haste/lua-idn/master/idn.lua"
URL_LUA_IPTOOL="https://github.com/gSpotx2f/iptool-lua/raw/master/5.1/iptool.lua"
URL_LUA_IDN="https://github.com/haste/lua-idn/raw/master/idn.lua"
### Local files
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

+4 -3
View File
@@ -5,11 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=0.9.0
PKG_RELEASE:=6
PKG_RELEASE:=7
LUCI_TITLE:=LuCI support for ruantiblock
LUCI_DEPENDS:=+ruantiblock +luci-mod-admin-full
LUCI_DEPENDS:=+ruantiblock
LUCI_PKGARCH:=all
include ../../luci.mk
#include ../../luci.mk
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
@@ -139,15 +139,15 @@ return L.Class.extend({
* View name (for local storage and downloads).
* Must be overridden by a subclass!
*/
viewName: null,
viewName : null,
/**
* Page title.
* Must be overridden by a subclass!
*/
title: null,
title : null,
logLevels: {
logLevels : {
'emerg': E('span', { 'class': 'zonebadge log-emerg' }, E('strong', _('Emergency'))),
'alert': E('span', { 'class': 'zonebadge log-alert' }, E('strong', _('Alert'))),
'crit': E('span', { 'class': 'zonebadge log-crit' }, E('strong', _('Critical'))),
@@ -158,25 +158,25 @@ return L.Class.extend({
'debug': E('span', { 'class': 'zonebadge log-debug' }, E('strong', _('Debug'))),
},
tailValue: 25,
tailValue : 25,
logSortingValue: 'asc',
logSortingValue : 'asc',
isHosts: false,
isHosts : false,
isLevels: false,
isLevels : false,
logHosts: {},
logHosts : {},
logLevelsStat: {},
logLevelsStat : {},
logHostsDropdown: null,
logHostsDropdown : null,
logLevelsDropdown: null,
totalLogLines: 0,
totalLogLines : 0,
htmlEntities: function(str) {
htmlEntities : function(str) {
return String(str).replace(
/&/g, '&').replace(
/</g, '&#60;').replace(
@@ -426,15 +426,15 @@ return L.Class.extend({
render: function(logdata) {
let logWrapper = E('div', {
'id': 'logWrapper',
'id' : 'logWrapper',
'style': 'width:100%; min-height:20em; padding: 0 0 0 45px; font-size:0.9em !important'
}, this.makeLogArea(this.parseLogData(logdata, this.tailValue)));
let tailInput = E('input', {
'id': 'tailInput',
'name': 'tailInput',
'type': 'text',
'form': 'logForm',
'id' : 'tailInput',
'name' : 'tailInput',
'type' : 'text',
'form' : 'logForm',
'class': 'cbi-input-text',
'style': 'width:4em !important; min-width:4em !important',
'maxlength': 5,
@@ -443,8 +443,8 @@ return L.Class.extend({
ui.addValidator(tailInput, 'uinteger', true);
let tailReset = E('input', {
'type': 'button',
'form': 'logForm',
'type' : 'button',
'form' : 'logForm',
'class': 'cbi-button btn cbi-button-reset',
'value': 'Χ',
'click': ev => {
@@ -465,17 +465,17 @@ return L.Class.extend({
};
let logFilter = E('input', {
'id': 'logFilter',
'name': 'logFilter',
'type': 'text',
'form': 'logForm',
'class': 'cbi-input-text',
'id' : 'logFilter',
'name' : 'logFilter',
'type' : 'text',
'form' : 'logForm',
'class' : 'cbi-input-text',
'placeholder': _('Type an expression...'),
});
let logFormSubmitBtn = E('input', {
'type': 'submit',
'form': 'logForm',
'type' : 'submit',
'form' : 'logForm',
'class': 'cbi-button btn cbi-button-action',
'value': _('Apply'),
'click': ev => ev.target.blur(),
@@ -483,9 +483,9 @@ return L.Class.extend({
});
let logSorting = E('select', {
'id': 'logSorting',
'name': 'logSorting',
'form': 'logForm',
'id' : 'logSorting',
'name' : 'logSorting',
'form' : 'logForm',
'class': "cbi-input-select",
}, [
E('option', { 'value': 'asc' }, _('ascending')),
@@ -494,8 +494,8 @@ return L.Class.extend({
logSorting.value = this.logSortingValue;
let logDownloadBtn = E('button', {
'id': 'logDownloadBtn',
'name': 'logDownloadBtn',
'id' : 'logDownloadBtn',
'name' : 'logDownloadBtn',
'class': 'cbi-button btn',
'click': ui.createHandlerFn(this, this.downloadLog),
}, _('Download log'));
@@ -509,7 +509,7 @@ return L.Class.extend({
E('div', { 'id': 'tailInputSection', 'class': 'cbi-value' }, [
E('label', {
'class': 'cbi-value-title',
'for': 'tailInput',
'for' : 'tailInput',
}, _('Last entries')),
E('div', { 'class': 'cbi-value-field' }, [
tailInput,
@@ -523,7 +523,7 @@ return L.Class.extend({
E('div', { 'class': 'cbi-value' }, [
E('label', {
'class': 'cbi-value-title',
'for': 'logFilter',
'for' : 'logFilter',
}, _('Message filter')),
E('div', { 'class': 'cbi-value-field' }, logFilter),
]),
@@ -531,7 +531,7 @@ return L.Class.extend({
E('div', { 'class': 'cbi-value' }, [
E('label', {
'class': 'cbi-value-title',
'for': 'logSorting',
'for' : 'logSorting',
}, _('Sorting entries')),
E('div', { 'class': 'cbi-value-field' }, logSorting,),
]),
@@ -539,14 +539,14 @@ return L.Class.extend({
E('div', { 'class': 'cbi-value' }, [
E('label', {
'class': 'cbi-value-title',
'for': 'logFilter',
'for' : 'logFilter',
}, _('Refresh log')),
E('div', { 'class': 'cbi-value-field' }, [
logFormSubmitBtn,
E('form', {
'id': 'logForm',
'name': 'logForm',
'style': 'display:inline-block; margin-top:0.5em',
'id' : 'logForm',
'name' : 'logForm',
'style' : 'display:inline-block; margin-top:0.5em',
'submit': ui.createHandlerFn(this, function(ev) {
ev.preventDefault();
let formElems = Array.from(document.forms.logForm.elements);
@@ -633,8 +633,8 @@ return L.Class.extend({
]);
},
handleSave : null,
handleSaveApply: null,
handleSave: null,
handleReset: null,
handleReset : null,
}),
})
@@ -3,73 +3,72 @@
'require ui';
'require view.ruantiblock.tools as tools';
let crontab_regexp = new RegExp(`^(\\*?\\/?(\\d){0,2}\\s){5}${tools.exec_path} update(\n)?`, 'gm');
let current_crontab_content;
return L.view.extend({
crontabRegexp: new RegExp(
`^(\\*?\\/?(\\d){0,2}\\s){5}${tools.execPath} update(\n)?`, 'gm'),
function to_dd(n){
currentCrontabContent: null,
toDD: function(n){
return String(n).replace(/^(\d)$/, "0$1");
};
},
function cron_status_string(s) {
cronStatusString: function(s) {
return s || _('No Shedule');
}
},
function pick_cron_task(content) {
pickCronTask: function(content) {
if(!content){
return;
};
let current_tasks = content.match(crontab_regexp) || [];
let current_tasks = content.match(this.crontabRegexp) || [];
return current_tasks.join('');
};
},
function set_cron_status(value) {
document.getElementById('cron_status').value = cron_status_string(value);
document.getElementById("btn_cron_del").style.visibility = (value) ? 'visible' : 'hidden';
}
setCronStatus: function(value) {
document.getElementById('cron_status').value = this.cronStatusString(value);
document.getElementById("btn_cron_del").style.visibility = (value) ?
'visible' : 'hidden';
},
function write_cron_file() {
writeCronFile: function() {
let btn_cron_add = document.getElementById('btn_cron_add');
let btn_cron_del = document.getElementById('btn_cron_del');
if(!current_crontab_content) {
if(!this.currentCrontabContent) {
ui.addNotification(null, E('p', _('No changes to save.')));
btn_cron_add.disabled = false;
return;
};
return fs.write(tools.crontab_file, current_crontab_content).then(rc => {
return fs.write(tools.crontabFile, this.currentCrontabContent).then(rc => {
ui.addNotification(null, E('p',_('Changes have been saved.')), 'info');
set_cron_status(pick_cron_task(current_crontab_content));
this.setCronStatus(this.pickCronTask(this.currentCrontabContent));
}).then(() => {
return fs.exec('/etc/init.d/cron', [ 'enabled' ]).then(res => {
if(res.code !== 0) {
return fs.exec('/etc/init.d/cron', [ 'enable' ]);
return tools.getInitStatus('cron').then(res => {
if(!res) {
return tools.handleServiceAction('cron', 'enable');
};
}).catch(e => {
ui.addNotification(null, E('p', _('Unable to execute or read contents')
+ ': %s<br />[ %s ]'.format(e.message, '/etc/init.d/cron')));
});
}).finally(() => {
return fs.exec('/etc/init.d/cron', [ 'restart' ]).catch(e => {
ui.addNotification(null, E('p', _('Unable to execute or read contents')
+ ': %s<br />[ %s ]'.format(e.message, '/etc/init.d/cron')));
});
return tools.handleServiceAction('cron', 'restart');
}).catch(e => {
ui.addNotification(null, E('p', _('Unable to save the changes')
+ ': %s<br />[ %s ]'.format(
e.message, tools.crontab_file
+ ': %s [ %s ]'.format(
e.message, tools.crontabFile
)));
});
}
},
function del_cron_schedule(ev) {
if(current_crontab_content) {
current_crontab_content = current_crontab_content.replace(crontab_regexp, "");
delCronSchedule: function(ev) {
if(this.currentCrontabContent) {
this.currentCrontabContent = this.currentCrontabContent.replace(
this.crontabRegexp, "");
};
return write_cron_file();
};
return this.writeCronFile();
},
function set_cron_schedule(ev) {
setCronSchedule: function(ev) {
let hour_interval = document.getElementById('cron_hour_interval').value;
let day_interval = document.getElementById('cron_day_interval').value;
let hour = document.getElementById('cron_hour').value;
@@ -78,15 +77,16 @@ function set_cron_schedule(ev) {
min,
(!hour_interval) ? hour : (hour_interval == "1") ? '*' : '*/' + hour_interval,
(hour_interval || day_interval == "1") ? '*' : '*/' + day_interval,
tools.exec_path
tools.execPath
);
if(current_crontab_content) {
current_crontab_content = current_crontab_content.replace(crontab_regexp, "") + task_string;
if(this.currentCrontabContent) {
this.currentCrontabContent = this.currentCrontabContent.replace(
this.crontabRegexp, "") + task_string;
};
return write_cron_file();
};
return this.writeCronFile();
},
function onchange_hour_interval(e) {
onchangeHourInterval: function(e) {
let value = e.target.value;
let bool = (value != '');
let cron_hour = document.getElementById('cron_hour');
@@ -94,6 +94,7 @@ function onchange_hour_interval(e) {
cron_hour.disabled = bool;
cron_day_interval.disabled = bool;
// For luci-theme-material
if(bool) {
cron_hour.style.opacity = '50%';
cron_day_interval.style.opacity = '50%';
@@ -101,21 +102,20 @@ function onchange_hour_interval(e) {
cron_hour.style.opacity = '100%';
cron_day_interval.style.opacity = '100%';
};
}
},
return L.view.extend({
load: function() {
return fs.read(tools.crontab_file).catch(e => {
return fs.read(tools.crontabFile).catch(e => {
ui.addNotification(null, E('p', _('Unable to read the contents')
+ ': %s<br />[ %s ]'.format(
e.message, tools.crontab_file
+ ': %s [ %s ]'.format(
e.message, tools.crontabFile
)));
});
},
render: function(content) {
current_crontab_content = content;
let current_task = pick_cron_task(content);
this.currentCrontabContent = content;
let current_task = this.pickCronTask(content);
let cron_status = E('textarea', {
'id': 'cron_status',
@@ -124,25 +124,27 @@ return L.view.extend({
'readonly': 'readonly',
'wrap': 'off',
'rows': 2,
}, cron_status_string(current_task));
}, this.cronStatusString(current_task));
let btn_cron_del = E('button', {
'class': 'cbi-button btn cbi-button-reset',
'id': 'btn_cron_del',
'name': 'btn_cron_del',
}, _('Reset'));
btn_cron_del.onclick = ui.createHandlerFn(this, del_cron_schedule);
btn_cron_del.onclick = ui.createHandlerFn(this, this.delCronSchedule);
btn_cron_del.style.visibility = (current_task) ? 'visible' : 'hidden';
let status_header = E('div', { 'class': 'cbi-section-node' }, [
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': 'cron_status' },
_('Current schedule')),
E('div', { 'class': 'cbi-value-field' }, cron_status),
E('div', { 'class': 'cbi-value-field' },
cron_status),
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': 'btn_cron_del' }),
E('div', { 'class': 'cbi-value-field' }, btn_cron_del),
E('div', { 'class': 'cbi-value-field' },
btn_cron_del),
])
]);
@@ -154,7 +156,8 @@ return L.view.extend({
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': elem.id || null },
title),
E('div', { 'class': 'cbi-value-field' }, [ elem, descr ]),
E('div', { 'class': 'cbi-value-field' },
[ elem, descr ]),
])
)
};
@@ -167,17 +170,21 @@ return L.view.extend({
E('option', { 'value': '1' }, '&#8727;')
]);
for(let i = 2; i <= 12 ; i += 2) {
cron_hour_interval.append(E('option', { 'value': String(i) }, '&#8727;/' + i));
cron_hour_interval.append(
E('option', { 'value': String(i) }, '&#8727;/' + i)
);
};
layout_append(cron_hour_interval, _('Hour'));
cron_hour_interval.onchange = onchange_hour_interval;
cron_hour_interval.onchange = this.onchangeHourInterval;
let cron_day_interval = E('select',
{ 'id': 'cron_day_interval', 'style': 'width:60px !important; min-width:60px !important' },
E('option', { 'value': '1' }, '&#8727;')
);
for(let i = 2; i < 8 ; i++) {
cron_day_interval.append(E('option', { 'value': String(i) }, '&#8727;/' + i));
cron_day_interval.append(
E('option', { 'value': String(i) }, '&#8727;/' + i)
);
};
cron_day_interval.append(E('option', { 'value': '14' }, '&#8727;/14'));
cron_day_interval.append(E('option', { 'value': '28' }, '&#8727;/28'));
@@ -188,14 +195,18 @@ return L.view.extend({
let cron_hour = E('select',
{ 'id': 'cron_hour', 'style': 'width:60px !important; min-width:60px !important' });
for(let i = 0; i < 24 ; i++) {
cron_hour.append(E('option', { 'value': String(i) }, to_dd(i)));
cron_hour.append(
E('option', { 'value': String(i) }, this.toDD(i))
);
};
layout_append(cron_hour, _('Hour'));
let cron_min = E('select',
{ 'id': 'cron_min', 'style': 'width:60px !important; min-width:60px !important' });
for(let i = 0; i < 60 ; i++) {
cron_min.append(E('option', { 'value': String(i) }, to_dd(i)));
cron_min.append(
E('option', { 'value': String(i) }, this.toDD(i))
);
};
layout_append(cron_min, _('Minute'));
@@ -204,12 +215,14 @@ return L.view.extend({
'id': 'btn_cron_add',
'name': 'btn_cron_add'
}, _('Set'));
btn_cron_add.onclick = ui.createHandlerFn(this, set_cron_schedule);
btn_cron_add.onclick = ui.createHandlerFn(this, this.setCronSchedule);
layout_append(btn_cron_add);
return E([
E('h2',
{ 'class': 'fade-in' }, _('Ruantiblock') + ' - ' + _('Blacklist updates') + ' (cron)'),
{ 'class': 'fade-in' },
_('Ruantiblock') + ' - ' + _('Blacklist updates') + ' (cron)'
),
E('div', { 'class': 'cbi-section-descr fade-in' }),
E('div', { 'class': 'cbi-section fade-in' }, status_header),
E('div', { 'class': 'cbi-section fade-in' }, layout),
@@ -217,7 +230,7 @@ return L.view.extend({
},
handleSave: null,
handleSave : null,
handleSaveApply: null,
handleReset: null,
handleReset : null,
});
@@ -4,10 +4,10 @@
'require view.ruantiblock.tools as tools';
return L.view.extend({
poll_info: function() {
return fs.exec_direct(tools.exec_path, [ 'html-info' ], 'json').catch(e => {
infoPoll: function() {
return fs.exec_direct(tools.execPath, [ 'html-info' ], 'json').catch(e => {
ui.addNotification(null, E('p', _('Unable to execute or read contents')
+ ': %s<br />[ %s ]'.format(e.message, tools.exec_path)
+ ': %s [ %s ]'.format(e.message, tools.execPath)
));
L.Poll.stop();
}).then(data => {
@@ -79,9 +79,9 @@ return L.view.extend({
},
load: function() {
return fs.exec_direct(tools.exec_path, [ 'html-info' ], 'json').catch(e => {
return fs.exec_direct(tools.execPath, [ 'html-info' ], 'json').catch(e => {
ui.addNotification(null, E('p', _('Unable to execute or read contents')
+ ': %s<br />[ %s ]'.format(e.message, tools.exec_path)
+ ': %s [ %s ]'.format(e.message, tools.execPath)
));
})
},
@@ -107,22 +107,26 @@ return L.view.extend({
E('div', { 'class': 'tr' }, [
E('div', { 'class': 'td left', 'style': 'min-width:33%' },
_('Last blacklist update') + ':'),
E('div', { 'class': 'td left', 'id': 'last_blacklist_update.date' },
E('div', { 'class': 'td left',
'id': 'last_blacklist_update.date' },
data.last_blacklist_update.date),
]),
E('div', { 'class': 'tr' }, [
E('div', { 'class': 'td left' }, 'IP:'),
E('div', { 'class': 'td left', 'id': 'last_blacklist_update.ip' },
E('div', { 'class': 'td left',
'id': 'last_blacklist_update.ip' },
data.last_blacklist_update.ip),
]),
E('div', { 'class': 'tr' }, [
E('div', { 'class': 'td left' }, 'CIDR:'),
E('div', { 'class': 'td left', 'id': 'last_blacklist_update.cidr' },
E('div', { 'class': 'td left',
'id': 'last_blacklist_update.cidr' },
data.last_blacklist_update.cidr),
]),
E('div', { 'class': 'tr' }, [
E('div', { 'class': 'td left' }, 'FQDN:'),
E('div', { 'class': 'td left', 'id': 'last_blacklist_update.fqdn' },
E('div', { 'class': 'td left',
'id': 'last_blacklist_update.fqdn' },
data.last_blacklist_update.fqdn),
])
);
@@ -147,16 +151,15 @@ return L.view.extend({
for(let [k, v] of Object.entries(data.iptables)) {
if(k === '_dummy') continue;
table_iptables.append(
E('div', { 'class': 'tr' }, [
E('div', {
'class': 'td left',
'class' : 'td left',
'data-title': _('Match-set'),
}, k),
E('div', {
'class': 'td left',
'id': 'iptables.' + k,
'class' : 'td left',
'id' : 'iptables.' + k,
'data-title': _('Bytes'),
}, v),
])
@@ -172,9 +175,12 @@ return L.view.extend({
if(data.ipset) {
let table_ipset = E('div', { 'class': 'table' },
E('div', { 'class': 'tr table-titles' }, [
E('div', { 'class': 'th left', 'style': 'min-width:33%' }, _('Name')),
E('div', { 'class': 'th left' }, _('Size in memory')),
E('div', { 'class': 'th left' }, _('Number of entries')),
E('div', { 'class': 'th left', 'style': 'min-width:33%' },
_('Name')),
E('div', { 'class': 'th left' },
_('Size in memory')),
E('div', { 'class': 'th left' },
_('Number of entries')),
])
);
@@ -184,17 +190,17 @@ return L.view.extend({
table_ipset.append(
E('div', { 'class': 'tr' }, [
E('div', {
'class': 'td left',
'class' : 'td left',
'data-title': _('Name'),
}, k),
E('div', {
'class': 'td left',
'id': 'ipset.' + k + '.' + '0',
'class' : 'td left',
'id' : 'ipset.' + k + '.' + '0',
'data-title': _('Size in memory'),
}, v[0]),
E('div', {
'class': 'td left',
'id': 'ipset.' + k + '.' + '1',
'class' : 'td left',
'id' : 'ipset.' + k + '.' + '1',
'data-title': _('Number of entries'),
}, v[1]),
])
@@ -207,13 +213,15 @@ return L.view.extend({
]);
};
L.Poll.add(this.poll_info);
L.Poll.add(this.infoPoll);
} else {
update_status = E('em', {}, _('Status') + ' : ' + _('disabled'));
};
};
return E([
E('h2', { 'class': 'fade-in' }, _('Ruantiblock') + ' - ' + _('Statistics')),
E('h2', { 'class': 'fade-in' },
_('Ruantiblock') + ' - ' + _('Statistics')
),
E('div', { 'class': 'cbi-section-descr fade-in' }),
E('div', { 'class': 'cbi-section fade-in' },
E('div', { 'class': 'cbi-section-node' }, update_status)
@@ -227,7 +235,7 @@ return L.view.extend({
]);
},
handleSave: null,
handleSave : null,
handleSaveApply: null,
handleReset: null,
handleReset : null,
});
@@ -8,13 +8,13 @@ return abc.view.extend({
title: _('Ruantiblock') + ' - ' + _('Log'),
appRegexp: new RegExp(`^.*${tools.app_name}\[[0-9]+\].*$`, 'gm'),
appRegexp : new RegExp(`^.*${tools.appName}\[[0-9]+\].*$`, 'gm'),
testRegexp: new RegExp(/([0-9]{2}:){2}[0-9]{2}/),
testRegexp : new RegExp(/([0-9]{2}:){2}[0-9]{2}/),
isLoggerChecked: false,
entriesHandler: null,
entriesHandler : null,
// logd
logdHandler: function(strArray, lineNum) {
@@ -53,8 +53,9 @@ return abc.view.extend({
let logger = (stat[0]) ? stat[0].path : (stat[1]) ? stat[1].path : null;
if(logger) {
return fs.exec_direct(logger, [ '-e', tools.app_name ]).catch(err => {
ui.addNotification(null, E('p', {}, _('Unable to load log data:') + ' ' + err.message));
return fs.exec_direct(logger, [ '-e', tools.appName ]).catch(err => {
ui.addNotification(
null, E('p', {}, _('Unable to load log data:') + ' ' + err.message));
return '';
});
};
@@ -6,12 +6,14 @@
const btn_style_neutral = 'btn'
const btn_style_action = 'btn cbi-button-action';
const btn_style_save = 'btn cbi-button-save important';
const btn_style_reset = 'btn cbi-button-reset important';
const btn_style_positive = 'btn cbi-button-save important';
const btn_style_negative = 'btn cbi-button-reset important';
const btn_style_warning = 'btn cbi-button-negative important'
let status_token_value;
function disable_buttons(bool, btn, elems=[]) {
return L.view.extend({
statusTokenValue: null,
disableButtons: function(bool, btn, elems=[]) {
let btn_start = elems[1] || document.getElementById("btn_start");
let btn_destroy = elems[5] || document.getElementById("btn_destroy");
let btn_enable = elems[2] || document.getElementById("btn_enable");
@@ -27,41 +29,40 @@ function disable_buttons(bool, btn, elems=[]) {
btn_enable.disabled = bool;
};
if(btn_tp) {
btn_tp.disabled = bool
btn_tp.disabled = bool;
};
}
},
function get_app_status() {
getAppStatus: function() {
return Promise.all([
fs.exec(tools.exec_path, [ 'raw-status' ]),
fs.exec(tools.exec_path, [ 'total-proxy-status' ]),
fs.exec(tools.exec_path, [ 'vpn-route-status' ]),
fs.exec(tools.init_path, [ 'enabled' ]),
L.resolveDefault(fs.read(tools.token_file), 0),
uci.load(tools.app_name),
fs.exec(tools.execPath, [ 'raw-status' ]),
fs.exec(tools.execPath, [ 'total-proxy-status' ]),
fs.exec(tools.execPath, [ 'vpn-route-status' ]),
tools.getInitStatus(tools.appName),
L.resolveDefault(fs.read(tools.tokenFile), 0),
uci.load(tools.appName),
]).catch(e => {
ui.addNotification(null, E('p', _('Unable to execute or read contents')
+ ': %s<br />[ %s | %s | %s ]'.format(
e.message, tools.exec_path, tools.init_path, 'uci.ruantiblock'
+ ': %s [ %s | %s | %s ]'.format(
e.message, tools.execPath, 'tools.getInitStatus', 'uci.ruantiblock'
)));
});
}
},
function set_app_status(status_array, elems=[], force_app_code) {
let section = uci.get(tools.app_name, 'config');
setAppStatus: function(status_array, elems=[], force_app_code) {
let section = uci.get(tools.appName, 'config');
if(!status_array || typeof(section) !== 'object') {
(elems[0] || document.getElementById("status")).innerHTML = tools.make_status_string(1);
(elems[0] || document.getElementById("status")).innerHTML = tools.makeStatusString(1);
ui.addNotification(null, E('p', _('Unable to read the contents')
+ ': set_app_status()'));
disable_buttons(true, null, elems);
+ ': setAppStatus()'));
this.disableButtons(true, null, elems);
return;
};
let app_status_code = (force_app_code) ? force_app_code : status_array[0].code;
let tp_status_code = status_array[1].code;
let vpn_route_status_code = status_array[2].code;
let enabled_flag = status_array[3].code;
let enabled_flag = status_array[3];
let proxy_local_clients = section.proxy_local_clients;
let proxy_mode = section.proxy_mode;
let bllist_mode = section.bllist_mode;
@@ -69,48 +70,54 @@ function set_app_status(status_array, elems=[], force_app_code) {
let bllist_source = section.bllist_source;
let btn_enable = elems[2] || document.getElementById('btn_enable');
if(enabled_flag == 0) {
btn_enable.onclick = ui.createHandlerFn(this, button_action, 'disable');
btn_enable.textContent = _('Disable');
btn_enable.className = btn_style_reset;
if(enabled_flag == true) {
btn_enable.onclick = ui.createHandlerFn(
this, this.serviceAction, 'disable', 'btn_enable');
btn_enable.textContent = _('Enabled');
btn_enable.className = btn_style_positive;
} else {
btn_enable.onclick = ui.createHandlerFn(this, button_action, 'enable');
btn_enable.textContent = _('Enable');
btn_enable.className = btn_style_save;
btn_enable.onclick = ui.createHandlerFn(
this, this.serviceAction, 'enable', 'btn_enable');
btn_enable.textContent = _('Disabled');
btn_enable.className = btn_style_negative;
};
let btn_tp = elems[3] || document.getElementById('btn_tp');
if(btn_tp) {
if(tp_status_code == 0) {
btn_tp.onclick = ui.createHandlerFn(this, button_action, 'total-proxy-off');
btn_tp.textContent = _('Disable');
btn_tp.className = btn_style_reset;
btn_tp.onclick = ui.createHandlerFn(
this, this.appAction, 'total-proxy-off', 'btn_tp');
btn_tp.textContent = _('Enabled');
btn_tp.className = btn_style_positive;
} else {
btn_tp.onclick = ui.createHandlerFn(this, button_action, 'total-proxy-on');
btn_tp.textContent = _('Enable');
btn_tp.className = btn_style_save;
btn_tp.onclick = ui.createHandlerFn(
this, this.appAction, 'total-proxy-on', 'btn_tp');
btn_tp.textContent = _('Disabled');
btn_tp.className = btn_style_negative;
};
};
let btn_start = elems[1] || document.getElementById("btn_start");
let btn_update = elems[4] || document.getElementById("btn_update");
let btn_destroy = elems[5] || document.getElementById("btn_destroy");
let btn_start = elems[1] || document.getElementById('btn_start');
let btn_update = elems[4] || document.getElementById('btn_update');
let btn_destroy = elems[5] || document.getElementById('btn_destroy');
function btn_start_state_on() {
btn_start.onclick = ui.createHandlerFn(this, button_action, 'stop');
btn_start.textContent = _('Disable');
btn_start.className = btn_style_reset;
let btnStartStateOn = () => {
btn_start.onclick = ui.createHandlerFn(
this, this.serviceAction, 'stop', 'btn_start');
btn_start.textContent = _('Enabled');
btn_start.className = btn_style_positive;
}
function btn_start_state_off() {
btn_start.onclick = ui.createHandlerFn(this, button_action, 'start');
btn_start.textContent = _('Enable');
btn_start.className = btn_style_action;
let btnStartStateOff = () => {
btn_start.onclick = ui.createHandlerFn(
this, this.serviceAction,'start', 'btn_start');
btn_start.textContent = _('Disabled');
btn_start.className = btn_style_negative;
}
if(app_status_code == 0) {
disable_buttons(false, null, elems);
btn_start_state_on();
this.disableButtons(false, null, elems);
btnStartStateOn();
btn_destroy.disabled = false;
btn_update.disabled = false;
if(btn_tp) {
@@ -118,28 +125,28 @@ function set_app_status(status_array, elems=[], force_app_code) {
};
}
else if(app_status_code == 2) {
disable_buttons(false, null, elems);
btn_start_state_off();
this.disableButtons(false, null, elems);
btnStartStateOff();
btn_update.disabled = true;
if(btn_tp) {
btn_tp.disabled = true;
};
}
else if(app_status_code == 3) {
btn_start_state_off();
disable_buttons(true, btn_start, elems);
btnStartStateOff();
this.disableButtons(true, btn_start, elems);
}
else if(app_status_code == 4) {
btn_start_state_on();
disable_buttons(true, btn_update, elems);
btnStartStateOn();
this.disableButtons(true, btn_update, elems);
}
else {
ui.addNotification(null, E('p', _('Error')
+ ' %s: return code = %s'.format(tools.exec_path, app_status_code)));
disable_buttons(true, null, elems);
+ ' %s: return code = %s'.format(tools.execPath, app_status_code)));
this.disableButtons(true, null, elems);
};
(elems[0] || document.getElementById("status")).innerHTML = tools.make_status_string(
(elems[0] || document.getElementById("status")).innerHTML = tools.makeStatusString(
app_status_code,
proxy_mode,
bllist_mode,
@@ -151,66 +158,64 @@ function set_app_status(status_array, elems=[], force_app_code) {
if(!L.Poll.active()) {
L.Poll.start();
};
}
},
function button_action(action) {
let btn,
cmd = tools.exec_path;
switch(action) {
case 'start':
case 'stop':
btn = document.getElementById('btn_start');
break;
case 'destroy':
btn = document.getElementById('btn_destroy');
break;
case 'update':
btn = document.getElementById('btn_update');
break;
case 'enable':
case 'disable':
btn = document.getElementById('btn_enable');
cmd = tools.init_path;
break;
case 'total-proxy-on':
case 'total-proxy-off':
btn = document.getElementById('btn_tp');
break;
}
disable_buttons(true, btn);
L.Poll.stop();
if(action === 'update') {
get_app_status().then(status_array => {
set_app_status(status_array, [], 4);
});
serviceAction: function(action, button) {
if(button) {
let elem = document.getElementById(button);
this.disableButtons(true, elem);
};
return fs.exec_direct(cmd, [ action ]).then(res => {
return get_app_status().then(
(status_array) => {
set_app_status(status_array);
ui.hideModal();
});
});
}
L.Poll.stop();
return L.view.extend({
poll_status: function() {
return fs.read(tools.token_file).then(v => {
v = tools.normalize_value(v);
if(v != status_token_value) {
get_app_status().then(set_app_status);
return tools.handleServiceAction(tools.appName, action).then(() => {
return this.getAppStatus().then(
(status_array) => {
this.setAppStatus(status_array);
}
status_token_value = v;
}).catch(e => {
status_token_value = 0;
);
});
},
dialog_destroy: function(ev) {
appAction: function(action, button) {
if(button) {
let elem = document.getElementById(button);
this.disableButtons(true, elem);
};
L.Poll.stop();
if(action === 'update') {
this.getAppStatus().then(status_array => {
this.setAppStatus(status_array, [], 4);
});
};
return fs.exec_direct(tools.execPath, [ action ]).then(res => {
return this.getAppStatus().then(
(status_array) => {
this.setAppStatus(status_array);
ui.hideModal();
}
);
});
},
statusPoll: function() {
return fs.read(tools.tokenFile).then(v => {
v = tools.normalizeValue(v);
if(v != this.statusTokenValue) {
this.getAppStatus().then(
L.bind(this.setAppStatus, this)
);
}
this.statusTokenValue = v;
}).catch(e => {
this.statusTokenValue = 0;
});
},
dialogDestroy: function(ev) {
ev.target.blur();
let cancel_button = E('button', {
'class': btn_style_neutral,
@@ -220,9 +225,9 @@ return L.view.extend({
let shutdown_btn = E('button', {
'class': btn_style_warning,
}, _('Shutdown'));
shutdown_btn.onclick = ui.createHandlerFn(this, function() {
shutdown_btn.onclick = ui.createHandlerFn(this, () => {
cancel_button.disabled = true;
return button_action('destroy');
return this.appAction('destroy');
});
ui.showModal(_('Shutdown'), [
@@ -238,7 +243,7 @@ return L.view.extend({
},
load: function() {
return get_app_status();
return this.getAppStatus();
},
render: function(status_array) {
@@ -246,13 +251,15 @@ return L.view.extend({
return;
};
let section = uci.get(tools.app_name, 'config');
let proxy_local_clients = (typeof(section) === 'object') ? section.proxy_local_clients : null;
status_token_value = (Array.isArray(status_array)) ? tools.normalize_value(status_array[4]) : null;
let section = uci.get(tools.appName, 'config');
let proxy_local_clients = (typeof(section) === 'object') ?
section.proxy_local_clients : null;
this.statusTokenValue = (Array.isArray(status_array)) ?
tools.normalizeValue(status_array[4]) : null;
let status_string = E('div', {
'id': 'status',
'name': 'status',
'id' : 'status',
'name' : 'status',
'class': 'cbi-section-node',
});
@@ -269,23 +276,23 @@ return L.view.extend({
};
let btn_start = E('button', {
'id': 'btn_start',
'name': 'btn_start',
'id' : 'btn_start',
'name' : 'btn_start',
'class': btn_style_action,
}, _('Enable'));
layout_append(btn_start, _('Service'));
let btn_enable = E('button', {
'id': 'btn_enable',
'name': 'btn_enable',
'class': btn_style_save,
'id' : 'btn_enable',
'name' : 'btn_enable',
'class': btn_style_positive,
}, _('Enable'));
layout_append(btn_enable, _('Run at startup'));
let btn_tp = E('button', {
'id': 'btn_tp',
'name': 'btn_tp',
'class': btn_style_save,
'id' : 'btn_tp',
'name' : 'btn_tp',
'class': btn_style_positive,
}, _('Enable'));
if(proxy_local_clients == '0') {
layout_append(btn_tp, _('Total-proxy'),
@@ -293,24 +300,24 @@ return L.view.extend({
};
let btn_update = E('button', {
'id': 'btn_update',
'name': 'btn_update',
'id' : 'btn_update',
'name' : 'btn_update',
'class': btn_style_action,
}, _('Update'));
btn_update.onclick = ui.createHandlerFn(this, () => { button_action('update') });
btn_update.onclick = ui.createHandlerFn(this, () => { this.appAction('update', 'btn_update') });
layout_append(btn_update, _('Update blacklist'));
let btn_destroy = E('button', {
'id': 'btn_destroy',
'name': 'btn_destroy',
'class': btn_style_reset,
'id' : 'btn_destroy',
'name' : 'btn_destroy',
'class': btn_style_negative,
}, _('Shutdown'));
btn_destroy.onclick = this.dialog_destroy;
btn_destroy.onclick = L.bind(this.dialogDestroy, this);
layout_append(btn_destroy, _('Shutdown'),
_('Complete service shutdown, as well as deleting ipsets and blacklist data'));
set_app_status(status_array, [
this.setAppStatus(status_array, [
status_string,
btn_start,
btn_enable,
@@ -319,7 +326,7 @@ return L.view.extend({
btn_destroy,
]);
L.Poll.add(this.poll_status);
L.Poll.add(L.bind(this.statusPoll, this));
return E([
E('h2', { 'class': 'fade-in' }, _('Ruantiblock')),
@@ -339,7 +346,7 @@ return L.view.extend({
]);
},
handleSave: null,
handleSave : null,
handleSaveApply: null,
handleReset: null,
handleReset : null,
});
@@ -6,26 +6,29 @@
'require tools.widgets as widgets';
'require view.ruantiblock.tools as tools';
let available_parsers = [];
return L.view.extend({
availableParsers: {},
function depends(elem, key, array, empty=true) {
appStatusCode : null,
depends: function(elem, key, array, empty=true) {
if(empty && array.length === 0) {
elem.depends(key, '_dummy');
} else {
array.forEach(e => elem.depends(key, e));
};
};
},
function depends_bllist_module(elem) {
depends(elem, 'bllist_module', available_parsers);
};
dependsBllistModule: function(elem) {
this.depends(elem, 'bllist_module', Object.values(this.availableParsers));
},
function validate_ip_port(section, value) {
validateIpPort: function(section, value) {
return (/^$|^([0-9]{1,3}\.){3}[0-9]{1,3}(#[\d]{2,5})?$/.test(value)) ? true : _('Expecting:')
+ ` ${_('One of the following:')}\n - ${_('valid IP address')}\n - ${_('valid address#port')}\n`;
};
},
let CBIBlockTitle = form.DummyValue.extend({
CBIBlockTitle: form.DummyValue.extend({
string: null,
renderWidget: function(section_id, option_index, cfgvalue) {
@@ -37,54 +40,17 @@ let CBIBlockTitle = form.DummyValue.extend({
),
]);
},
});
let ip_filter_edit = new tools.file_edit_dialog(
tools.ip_filter_file,
_('IP filter'),
_('Patterns can be strings or regular expressions. Each pattern in a separate line, the symbol <code>#</code> in the first position of the line - comments on the line.<br />Examples (dot is a special character):') + '<br /><code>128[.]199[.]0[.]0/16<br />34[.]217[.]90[.]52<br />162[.]13[.]190[.]</code>'
);
let fqdn_filter_edit = new tools.file_edit_dialog(
tools.fqdn_filter_file,
_('FQDN filter'),
_('Patterns can be strings or regular expressions. Each pattern in a separate line, the symbol <code>#</code> in the first position of the line - comments on the line.<br />Examples:') + '<br /><code>poker<br />[ck]?a[sz]ino?<br />[vw]ulkan<br />slots?</code>'
);
let user_entries_edit = new tools.file_edit_dialog(
tools.user_entries_file,
_('User entries'),
_('One entry (IP, CIDR or FQDN) per line. In the FQDN records, you can specify the DNS server for resolving this domain (separated by a space). You can also comment on lines (<code>#</code> is the first character of a line).<br />Examples:') + '<br /><code>#comment<br />domain.net<br />sub.domain.com 8.8.8.8<br />sub.domain.com 8.8.8.8#53<br />74.125.131.19<br />74.125.0.0/16</code>'
);
let torrc_edit = new tools.file_edit_dialog(
tools.torrc_file,
_('Tor configuration file'),
null,
function(rc) {
return fs.exec('/etc/init.d/tor', [ 'enabled' ]).then(res => {
if(res.code === 0) {
return fs.exec('/etc/init.d/tor', [ 'restart' ]);
};
}).catch(e => {
ui.addNotification(null, E('p', _('Unable to execute or read contents')
+ ': %s<br />[ %s ]'.format(e.message, '/etc/init.d/tor')));
});
}
);
return L.view.extend({
app_status_code: null,
}),
load: function() {
return Promise.all([
L.resolveDefault(fs.exec(tools.exec_path, [ 'raw-status' ]), 1),
fs.list(tools.parsers_dir),
L.resolveDefault(fs.exec(tools.execPath, [ 'raw-status' ]), 1),
fs.list(tools.parsersDir),
uci.load('network'),
]).catch(e => {
ui.addNotification(null, E('p', _('Unable to read the contents')
+ ': %s<br />[ %s ]'.format(
e.message, tools.parsers_dir
+ ': %s [ %s ]'.format(
e.message, tools.parsersDir
)));
});
},
@@ -93,7 +59,7 @@ return L.view.extend({
if(!data) {
return;
};
this.app_status_code = data[0].code;
this.appStatusCode = data[0].code;
let p_dir_arr = data[1];
let lan_iface = uci.get('network', 'lan', 'ifname') || 'eth0';
let vpn_iface = uci.get('network', 'VPN', 'ifname') || 'tun0';
@@ -102,14 +68,48 @@ return L.view.extend({
p_dir_arr.forEach(e => {
let fname = e.name;
if(fname.startsWith('ruab_parser')) {
available_parsers.push(tools.parsers_dir + '/' + fname);
this.availableParsers[fname] = tools.parsersDir + '/' + fname;
};
});
};
let ip_filter_edit = new tools.fileEditDialog(
tools.ipFilterFile,
_('IP filter'),
_('Patterns can be strings or regular expressions. Each pattern in a separate line, the symbol <code>#</code> in the first position of the line - comments on the line.<br />Examples (dot is a special character):') +
'<br /><code>128[.]199[.]0[.]0/16<br />34[.]217[.]90[.]52<br />162[.]13[.]190[.]</code>'
);
let fqdn_filter_edit = new tools.fileEditDialog(
tools.fqdnFilterFile,
_('FQDN filter'),
_('Patterns can be strings or regular expressions. Each pattern in a separate line, the symbol <code>#</code> in the first position of the line - comments on the line.<br />Examples:') +
'<br /><code>poker<br />[ck]?a[sz]ino?<br />[vw]ulkan<br />slots?</code>'
);
let user_entries_edit = new tools.fileEditDialog(
tools.userEntriesFile,
_('User entries'),
_('One entry (IP, CIDR or FQDN) per line. In the FQDN records, you can specify the DNS server for resolving this domain (separated by a space). You can also comment on lines (<code>#</code> is the first character of a line).<br />Examples:') +
'<br /><code>#comment<br />domain.net<br />sub.domain.com 8.8.8.8<br />sub.domain.com 8.8.8.8#53<br />74.125.131.19<br />74.125.0.0/16</code>'
);
let torrc_edit = new tools.fileEditDialog(
tools.torrcFile,
_('Tor configuration file'),
null,
function(rc) {
return tools.getInitStatus('tor').then(res => {
if(res) {
return tools.handleServiceAction('tor', 'restart');
};
});
}
);
let m, s, o;
m = new form.Map(tools.app_name, _('Ruantiblock') + ' - ' + _('Settings'));
m = new form.Map(tools.appName, _('Ruantiblock') + ' - ' + _('Settings'));
s = m.section(form.NamedSection, 'config');
s.anonymous = true;
@@ -120,7 +120,7 @@ return L.view.extend({
s.tab('main_settings', _('Main settings'));
// PROXY_MODE
if(this.app_status_code == 1 || this.app_status_code == 2) {
if(this.appStatusCode == 1 || this.appStatusCode == 2) {
o = s.taboption('main_settings', form.ListValue, 'proxy_mode',
_('Proxy mode'));
o.value('1', 'Tor');
@@ -131,13 +131,11 @@ return L.view.extend({
let proxy_local_clients = s.taboption('main_settings', form.Flag, 'proxy_local_clients',
_("Apply proxy rules to router application traffic"));
proxy_local_clients.rmempty = false;
proxy_local_clients.default = proxy_local_clients.enabled;
// USE_LOGGER
o = s.taboption('main_settings', form.Flag, 'use_logger',
_('Logging events'));
o.rmempty = false;
o.default = 1;
// DEF_TOTAL_PROXY
o = s.taboption('main_settings', form.Flag, 'def_total_proxy',
@@ -151,10 +149,9 @@ return L.view.extend({
_('Clean up ipsets before updating blacklist'));
o.description = _('Reduces RAM consumption during update');
o.rmempty = false;
o.default = 0;
if(this.app_status_code == 1 || this.app_status_code == 2) {
if(this.appStatusCode == 1 || this.appStatusCode == 2) {
/* Tor tab */
s.tab('tor_settings', _('Tor mode'));
@@ -172,14 +169,12 @@ return L.view.extend({
_('Transparent proxy port for iptables rules'));
o.rmempty = false;
o.datatype = "port";
o.default = '9040';
// ONION_DNS_ADDR
o = s.taboption('tor_settings', form.Value, 'onion_dns_addr',
_("Optional DNS resolver for '.onion' zone"), '<code>ipaddress#port</code>');
o.rmempty = false;
o.default = '127.0.0.1#9053';
o.validate = validate_ip_port;
o.validate = this.validateIpPort;
// Torrc edit dialog
o = s.taboption('tor_settings', form.Button, '_torrc_btn',
@@ -210,126 +205,97 @@ return L.view.extend({
// BLLIST_MODULE
let bllist_module = s.taboption('parser_settings', form.ListValue,
'bllist_module', _('Blacklist module'));
bllist_module.value("", _("user entries only"));
available_parsers.forEach(e => bllist_module.value(e));
bllist_module.value('', _('none (user entries only)'));
Object.entries(this.availableParsers).forEach(
e => bllist_module.value(e[1], e[0]));
// BLLIST_MODE
let bllist_mode = s.taboption('parser_settings', form.ListValue,
'bllist_mode', _('Module operation mode'));
bllist_mode.value('ip');
bllist_mode.value('fqdn');
depends_bllist_module(bllist_mode);
// BLLIST_SOURCE
let bllist_source = s.taboption('parser_settings', form.ListValue,
'bllist_source', _('Blacklist source'));
bllist_source.description = _("Options") + ':';
for(let [k, v] of Object.entries(tools.blacklist_sources)) {
for(let [k, v] of Object.entries(tools.blacklistSources)) {
bllist_source.value(k);
bllist_source.description += `<br />${k} - <a href="${v}" target="_blank">${v}</a>`;
};
depends_bllist_module(bllist_source);
o = s.taboption('parser_settings', CBIBlockTitle, '_dummy_ip');
o = s.taboption('parser_settings', this.CBIBlockTitle, '_dummy_ip');
o.string = _('IP configuration') + ':';
depends_bllist_module(o);
// IP_LIMIT
o = s.taboption('parser_settings', form.Value, 'ip_limit', _("IP limit"));
o.description = _("The number of IP addresses in the subnet, upon reaching which the entire '/24' subnet is added to the list");
o.rmempty = false;
o.datatype = 'uinteger';
o.default = '0';
depends_bllist_module(o);
// OPT_EXCLUDE_NETS
o = s.taboption('parser_settings', form.DynamicList, 'opt_exclude_nets');
o.title = _('IP subnet patterns (/24) that are excluded from optimization');
o.description = _('e.g:') + ' <code>192.168.1.</code>';
o.placeholder = _('e.g:') + ' 192.168.1.';
o.default = '';
depends_bllist_module(o);
o.validate = (section, value) => {
return (/^$|^([0-9]{1,3}[.]){3}$/.test(value)) ? true : _('Expecting:')
+ ' ' + _('net pattern') + ' (' + _('e.g:') + ' 192.168.3.)\n';
};
// SUMMARIZE_IP
o = s.taboption('parser_settings', form.Flag, 'summarize_ip',
_("Summarize IP ranges"));
o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// SUMMARIZE_CIDR
o = s.taboption('parser_settings', form.Flag, 'summarize_cidr',
_("Summarize '/24' networks"));
o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
o = s.taboption('parser_settings', CBIBlockTitle, '_dummy_fqdn');
o = s.taboption('parser_settings', this.CBIBlockTitle, '_dummy_fqdn');
o.string = _('FQDN configuration') + ':';
depends_bllist_module(o);
// SD_LIMIT
o = s.taboption('parser_settings', form.Value, 'sd_limit',
_("Subdomains limit"));
o.description = _('The number of subdomains in the domain, upon reaching which the entire 2nd level domain is added to the list');
o.rmempty = false;
o.datatype = 'uinteger';
o.default = '16';
depends_bllist_module(o);
// OPT_EXCLUDE_SLD
o = s.taboption('parser_settings', form.DynamicList, 'opt_exclude_sld',
_('2nd level domains that are excluded from optimization'));
o.description = _('e.g:') + ' <code>livejournal.com</code>';
o.placeholder = _('e.g:') + ' livejournal.com';
o.datatype = "hostname";
o.default = [
'livejournal.com',
'facebook.com',
'vk.com',
'blog.jp',
'msk.ru',
'net.ru',
'org.ru',
'net.ua',
'com.ua',
'org.ua',
'co.uk',
'amazonaws.com',
];
depends_bllist_module(o);
// USE_IDN
o = s.taboption('parser_settings', form.Flag, 'use_idn',
_("Convert cyrillic domains to punycode"));
o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// ALT_NSLOOKUP
o = s.taboption('parser_settings', form.Flag, 'alt_nslookup',
_('Use optional DNS resolver'));
o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// ALT_DNS_ADDR
o = s.taboption('parser_settings', form.Value, 'alt_dns_addr',
_("Optional DNS resolver"), '<code>ipaddress[#port]</code>');
o.rmempty = false;
o.depends('alt_nslookup', '1');
o.validate = validate_ip_port;
o.default = '8.8.8.8';
o.validate = this.validateIpPort;
/* Entries filters tab */
/* Blacklist entry filters tab */
s.tab('entries_filter_tab', _('Entries filters'));
s.tab('entries_filter_tab', _('Blacklist entry filters'));
// IP_FILTER
o = s.taboption('entries_filter_tab', form.Flag, 'ip_filter',
_("Enable IP filter"));
o.description = _('Exclude IP addresses from blacklist by IP filter patterns');
o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// IP_FILTER edit dialog
o = s.taboption('entries_filter_tab', form.Button, '_ip_filter_btn',
@@ -337,15 +303,12 @@ return L.view.extend({
o.onclick = () => ip_filter_edit.show();
o.inputtitle = _('Edit');
o.inputstyle = 'edit btn';
depends_bllist_module(o);
// FQDN_FILTER
o = s.taboption('entries_filter_tab', form.Flag, 'fqdn_filter',
_("Enable FQDN filter"));
o.description = _('Exclude domains from blacklist by FQDN filter patterns');
o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// FQDN_FILTER edit dialog
o = s.taboption('entries_filter_tab', form.Button, '_fqdn_filter_btn',
@@ -353,7 +316,6 @@ return L.view.extend({
o.onclick = () => fqdn_filter_edit.show();
o.inputtitle = _('Edit');
o.inputstyle = 'edit btn';
depends_bllist_module(o);
/* User entries tab */
@@ -365,12 +327,12 @@ return L.view.extend({
_('Enable'), _("Add user entries to the blacklist when updating"));
o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
this.dependsBllistModule(o);
// USER_ENTRIES_DNS
o = s.taboption('user_entries_tab', form.Value, 'user_entries_dns',
_("DNS server that is used for FQDN entries"), '<code>ipaddress[#port]</code>');
o.validate = validate_ip_port;
o.validate = this.validateIpPort;
// USER_ENTRIES edit dialog
o = s.taboption('user_entries_tab', form.Button, '_user_entries_btn',
@@ -379,10 +341,8 @@ return L.view.extend({
o.inputtitle = _('Edit');
o.inputstyle = 'edit btn';
let map_promise = m.render();
map_promise.then(node => node.classList.add('fade-in'));
return map_promise;
},
@@ -390,8 +350,9 @@ return L.view.extend({
return this.handleSave(ev).then(() => {
ui.changes.apply(mode == '0');
if(this.app_status_code != 1 && this.app_status_code != 2) {
window.setTimeout(() => fs.exec(tools.init_path, [ 'restart' ]), 3000);
if(this.appStatusCode != 1 && this.appStatusCode != 2) {
window.setTimeout(() => tools.handleServiceAction(
tools.appName, 'restart'), 3000);
};
});
},
@@ -1,5 +1,6 @@
'use strict';
'require fs';
'require rpc';
'require ui';
document.head.append(E('style', {'type': 'text/css'},
@@ -35,33 +36,71 @@ document.head.append(E('style', {'type': 'text/css'},
`));
return L.Class.extend({
app_name: 'ruantiblock',
exec_path: '/usr/bin/ruantiblock',
init_path: '/etc/init.d/ruantiblock',
token_file: '/var/run/ruantiblock.token',
parsers_dir: '/usr/bin',
torrc_file: '/etc/tor/torrc',
user_entries_file: '/etc/ruantiblock/user_entries',
fqdn_filter_file: '/etc/ruantiblock/fqdn_filter',
ip_filter_file: '/etc/ruantiblock/ip_filter',
crontab_file: '/etc/crontabs/root',
info_label_starting: '<span class="label-status starting">' + _('Starting') + '</span>',
info_label_running: '<span class="label-status running">' + _('Enabled') + '</span>',
info_label_updating: '<span class="label-status updating">' + _('Updating') + '</span>',
info_label_stopped: '<span class="label-status stopped">' + _('Disabled') + '</span>',
info_label_error: '<span class="label-status error">' + _('Error') + '</span>',
appName : 'ruantiblock',
execPath : '/usr/bin/ruantiblock',
tokenFile : '/var/run/ruantiblock.token',
parsersDir : '/usr/bin',
torrcFile : '/etc/tor/torrc',
userEntriesFile : '/etc/ruantiblock/user_entries',
fqdnFilterFile : '/etc/ruantiblock/fqdn_filter',
ipFilterFile : '/etc/ruantiblock/ip_filter',
crontabFile : '/etc/crontabs/root',
infoLabelStarting: '<span class="label-status starting">' + _('Starting') + '</span>',
infoLabelRunning : '<span class="label-status running">' + _('Enabled') + '</span>',
infoLabelUpdating: '<span class="label-status updating">' + _('Updating') + '</span>',
infoLabelStopped : '<span class="label-status stopped">' + _('Disabled') + '</span>',
infoLabelError : '<span class="label-status error">' + _('Error') + '</span>',
blacklist_sources: {
blacklistSources: {
'rublacklist': 'https://rublacklist.net',
'zapret-info': 'https://github.com/zapret-info/z-i',
'antifilter': 'https://antifilter.download',
'antifilter' : 'https://antifilter.download',
},
normalize_value: function(v) {
callInitStatus: rpc.declare({
object: 'luci',
method: 'getInitList',
params: [ 'name' ],
expect: { '': {} }
}),
callInitAction: rpc.declare({
object: 'luci',
method: 'setInitAction',
params: [ 'name', 'action' ],
expect: { result: false }
}),
getInitStatus: function(name) {
return this.callInitStatus(name).then(res => {
if(res) {
return res[name].enabled;
} else {
throw _('Command failed');
}
}).catch(e => {
ui.addNotification(null,
E('p', _('Failed to get %s init status: %s').format(name, e)));
});
},
handleServiceAction: function(name, action) {
return this.callInitAction(name, action).then(success => {
if(!success) {
throw _('Command failed');
};
return true;
}).catch(e => {
ui.addNotification(null,
E('p', _('Service action failed "%s %s": %s').format(name, action, e)));
});
},
normalizeValue: function(v) {
return (v && typeof(v) === 'string') ? v.trim().replace(/\r?\n/g, '') : v;
},
make_status_string: function(
makeStatusString: function(
app_status_code,
proxy_mode,
bllist_mode,
@@ -74,21 +113,21 @@ return L.Class.extend({
switch(app_status_code) {
case 0:
app_status_label = this.info_label_running;
app_status_label = this.infoLabelRunning;
break;
case 2:
app_status_label = this.info_label_stopped;
app_status_label = this.infoLabelStopped;
break;
case 3:
app_status_label = this.info_label_starting;
app_status_label = this.infoLabelStarting;
spinning = ' spinning';
break;
case 4:
app_status_label = this.info_label_updating;
app_status_label = this.infoLabelUpdating;
spinning = ' spinning';
break;
default:
app_status_label = this.info_label_error;
app_status_label = this.infoLabelError;
return `<div class="table">
<div class="tr">
<div class="td left" style="min-width:33%%">
@@ -144,7 +183,7 @@ return L.Class.extend({
${_('Blacklist source')}:
</div>
<div class="td left">
<span style="cursor:help; border-bottom:1px dotted" data-tooltip="${this.blacklist_sources[bllist_source]}">
<span style="cursor:help; border-bottom:1px dotted" data-tooltip="${this.blacklistSources[bllist_source]}">
${bllist_source}
</span>
</div>
@@ -152,7 +191,7 @@ return L.Class.extend({
);
},
file_edit_dialog: L.Class.extend({
fileEditDialog: L.Class.extend({
__init__: function(file, title, description, callback, file_exists=false) {
this.file = file;
this.title = title;
+127 -103
View File
@@ -19,6 +19,12 @@ msgstr "Домены 2-го уровня не подлежащие оптими
msgid "Add user entries to the blacklist when updating"
msgstr "Добавлять записи пользователя в блэклист при обновлении"
msgid "Alert"
msgstr "Тревога"
msgid "All"
msgstr "Все"
msgid "All traffic goes through the proxy without applying rules"
msgstr "Весь трафик отправляется в прокси, без применения правил"
@@ -28,6 +34,9 @@ msgstr "Применить"
msgid "Apply proxy rules to router application traffic"
msgstr "Применять правила прокси к трафику приложений роутера"
msgid "Blacklist entry filters"
msgstr "Фильтры записей блэклиста"
msgid "Blacklist module"
msgstr "Модуль блэклиста"
@@ -55,10 +64,12 @@ msgstr "Изменения сохранены."
msgid "Clean up ipsets before updating blacklist"
msgstr "Очищать ipset'ы перед обновлением блэклиста"
msgid "Command failed"
msgstr "Команда не выполнена"
msgid ""
"Complete service shutdown, as well as deleting ipsets and blacklist data"
msgstr ""
"Полное выключение службы, а также удаление ipset'ов и данных блэклиста"
msgstr "Полное выключение службы, а также удаление ipset'ов и данных блэклиста"
msgid "Contents have been saved."
msgstr "Содержимое сохранено."
@@ -66,6 +77,9 @@ msgstr "Содержимое сохранено."
msgid "Convert cyrillic domains to punycode"
msgstr "Конвертировать кириллические домены в punycode"
msgid "Critical"
msgstr "Критическая ситуация"
msgid "Current schedule"
msgstr "Текущее расписание"
@@ -75,18 +89,27 @@ msgstr "DNS сервер для FQDN записей"
msgid "Day"
msgstr "День"
msgid "Disable"
msgstr "Отключить"
msgid "Debug"
msgstr "Отладка"
msgid "Disabled"
msgstr "Отключено"
msgid "Dismiss"
msgstr "Закрыть"
msgstr "Отмена"
msgid "Download error"
msgstr "Ошибка загрузки"
msgid "Download log"
msgstr "Скачать лог"
msgid "Edit"
msgstr "Изменить"
msgid "Emergency"
msgstr "Чрезвычайная ситуация"
msgid "Enable"
msgstr "Включить"
@@ -102,18 +125,18 @@ msgstr "Включать 'total-proxy' при старте"
msgid "Enabled"
msgstr "Включено"
msgid "Entries filters"
msgstr "Фильтры записей"
msgid "Entries"
msgstr "Записи"
msgid "Error"
msgstr "Ошибка"
msgid "Exclude domains from blacklist by FQDN filter patterns"
msgstr "Исключение доменов из блэклиста по шаблонам фильтра FQDN"
msgid "Exclude IP addresses from blacklist by IP filter patterns"
msgstr "Исключение IP адресов из блэклиста по шаблонам фильтра IP"
msgid "Exclude domains from blacklist by FQDN filter patterns"
msgstr "Исключение доменов из блэклиста по шаблонам фильтра FQDN"
msgid "Expecting:"
msgstr "Ожидается:"
@@ -123,12 +146,24 @@ msgstr "Конфигурация FQDN"
msgid "FQDN filter"
msgstr "Фильтр FQDN"
msgid "Facility"
msgstr "Категория"
msgid "Failed to get %s init status: %s"
msgstr "Не удалось получить статус инициализации %s: %s"
msgid "HTML/XML error"
msgstr "Ошибка HTML/XML"
msgid "Host"
msgstr "Хост"
msgid "Hosts"
msgstr "Хосты"
msgid "Hour"
msgstr "Час"
msgid "Interval"
msgstr "Интервал"
msgid "IP configuration"
msgstr "Конфигурация IP"
@@ -141,6 +176,15 @@ msgstr "Лимит IP адресов"
msgid "IP subnet patterns (/24) that are excluded from optimization"
msgstr "Шаблоны IP подсетей (/24) не подлежащих оптимизации"
msgid "Info"
msgstr "Информация"
msgid "Interval"
msgstr "Интервал"
msgid "Invalid regular expression"
msgstr "Неправильное регулярное выражение"
msgid "Ipset"
msgstr "Ipset"
@@ -153,6 +197,12 @@ msgstr "LAN интерфейс"
msgid "Last blacklist update"
msgstr "Последнее обновление блэклиста"
msgid "Last entries"
msgstr "Последние записи"
msgid "Level"
msgstr "Уровень"
msgid "Loading"
msgstr "Загрузка"
@@ -162,12 +212,21 @@ msgstr "Лог"
msgid "Logging events"
msgstr "Записывать события в лог"
msgid "Logging levels"
msgstr "Уровни логирования"
msgid "Main settings"
msgstr "Основные настройки"
msgid "Match-set"
msgstr "Правило"
msgid "Message"
msgstr "Сообщение"
msgid "Message filter"
msgstr "Фильтр сообщений"
msgid "Minute"
msgstr "Минута"
@@ -177,14 +236,20 @@ msgstr "Режим работы модуля"
msgid "Name"
msgstr "Имя"
msgid "No Shedule"
msgstr "Нет расписания"
msgid "No changes to save."
msgstr "Нет изменений для сохранения."
msgid "No data"
msgstr "Нет данных"
msgid "No Shedule"
msgstr "Нет расписания"
msgid "No entries available..."
msgstr "Нет доступных записей..."
msgid "Notice"
msgstr "Сообщение"
msgid "Number of entries"
msgstr "Кол-во записей"
@@ -202,15 +267,12 @@ msgstr ""
msgid "One of the following:"
msgstr "Одно из следующих значений:"
msgid "Only messages that include the specified string will be displayed"
msgstr "Будут показаны только сообщения включающие указанную строку"
msgid "Optional DNS resolver"
msgstr "Альтернативный DNS резолвер"
msgid "Optional DNS resolver for '.onion' zone"
msgstr "Дополнительный DNS резолвер для '.onion' зоны"
msgid "Optional DNS resolver"
msgstr "Альтернативный DNS резолвер"
msgid "Options"
msgstr "Опции"
@@ -238,15 +300,27 @@ msgstr "Режим прокси"
msgid "Reduces RAM consumption during update"
msgstr "Уменьшает потребление оперативной памяти при обновлении"
msgid "Refresh log"
msgstr "Обновить лог"
msgid "Reset"
msgstr "Сбросить"
msgid "Ruantiblock"
msgstr "Ruantiblock"
msgid "Run at startup"
msgstr "Запуск при старте системы"
msgid "Save"
msgstr "Сохранить"
msgid "Service"
msgstr "Служба"
msgid "Service action failed \"%s %s\": %s"
msgstr "Не удалось выполнить действие службы \"%s %s\": %s"
msgid "Set"
msgstr "Установить"
@@ -259,6 +333,9 @@ msgstr "Выключение"
msgid "Size in memory"
msgstr "Размер в памяти"
msgid "Sorting entries"
msgstr "Сортировка записей"
msgid "Starting"
msgstr "Запускается"
@@ -300,21 +377,33 @@ msgstr ""
msgid "Time"
msgstr "Время"
msgid "Timestamp"
msgstr "Время"
msgid "Tor configuration file"
msgstr "Конфигурационный файл Tor"
msgid "Tor mode"
msgstr "Режим Tor"
msgid "Total-proxy"
msgstr "Total-proxy"
msgid "Total-proxy is on"
msgstr "Total-proxy включен"
msgid "Transparent proxy port for iptables rules"
msgstr "Порт прозрачного прокси для правил iptables"
msgid "Type an expression..."
msgstr "Введите выражение..."
msgid "Unable to execute or read contents"
msgstr "Невозможно выполнить или прочитать содержимое"
msgid "Unable to load log data:"
msgstr "Невозможно загрузить данные лога:"
msgid "Unable to read the contents"
msgstr "Невозможно прочитать содержимое"
@@ -348,9 +437,27 @@ msgstr "Режим VPN"
msgid "VPN routing error! Need restart"
msgstr "Ошибка маршрутизации VPN! Необходим перезапуск"
msgid "Warning"
msgstr "Внимание"
msgid "ascending"
msgstr "по возрастанию"
msgid "descending"
msgstr "по убыванию"
msgid "disabled"
msgstr "отключен"
msgid "e.g:"
msgstr "прим:"
msgid "net pattern"
msgstr "шаблон сети"
msgid "none (user entries only)"
msgstr "нет (только записи пользователя)"
msgid "user entries only"
msgstr "только записи пользователя"
@@ -359,86 +466,3 @@ msgstr "верный IP-адрес"
msgid "valid address#port"
msgstr "верный IP-адрес#порт"
msgid "Alert"
msgstr "Тревога"
msgid "All"
msgstr "Все"
msgid "Critical"
msgstr "Критическая ситуация"
msgid "Debug"
msgstr "Отладка"
msgid "Download log"
msgstr "Скачать лог"
msgid "Emergency"
msgstr "Чрезвычайная ситуация"
msgid "Entries"
msgstr "Записи"
msgid "Facility"
msgstr "Категория"
msgid "Host"
msgstr "Хост"
msgid "Hosts"
msgstr "Хосты"
msgid "Info"
msgstr "Информация"
msgid "Invalid regular expression"
msgstr "Неправильное регулярное выражение"
msgid "Last entries"
msgstr "Последние записи"
msgid "Level"
msgstr "Уровень"
msgid "Logging levels"
msgstr "Уровни логирования"
msgid "Message"
msgstr "Сообщение"
msgid "Message filter"
msgstr "Фильтр сообщений"
msgid "No entries available..."
msgstr "Нет доступных записей..."
msgid "Notice"
msgstr "Сообщение"
msgid "Refresh log"
msgstr "Обновить лог"
msgid "Sorting entries"
msgstr "Сортировка записей"
msgid "Timestamp"
msgstr "Время"
msgid "Type an expression..."
msgstr "Введите выражение..."
msgid "Unable to load log data:"
msgstr "Невозможно загрузить данные лога:"
msgid "Warning"
msgstr "Внимание"
msgid "ascending"
msgstr "по возрастанию"
msgid "descending"
msgstr "по убыванию"
+116 -101
View File
@@ -7,6 +7,12 @@ msgstr ""
msgid "Add user entries to the blacklist when updating"
msgstr ""
msgid "Alert"
msgstr ""
msgid "All"
msgstr ""
msgid "All traffic goes through the proxy without applying rules"
msgstr ""
@@ -16,6 +22,9 @@ msgstr ""
msgid "Apply proxy rules to router application traffic"
msgstr ""
msgid "Blacklist entry filters"
msgstr ""
msgid "Blacklist module"
msgstr ""
@@ -43,6 +52,9 @@ msgstr ""
msgid "Clean up ipsets before updating blacklist"
msgstr ""
msgid "Command failed"
msgstr ""
msgid ""
"Complete service shutdown, as well as deleting ipsets and blacklist data"
msgstr ""
@@ -53,6 +65,9 @@ msgstr ""
msgid "Convert cyrillic domains to punycode"
msgstr ""
msgid "Critical"
msgstr ""
msgid "Current schedule"
msgstr ""
@@ -62,43 +77,47 @@ msgstr ""
msgid "Day"
msgstr ""
msgid "Disable"
msgid "Debug"
msgstr ""
msgid "Disabled"
msgstr ""
msgid "Dismiss"
msgstr ""
msgid "Download error"
msgstr ""
msgid "Download log"
msgstr ""
msgid "Edit"
msgstr ""
msgid "Emergency"
msgstr ""
msgid "Enable"
msgstr ""
msgid "Enable FQDN filter"
msgstr ""
msgid "Enable ip filter"
msgid "Enable IP filter"
msgstr ""
msgid "Enable the 'total-proxy' option at startup"
msgstr ""
msgid "Enabled"
msgstr ""
msgid "Entries filters"
msgid "Entries"
msgstr ""
msgid "Error"
msgstr ""
msgid "Exclude domains from blacklist by FQDN filter patterns"
msgid "Exclude IP addresses from blacklist by IP filter patterns"
msgstr ""
msgid "Exclude IP addresses from blacklist by IP filter patterns"
msgid "Exclude domains from blacklist by FQDN filter patterns"
msgstr ""
msgid "Expecting:"
@@ -110,15 +129,24 @@ msgstr ""
msgid "FQDN filter"
msgstr ""
msgid "Hour"
msgid "Facility"
msgstr ""
msgid "Failed to get %s init status: %s"
msgstr ""
msgid "HTML/XML error"
msgstr ""
msgid "Host"
msgstr ""
msgid "Hosts"
msgstr ""
msgid "Hour"
msgstr ""
msgid "Interval"
msgstr ""
msgid "IP configuration"
msgstr ""
@@ -131,6 +159,15 @@ msgstr ""
msgid "IP subnet patterns (/24) that are excluded from optimization"
msgstr ""
msgid "Info"
msgstr ""
msgid "Interval"
msgstr ""
msgid "Invalid regular expression"
msgstr ""
msgid "Ipset"
msgstr ""
@@ -143,6 +180,12 @@ msgstr ""
msgid "Last blacklist update"
msgstr ""
msgid "Last entries"
msgstr ""
msgid "Level"
msgstr ""
msgid "Loading"
msgstr ""
@@ -152,12 +195,21 @@ msgstr ""
msgid "Logging events"
msgstr ""
msgid "Logging levels"
msgstr ""
msgid "Main settings"
msgstr ""
msgid "Match-set"
msgstr ""
msgid "Message"
msgstr ""
msgid "Message filter"
msgstr ""
msgid "Minute"
msgstr ""
@@ -167,13 +219,19 @@ msgstr ""
msgid "Name"
msgstr ""
msgid "No Shedule"
msgstr ""
msgid "No changes to save."
msgstr ""
msgid "No data"
msgstr ""
msgid "No Shedule"
msgid "No entries available..."
msgstr ""
msgid "Notice"
msgstr ""
msgid "Number of entries"
@@ -189,10 +247,10 @@ msgstr ""
msgid "One of the following:"
msgstr ""
msgid "Optional DNS resolver for '.onion' zone"
msgid "Optional DNS resolver"
msgstr ""
msgid "Optional DNS resolver"
msgid "Optional DNS resolver for '.onion' zone"
msgstr ""
msgid "Options"
@@ -216,33 +274,46 @@ msgstr ""
msgid "Reduces RAM consumption during update"
msgstr ""
msgid "Refresh log"
msgstr ""
msgid "Reset"
msgstr ""
msgid "Ruantiblock"
msgstr ""
msgid "Run at startup"
msgstr ""
msgid "Save"
msgstr ""
msgid "Service"
msgstr ""
msgid "Service action failed \"%s %s\": %s"
msgstr ""
msgid "Set"
msgstr ""
msgid "Settings"
msgstr ""
msgid "Shutdown"
msgstr ""
msgid "Size in memory"
msgstr ""
msgid "Sorting entries"
msgstr ""
msgid "Starting"
msgstr ""
msgid "Statistics"
msgstr ""
msgid "Status"
msgstr ""
@@ -273,21 +344,31 @@ msgstr ""
msgid "Time"
msgstr ""
msgid "Timestamp"
msgstr ""
msgid "Tor configuration file"
msgstr ""
msgid "Tor mode"
msgstr ""
msgid "Total-proxy"
msgstr ""
msgid "Total-proxy is on"
msgstr ""
msgid "Transparent proxy port for iptables rules"
msgstr ""
msgid "Type an expression..."
msgstr ""
msgid "Unable to execute or read contents"
msgstr ""
msgid "Unable to load log data:"
msgstr ""
msgid "Unable to read the contents"
msgstr ""
@@ -308,7 +389,6 @@ msgstr ""
msgid "Use optional DNS resolver"
msgstr ""
msgid "User entries"
msgstr ""
@@ -321,9 +401,27 @@ msgstr ""
msgid "VPN routing error! Need restart"
msgstr ""
msgid "Warning"
msgstr ""
msgid "ascending"
msgstr ""
msgid "descending"
msgstr ""
msgid "disabled"
msgstr ""
msgid "e.g:"
msgstr ""
msgid "net pattern"
msgstr ""
msgid "none (user entries only)"
msgstr ""
msgid "user entries only"
msgstr ""
@@ -332,86 +430,3 @@ msgstr ""
msgid "valid address#port"
msgstr ""
msgid "Alert"
msgstr ""
msgid "All"
msgstr ""
msgid "Critical"
msgstr ""
msgid "Debug"
msgstr ""
msgid "Download log"
msgstr ""
msgid "Emergency"
msgstr ""
msgid "Entries"
msgstr ""
msgid "Facility"
msgstr ""
msgid "Host"
msgstr ""
msgid "Hosts"
msgstr ""
msgid "Info"
msgstr ""
msgid "Invalid regular expression"
msgstr ""
msgid "Last entries"
msgstr ""
msgid "Level"
msgstr ""
msgid "Logging levels"
msgstr ""
msgid "Message"
msgstr ""
msgid "Message filter"
msgstr ""
msgid "No entries available..."
msgstr ""
msgid "Notice"
msgstr ""
msgid "Refresh log"
msgstr ""
msgid "Sorting entries"
msgstr ""
msgid "Timestamp"
msgstr ""
msgid "Type an expression..."
msgstr ""
msgid "Unable to load log data:"
msgstr ""
msgid "Warning"
msgstr ""
msgid "ascending"
msgstr ""
msgid "descending"
msgstr ""
@@ -12,16 +12,13 @@
"/etc/tor/torrc": [ "read" ],
"/etc/crontabs/root": [ "read" ],
"/usr/bin/ruantiblock": [ "exec" ],
"/etc/init.d/ruantiblock": [ "exec" ],
"/etc/init.d/tor enabled": [ "exec" ],
"/etc/init.d/tor restart": [ "exec" ],
"/etc/init.d/cron enabled": [ "exec" ],
"/etc/init.d/cron enable": [ "exec" ],
"/etc/init.d/cron restart": [ "exec" ],
"/sbin/logread -e ruantiblock": [ "exec" ],
"/usr/sbin/logread -e ruantiblock": [ "exec" ]
},
"uci": [ "network", "ruantiblock" ]
"uci": [ "network", "ruantiblock" ],
"ubus": {
"luci": [ "getInitList", "setInitAction" ]
}
},
"write": {
"file": {
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB