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" OWRT_VERSION="19.07"
RUAB_VERSION="0.9.0-2" RUAB_VERSION="0.9.0-2"
RUAB_MOD_LUA_VERSION="0.9.0-2" RUAB_MOD_LUA_VERSION="0.9.0-2"
RUAB_LUCI_APP_VERSION="0.9.0-6" RUAB_LUCI_APP_VERSION="0.9.0-7"
BASE_URL="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master" BASE_URL="https://github.com/gSpotx2f/ruantiblock_openwrt/raw/master"
PKG_DIR="/tmp" PKG_DIR="/tmp"
if [ -n "$1" ]; then if [ -n "$1" ]; then
@@ -29,8 +29,8 @@ URL_LUCI_APP_RU_PKG="${BASE_URL}/packages/${OWRT_VERSION}/luci-i18n-ruantiblock-
### tor ### tor
URL_TORRC="${BASE_URL}/tor/etc/tor/torrc" URL_TORRC="${BASE_URL}/tor/etc/tor/torrc"
### ruantiblock-mod-lua ### ruantiblock-mod-lua
URL_LUA_IPTOOL="https://raw.githubusercontent.com/gSpotx2f/iptool-lua/master/5.1/iptool.lua" URL_LUA_IPTOOL="https://github.com/gSpotx2f/iptool-lua/raw/master/5.1/iptool.lua"
URL_LUA_IDN="https://raw.githubusercontent.com/haste/lua-idn/master/idn.lua" URL_LUA_IDN="https://github.com/haste/lua-idn/raw/master/idn.lua"
### Local files ### 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 include $(TOPDIR)/rules.mk
PKG_VERSION:=0.9.0 PKG_VERSION:=0.9.0
PKG_RELEASE:=6 PKG_RELEASE:=7
LUCI_TITLE:=LuCI support for ruantiblock LUCI_TITLE:=LuCI support for ruantiblock
LUCI_DEPENDS:=+ruantiblock +luci-mod-admin-full LUCI_DEPENDS:=+ruantiblock
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
include ../../luci.mk #include ../../luci.mk
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature # call BuildPackage - OpenWrt buildroot signature
@@ -633,8 +633,8 @@ return L.Class.extend({
]); ]);
}, },
handleSaveApply: null,
handleSave : null, handleSave : null,
handleSaveApply: null,
handleReset : null, handleReset : null,
}), }),
}) })
@@ -3,73 +3,72 @@
'require ui'; 'require ui';
'require view.ruantiblock.tools as tools'; 'require view.ruantiblock.tools as tools';
let crontab_regexp = new RegExp(`^(\\*?\\/?(\\d){0,2}\\s){5}${tools.exec_path} update(\n)?`, 'gm'); return L.view.extend({
let current_crontab_content; 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"); return String(n).replace(/^(\d)$/, "0$1");
}; },
function cron_status_string(s) { cronStatusString: function(s) {
return s || _('No Shedule'); return s || _('No Shedule');
} },
function pick_cron_task(content) { pickCronTask: function(content) {
if(!content){ if(!content){
return; return;
}; };
let current_tasks = content.match(crontab_regexp) || []; let current_tasks = content.match(this.crontabRegexp) || [];
return current_tasks.join(''); return current_tasks.join('');
}; },
function set_cron_status(value) { setCronStatus: function(value) {
document.getElementById('cron_status').value = cron_status_string(value); document.getElementById('cron_status').value = this.cronStatusString(value);
document.getElementById("btn_cron_del").style.visibility = (value) ? 'visible' : 'hidden'; 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_add = document.getElementById('btn_cron_add');
let btn_cron_del = document.getElementById('btn_cron_del'); 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.'))); ui.addNotification(null, E('p', _('No changes to save.')));
btn_cron_add.disabled = false; btn_cron_add.disabled = false;
return; 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'); 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(() => { }).then(() => {
return fs.exec('/etc/init.d/cron', [ 'enabled' ]).then(res => { return tools.getInitStatus('cron').then(res => {
if(res.code !== 0) { if(!res) {
return fs.exec('/etc/init.d/cron', [ 'enable' ]); 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(() => { }).finally(() => {
return fs.exec('/etc/init.d/cron', [ 'restart' ]).catch(e => { return tools.handleServiceAction('cron', 'restart');
ui.addNotification(null, E('p', _('Unable to execute or read contents')
+ ': %s<br />[ %s ]'.format(e.message, '/etc/init.d/cron')));
});
}).catch(e => { }).catch(e => {
ui.addNotification(null, E('p', _('Unable to save the changes') ui.addNotification(null, E('p', _('Unable to save the changes')
+ ': %s<br />[ %s ]'.format( + ': %s [ %s ]'.format(
e.message, tools.crontab_file e.message, tools.crontabFile
))); )));
}); });
} },
function del_cron_schedule(ev) { delCronSchedule: function(ev) {
if(current_crontab_content) { if(this.currentCrontabContent) {
current_crontab_content = current_crontab_content.replace(crontab_regexp, ""); 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 hour_interval = document.getElementById('cron_hour_interval').value;
let day_interval = document.getElementById('cron_day_interval').value; let day_interval = document.getElementById('cron_day_interval').value;
let hour = document.getElementById('cron_hour').value; let hour = document.getElementById('cron_hour').value;
@@ -78,15 +77,16 @@ function set_cron_schedule(ev) {
min, min,
(!hour_interval) ? hour : (hour_interval == "1") ? '*' : '*/' + hour_interval, (!hour_interval) ? hour : (hour_interval == "1") ? '*' : '*/' + hour_interval,
(hour_interval || day_interval == "1") ? '*' : '*/' + day_interval, (hour_interval || day_interval == "1") ? '*' : '*/' + day_interval,
tools.exec_path tools.execPath
); );
if(current_crontab_content) { if(this.currentCrontabContent) {
current_crontab_content = current_crontab_content.replace(crontab_regexp, "") + task_string; 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 value = e.target.value;
let bool = (value != ''); let bool = (value != '');
let cron_hour = document.getElementById('cron_hour'); let cron_hour = document.getElementById('cron_hour');
@@ -94,6 +94,7 @@ function onchange_hour_interval(e) {
cron_hour.disabled = bool; cron_hour.disabled = bool;
cron_day_interval.disabled = bool; cron_day_interval.disabled = bool;
// For luci-theme-material
if(bool) { if(bool) {
cron_hour.style.opacity = '50%'; cron_hour.style.opacity = '50%';
cron_day_interval.style.opacity = '50%'; cron_day_interval.style.opacity = '50%';
@@ -101,21 +102,20 @@ function onchange_hour_interval(e) {
cron_hour.style.opacity = '100%'; cron_hour.style.opacity = '100%';
cron_day_interval.style.opacity = '100%'; cron_day_interval.style.opacity = '100%';
}; };
} },
return L.view.extend({
load: function() { 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') ui.addNotification(null, E('p', _('Unable to read the contents')
+ ': %s<br />[ %s ]'.format( + ': %s [ %s ]'.format(
e.message, tools.crontab_file e.message, tools.crontabFile
))); )));
}); });
}, },
render: function(content) { render: function(content) {
current_crontab_content = content; this.currentCrontabContent = content;
let current_task = pick_cron_task(content); let current_task = this.pickCronTask(content);
let cron_status = E('textarea', { let cron_status = E('textarea', {
'id': 'cron_status', 'id': 'cron_status',
@@ -124,25 +124,27 @@ return L.view.extend({
'readonly': 'readonly', 'readonly': 'readonly',
'wrap': 'off', 'wrap': 'off',
'rows': 2, 'rows': 2,
}, cron_status_string(current_task)); }, this.cronStatusString(current_task));
let btn_cron_del = E('button', { let btn_cron_del = E('button', {
'class': 'cbi-button btn cbi-button-reset', 'class': 'cbi-button btn cbi-button-reset',
'id': 'btn_cron_del', 'id': 'btn_cron_del',
'name': 'btn_cron_del', 'name': 'btn_cron_del',
}, _('Reset')); }, _('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'; btn_cron_del.style.visibility = (current_task) ? 'visible' : 'hidden';
let status_header = E('div', { 'class': 'cbi-section-node' }, [ let status_header = E('div', { 'class': 'cbi-section-node' }, [
E('div', { 'class': 'cbi-value' }, [ E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': 'cron_status' }, E('label', { 'class': 'cbi-value-title', 'for': 'cron_status' },
_('Current schedule')), _('Current schedule')),
E('div', { 'class': 'cbi-value-field' }, cron_status), E('div', { 'class': 'cbi-value-field' },
cron_status),
]), ]),
E('div', { 'class': 'cbi-value' }, [ E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': 'btn_cron_del' }), 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('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': elem.id || null }, E('label', { 'class': 'cbi-value-title', 'for': elem.id || null },
title), 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;') E('option', { 'value': '1' }, '&#8727;')
]); ]);
for(let i = 2; i <= 12 ; i += 2) { 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')); 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', let cron_day_interval = E('select',
{ 'id': 'cron_day_interval', 'style': 'width:60px !important; min-width:60px !important' }, { 'id': 'cron_day_interval', 'style': 'width:60px !important; min-width:60px !important' },
E('option', { 'value': '1' }, '&#8727;') E('option', { 'value': '1' }, '&#8727;')
); );
for(let i = 2; i < 8 ; i++) { 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': '14' }, '&#8727;/14'));
cron_day_interval.append(E('option', { 'value': '28' }, '&#8727;/28')); cron_day_interval.append(E('option', { 'value': '28' }, '&#8727;/28'));
@@ -188,14 +195,18 @@ return L.view.extend({
let cron_hour = E('select', let cron_hour = E('select',
{ 'id': 'cron_hour', 'style': 'width:60px !important; min-width:60px !important' }); { 'id': 'cron_hour', 'style': 'width:60px !important; min-width:60px !important' });
for(let i = 0; i < 24 ; i++) { 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')); layout_append(cron_hour, _('Hour'));
let cron_min = E('select', let cron_min = E('select',
{ 'id': 'cron_min', 'style': 'width:60px !important; min-width:60px !important' }); { 'id': 'cron_min', 'style': 'width:60px !important; min-width:60px !important' });
for(let i = 0; i < 60 ; i++) { 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')); layout_append(cron_min, _('Minute'));
@@ -204,12 +215,14 @@ return L.view.extend({
'id': 'btn_cron_add', 'id': 'btn_cron_add',
'name': 'btn_cron_add' 'name': 'btn_cron_add'
}, _('Set')); }, _('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); layout_append(btn_cron_add);
return E([ return E([
E('h2', 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-descr fade-in' }),
E('div', { 'class': 'cbi-section fade-in' }, status_header), E('div', { 'class': 'cbi-section fade-in' }, status_header),
E('div', { 'class': 'cbi-section fade-in' }, layout), E('div', { 'class': 'cbi-section fade-in' }, layout),
@@ -4,10 +4,10 @@
'require view.ruantiblock.tools as tools'; 'require view.ruantiblock.tools as tools';
return L.view.extend({ return L.view.extend({
poll_info: function() { infoPoll: 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') 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(); L.Poll.stop();
}).then(data => { }).then(data => {
@@ -79,9 +79,9 @@ return L.view.extend({
}, },
load: function() { 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') 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': 'tr' }, [
E('div', { 'class': 'td left', 'style': 'min-width:33%' }, E('div', { 'class': 'td left', 'style': 'min-width:33%' },
_('Last blacklist update') + ':'), _('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), data.last_blacklist_update.date),
]), ]),
E('div', { 'class': 'tr' }, [ E('div', { 'class': 'tr' }, [
E('div', { 'class': 'td left' }, 'IP:'), 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), data.last_blacklist_update.ip),
]), ]),
E('div', { 'class': 'tr' }, [ E('div', { 'class': 'tr' }, [
E('div', { 'class': 'td left' }, 'CIDR:'), 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), data.last_blacklist_update.cidr),
]), ]),
E('div', { 'class': 'tr' }, [ E('div', { 'class': 'tr' }, [
E('div', { 'class': 'td left' }, 'FQDN:'), 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), data.last_blacklist_update.fqdn),
]) ])
); );
@@ -147,7 +151,6 @@ return L.view.extend({
for(let [k, v] of Object.entries(data.iptables)) { for(let [k, v] of Object.entries(data.iptables)) {
if(k === '_dummy') continue; if(k === '_dummy') continue;
table_iptables.append( table_iptables.append(
E('div', { 'class': 'tr' }, [ E('div', { 'class': 'tr' }, [
E('div', { E('div', {
@@ -172,9 +175,12 @@ return L.view.extend({
if(data.ipset) { if(data.ipset) {
let table_ipset = E('div', { 'class': 'table' }, let table_ipset = E('div', { 'class': 'table' },
E('div', { 'class': 'tr table-titles' }, [ E('div', { 'class': 'tr table-titles' }, [
E('div', { 'class': 'th left', 'style': 'min-width:33%' }, _('Name')), E('div', { 'class': 'th left', 'style': 'min-width:33%' },
E('div', { 'class': 'th left' }, _('Size in memory')), _('Name')),
E('div', { 'class': 'th left' }, _('Number of entries')), E('div', { 'class': 'th left' },
_('Size in memory')),
E('div', { 'class': 'th left' },
_('Number of entries')),
]) ])
); );
@@ -207,13 +213,15 @@ return L.view.extend({
]); ]);
}; };
L.Poll.add(this.poll_info); L.Poll.add(this.infoPoll);
} else { } else {
update_status = E('em', {}, _('Status') + ' : ' + _('disabled')); update_status = E('em', {}, _('Status') + ' : ' + _('disabled'));
}; };
}; };
return E([ 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-descr fade-in' }),
E('div', { 'class': 'cbi-section fade-in' }, E('div', { 'class': 'cbi-section fade-in' },
E('div', { 'class': 'cbi-section-node' }, update_status) E('div', { 'class': 'cbi-section-node' }, update_status)
@@ -8,7 +8,7 @@ return abc.view.extend({
title: _('Ruantiblock') + ' - ' + _('Log'), 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}/),
@@ -53,8 +53,9 @@ return abc.view.extend({
let logger = (stat[0]) ? stat[0].path : (stat[1]) ? stat[1].path : null; let logger = (stat[0]) ? stat[0].path : (stat[1]) ? stat[1].path : null;
if(logger) { if(logger) {
return fs.exec_direct(logger, [ '-e', tools.app_name ]).catch(err => { return fs.exec_direct(logger, [ '-e', tools.appName ]).catch(err => {
ui.addNotification(null, E('p', {}, _('Unable to load log data:') + ' ' + err.message)); ui.addNotification(
null, E('p', {}, _('Unable to load log data:') + ' ' + err.message));
return ''; return '';
}); });
}; };
@@ -6,12 +6,14 @@
const btn_style_neutral = 'btn' const btn_style_neutral = 'btn'
const btn_style_action = 'btn cbi-button-action'; const btn_style_action = 'btn cbi-button-action';
const btn_style_save = 'btn cbi-button-save important'; const btn_style_positive = 'btn cbi-button-save important';
const btn_style_reset = 'btn cbi-button-reset important'; const btn_style_negative = 'btn cbi-button-reset important';
const btn_style_warning = 'btn cbi-button-negative 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_start = elems[1] || document.getElementById("btn_start");
let btn_destroy = elems[5] || document.getElementById("btn_destroy"); let btn_destroy = elems[5] || document.getElementById("btn_destroy");
let btn_enable = elems[2] || document.getElementById("btn_enable"); let btn_enable = elems[2] || document.getElementById("btn_enable");
@@ -27,41 +29,40 @@ function disable_buttons(bool, btn, elems=[]) {
btn_enable.disabled = bool; btn_enable.disabled = bool;
}; };
if(btn_tp) { if(btn_tp) {
btn_tp.disabled = bool btn_tp.disabled = bool;
}; };
} },
function get_app_status() { getAppStatus: function() {
return Promise.all([ return Promise.all([
fs.exec(tools.exec_path, [ 'raw-status' ]), fs.exec(tools.execPath, [ 'raw-status' ]),
fs.exec(tools.exec_path, [ 'total-proxy-status' ]), fs.exec(tools.execPath, [ 'total-proxy-status' ]),
fs.exec(tools.exec_path, [ 'vpn-route-status' ]), fs.exec(tools.execPath, [ 'vpn-route-status' ]),
fs.exec(tools.init_path, [ 'enabled' ]), tools.getInitStatus(tools.appName),
L.resolveDefault(fs.read(tools.token_file), 0), L.resolveDefault(fs.read(tools.tokenFile), 0),
uci.load(tools.app_name), uci.load(tools.appName),
]).catch(e => { ]).catch(e => {
ui.addNotification(null, E('p', _('Unable to execute or read contents') ui.addNotification(null, E('p', _('Unable to execute or read contents')
+ ': %s<br />[ %s | %s | %s ]'.format( + ': %s [ %s | %s | %s ]'.format(
e.message, tools.exec_path, tools.init_path, 'uci.ruantiblock' e.message, tools.execPath, 'tools.getInitStatus', 'uci.ruantiblock'
))); )));
}); });
} },
function set_app_status(status_array, elems=[], force_app_code) { setAppStatus: function(status_array, elems=[], force_app_code) {
let section = uci.get(tools.app_name, 'config'); let section = uci.get(tools.appName, 'config');
if(!status_array || typeof(section) !== 'object') { 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') ui.addNotification(null, E('p', _('Unable to read the contents')
+ ': set_app_status()')); + ': setAppStatus()'));
disable_buttons(true, null, elems); this.disableButtons(true, null, elems);
return; return;
}; };
let app_status_code = (force_app_code) ? force_app_code : status_array[0].code; let app_status_code = (force_app_code) ? force_app_code : status_array[0].code;
let tp_status_code = status_array[1].code; let tp_status_code = status_array[1].code;
let vpn_route_status_code = status_array[2].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_local_clients = section.proxy_local_clients;
let proxy_mode = section.proxy_mode; let proxy_mode = section.proxy_mode;
let bllist_mode = section.bllist_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 bllist_source = section.bllist_source;
let btn_enable = elems[2] || document.getElementById('btn_enable'); let btn_enable = elems[2] || document.getElementById('btn_enable');
if(enabled_flag == 0) { if(enabled_flag == true) {
btn_enable.onclick = ui.createHandlerFn(this, button_action, 'disable'); btn_enable.onclick = ui.createHandlerFn(
btn_enable.textContent = _('Disable'); this, this.serviceAction, 'disable', 'btn_enable');
btn_enable.className = btn_style_reset; btn_enable.textContent = _('Enabled');
btn_enable.className = btn_style_positive;
} else { } else {
btn_enable.onclick = ui.createHandlerFn(this, button_action, 'enable'); btn_enable.onclick = ui.createHandlerFn(
btn_enable.textContent = _('Enable'); this, this.serviceAction, 'enable', 'btn_enable');
btn_enable.className = btn_style_save; btn_enable.textContent = _('Disabled');
btn_enable.className = btn_style_negative;
}; };
let btn_tp = elems[3] || document.getElementById('btn_tp'); let btn_tp = elems[3] || document.getElementById('btn_tp');
if(btn_tp) { if(btn_tp) {
if(tp_status_code == 0) { if(tp_status_code == 0) {
btn_tp.onclick = ui.createHandlerFn(this, button_action, 'total-proxy-off'); btn_tp.onclick = ui.createHandlerFn(
btn_tp.textContent = _('Disable'); this, this.appAction, 'total-proxy-off', 'btn_tp');
btn_tp.className = btn_style_reset; btn_tp.textContent = _('Enabled');
btn_tp.className = btn_style_positive;
} else { } else {
btn_tp.onclick = ui.createHandlerFn(this, button_action, 'total-proxy-on'); btn_tp.onclick = ui.createHandlerFn(
btn_tp.textContent = _('Enable'); this, this.appAction, 'total-proxy-on', 'btn_tp');
btn_tp.className = btn_style_save; btn_tp.textContent = _('Disabled');
btn_tp.className = btn_style_negative;
}; };
}; };
let btn_start = elems[1] || document.getElementById("btn_start"); let btn_start = elems[1] || document.getElementById('btn_start');
let btn_update = elems[4] || document.getElementById("btn_update"); let btn_update = elems[4] || document.getElementById('btn_update');
let btn_destroy = elems[5] || document.getElementById("btn_destroy"); let btn_destroy = elems[5] || document.getElementById('btn_destroy');
function btn_start_state_on() { let btnStartStateOn = () => {
btn_start.onclick = ui.createHandlerFn(this, button_action, 'stop'); btn_start.onclick = ui.createHandlerFn(
btn_start.textContent = _('Disable'); this, this.serviceAction, 'stop', 'btn_start');
btn_start.className = btn_style_reset; btn_start.textContent = _('Enabled');
btn_start.className = btn_style_positive;
} }
function btn_start_state_off() { let btnStartStateOff = () => {
btn_start.onclick = ui.createHandlerFn(this, button_action, 'start'); btn_start.onclick = ui.createHandlerFn(
btn_start.textContent = _('Enable'); this, this.serviceAction,'start', 'btn_start');
btn_start.className = btn_style_action; btn_start.textContent = _('Disabled');
btn_start.className = btn_style_negative;
} }
if(app_status_code == 0) { if(app_status_code == 0) {
disable_buttons(false, null, elems); this.disableButtons(false, null, elems);
btn_start_state_on(); btnStartStateOn();
btn_destroy.disabled = false; btn_destroy.disabled = false;
btn_update.disabled = false; btn_update.disabled = false;
if(btn_tp) { if(btn_tp) {
@@ -118,28 +125,28 @@ function set_app_status(status_array, elems=[], force_app_code) {
}; };
} }
else if(app_status_code == 2) { else if(app_status_code == 2) {
disable_buttons(false, null, elems); this.disableButtons(false, null, elems);
btn_start_state_off(); btnStartStateOff();
btn_update.disabled = true; btn_update.disabled = true;
if(btn_tp) { if(btn_tp) {
btn_tp.disabled = true; btn_tp.disabled = true;
}; };
} }
else if(app_status_code == 3) { else if(app_status_code == 3) {
btn_start_state_off(); btnStartStateOff();
disable_buttons(true, btn_start, elems); this.disableButtons(true, btn_start, elems);
} }
else if(app_status_code == 4) { else if(app_status_code == 4) {
btn_start_state_on(); btnStartStateOn();
disable_buttons(true, btn_update, elems); this.disableButtons(true, btn_update, elems);
} }
else { else {
ui.addNotification(null, E('p', _('Error') ui.addNotification(null, E('p', _('Error')
+ ' %s: return code = %s'.format(tools.exec_path, app_status_code))); + ' %s: return code = %s'.format(tools.execPath, app_status_code)));
disable_buttons(true, null, elems); 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, app_status_code,
proxy_mode, proxy_mode,
bllist_mode, bllist_mode,
@@ -151,66 +158,64 @@ function set_app_status(status_array, elems=[], force_app_code) {
if(!L.Poll.active()) { if(!L.Poll.active()) {
L.Poll.start(); L.Poll.start();
}; };
} },
function button_action(action) { serviceAction: function(action, button) {
let btn, if(button) {
cmd = tools.exec_path; let elem = document.getElementById(button);
this.disableButtons(true, elem);
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);
});
}; };
return fs.exec_direct(cmd, [ action ]).then(res => { L.Poll.stop();
return get_app_status().then(
(status_array) => {
set_app_status(status_array);
ui.hideModal();
});
});
}
return L.view.extend({ return tools.handleServiceAction(tools.appName, action).then(() => {
poll_status: function() { return this.getAppStatus().then(
return fs.read(tools.token_file).then(v => { (status_array) => {
v = tools.normalize_value(v); this.setAppStatus(status_array);
if(v != status_token_value) {
get_app_status().then(set_app_status);
} }
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(); ev.target.blur();
let cancel_button = E('button', { let cancel_button = E('button', {
'class': btn_style_neutral, 'class': btn_style_neutral,
@@ -220,9 +225,9 @@ return L.view.extend({
let shutdown_btn = E('button', { let shutdown_btn = E('button', {
'class': btn_style_warning, 'class': btn_style_warning,
}, _('Shutdown')); }, _('Shutdown'));
shutdown_btn.onclick = ui.createHandlerFn(this, function() { shutdown_btn.onclick = ui.createHandlerFn(this, () => {
cancel_button.disabled = true; cancel_button.disabled = true;
return button_action('destroy'); return this.appAction('destroy');
}); });
ui.showModal(_('Shutdown'), [ ui.showModal(_('Shutdown'), [
@@ -238,7 +243,7 @@ return L.view.extend({
}, },
load: function() { load: function() {
return get_app_status(); return this.getAppStatus();
}, },
render: function(status_array) { render: function(status_array) {
@@ -246,9 +251,11 @@ return L.view.extend({
return; return;
}; };
let section = uci.get(tools.app_name, 'config'); let section = uci.get(tools.appName, 'config');
let proxy_local_clients = (typeof(section) === 'object') ? section.proxy_local_clients : null; let proxy_local_clients = (typeof(section) === 'object') ?
status_token_value = (Array.isArray(status_array)) ? tools.normalize_value(status_array[4]) : null; section.proxy_local_clients : null;
this.statusTokenValue = (Array.isArray(status_array)) ?
tools.normalizeValue(status_array[4]) : null;
let status_string = E('div', { let status_string = E('div', {
'id' : 'status', 'id' : 'status',
@@ -278,14 +285,14 @@ return L.view.extend({
let btn_enable = E('button', { let btn_enable = E('button', {
'id' : 'btn_enable', 'id' : 'btn_enable',
'name' : 'btn_enable', 'name' : 'btn_enable',
'class': btn_style_save, 'class': btn_style_positive,
}, _('Enable')); }, _('Enable'));
layout_append(btn_enable, _('Run at startup')); layout_append(btn_enable, _('Run at startup'));
let btn_tp = E('button', { let btn_tp = E('button', {
'id' : 'btn_tp', 'id' : 'btn_tp',
'name' : 'btn_tp', 'name' : 'btn_tp',
'class': btn_style_save, 'class': btn_style_positive,
}, _('Enable')); }, _('Enable'));
if(proxy_local_clients == '0') { if(proxy_local_clients == '0') {
layout_append(btn_tp, _('Total-proxy'), layout_append(btn_tp, _('Total-proxy'),
@@ -297,20 +304,20 @@ return L.view.extend({
'name' : 'btn_update', 'name' : 'btn_update',
'class': btn_style_action, 'class': btn_style_action,
}, _('Update')); }, _('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')); layout_append(btn_update, _('Update blacklist'));
let btn_destroy = E('button', { let btn_destroy = E('button', {
'id' : 'btn_destroy', 'id' : 'btn_destroy',
'name' : 'btn_destroy', 'name' : 'btn_destroy',
'class': btn_style_reset, 'class': btn_style_negative,
}, _('Shutdown')); }, _('Shutdown'));
btn_destroy.onclick = this.dialog_destroy; btn_destroy.onclick = L.bind(this.dialogDestroy, this);
layout_append(btn_destroy, _('Shutdown'), layout_append(btn_destroy, _('Shutdown'),
_('Complete service shutdown, as well as deleting ipsets and blacklist data')); _('Complete service shutdown, as well as deleting ipsets and blacklist data'));
set_app_status(status_array, [ this.setAppStatus(status_array, [
status_string, status_string,
btn_start, btn_start,
btn_enable, btn_enable,
@@ -319,7 +326,7 @@ return L.view.extend({
btn_destroy, btn_destroy,
]); ]);
L.Poll.add(this.poll_status); L.Poll.add(L.bind(this.statusPoll, this));
return E([ return E([
E('h2', { 'class': 'fade-in' }, _('Ruantiblock')), E('h2', { 'class': 'fade-in' }, _('Ruantiblock')),
@@ -6,26 +6,29 @@
'require tools.widgets as widgets'; 'require tools.widgets as widgets';
'require view.ruantiblock.tools as tools'; '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) { if(empty && array.length === 0) {
elem.depends(key, '_dummy'); elem.depends(key, '_dummy');
} else { } else {
array.forEach(e => elem.depends(key, e)); array.forEach(e => elem.depends(key, e));
}; };
}; },
function depends_bllist_module(elem) { dependsBllistModule: function(elem) {
depends(elem, 'bllist_module', available_parsers); 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:') 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`; + ` ${_('One of the following:')}\n - ${_('valid IP address')}\n - ${_('valid address#port')}\n`;
}; },
let CBIBlockTitle = form.DummyValue.extend({ CBIBlockTitle: form.DummyValue.extend({
string: null, string: null,
renderWidget: function(section_id, option_index, cfgvalue) { 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() { load: function() {
return Promise.all([ return Promise.all([
L.resolveDefault(fs.exec(tools.exec_path, [ 'raw-status' ]), 1), L.resolveDefault(fs.exec(tools.execPath, [ 'raw-status' ]), 1),
fs.list(tools.parsers_dir), fs.list(tools.parsersDir),
uci.load('network'), uci.load('network'),
]).catch(e => { ]).catch(e => {
ui.addNotification(null, E('p', _('Unable to read the contents') ui.addNotification(null, E('p', _('Unable to read the contents')
+ ': %s<br />[ %s ]'.format( + ': %s [ %s ]'.format(
e.message, tools.parsers_dir e.message, tools.parsersDir
))); )));
}); });
}, },
@@ -93,7 +59,7 @@ return L.view.extend({
if(!data) { if(!data) {
return; return;
}; };
this.app_status_code = data[0].code; this.appStatusCode = data[0].code;
let p_dir_arr = data[1]; let p_dir_arr = data[1];
let lan_iface = uci.get('network', 'lan', 'ifname') || 'eth0'; let lan_iface = uci.get('network', 'lan', 'ifname') || 'eth0';
let vpn_iface = uci.get('network', 'VPN', 'ifname') || 'tun0'; let vpn_iface = uci.get('network', 'VPN', 'ifname') || 'tun0';
@@ -102,14 +68,48 @@ return L.view.extend({
p_dir_arr.forEach(e => { p_dir_arr.forEach(e => {
let fname = e.name; let fname = e.name;
if(fname.startsWith('ruab_parser')) { 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; 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 = m.section(form.NamedSection, 'config');
s.anonymous = true; s.anonymous = true;
@@ -120,7 +120,7 @@ return L.view.extend({
s.tab('main_settings', _('Main settings')); s.tab('main_settings', _('Main settings'));
// PROXY_MODE // 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', o = s.taboption('main_settings', form.ListValue, 'proxy_mode',
_('Proxy mode')); _('Proxy mode'));
o.value('1', 'Tor'); 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', let proxy_local_clients = s.taboption('main_settings', form.Flag, 'proxy_local_clients',
_("Apply proxy rules to router application traffic")); _("Apply proxy rules to router application traffic"));
proxy_local_clients.rmempty = false; proxy_local_clients.rmempty = false;
proxy_local_clients.default = proxy_local_clients.enabled;
// USE_LOGGER // USE_LOGGER
o = s.taboption('main_settings', form.Flag, 'use_logger', o = s.taboption('main_settings', form.Flag, 'use_logger',
_('Logging events')); _('Logging events'));
o.rmempty = false; o.rmempty = false;
o.default = 1;
// DEF_TOTAL_PROXY // DEF_TOTAL_PROXY
o = s.taboption('main_settings', form.Flag, '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')); _('Clean up ipsets before updating blacklist'));
o.description = _('Reduces RAM consumption during update'); o.description = _('Reduces RAM consumption during update');
o.rmempty = false; 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 */ /* Tor tab */
s.tab('tor_settings', _('Tor mode')); s.tab('tor_settings', _('Tor mode'));
@@ -172,14 +169,12 @@ return L.view.extend({
_('Transparent proxy port for iptables rules')); _('Transparent proxy port for iptables rules'));
o.rmempty = false; o.rmempty = false;
o.datatype = "port"; o.datatype = "port";
o.default = '9040';
// ONION_DNS_ADDR // ONION_DNS_ADDR
o = s.taboption('tor_settings', form.Value, 'onion_dns_addr', o = s.taboption('tor_settings', form.Value, 'onion_dns_addr',
_("Optional DNS resolver for '.onion' zone"), '<code>ipaddress#port</code>'); _("Optional DNS resolver for '.onion' zone"), '<code>ipaddress#port</code>');
o.rmempty = false; o.rmempty = false;
o.default = '127.0.0.1#9053'; o.validate = this.validateIpPort;
o.validate = validate_ip_port;
// Torrc edit dialog // Torrc edit dialog
o = s.taboption('tor_settings', form.Button, '_torrc_btn', o = s.taboption('tor_settings', form.Button, '_torrc_btn',
@@ -210,126 +205,97 @@ return L.view.extend({
// BLLIST_MODULE // BLLIST_MODULE
let bllist_module = s.taboption('parser_settings', form.ListValue, let bllist_module = s.taboption('parser_settings', form.ListValue,
'bllist_module', _('Blacklist module')); 'bllist_module', _('Blacklist module'));
bllist_module.value("", _("user entries only")); bllist_module.value('', _('none (user entries only)'));
available_parsers.forEach(e => bllist_module.value(e)); Object.entries(this.availableParsers).forEach(
e => bllist_module.value(e[1], e[0]));
// BLLIST_MODE // BLLIST_MODE
let bllist_mode = s.taboption('parser_settings', form.ListValue, let bllist_mode = s.taboption('parser_settings', form.ListValue,
'bllist_mode', _('Module operation mode')); 'bllist_mode', _('Module operation mode'));
bllist_mode.value('ip'); bllist_mode.value('ip');
bllist_mode.value('fqdn'); bllist_mode.value('fqdn');
depends_bllist_module(bllist_mode);
// BLLIST_SOURCE // BLLIST_SOURCE
let bllist_source = s.taboption('parser_settings', form.ListValue, let bllist_source = s.taboption('parser_settings', form.ListValue,
'bllist_source', _('Blacklist source')); 'bllist_source', _('Blacklist source'));
bllist_source.description = _("Options") + ':'; 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.value(k);
bllist_source.description += `<br />${k} - <a href="${v}" target="_blank">${v}</a>`; 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') + ':'; o.string = _('IP configuration') + ':';
depends_bllist_module(o);
// IP_LIMIT // IP_LIMIT
o = s.taboption('parser_settings', form.Value, 'ip_limit', _("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.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.datatype = 'uinteger';
o.default = '0';
depends_bllist_module(o);
// OPT_EXCLUDE_NETS // OPT_EXCLUDE_NETS
o = s.taboption('parser_settings', form.DynamicList, '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.title = _('IP subnet patterns (/24) that are excluded from optimization');
o.description = _('e.g:') + ' <code>192.168.1.</code>'; o.description = _('e.g:') + ' <code>192.168.1.</code>';
o.placeholder = _('e.g:') + ' 192.168.1.'; o.placeholder = _('e.g:') + ' 192.168.1.';
o.default = ''; o.validate = (section, value) => {
return (/^$|^([0-9]{1,3}[.]){3}$/.test(value)) ? true : _('Expecting:')
depends_bllist_module(o); + ' ' + _('net pattern') + ' (' + _('e.g:') + ' 192.168.3.)\n';
};
// SUMMARIZE_IP // SUMMARIZE_IP
o = s.taboption('parser_settings', form.Flag, 'summarize_ip', o = s.taboption('parser_settings', form.Flag, 'summarize_ip',
_("Summarize IP ranges")); _("Summarize IP ranges"));
o.rmempty = false; o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// SUMMARIZE_CIDR // SUMMARIZE_CIDR
o = s.taboption('parser_settings', form.Flag, 'summarize_cidr', o = s.taboption('parser_settings', form.Flag, 'summarize_cidr',
_("Summarize '/24' networks")); _("Summarize '/24' networks"));
o.rmempty = false; 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') + ':'; o.string = _('FQDN configuration') + ':';
depends_bllist_module(o);
// SD_LIMIT // SD_LIMIT
o = s.taboption('parser_settings', form.Value, 'sd_limit', o = s.taboption('parser_settings', form.Value, 'sd_limit',
_("Subdomains 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.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.datatype = 'uinteger';
o.default = '16';
depends_bllist_module(o);
// OPT_EXCLUDE_SLD // OPT_EXCLUDE_SLD
o = s.taboption('parser_settings', form.DynamicList, 'opt_exclude_sld', o = s.taboption('parser_settings', form.DynamicList, 'opt_exclude_sld',
_('2nd level domains that are excluded from optimization')); _('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.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 // USE_IDN
o = s.taboption('parser_settings', form.Flag, 'use_idn', o = s.taboption('parser_settings', form.Flag, 'use_idn',
_("Convert cyrillic domains to punycode")); _("Convert cyrillic domains to punycode"));
o.rmempty = false; o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// ALT_NSLOOKUP // ALT_NSLOOKUP
o = s.taboption('parser_settings', form.Flag, 'alt_nslookup', o = s.taboption('parser_settings', form.Flag, 'alt_nslookup',
_('Use optional DNS resolver')); _('Use optional DNS resolver'));
o.rmempty = false; o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// ALT_DNS_ADDR // ALT_DNS_ADDR
o = s.taboption('parser_settings', form.Value, 'alt_dns_addr', o = s.taboption('parser_settings', form.Value, 'alt_dns_addr',
_("Optional DNS resolver"), '<code>ipaddress[#port]</code>'); _("Optional DNS resolver"), '<code>ipaddress[#port]</code>');
o.rmempty = false; o.rmempty = false;
o.depends('alt_nslookup', '1'); o.validate = this.validateIpPort;
o.validate = validate_ip_port;
o.default = '8.8.8.8';
/* Entries filters tab */ /* Blacklist entry filters tab */
s.tab('entries_filter_tab', _('Entries filters')); s.tab('entries_filter_tab', _('Blacklist entry filters'));
// IP_FILTER // IP_FILTER
o = s.taboption('entries_filter_tab', form.Flag, 'ip_filter', o = s.taboption('entries_filter_tab', form.Flag, 'ip_filter',
_("Enable IP filter")); _("Enable IP filter"));
o.description = _('Exclude IP addresses from blacklist by IP filter patterns'); o.description = _('Exclude IP addresses from blacklist by IP filter patterns');
o.rmempty = false; o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// IP_FILTER edit dialog // IP_FILTER edit dialog
o = s.taboption('entries_filter_tab', form.Button, '_ip_filter_btn', 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.onclick = () => ip_filter_edit.show();
o.inputtitle = _('Edit'); o.inputtitle = _('Edit');
o.inputstyle = 'edit btn'; o.inputstyle = 'edit btn';
depends_bllist_module(o);
// FQDN_FILTER // FQDN_FILTER
o = s.taboption('entries_filter_tab', form.Flag, 'fqdn_filter', o = s.taboption('entries_filter_tab', form.Flag, 'fqdn_filter',
_("Enable FQDN filter")); _("Enable FQDN filter"));
o.description = _('Exclude domains from blacklist by FQDN filter patterns'); o.description = _('Exclude domains from blacklist by FQDN filter patterns');
o.rmempty = false; o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// FQDN_FILTER edit dialog // FQDN_FILTER edit dialog
o = s.taboption('entries_filter_tab', form.Button, '_fqdn_filter_btn', 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.onclick = () => fqdn_filter_edit.show();
o.inputtitle = _('Edit'); o.inputtitle = _('Edit');
o.inputstyle = 'edit btn'; o.inputstyle = 'edit btn';
depends_bllist_module(o);
/* User entries tab */ /* User entries tab */
@@ -365,12 +327,12 @@ return L.view.extend({
_('Enable'), _("Add user entries to the blacklist when updating")); _('Enable'), _("Add user entries to the blacklist when updating"));
o.rmempty = false; o.rmempty = false;
o.default = 0; o.default = 0;
depends_bllist_module(o); this.dependsBllistModule(o);
// USER_ENTRIES_DNS // USER_ENTRIES_DNS
o = s.taboption('user_entries_tab', form.Value, '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>'); _("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 // USER_ENTRIES edit dialog
o = s.taboption('user_entries_tab', form.Button, '_user_entries_btn', o = s.taboption('user_entries_tab', form.Button, '_user_entries_btn',
@@ -379,10 +341,8 @@ return L.view.extend({
o.inputtitle = _('Edit'); o.inputtitle = _('Edit');
o.inputstyle = 'edit btn'; o.inputstyle = 'edit btn';
let map_promise = m.render(); let map_promise = m.render();
map_promise.then(node => node.classList.add('fade-in')); map_promise.then(node => node.classList.add('fade-in'));
return map_promise; return map_promise;
}, },
@@ -390,8 +350,9 @@ return L.view.extend({
return this.handleSave(ev).then(() => { return this.handleSave(ev).then(() => {
ui.changes.apply(mode == '0'); ui.changes.apply(mode == '0');
if(this.app_status_code != 1 && this.app_status_code != 2) { if(this.appStatusCode != 1 && this.appStatusCode != 2) {
window.setTimeout(() => fs.exec(tools.init_path, [ 'restart' ]), 3000); window.setTimeout(() => tools.handleServiceAction(
tools.appName, 'restart'), 3000);
}; };
}); });
}, },
@@ -1,5 +1,6 @@
'use strict'; 'use strict';
'require fs'; 'require fs';
'require rpc';
'require ui'; 'require ui';
document.head.append(E('style', {'type': 'text/css'}, document.head.append(E('style', {'type': 'text/css'},
@@ -35,33 +36,71 @@ document.head.append(E('style', {'type': 'text/css'},
`)); `));
return L.Class.extend({ return L.Class.extend({
app_name: 'ruantiblock', appName : 'ruantiblock',
exec_path: '/usr/bin/ruantiblock', execPath : '/usr/bin/ruantiblock',
init_path: '/etc/init.d/ruantiblock', tokenFile : '/var/run/ruantiblock.token',
token_file: '/var/run/ruantiblock.token', parsersDir : '/usr/bin',
parsers_dir: '/usr/bin', torrcFile : '/etc/tor/torrc',
torrc_file: '/etc/tor/torrc', userEntriesFile : '/etc/ruantiblock/user_entries',
user_entries_file: '/etc/ruantiblock/user_entries', fqdnFilterFile : '/etc/ruantiblock/fqdn_filter',
fqdn_filter_file: '/etc/ruantiblock/fqdn_filter', ipFilterFile : '/etc/ruantiblock/ip_filter',
ip_filter_file: '/etc/ruantiblock/ip_filter', crontabFile : '/etc/crontabs/root',
crontab_file: '/etc/crontabs/root', infoLabelStarting: '<span class="label-status starting">' + _('Starting') + '</span>',
info_label_starting: '<span class="label-status starting">' + _('Starting') + '</span>', infoLabelRunning : '<span class="label-status running">' + _('Enabled') + '</span>',
info_label_running: '<span class="label-status running">' + _('Enabled') + '</span>', infoLabelUpdating: '<span class="label-status updating">' + _('Updating') + '</span>',
info_label_updating: '<span class="label-status updating">' + _('Updating') + '</span>', infoLabelStopped : '<span class="label-status stopped">' + _('Disabled') + '</span>',
info_label_stopped: '<span class="label-status stopped">' + _('Disabled') + '</span>', infoLabelError : '<span class="label-status error">' + _('Error') + '</span>',
info_label_error: '<span class="label-status error">' + _('Error') + '</span>',
blacklist_sources: { blacklistSources: {
'rublacklist': 'https://rublacklist.net', 'rublacklist': 'https://rublacklist.net',
'zapret-info': 'https://github.com/zapret-info/z-i', '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; return (v && typeof(v) === 'string') ? v.trim().replace(/\r?\n/g, '') : v;
}, },
make_status_string: function( makeStatusString: function(
app_status_code, app_status_code,
proxy_mode, proxy_mode,
bllist_mode, bllist_mode,
@@ -74,21 +113,21 @@ return L.Class.extend({
switch(app_status_code) { switch(app_status_code) {
case 0: case 0:
app_status_label = this.info_label_running; app_status_label = this.infoLabelRunning;
break; break;
case 2: case 2:
app_status_label = this.info_label_stopped; app_status_label = this.infoLabelStopped;
break; break;
case 3: case 3:
app_status_label = this.info_label_starting; app_status_label = this.infoLabelStarting;
spinning = ' spinning'; spinning = ' spinning';
break; break;
case 4: case 4:
app_status_label = this.info_label_updating; app_status_label = this.infoLabelUpdating;
spinning = ' spinning'; spinning = ' spinning';
break; break;
default: default:
app_status_label = this.info_label_error; app_status_label = this.infoLabelError;
return `<div class="table"> return `<div class="table">
<div class="tr"> <div class="tr">
<div class="td left" style="min-width:33%%"> <div class="td left" style="min-width:33%%">
@@ -144,7 +183,7 @@ return L.Class.extend({
${_('Blacklist source')}: ${_('Blacklist source')}:
</div> </div>
<div class="td left"> <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} ${bllist_source}
</span> </span>
</div> </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) { __init__: function(file, title, description, callback, file_exists=false) {
this.file = file; this.file = file;
this.title = title; this.title = title;
+127 -103
View File
@@ -19,6 +19,12 @@ msgstr "Домены 2-го уровня не подлежащие оптими
msgid "Add user entries to the blacklist when updating" msgid "Add user entries to the blacklist when updating"
msgstr "Добавлять записи пользователя в блэклист при обновлении" msgstr "Добавлять записи пользователя в блэклист при обновлении"
msgid "Alert"
msgstr "Тревога"
msgid "All"
msgstr "Все"
msgid "All traffic goes through the proxy without applying rules" msgid "All traffic goes through the proxy without applying rules"
msgstr "Весь трафик отправляется в прокси, без применения правил" msgstr "Весь трафик отправляется в прокси, без применения правил"
@@ -28,6 +34,9 @@ msgstr "Применить"
msgid "Apply proxy rules to router application traffic" msgid "Apply proxy rules to router application traffic"
msgstr "Применять правила прокси к трафику приложений роутера" msgstr "Применять правила прокси к трафику приложений роутера"
msgid "Blacklist entry filters"
msgstr "Фильтры записей блэклиста"
msgid "Blacklist module" msgid "Blacklist module"
msgstr "Модуль блэклиста" msgstr "Модуль блэклиста"
@@ -55,10 +64,12 @@ msgstr "Изменения сохранены."
msgid "Clean up ipsets before updating blacklist" msgid "Clean up ipsets before updating blacklist"
msgstr "Очищать ipset'ы перед обновлением блэклиста" msgstr "Очищать ipset'ы перед обновлением блэклиста"
msgid "Command failed"
msgstr "Команда не выполнена"
msgid "" msgid ""
"Complete service shutdown, as well as deleting ipsets and blacklist data" "Complete service shutdown, as well as deleting ipsets and blacklist data"
msgstr "" msgstr "Полное выключение службы, а также удаление ipset'ов и данных блэклиста"
"Полное выключение службы, а также удаление ipset'ов и данных блэклиста"
msgid "Contents have been saved." msgid "Contents have been saved."
msgstr "Содержимое сохранено." msgstr "Содержимое сохранено."
@@ -66,6 +77,9 @@ msgstr "Содержимое сохранено."
msgid "Convert cyrillic domains to punycode" msgid "Convert cyrillic domains to punycode"
msgstr "Конвертировать кириллические домены в punycode" msgstr "Конвертировать кириллические домены в punycode"
msgid "Critical"
msgstr "Критическая ситуация"
msgid "Current schedule" msgid "Current schedule"
msgstr "Текущее расписание" msgstr "Текущее расписание"
@@ -75,18 +89,27 @@ msgstr "DNS сервер для FQDN записей"
msgid "Day" msgid "Day"
msgstr "День" msgstr "День"
msgid "Disable" msgid "Debug"
msgstr "Отключить" msgstr "Отладка"
msgid "Disabled" msgid "Disabled"
msgstr "Отключено" msgstr "Отключено"
msgid "Dismiss" msgid "Dismiss"
msgstr "Закрыть" msgstr "Отмена"
msgid "Download error"
msgstr "Ошибка загрузки"
msgid "Download log"
msgstr "Скачать лог"
msgid "Edit" msgid "Edit"
msgstr "Изменить" msgstr "Изменить"
msgid "Emergency"
msgstr "Чрезвычайная ситуация"
msgid "Enable" msgid "Enable"
msgstr "Включить" msgstr "Включить"
@@ -102,18 +125,18 @@ msgstr "Включать 'total-proxy' при старте"
msgid "Enabled" msgid "Enabled"
msgstr "Включено" msgstr "Включено"
msgid "Entries filters" msgid "Entries"
msgstr "Фильтры записей" msgstr "Записи"
msgid "Error" msgid "Error"
msgstr "Ошибка" msgstr "Ошибка"
msgid "Exclude domains from blacklist by FQDN filter patterns"
msgstr "Исключение доменов из блэклиста по шаблонам фильтра FQDN"
msgid "Exclude IP addresses from blacklist by IP filter patterns" msgid "Exclude IP addresses from blacklist by IP filter patterns"
msgstr "Исключение IP адресов из блэклиста по шаблонам фильтра IP" msgstr "Исключение IP адресов из блэклиста по шаблонам фильтра IP"
msgid "Exclude domains from blacklist by FQDN filter patterns"
msgstr "Исключение доменов из блэклиста по шаблонам фильтра FQDN"
msgid "Expecting:" msgid "Expecting:"
msgstr "Ожидается:" msgstr "Ожидается:"
@@ -123,12 +146,24 @@ msgstr "Конфигурация FQDN"
msgid "FQDN filter" msgid "FQDN filter"
msgstr "Фильтр FQDN" 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" msgid "Hour"
msgstr "Час" msgstr "Час"
msgid "Interval"
msgstr "Интервал"
msgid "IP configuration" msgid "IP configuration"
msgstr "Конфигурация IP" msgstr "Конфигурация IP"
@@ -141,6 +176,15 @@ msgstr "Лимит IP адресов"
msgid "IP subnet patterns (/24) that are excluded from optimization" msgid "IP subnet patterns (/24) that are excluded from optimization"
msgstr "Шаблоны IP подсетей (/24) не подлежащих оптимизации" msgstr "Шаблоны IP подсетей (/24) не подлежащих оптимизации"
msgid "Info"
msgstr "Информация"
msgid "Interval"
msgstr "Интервал"
msgid "Invalid regular expression"
msgstr "Неправильное регулярное выражение"
msgid "Ipset" msgid "Ipset"
msgstr "Ipset" msgstr "Ipset"
@@ -153,6 +197,12 @@ msgstr "LAN интерфейс"
msgid "Last blacklist update" msgid "Last blacklist update"
msgstr "Последнее обновление блэклиста" msgstr "Последнее обновление блэклиста"
msgid "Last entries"
msgstr "Последние записи"
msgid "Level"
msgstr "Уровень"
msgid "Loading" msgid "Loading"
msgstr "Загрузка" msgstr "Загрузка"
@@ -162,12 +212,21 @@ msgstr "Лог"
msgid "Logging events" msgid "Logging events"
msgstr "Записывать события в лог" msgstr "Записывать события в лог"
msgid "Logging levels"
msgstr "Уровни логирования"
msgid "Main settings" msgid "Main settings"
msgstr "Основные настройки" msgstr "Основные настройки"
msgid "Match-set" msgid "Match-set"
msgstr "Правило" msgstr "Правило"
msgid "Message"
msgstr "Сообщение"
msgid "Message filter"
msgstr "Фильтр сообщений"
msgid "Minute" msgid "Minute"
msgstr "Минута" msgstr "Минута"
@@ -177,14 +236,20 @@ msgstr "Режим работы модуля"
msgid "Name" msgid "Name"
msgstr "Имя" msgstr "Имя"
msgid "No Shedule"
msgstr "Нет расписания"
msgid "No changes to save." msgid "No changes to save."
msgstr "Нет изменений для сохранения." msgstr "Нет изменений для сохранения."
msgid "No data" msgid "No data"
msgstr "Нет данных" msgstr "Нет данных"
msgid "No Shedule" msgid "No entries available..."
msgstr "Нет расписания" msgstr "Нет доступных записей..."
msgid "Notice"
msgstr "Сообщение"
msgid "Number of entries" msgid "Number of entries"
msgstr "Кол-во записей" msgstr "Кол-во записей"
@@ -202,15 +267,12 @@ msgstr ""
msgid "One of the following:" msgid "One of the following:"
msgstr "Одно из следующих значений:" msgstr "Одно из следующих значений:"
msgid "Only messages that include the specified string will be displayed" msgid "Optional DNS resolver"
msgstr "Будут показаны только сообщения включающие указанную строку" msgstr "Альтернативный DNS резолвер"
msgid "Optional DNS resolver for '.onion' zone" msgid "Optional DNS resolver for '.onion' zone"
msgstr "Дополнительный DNS резолвер для '.onion' зоны" msgstr "Дополнительный DNS резолвер для '.onion' зоны"
msgid "Optional DNS resolver"
msgstr "Альтернативный DNS резолвер"
msgid "Options" msgid "Options"
msgstr "Опции" msgstr "Опции"
@@ -238,15 +300,27 @@ msgstr "Режим прокси"
msgid "Reduces RAM consumption during update" msgid "Reduces RAM consumption during update"
msgstr "Уменьшает потребление оперативной памяти при обновлении" msgstr "Уменьшает потребление оперативной памяти при обновлении"
msgid "Refresh log"
msgstr "Обновить лог"
msgid "Reset" msgid "Reset"
msgstr "Сбросить" msgstr "Сбросить"
msgid "Ruantiblock"
msgstr "Ruantiblock"
msgid "Run at startup" msgid "Run at startup"
msgstr "Запуск при старте системы" msgstr "Запуск при старте системы"
msgid "Save"
msgstr "Сохранить"
msgid "Service" msgid "Service"
msgstr "Служба" msgstr "Служба"
msgid "Service action failed \"%s %s\": %s"
msgstr "Не удалось выполнить действие службы \"%s %s\": %s"
msgid "Set" msgid "Set"
msgstr "Установить" msgstr "Установить"
@@ -259,6 +333,9 @@ msgstr "Выключение"
msgid "Size in memory" msgid "Size in memory"
msgstr "Размер в памяти" msgstr "Размер в памяти"
msgid "Sorting entries"
msgstr "Сортировка записей"
msgid "Starting" msgid "Starting"
msgstr "Запускается" msgstr "Запускается"
@@ -300,21 +377,33 @@ msgstr ""
msgid "Time" msgid "Time"
msgstr "Время" msgstr "Время"
msgid "Timestamp"
msgstr "Время"
msgid "Tor configuration file" msgid "Tor configuration file"
msgstr "Конфигурационный файл Tor" msgstr "Конфигурационный файл Tor"
msgid "Tor mode" msgid "Tor mode"
msgstr "Режим Tor" msgstr "Режим Tor"
msgid "Total-proxy"
msgstr "Total-proxy"
msgid "Total-proxy is on" msgid "Total-proxy is on"
msgstr "Total-proxy включен" msgstr "Total-proxy включен"
msgid "Transparent proxy port for iptables rules" msgid "Transparent proxy port for iptables rules"
msgstr "Порт прозрачного прокси для правил iptables" msgstr "Порт прозрачного прокси для правил iptables"
msgid "Type an expression..."
msgstr "Введите выражение..."
msgid "Unable to execute or read contents" msgid "Unable to execute or read contents"
msgstr "Невозможно выполнить или прочитать содержимое" msgstr "Невозможно выполнить или прочитать содержимое"
msgid "Unable to load log data:"
msgstr "Невозможно загрузить данные лога:"
msgid "Unable to read the contents" msgid "Unable to read the contents"
msgstr "Невозможно прочитать содержимое" msgstr "Невозможно прочитать содержимое"
@@ -348,9 +437,27 @@ msgstr "Режим VPN"
msgid "VPN routing error! Need restart" msgid "VPN routing error! Need restart"
msgstr "Ошибка маршрутизации VPN! Необходим перезапуск" msgstr "Ошибка маршрутизации VPN! Необходим перезапуск"
msgid "Warning"
msgstr "Внимание"
msgid "ascending"
msgstr "по возрастанию"
msgid "descending"
msgstr "по убыванию"
msgid "disabled"
msgstr "отключен"
msgid "e.g:" msgid "e.g:"
msgstr "прим:" msgstr "прим:"
msgid "net pattern"
msgstr "шаблон сети"
msgid "none (user entries only)"
msgstr "нет (только записи пользователя)"
msgid "user entries only" msgid "user entries only"
msgstr "только записи пользователя" msgstr "только записи пользователя"
@@ -359,86 +466,3 @@ msgstr "верный IP-адрес"
msgid "valid address#port" msgid "valid address#port"
msgstr "верный IP-адрес#порт" 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" msgid "Add user entries to the blacklist when updating"
msgstr "" msgstr ""
msgid "Alert"
msgstr ""
msgid "All"
msgstr ""
msgid "All traffic goes through the proxy without applying rules" msgid "All traffic goes through the proxy without applying rules"
msgstr "" msgstr ""
@@ -16,6 +22,9 @@ msgstr ""
msgid "Apply proxy rules to router application traffic" msgid "Apply proxy rules to router application traffic"
msgstr "" msgstr ""
msgid "Blacklist entry filters"
msgstr ""
msgid "Blacklist module" msgid "Blacklist module"
msgstr "" msgstr ""
@@ -43,6 +52,9 @@ msgstr ""
msgid "Clean up ipsets before updating blacklist" msgid "Clean up ipsets before updating blacklist"
msgstr "" msgstr ""
msgid "Command failed"
msgstr ""
msgid "" msgid ""
"Complete service shutdown, as well as deleting ipsets and blacklist data" "Complete service shutdown, as well as deleting ipsets and blacklist data"
msgstr "" msgstr ""
@@ -53,6 +65,9 @@ msgstr ""
msgid "Convert cyrillic domains to punycode" msgid "Convert cyrillic domains to punycode"
msgstr "" msgstr ""
msgid "Critical"
msgstr ""
msgid "Current schedule" msgid "Current schedule"
msgstr "" msgstr ""
@@ -62,43 +77,47 @@ msgstr ""
msgid "Day" msgid "Day"
msgstr "" msgstr ""
msgid "Disable" msgid "Debug"
msgstr "" msgstr ""
msgid "Disabled" msgid "Disabled"
msgstr "" msgstr ""
msgid "Dismiss" msgid "Dismiss"
msgstr "" msgstr ""
msgid "Download error"
msgstr ""
msgid "Download log"
msgstr ""
msgid "Edit" msgid "Edit"
msgstr "" msgstr ""
msgid "Emergency"
msgstr ""
msgid "Enable" msgid "Enable"
msgstr "" msgstr ""
msgid "Enable FQDN filter" msgid "Enable FQDN filter"
msgstr "" msgstr ""
msgid "Enable ip filter" msgid "Enable IP filter"
msgstr "" msgstr ""
msgid "Enable the 'total-proxy' option at startup" msgid "Enable the 'total-proxy' option at startup"
msgstr "" msgstr ""
msgid "Enabled" msgid "Enabled"
msgstr "" msgstr ""
msgid "Entries filters" msgid "Entries"
msgstr "" msgstr ""
msgid "Error" msgid "Error"
msgstr "" msgstr ""
msgid "Exclude domains from blacklist by FQDN filter patterns" msgid "Exclude IP addresses from blacklist by IP filter patterns"
msgstr "" msgstr ""
msgid "Exclude IP addresses from blacklist by IP filter patterns" msgid "Exclude domains from blacklist by FQDN filter patterns"
msgstr "" msgstr ""
msgid "Expecting:" msgid "Expecting:"
@@ -110,15 +129,24 @@ msgstr ""
msgid "FQDN filter" msgid "FQDN filter"
msgstr "" 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 "" msgstr ""
msgid "Hour" msgid "Hour"
msgstr "" msgstr ""
msgid "Interval"
msgstr ""
msgid "IP configuration" msgid "IP configuration"
msgstr "" msgstr ""
@@ -131,6 +159,15 @@ msgstr ""
msgid "IP subnet patterns (/24) that are excluded from optimization" msgid "IP subnet patterns (/24) that are excluded from optimization"
msgstr "" msgstr ""
msgid "Info"
msgstr ""
msgid "Interval"
msgstr ""
msgid "Invalid regular expression"
msgstr ""
msgid "Ipset" msgid "Ipset"
msgstr "" msgstr ""
@@ -143,6 +180,12 @@ msgstr ""
msgid "Last blacklist update" msgid "Last blacklist update"
msgstr "" msgstr ""
msgid "Last entries"
msgstr ""
msgid "Level"
msgstr ""
msgid "Loading" msgid "Loading"
msgstr "" msgstr ""
@@ -152,12 +195,21 @@ msgstr ""
msgid "Logging events" msgid "Logging events"
msgstr "" msgstr ""
msgid "Logging levels"
msgstr ""
msgid "Main settings" msgid "Main settings"
msgstr "" msgstr ""
msgid "Match-set" msgid "Match-set"
msgstr "" msgstr ""
msgid "Message"
msgstr ""
msgid "Message filter"
msgstr ""
msgid "Minute" msgid "Minute"
msgstr "" msgstr ""
@@ -167,13 +219,19 @@ msgstr ""
msgid "Name" msgid "Name"
msgstr "" msgstr ""
msgid "No Shedule"
msgstr ""
msgid "No changes to save." msgid "No changes to save."
msgstr "" msgstr ""
msgid "No data" msgid "No data"
msgstr "" msgstr ""
msgid "No Shedule" msgid "No entries available..."
msgstr ""
msgid "Notice"
msgstr "" msgstr ""
msgid "Number of entries" msgid "Number of entries"
@@ -189,10 +247,10 @@ msgstr ""
msgid "One of the following:" msgid "One of the following:"
msgstr "" msgstr ""
msgid "Optional DNS resolver for '.onion' zone" msgid "Optional DNS resolver"
msgstr "" msgstr ""
msgid "Optional DNS resolver" msgid "Optional DNS resolver for '.onion' zone"
msgstr "" msgstr ""
msgid "Options" msgid "Options"
@@ -216,33 +274,46 @@ msgstr ""
msgid "Reduces RAM consumption during update" msgid "Reduces RAM consumption during update"
msgstr "" msgstr ""
msgid "Refresh log"
msgstr ""
msgid "Reset" msgid "Reset"
msgstr "" msgstr ""
msgid "Ruantiblock"
msgstr ""
msgid "Run at startup" msgid "Run at startup"
msgstr "" msgstr ""
msgid "Save"
msgstr ""
msgid "Service" msgid "Service"
msgstr "" msgstr ""
msgid "Service action failed \"%s %s\": %s"
msgstr ""
msgid "Set" msgid "Set"
msgstr "" msgstr ""
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
msgid "Shutdown" msgid "Shutdown"
msgstr "" msgstr ""
msgid "Size in memory" msgid "Size in memory"
msgstr "" msgstr ""
msgid "Sorting entries"
msgstr ""
msgid "Starting" msgid "Starting"
msgstr "" msgstr ""
msgid "Statistics" msgid "Statistics"
msgstr "" msgstr ""
msgid "Status" msgid "Status"
msgstr "" msgstr ""
@@ -273,21 +344,31 @@ msgstr ""
msgid "Time" msgid "Time"
msgstr "" msgstr ""
msgid "Timestamp"
msgstr ""
msgid "Tor configuration file" msgid "Tor configuration file"
msgstr "" msgstr ""
msgid "Tor mode" msgid "Tor mode"
msgstr "" msgstr ""
msgid "Total-proxy"
msgstr ""
msgid "Total-proxy is on" msgid "Total-proxy is on"
msgstr "" msgstr ""
msgid "Transparent proxy port for iptables rules" msgid "Transparent proxy port for iptables rules"
msgstr "" msgstr ""
msgid "Type an expression..."
msgstr ""
msgid "Unable to execute or read contents" msgid "Unable to execute or read contents"
msgstr "" msgstr ""
msgid "Unable to load log data:"
msgstr ""
msgid "Unable to read the contents" msgid "Unable to read the contents"
msgstr "" msgstr ""
@@ -308,7 +389,6 @@ msgstr ""
msgid "Use optional DNS resolver" msgid "Use optional DNS resolver"
msgstr "" msgstr ""
msgid "User entries" msgid "User entries"
msgstr "" msgstr ""
@@ -321,9 +401,27 @@ msgstr ""
msgid "VPN routing error! Need restart" msgid "VPN routing error! Need restart"
msgstr "" msgstr ""
msgid "Warning"
msgstr ""
msgid "ascending"
msgstr ""
msgid "descending"
msgstr ""
msgid "disabled"
msgstr ""
msgid "e.g:" msgid "e.g:"
msgstr "" msgstr ""
msgid "net pattern"
msgstr ""
msgid "none (user entries only)"
msgstr ""
msgid "user entries only" msgid "user entries only"
msgstr "" msgstr ""
@@ -332,86 +430,3 @@ msgstr ""
msgid "valid address#port" msgid "valid address#port"
msgstr "" 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/tor/torrc": [ "read" ],
"/etc/crontabs/root": [ "read" ], "/etc/crontabs/root": [ "read" ],
"/usr/bin/ruantiblock": [ "exec" ], "/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" ], "/sbin/logread -e ruantiblock": [ "exec" ],
"/usr/sbin/logread -e ruantiblock": [ "exec" ] "/usr/sbin/logread -e ruantiblock": [ "exec" ]
}, },
"uci": [ "network", "ruantiblock" ] "uci": [ "network", "ruantiblock" ],
"ubus": {
"luci": [ "getInitList", "setInitAction" ]
}
}, },
"write": { "write": {
"file": { "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