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
@@ -139,15 +139,15 @@ return L.Class.extend({
* View name (for local storage and downloads). * View name (for local storage and downloads).
* Must be overridden by a subclass! * Must be overridden by a subclass!
*/ */
viewName: null, viewName : null,
/** /**
* Page title. * Page title.
* Must be overridden by a subclass! * Must be overridden by a subclass!
*/ */
title: null, title : null,
logLevels: { logLevels : {
'emerg': E('span', { 'class': 'zonebadge log-emerg' }, E('strong', _('Emergency'))), 'emerg': E('span', { 'class': 'zonebadge log-emerg' }, E('strong', _('Emergency'))),
'alert': E('span', { 'class': 'zonebadge log-alert' }, E('strong', _('Alert'))), 'alert': E('span', { 'class': 'zonebadge log-alert' }, E('strong', _('Alert'))),
'crit': E('span', { 'class': 'zonebadge log-crit' }, E('strong', _('Critical'))), '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'))), '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, logLevelsDropdown: null,
totalLogLines: 0, totalLogLines : 0,
htmlEntities: function(str) { htmlEntities : function(str) {
return String(str).replace( return String(str).replace(
/&/g, '&').replace( /&/g, '&').replace(
/</g, '&#60;').replace( /</g, '&#60;').replace(
@@ -426,15 +426,15 @@ return L.Class.extend({
render: function(logdata) { render: function(logdata) {
let logWrapper = E('div', { let logWrapper = E('div', {
'id': 'logWrapper', 'id' : 'logWrapper',
'style': 'width:100%; min-height:20em; padding: 0 0 0 45px; font-size:0.9em !important' 'style': 'width:100%; min-height:20em; padding: 0 0 0 45px; font-size:0.9em !important'
}, this.makeLogArea(this.parseLogData(logdata, this.tailValue))); }, this.makeLogArea(this.parseLogData(logdata, this.tailValue)));
let tailInput = E('input', { let tailInput = E('input', {
'id': 'tailInput', 'id' : 'tailInput',
'name': 'tailInput', 'name' : 'tailInput',
'type': 'text', 'type' : 'text',
'form': 'logForm', 'form' : 'logForm',
'class': 'cbi-input-text', 'class': 'cbi-input-text',
'style': 'width:4em !important; min-width:4em !important', 'style': 'width:4em !important; min-width:4em !important',
'maxlength': 5, 'maxlength': 5,
@@ -443,8 +443,8 @@ return L.Class.extend({
ui.addValidator(tailInput, 'uinteger', true); ui.addValidator(tailInput, 'uinteger', true);
let tailReset = E('input', { let tailReset = E('input', {
'type': 'button', 'type' : 'button',
'form': 'logForm', 'form' : 'logForm',
'class': 'cbi-button btn cbi-button-reset', 'class': 'cbi-button btn cbi-button-reset',
'value': 'Χ', 'value': 'Χ',
'click': ev => { 'click': ev => {
@@ -455,27 +455,27 @@ return L.Class.extend({
'style': 'max-width:4em !important', 'style': 'max-width:4em !important',
}); });
let logHostsDropdownElem = ''; let logHostsDropdownElem = '';
let logLevelsDropdownElem = ''; let logLevelsDropdownElem = '';
if(this.isLevels) { if(this.isLevels) {
logLevelsDropdownElem = this.makeLogLevelsDropdownSection(); logLevelsDropdownElem = this.makeLogLevelsDropdownSection();
}; };
if(this.isHosts) { if(this.isHosts) {
logHostsDropdownElem = this.makeLogHostsDropdownSection(); logHostsDropdownElem = this.makeLogHostsDropdownSection();
}; };
let logFilter = E('input', { let logFilter = E('input', {
'id': 'logFilter', 'id' : 'logFilter',
'name': 'logFilter', 'name' : 'logFilter',
'type': 'text', 'type' : 'text',
'form': 'logForm', 'form' : 'logForm',
'class': 'cbi-input-text', 'class' : 'cbi-input-text',
'placeholder': _('Type an expression...'), 'placeholder': _('Type an expression...'),
}); });
let logFormSubmitBtn = E('input', { let logFormSubmitBtn = E('input', {
'type': 'submit', 'type' : 'submit',
'form': 'logForm', 'form' : 'logForm',
'class': 'cbi-button btn cbi-button-action', 'class': 'cbi-button btn cbi-button-action',
'value': _('Apply'), 'value': _('Apply'),
'click': ev => ev.target.blur(), 'click': ev => ev.target.blur(),
@@ -483,9 +483,9 @@ return L.Class.extend({
}); });
let logSorting = E('select', { let logSorting = E('select', {
'id': 'logSorting', 'id' : 'logSorting',
'name': 'logSorting', 'name' : 'logSorting',
'form': 'logForm', 'form' : 'logForm',
'class': "cbi-input-select", 'class': "cbi-input-select",
}, [ }, [
E('option', { 'value': 'asc' }, _('ascending')), E('option', { 'value': 'asc' }, _('ascending')),
@@ -494,8 +494,8 @@ return L.Class.extend({
logSorting.value = this.logSortingValue; logSorting.value = this.logSortingValue;
let logDownloadBtn = E('button', { let logDownloadBtn = E('button', {
'id': 'logDownloadBtn', 'id' : 'logDownloadBtn',
'name': 'logDownloadBtn', 'name' : 'logDownloadBtn',
'class': 'cbi-button btn', 'class': 'cbi-button btn',
'click': ui.createHandlerFn(this, this.downloadLog), 'click': ui.createHandlerFn(this, this.downloadLog),
}, _('Download log')); }, _('Download log'));
@@ -509,7 +509,7 @@ return L.Class.extend({
E('div', { 'id': 'tailInputSection', 'class': 'cbi-value' }, [ E('div', { 'id': 'tailInputSection', 'class': 'cbi-value' }, [
E('label', { E('label', {
'class': 'cbi-value-title', 'class': 'cbi-value-title',
'for': 'tailInput', 'for' : 'tailInput',
}, _('Last entries')), }, _('Last entries')),
E('div', { 'class': 'cbi-value-field' }, [ E('div', { 'class': 'cbi-value-field' }, [
tailInput, tailInput,
@@ -523,7 +523,7 @@ return L.Class.extend({
E('div', { 'class': 'cbi-value' }, [ E('div', { 'class': 'cbi-value' }, [
E('label', { E('label', {
'class': 'cbi-value-title', 'class': 'cbi-value-title',
'for': 'logFilter', 'for' : 'logFilter',
}, _('Message filter')), }, _('Message filter')),
E('div', { 'class': 'cbi-value-field' }, logFilter), E('div', { 'class': 'cbi-value-field' }, logFilter),
]), ]),
@@ -531,7 +531,7 @@ return L.Class.extend({
E('div', { 'class': 'cbi-value' }, [ E('div', { 'class': 'cbi-value' }, [
E('label', { E('label', {
'class': 'cbi-value-title', 'class': 'cbi-value-title',
'for': 'logSorting', 'for' : 'logSorting',
}, _('Sorting entries')), }, _('Sorting entries')),
E('div', { 'class': 'cbi-value-field' }, logSorting,), E('div', { 'class': 'cbi-value-field' }, logSorting,),
]), ]),
@@ -539,14 +539,14 @@ return L.Class.extend({
E('div', { 'class': 'cbi-value' }, [ E('div', { 'class': 'cbi-value' }, [
E('label', { E('label', {
'class': 'cbi-value-title', 'class': 'cbi-value-title',
'for': 'logFilter', 'for' : 'logFilter',
}, _('Refresh log')), }, _('Refresh log')),
E('div', { 'class': 'cbi-value-field' }, [ E('div', { 'class': 'cbi-value-field' }, [
logFormSubmitBtn, logFormSubmitBtn,
E('form', { E('form', {
'id': 'logForm', 'id' : 'logForm',
'name': 'logForm', 'name' : 'logForm',
'style': 'display:inline-block; margin-top:0.5em', 'style' : 'display:inline-block; margin-top:0.5em',
'submit': ui.createHandlerFn(this, function(ev) { 'submit': ui.createHandlerFn(this, function(ev) {
ev.preventDefault(); ev.preventDefault();
let formElems = Array.from(document.forms.logForm.elements); let formElems = Array.from(document.forms.logForm.elements);
@@ -633,8 +633,8 @@ return L.Class.extend({
]); ]);
}, },
handleSave : null,
handleSaveApply: null, handleSaveApply: null,
handleSave: null, handleReset : null,
handleReset: null,
}), }),
}) })
@@ -3,221 +3,234 @@
'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');
let current_crontab_content;
function to_dd(n){
return String(n).replace(/^(\d)$/, "0$1");
};
function cron_status_string(s) {
return s || _('No Shedule');
}
function pick_cron_task(content) {
if(!content){
return;
};
let current_tasks = content.match(crontab_regexp) || [];
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';
}
function write_cron_file() {
let btn_cron_add = document.getElementById('btn_cron_add');
let btn_cron_del = document.getElementById('btn_cron_del');
if(!current_crontab_content) {
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 => {
ui.addNotification(null, E('p',_('Changes have been saved.')), 'info');
set_cron_status(pick_cron_task(current_crontab_content));
}).then(() => {
return fs.exec('/etc/init.d/cron', [ 'enabled' ]).then(res => {
if(res.code !== 0) {
return fs.exec('/etc/init.d/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')));
});
}).catch(e => {
ui.addNotification(null, E('p', _('Unable to save the changes')
+ ': %s<br />[ %s ]'.format(
e.message, tools.crontab_file
)));
});
}
function del_cron_schedule(ev) {
if(current_crontab_content) {
current_crontab_content = current_crontab_content.replace(crontab_regexp, "");
};
return write_cron_file();
};
function set_cron_schedule(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;
let min = document.getElementById('cron_min').value;
let task_string = '%s %s %s * * %s update\n'.format(
min,
(!hour_interval) ? hour : (hour_interval == "1") ? '*' : '*/' + hour_interval,
(hour_interval || day_interval == "1") ? '*' : '*/' + day_interval,
tools.exec_path
);
if(current_crontab_content) {
current_crontab_content = current_crontab_content.replace(crontab_regexp, "") + task_string;
};
return write_cron_file();
};
function onchange_hour_interval(e) {
let value = e.target.value;
let bool = (value != '');
let cron_hour = document.getElementById('cron_hour');
let cron_day_interval = document.getElementById('cron_day_interval');
cron_hour.disabled = bool;
cron_day_interval.disabled = bool;
if(bool) {
cron_hour.style.opacity = '50%';
cron_day_interval.style.opacity = '50%';
} else {
cron_hour.style.opacity = '100%';
cron_day_interval.style.opacity = '100%';
};
}
return L.view.extend({ return L.view.extend({
load: function() { crontabRegexp: new RegExp(
return fs.read(tools.crontab_file).catch(e => { `^(\\*?\\/?(\\d){0,2}\\s){5}${tools.execPath} update(\n)?`, 'gm'),
ui.addNotification(null, E('p', _('Unable to read the contents')
+ ': %s<br />[ %s ]'.format(
e.message, tools.crontab_file
)));
});
},
render: function(content) { currentCrontabContent: null,
current_crontab_content = content;
let current_task = pick_cron_task(content);
let cron_status = E('textarea', { toDD: function(n){
'id': 'cron_status', return String(n).replace(/^(\d)$/, "0$1");
'name': 'cron_status', },
'style': 'width:100% !important; padding:5px 10px 5px 10px !important; resize:none !important;',
'readonly': 'readonly',
'wrap': 'off',
'rows': 2,
}, cron_status_string(current_task));
let btn_cron_del = E('button', { cronStatusString: function(s) {
'class': 'cbi-button btn cbi-button-reset', return s || _('No Shedule');
'id': 'btn_cron_del', },
'name': 'btn_cron_del',
}, _('Reset'));
btn_cron_del.onclick = ui.createHandlerFn(this, del_cron_schedule);
btn_cron_del.style.visibility = (current_task) ? 'visible' : 'hidden';
let status_header = E('div', { 'class': 'cbi-section-node' }, [ pickCronTask: function(content) {
E('div', { 'class': 'cbi-value' }, [ if(!content){
E('label', { 'class': 'cbi-value-title', 'for': 'cron_status' }, return;
_('Current schedule')), };
E('div', { 'class': 'cbi-value-field' }, cron_status), let current_tasks = content.match(this.crontabRegexp) || [];
]), return current_tasks.join('');
E('div', { 'class': 'cbi-value' }, [ },
E('label', { 'class': 'cbi-value-title', 'for': 'btn_cron_del' }),
E('div', { 'class': 'cbi-value-field' }, btn_cron_del),
])
]);
let layout = E('div', { 'class': 'cbi-section-node' }); setCronStatus: function(value) {
document.getElementById('cron_status').value = this.cronStatusString(value);
document.getElementById("btn_cron_del").style.visibility = (value) ?
'visible' : 'hidden';
},
function layout_append(elem, title, descr) { writeCronFile: function() {
descr = (descr) ? E('div', { 'class': 'cbi-value-description' }, descr) : ''; let btn_cron_add = document.getElementById('btn_cron_add');
layout.append( let btn_cron_del = document.getElementById('btn_cron_del');
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': elem.id || null },
title),
E('div', { 'class': 'cbi-value-field' }, [ elem, descr ]),
])
)
};
layout_append(E('b', {}, _('Interval'))); if(!this.currentCrontabContent) {
ui.addNotification(null, E('p', _('No changes to save.')));
btn_cron_add.disabled = false;
return;
};
let cron_hour_interval = E('select', return fs.write(tools.crontabFile, this.currentCrontabContent).then(rc => {
{ 'id': 'cron_hour_interval', 'style': 'width:60px !important; min-width:60px !important' }, [ ui.addNotification(null, E('p',_('Changes have been saved.')), 'info');
E('option', { 'value': '' }, ''), this.setCronStatus(this.pickCronTask(this.currentCrontabContent));
E('option', { 'value': '1' }, '&#8727;') }).then(() => {
]); return tools.getInitStatus('cron').then(res => {
for(let i = 2; i <= 12 ; i += 2) { if(!res) {
cron_hour_interval.append(E('option', { 'value': String(i) }, '&#8727;/' + i)); return tools.handleServiceAction('cron', 'enable');
}; };
layout_append(cron_hour_interval, _('Hour')); });
cron_hour_interval.onchange = onchange_hour_interval; }).finally(() => {
return tools.handleServiceAction('cron', 'restart');
}).catch(e => {
ui.addNotification(null, E('p', _('Unable to save the changes')
+ ': %s [ %s ]'.format(
e.message, tools.crontabFile
)));
});
},
let cron_day_interval = E('select', delCronSchedule: function(ev) {
{ 'id': 'cron_day_interval', 'style': 'width:60px !important; min-width:60px !important' }, if(this.currentCrontabContent) {
E('option', { 'value': '1' }, '&#8727;') this.currentCrontabContent = this.currentCrontabContent.replace(
); this.crontabRegexp, "");
for(let i = 2; i < 8 ; i++) { };
cron_day_interval.append(E('option', { 'value': String(i) }, '&#8727;/' + i)); return this.writeCronFile();
}; },
cron_day_interval.append(E('option', { 'value': '14' }, '&#8727;/14'));
cron_day_interval.append(E('option', { 'value': '28' }, '&#8727;/28'));
layout_append(cron_day_interval, _('Day'));
layout_append(E('b', {}, _('Time'))); 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;
let min = document.getElementById('cron_min').value;
let task_string = '%s %s %s * * %s update\n'.format(
min,
(!hour_interval) ? hour : (hour_interval == "1") ? '*' : '*/' + hour_interval,
(hour_interval || day_interval == "1") ? '*' : '*/' + day_interval,
tools.execPath
);
if(this.currentCrontabContent) {
this.currentCrontabContent = this.currentCrontabContent.replace(
this.crontabRegexp, "") + task_string;
};
return this.writeCronFile();
},
let cron_hour = E('select', onchangeHourInterval: function(e) {
{ 'id': 'cron_hour', 'style': 'width:60px !important; min-width:60px !important' }); let value = e.target.value;
for(let i = 0; i < 24 ; i++) { let bool = (value != '');
cron_hour.append(E('option', { 'value': String(i) }, to_dd(i))); let cron_hour = document.getElementById('cron_hour');
}; let cron_day_interval = document.getElementById('cron_day_interval');
layout_append(cron_hour, _('Hour')); cron_hour.disabled = bool;
cron_day_interval.disabled = bool;
let cron_min = E('select', // For luci-theme-material
{ 'id': 'cron_min', 'style': 'width:60px !important; min-width:60px !important' }); if(bool) {
for(let i = 0; i < 60 ; i++) { cron_hour.style.opacity = '50%';
cron_min.append(E('option', { 'value': String(i) }, to_dd(i))); cron_day_interval.style.opacity = '50%';
}; } else {
layout_append(cron_min, _('Minute')); cron_hour.style.opacity = '100%';
cron_day_interval.style.opacity = '100%';
};
},
let btn_cron_add = E('button', { load: function() {
'class': 'btn cbi-button-save', return fs.read(tools.crontabFile).catch(e => {
'id': 'btn_cron_add', ui.addNotification(null, E('p', _('Unable to read the contents')
'name': 'btn_cron_add' + ': %s [ %s ]'.format(
}, _('Set')); e.message, tools.crontabFile
btn_cron_add.onclick = ui.createHandlerFn(this, set_cron_schedule); )));
layout_append(btn_cron_add); });
},
return E([ render: function(content) {
E('h2', this.currentCrontabContent = content;
{ 'class': 'fade-in' }, _('Ruantiblock') + ' - ' + _('Blacklist updates') + ' (cron)'), let current_task = this.pickCronTask(content);
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),
]);
}, let cron_status = E('textarea', {
'id': 'cron_status',
'name': 'cron_status',
'style': 'width:100% !important; padding:5px 10px 5px 10px !important; resize:none !important;',
'readonly': 'readonly',
'wrap': 'off',
'rows': 2,
}, this.cronStatusString(current_task));
handleSave: null, let btn_cron_del = E('button', {
handleSaveApply: null, 'class': 'cbi-button btn cbi-button-reset',
handleReset: null, 'id': 'btn_cron_del',
'name': 'btn_cron_del',
}, _('Reset'));
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' }, [
E('label', { 'class': 'cbi-value-title', 'for': 'btn_cron_del' }),
E('div', { 'class': 'cbi-value-field' },
btn_cron_del),
])
]);
let layout = E('div', { 'class': 'cbi-section-node' });
function layout_append(elem, title, descr) {
descr = (descr) ? E('div', { 'class': 'cbi-value-description' }, descr) : '';
layout.append(
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'for': elem.id || null },
title),
E('div', { 'class': 'cbi-value-field' },
[ elem, descr ]),
])
)
};
layout_append(E('b', {}, _('Interval')));
let cron_hour_interval = E('select',
{ 'id': 'cron_hour_interval', 'style': 'width:60px !important; min-width:60px !important' }, [
E('option', { 'value': '' }, ''),
E('option', { 'value': '1' }, '&#8727;')
]);
for(let i = 2; i <= 12 ; i += 2) {
cron_hour_interval.append(
E('option', { 'value': String(i) }, '&#8727;/' + i)
);
};
layout_append(cron_hour_interval, _('Hour'));
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': '14' }, '&#8727;/14'));
cron_day_interval.append(E('option', { 'value': '28' }, '&#8727;/28'));
layout_append(cron_day_interval, _('Day'));
layout_append(E('b', {}, _('Time')));
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) }, 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) }, this.toDD(i))
);
};
layout_append(cron_min, _('Minute'));
let btn_cron_add = E('button', {
'class': 'btn cbi-button-save',
'id': 'btn_cron_add',
'name': 'btn_cron_add'
}, _('Set'));
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)'
),
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),
]);
},
handleSave : null,
handleSaveApply: null,
handleReset : null,
}); });
@@ -4,230 +4,238 @@
'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 => {
if(!data) { if(!data) {
return; return;
}; };
try { try {
data = JSON.parse(data); data = JSON.parse(data);
} catch(err) {}; } catch(err) {};
if(data.status === 'enabled') { if(data.status === 'enabled') {
let date = document.getElementById('last_blacklist_update.date'); let date = document.getElementById('last_blacklist_update.date');
if(data.last_blacklist_update.status) { if(data.last_blacklist_update.status) {
if(date) { if(date) {
date.textContent = data.last_blacklist_update.date; date.textContent = data.last_blacklist_update.date;
}; };
let ip = document.getElementById('last_blacklist_update.ip'); let ip = document.getElementById('last_blacklist_update.ip');
if(ip) { if(ip) {
ip.textContent = data.last_blacklist_update.ip; ip.textContent = data.last_blacklist_update.ip;
}; };
let cidr = document.getElementById('last_blacklist_update.cidr'); let cidr = document.getElementById('last_blacklist_update.cidr');
if(cidr) { if(cidr) {
cidr.textContent = data.last_blacklist_update.cidr; cidr.textContent = data.last_blacklist_update.cidr;
}; };
let fqdn = document.getElementById('last_blacklist_update.fqdn'); let fqdn = document.getElementById('last_blacklist_update.fqdn');
if(fqdn) { if(fqdn) {
fqdn.textContent = data.last_blacklist_update.fqdn; fqdn.textContent = data.last_blacklist_update.fqdn;
}; };
} else { } else {
if(date) { if(date) {
date.textContent = _('No data'); date.textContent = _('No data');
}; };
}; };
if(data.iptables) { if(data.iptables) {
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;
let elem = document.getElementById('iptables.' + k); let elem = document.getElementById('iptables.' + k);
if(elem) { if(elem) {
elem.textContent = v; elem.textContent = v;
}; };
}; };
}; };
if(data.ipset) { if(data.ipset) {
for(let [k, v] of Object.entries(data.ipset)) { for(let [k, v] of Object.entries(data.ipset)) {
if(k === '_dummy') continue; if(k === '_dummy') continue;
let elem0 = document.getElementById('ipset.' + k + '.' + '0'); let elem0 = document.getElementById('ipset.' + k + '.' + '0');
let elem1 = document.getElementById('ipset.' + k + '.' + '1'); let elem1 = document.getElementById('ipset.' + k + '.' + '1');
if(elem0 && elem1) { if(elem0 && elem1) {
elem0.textContent = v[0]; elem0.textContent = v[0];
elem1.textContent = v[1]; elem1.textContent = v[1];
}; };
}; };
}; };
} else { } else {
if(L.Poll.active()) { if(L.Poll.active()) {
L.Poll.stop(); L.Poll.stop();
}; };
}; };
}); });
}, },
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)
)); ));
}) })
}, },
render: function(data) { render: function(data) {
if(!data) { if(!data) {
return; return;
}; };
try { try {
data = JSON.parse(data); data = JSON.parse(data);
} catch(err) {}; } catch(err) {};
let update_status = null, let update_status = null,
iptables = null, iptables = null,
ipset = null; ipset = null;
if(data) { if(data) {
if(data.status === 'enabled') { if(data.status === 'enabled') {
update_status = E('div', { 'class': 'table' }); update_status = E('div', { 'class': 'table' });
if(data.last_blacklist_update.status) { if(data.last_blacklist_update.status) {
update_status.append( update_status.append(
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',
data.last_blacklist_update.date), 'id': 'last_blacklist_update.date' },
]), data.last_blacklist_update.date),
E('div', { 'class': 'tr' }, [ ]),
E('div', { 'class': 'td left' }, 'IP:'), E('div', { 'class': 'tr' }, [
E('div', { 'class': 'td left', 'id': 'last_blacklist_update.ip' }, E('div', { 'class': 'td left' }, 'IP:'),
data.last_blacklist_update.ip), E('div', { 'class': 'td left',
]), 'id': 'last_blacklist_update.ip' },
E('div', { 'class': 'tr' }, [ data.last_blacklist_update.ip),
E('div', { 'class': 'td left' }, 'CIDR:'), ]),
E('div', { 'class': 'td left', 'id': 'last_blacklist_update.cidr' }, E('div', { 'class': 'tr' }, [
data.last_blacklist_update.cidr), E('div', { 'class': 'td left' }, 'CIDR:'),
]), E('div', { 'class': 'td left',
E('div', { 'class': 'tr' }, [ 'id': 'last_blacklist_update.cidr' },
E('div', { 'class': 'td left' }, 'FQDN:'), data.last_blacklist_update.cidr),
E('div', { 'class': 'td left', 'id': 'last_blacklist_update.fqdn' }, ]),
data.last_blacklist_update.fqdn), E('div', { 'class': 'tr' }, [
]) E('div', { 'class': 'td left' }, 'FQDN:'),
); E('div', { 'class': 'td left',
} else { 'id': 'last_blacklist_update.fqdn' },
update_status.append( data.last_blacklist_update.fqdn),
E('div', { 'class': 'tr' }, [ ])
E('div', { 'class': 'td left' }, );
_('Last blacklist update')), } else {
E('div', { 'class': 'td left' }, _('No data')), update_status.append(
]) E('div', { 'class': 'tr' }, [
); E('div', { 'class': 'td left' },
}; _('Last blacklist update')),
E('div', { 'class': 'td left' }, _('No data')),
])
);
};
if(data.iptables) { if(data.iptables) {
let table_iptables = E('div', { 'class': 'table' }, [ let table_iptables = 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%' }, E('div', { 'class': 'th left', 'style': 'min-width:33%' },
_('Match-set')), _('Match-set')),
E('div', { 'class': 'th left' }, _('Bytes')), E('div', { 'class': 'th left' }, _('Bytes')),
]), ]),
]); ]);
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(
E('div', { 'class': 'tr' }, [
E('div', {
'class' : 'td left',
'data-title': _('Match-set'),
}, k),
E('div', {
'class' : 'td left',
'id' : 'iptables.' + k,
'data-title': _('Bytes'),
}, v),
])
);
};
table_iptables.append( iptables = E([
E('div', { 'class': 'tr' }, [ E('h3', {}, _('Iptables rules')),
E('div', { table_iptables,
'class': 'td left', ]);
'data-title': _('Match-set'), };
}, k),
E('div', {
'class': 'td left',
'id': 'iptables.' + k,
'data-title': _('Bytes'),
}, v),
])
);
};
iptables = E([ if(data.ipset) {
E('h3', {}, _('Iptables rules')), let table_ipset = E('div', { 'class': 'table' },
table_iptables, 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')),
])
);
if(data.ipset) { for(let [k, v] of Object.entries(data.ipset)) {
let table_ipset = E('div', { 'class': 'table' }, if(k === '_dummy') continue;
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')),
])
);
for(let [k, v] of Object.entries(data.ipset)) { table_ipset.append(
if(k === '_dummy') continue; E('div', { 'class': 'tr' }, [
E('div', {
'class' : 'td left',
'data-title': _('Name'),
}, k),
E('div', {
'class' : 'td left',
'id' : 'ipset.' + k + '.' + '0',
'data-title': _('Size in memory'),
}, v[0]),
E('div', {
'class' : 'td left',
'id' : 'ipset.' + k + '.' + '1',
'data-title': _('Number of entries'),
}, v[1]),
])
);
};
table_ipset.append( ipset = E([
E('div', { 'class': 'tr' }, [ E('h3', {}, _('Ipset')),
E('div', { table_ipset,
'class': 'td left', ]);
'data-title': _('Name'), };
}, k),
E('div', {
'class': 'td left',
'id': 'ipset.' + k + '.' + '0',
'data-title': _('Size in memory'),
}, v[0]),
E('div', {
'class': 'td left',
'id': 'ipset.' + k + '.' + '1',
'data-title': _('Number of entries'),
}, v[1]),
])
);
};
ipset = E([ L.Poll.add(this.infoPoll);
E('h3', {}, _('Ipset')), } else {
table_ipset, update_status = E('em', {}, _('Status') + ' : ' + _('disabled'));
]); };
}; };
return E([
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)
),
E('div', { 'class': 'cbi-section fade-in' },
E('div', { 'class': 'cbi-section-node' }, iptables)
),
E('div', { 'class': 'cbi-section fade-in' },
E('div', { 'class': 'cbi-section-node' }, ipset)
),
]);
},
L.Poll.add(this.poll_info); handleSave : null,
} else { handleSaveApply: null,
update_status = E('em', {}, _('Status') + ' : ' + _('disabled')); handleReset : null,
};
};
return E([
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)
),
E('div', { 'class': 'cbi-section fade-in' },
E('div', { 'class': 'cbi-section-node' }, iptables)
),
E('div', { 'class': 'cbi-section fade-in' },
E('div', { 'class': 'cbi-section-node' }, ipset)
),
]);
},
handleSave: null,
handleSaveApply: null,
handleReset: null,
}); });
@@ -8,13 +8,13 @@ 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}/),
isLoggerChecked: false, isLoggerChecked: false,
entriesHandler: null, entriesHandler : null,
// logd // logd
logdHandler: function(strArray, lineNum) { 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; 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 '';
}); });
}; };
@@ -4,342 +4,349 @@
'require ui'; 'require ui';
'require view.ruantiblock.tools as tools'; 'require view.ruantiblock.tools as tools';
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=[]) {
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");
let btn_update = elems[4] || document.getElementById("btn_update");
let btn_tp = elems[3] || document.getElementById("btn_tp");
btn_start.disabled = bool;
btn_update.disabled = bool;
btn_destroy.disabled = bool;
if(btn === btn_update) {
btn_enable.disabled = false;
} else {
btn_enable.disabled = bool;
};
if(btn_tp) {
btn_tp.disabled = bool
};
}
function get_app_status() {
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),
]).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'
)));
});
}
function set_app_status(status_array, elems=[], force_app_code) {
let section = uci.get(tools.app_name, 'config');
if(!status_array || typeof(section) !== 'object') {
(elems[0] || document.getElementById("status")).innerHTML = tools.make_status_string(1);
ui.addNotification(null, E('p', _('Unable to read the contents')
+ ': set_app_status()'));
disable_buttons(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 proxy_local_clients = section.proxy_local_clients;
let proxy_mode = section.proxy_mode;
let bllist_mode = section.bllist_mode;
let bllist_module = section.bllist_module;
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;
} else {
btn_enable.onclick = ui.createHandlerFn(this, button_action, 'enable');
btn_enable.textContent = _('Enable');
btn_enable.className = btn_style_save;
};
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;
} else {
btn_tp.onclick = ui.createHandlerFn(this, button_action, 'total-proxy-on');
btn_tp.textContent = _('Enable');
btn_tp.className = btn_style_save;
};
};
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;
}
function btn_start_state_off() {
btn_start.onclick = ui.createHandlerFn(this, button_action, 'start');
btn_start.textContent = _('Enable');
btn_start.className = btn_style_action;
}
if(app_status_code == 0) {
disable_buttons(false, null, elems);
btn_start_state_on();
btn_destroy.disabled = false;
btn_update.disabled = false;
if(btn_tp) {
btn_tp.disabled = false;
};
}
else if(app_status_code == 2) {
disable_buttons(false, null, elems);
btn_start_state_off();
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);
}
else if(app_status_code == 4) {
btn_start_state_on();
disable_buttons(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);
};
(elems[0] || document.getElementById("status")).innerHTML = tools.make_status_string(
app_status_code,
proxy_mode,
bllist_mode,
bllist_module,
bllist_source,
tp_status_code,
vpn_route_status_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);
});
};
return fs.exec_direct(cmd, [ action ]).then(res => {
return get_app_status().then(
(status_array) => {
set_app_status(status_array);
ui.hideModal();
});
});
}
return L.view.extend({ return L.view.extend({
poll_status: function() { statusTokenValue: null,
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);
}
status_token_value = v;
}).catch(e => {
status_token_value = 0;
});
},
dialog_destroy: function(ev) { disableButtons: function(bool, btn, elems=[]) {
ev.target.blur(); let btn_start = elems[1] || document.getElementById("btn_start");
let cancel_button = E('button', { let btn_destroy = elems[5] || document.getElementById("btn_destroy");
'class': btn_style_neutral, let btn_enable = elems[2] || document.getElementById("btn_enable");
'click': ui.hideModal, let btn_update = elems[4] || document.getElementById("btn_update");
}, _('Cancel')); let btn_tp = elems[3] || document.getElementById("btn_tp");
let shutdown_btn = E('button', { btn_start.disabled = bool;
'class': btn_style_warning, btn_update.disabled = bool;
}, _('Shutdown')); btn_destroy.disabled = bool;
shutdown_btn.onclick = ui.createHandlerFn(this, function() { if(btn === btn_update) {
cancel_button.disabled = true; btn_enable.disabled = false;
return button_action('destroy'); } else {
}); btn_enable.disabled = bool;
};
if(btn_tp) {
btn_tp.disabled = bool;
};
},
ui.showModal(_('Shutdown'), [ getAppStatus: function() {
E('div', { 'class': 'cbi-section' }, [ return Promise.all([
E('p', _('The service will be disabled and all blacklist data will be deleted. Continue?')), fs.exec(tools.execPath, [ 'raw-status' ]),
]), fs.exec(tools.execPath, [ 'total-proxy-status' ]),
E('div', { 'class': 'right' }, [ fs.exec(tools.execPath, [ 'vpn-route-status' ]),
shutdown_btn, tools.getInitStatus(tools.appName),
' ', L.resolveDefault(fs.read(tools.tokenFile), 0),
cancel_button, uci.load(tools.appName),
]) ]).catch(e => {
]); ui.addNotification(null, E('p', _('Unable to execute or read contents')
}, + ': %s [ %s | %s | %s ]'.format(
e.message, tools.execPath, 'tools.getInitStatus', 'uci.ruantiblock'
)));
});
},
load: function() { setAppStatus: function(status_array, elems=[], force_app_code) {
return get_app_status(); let section = uci.get(tools.appName, 'config');
}, if(!status_array || typeof(section) !== 'object') {
(elems[0] || document.getElementById("status")).innerHTML = tools.makeStatusString(1);
ui.addNotification(null, E('p', _('Unable to read the contents')
+ ': setAppStatus()'));
this.disableButtons(true, null, elems);
return;
};
render: function(status_array) { let app_status_code = (force_app_code) ? force_app_code : status_array[0].code;
if(!status_array) { let tp_status_code = status_array[1].code;
return; let vpn_route_status_code = status_array[2].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;
let bllist_module = section.bllist_module;
let bllist_source = section.bllist_source;
let section = uci.get(tools.app_name, 'config'); let btn_enable = elems[2] || document.getElementById('btn_enable');
let proxy_local_clients = (typeof(section) === 'object') ? section.proxy_local_clients : null; if(enabled_flag == true) {
status_token_value = (Array.isArray(status_array)) ? tools.normalize_value(status_array[4]) : null; 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, this.serviceAction, 'enable', 'btn_enable');
btn_enable.textContent = _('Disabled');
btn_enable.className = btn_style_negative;
};
let status_string = E('div', { let btn_tp = elems[3] || document.getElementById('btn_tp');
'id': 'status', if(btn_tp) {
'name': 'status', if(tp_status_code == 0) {
'class': 'cbi-section-node', 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, this.appAction, 'total-proxy-on', 'btn_tp');
btn_tp.textContent = _('Disabled');
btn_tp.className = btn_style_negative;
};
};
let layout = E('div', { 'class': 'cbi-section-node' }); 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 layout_append(elem, title, descr) { let btnStartStateOn = () => {
descr = (descr) ? E('div', { 'class': 'cbi-value-description' }, descr) : ''; btn_start.onclick = ui.createHandlerFn(
layout.append( this, this.serviceAction, 'stop', 'btn_start');
E('div', { 'class': 'cbi-value' }, [ btn_start.textContent = _('Enabled');
E('label', { 'class': 'cbi-value-title' }, title), btn_start.className = btn_style_positive;
E('div', { 'class': 'cbi-value-field' }, [ elem, descr ]), }
])
)
};
let btn_start = E('button', { let btnStartStateOff = () => {
'id': 'btn_start', btn_start.onclick = ui.createHandlerFn(
'name': 'btn_start', this, this.serviceAction,'start', 'btn_start');
'class': btn_style_action, btn_start.textContent = _('Disabled');
}, _('Enable')); btn_start.className = btn_style_negative;
layout_append(btn_start, _('Service')); }
let btn_enable = E('button', { if(app_status_code == 0) {
'id': 'btn_enable', this.disableButtons(false, null, elems);
'name': 'btn_enable', btnStartStateOn();
'class': btn_style_save, btn_destroy.disabled = false;
}, _('Enable')); btn_update.disabled = false;
layout_append(btn_enable, _('Run at startup')); if(btn_tp) {
btn_tp.disabled = false;
};
}
else if(app_status_code == 2) {
this.disableButtons(false, null, elems);
btnStartStateOff();
btn_update.disabled = true;
if(btn_tp) {
btn_tp.disabled = true;
};
}
else if(app_status_code == 3) {
btnStartStateOff();
this.disableButtons(true, btn_start, elems);
}
else if(app_status_code == 4) {
btnStartStateOn();
this.disableButtons(true, btn_update, elems);
}
else {
ui.addNotification(null, E('p', _('Error')
+ ' %s: return code = %s'.format(tools.execPath, app_status_code)));
this.disableButtons(true, null, elems);
};
let btn_tp = E('button', { (elems[0] || document.getElementById("status")).innerHTML = tools.makeStatusString(
'id': 'btn_tp', app_status_code,
'name': 'btn_tp', proxy_mode,
'class': btn_style_save, bllist_mode,
}, _('Enable')); bllist_module,
if(proxy_local_clients == '0') { bllist_source,
layout_append(btn_tp, _('Total-proxy'), tp_status_code,
_('All traffic goes through the proxy without applying rules')); vpn_route_status_code);
};
let btn_update = E('button', { if(!L.Poll.active()) {
'id': 'btn_update', L.Poll.start();
'name': 'btn_update', };
'class': btn_style_action, },
}, _('Update'));
btn_update.onclick = ui.createHandlerFn(this, () => { button_action('update') });
layout_append(btn_update, _('Update blacklist'));
let btn_destroy = E('button', { serviceAction: function(action, button) {
'id': 'btn_destroy', if(button) {
'name': 'btn_destroy', let elem = document.getElementById(button);
'class': btn_style_reset, this.disableButtons(true, elem);
}, _('Shutdown')); };
btn_destroy.onclick = this.dialog_destroy;
layout_append(btn_destroy, _('Shutdown'), L.Poll.stop();
_('Complete service shutdown, as well as deleting ipsets and blacklist data'));
set_app_status(status_array, [ return tools.handleServiceAction(tools.appName, action).then(() => {
status_string, return this.getAppStatus().then(
btn_start, (status_array) => {
btn_enable, this.setAppStatus(status_array);
btn_tp, }
btn_update, );
btn_destroy, });
]); },
L.Poll.add(this.poll_status); appAction: function(action, button) {
if(button) {
let elem = document.getElementById(button);
this.disableButtons(true, elem);
};
return E([ L.Poll.stop();
E('h2', { 'class': 'fade-in' }, _('Ruantiblock')),
E('div', { 'class': 'cbi-section-descr fade-in' },
E('a', {
'href': 'https://github.com/gSpotx2f/ruantiblock_openwrt/wiki',
'target': '_blank' },
'https://github.com/gSpotx2f/ruantiblock_openwrt/wiki')
),
E('div', { 'class': 'cbi-section fade-in' }, [
status_string,
E('hr'),
]),
E('div', { 'class': 'cbi-section fade-in' },
layout
),
]);
},
handleSave: null, if(action === 'update') {
handleSaveApply: null, this.getAppStatus().then(status_array => {
handleReset: null, 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,
'click': ui.hideModal,
}, _('Cancel'));
let shutdown_btn = E('button', {
'class': btn_style_warning,
}, _('Shutdown'));
shutdown_btn.onclick = ui.createHandlerFn(this, () => {
cancel_button.disabled = true;
return this.appAction('destroy');
});
ui.showModal(_('Shutdown'), [
E('div', { 'class': 'cbi-section' }, [
E('p', _('The service will be disabled and all blacklist data will be deleted. Continue?')),
]),
E('div', { 'class': 'right' }, [
shutdown_btn,
' ',
cancel_button,
])
]);
},
load: function() {
return this.getAppStatus();
},
render: function(status_array) {
if(!status_array) {
return;
};
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',
'class': 'cbi-section-node',
});
let layout = E('div', { 'class': 'cbi-section-node' });
function layout_append(elem, title, descr) {
descr = (descr) ? E('div', { 'class': 'cbi-value-description' }, descr) : '';
layout.append(
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title' }, title),
E('div', { 'class': 'cbi-value-field' }, [ elem, descr ]),
])
)
};
let btn_start = E('button', {
'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_positive,
}, _('Enable'));
layout_append(btn_enable, _('Run at startup'));
let btn_tp = E('button', {
'id' : 'btn_tp',
'name' : 'btn_tp',
'class': btn_style_positive,
}, _('Enable'));
if(proxy_local_clients == '0') {
layout_append(btn_tp, _('Total-proxy'),
_('All traffic goes through the proxy without applying rules'));
};
let btn_update = E('button', {
'id' : 'btn_update',
'name' : 'btn_update',
'class': btn_style_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_negative,
}, _('Shutdown'));
btn_destroy.onclick = L.bind(this.dialogDestroy, this);
layout_append(btn_destroy, _('Shutdown'),
_('Complete service shutdown, as well as deleting ipsets and blacklist data'));
this.setAppStatus(status_array, [
status_string,
btn_start,
btn_enable,
btn_tp,
btn_update,
btn_destroy,
]);
L.Poll.add(L.bind(this.statusPoll, this));
return E([
E('h2', { 'class': 'fade-in' }, _('Ruantiblock')),
E('div', { 'class': 'cbi-section-descr fade-in' },
E('a', {
'href': 'https://github.com/gSpotx2f/ruantiblock_openwrt/wiki',
'target': '_blank' },
'https://github.com/gSpotx2f/ruantiblock_openwrt/wiki')
),
E('div', { 'class': 'cbi-section fade-in' }, [
status_string,
E('hr'),
]),
E('div', { 'class': 'cbi-section fade-in' },
layout
),
]);
},
handleSave : null,
handleSaveApply: null,
handleReset : null,
}); });
@@ -6,393 +6,354 @@
'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 = [];
function depends(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);
};
function validate_ip_port(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({
string: null,
renderWidget: function(section_id, option_index, cfgvalue) {
this.title = this.description = null;
return E([
E('label', { 'class': 'cbi-value-title' }),
E('div', { 'class': 'cbi-value-field' },
E('b', {}, this.string)
),
]);
},
});
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({ return L.view.extend({
app_status_code: null, availableParsers: {},
load: function() { appStatusCode : null,
return Promise.all([
L.resolveDefault(fs.exec(tools.exec_path, [ 'raw-status' ]), 1),
fs.list(tools.parsers_dir),
uci.load('network'),
]).catch(e => {
ui.addNotification(null, E('p', _('Unable to read the contents')
+ ': %s<br />[ %s ]'.format(
e.message, tools.parsers_dir
)));
});
},
render: function(data) { depends: function(elem, key, array, empty=true) {
if(!data) { if(empty && array.length === 0) {
return; elem.depends(key, '_dummy');
}; } else {
this.app_status_code = data[0].code; array.forEach(e => elem.depends(key, e));
let p_dir_arr = data[1]; };
let lan_iface = uci.get('network', 'lan', 'ifname') || 'eth0'; },
let vpn_iface = uci.get('network', 'VPN', 'ifname') || 'tun0';
if(p_dir_arr) { dependsBllistModule: function(elem) {
p_dir_arr.forEach(e => { this.depends(elem, 'bllist_module', Object.values(this.availableParsers));
let fname = e.name; },
if(fname.startsWith('ruab_parser')) {
available_parsers.push(tools.parsers_dir + '/' + fname);
};
});
};
let m, s, o; 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`;
},
m = new form.Map(tools.app_name, _('Ruantiblock') + ' - ' + _('Settings')); CBIBlockTitle: form.DummyValue.extend({
string: null,
s = m.section(form.NamedSection, 'config'); renderWidget: function(section_id, option_index, cfgvalue) {
s.anonymous = true; this.title = this.description = null;
s.addremove = false; return E([
E('label', { 'class': 'cbi-value-title' }),
E('div', { 'class': 'cbi-value-field' },
E('b', {}, this.string)
),
]);
},
}),
/* Main settings tab */ load: function() {
return Promise.all([
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 [ %s ]'.format(
e.message, tools.parsersDir
)));
});
},
s.tab('main_settings', _('Main settings')); render: function(data) {
if(!data) {
return;
};
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';
// PROXY_MODE if(p_dir_arr) {
if(this.app_status_code == 1 || this.app_status_code == 2) { p_dir_arr.forEach(e => {
o = s.taboption('main_settings', form.ListValue, 'proxy_mode', let fname = e.name;
_('Proxy mode')); if(fname.startsWith('ruab_parser')) {
o.value('1', 'Tor'); this.availableParsers[fname] = tools.parsersDir + '/' + fname;
o.value('2', 'VPN'); };
}; });
};
// PROXY_LOCAL_CLIENTS let ip_filter_edit = new tools.fileEditDialog(
let proxy_local_clients = s.taboption('main_settings', form.Flag, 'proxy_local_clients', tools.ipFilterFile,
_("Apply proxy rules to router application traffic")); _('IP filter'),
proxy_local_clients.rmempty = false; _('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):') +
proxy_local_clients.default = proxy_local_clients.enabled; '<br /><code>128[.]199[.]0[.]0/16<br />34[.]217[.]90[.]52<br />162[.]13[.]190[.]</code>'
);
// USE_LOGGER let fqdn_filter_edit = new tools.fileEditDialog(
o = s.taboption('main_settings', form.Flag, 'use_logger', tools.fqdnFilterFile,
_('Logging events')); _('FQDN filter'),
o.rmempty = false; _('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:') +
o.default = 1; '<br /><code>poker<br />[ck]?a[sz]ino?<br />[vw]ulkan<br />slots?</code>'
);
// DEF_TOTAL_PROXY let user_entries_edit = new tools.fileEditDialog(
o = s.taboption('main_settings', form.Flag, 'def_total_proxy', tools.userEntriesFile,
_("Enable the 'total-proxy' option at startup")); _('User entries'),
o.rmempty = false; _('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:') +
o.default = 0; '<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>'
o.depends('proxy_local_clients', '0'); );
// IPSET_CLEAR_SETS let torrc_edit = new tools.fileEditDialog(
o = s.taboption('main_settings', form.Flag, 'ipset_clear_sets', tools.torrcFile,
_('Clean up ipsets before updating blacklist')); _('Tor configuration file'),
o.description = _('Reduces RAM consumption during update'); null,
o.rmempty = false; function(rc) {
o.default = 0; return tools.getInitStatus('tor').then(res => {
if(res) {
return tools.handleServiceAction('tor', 'restart');
};
});
}
);
let m, s, o;
m = new form.Map(tools.appName, _('Ruantiblock') + ' - ' + _('Settings'));
s = m.section(form.NamedSection, 'config');
s.anonymous = true;
s.addremove = false;
/* Main settings tab */
s.tab('main_settings', _('Main settings'));
// PROXY_MODE
if(this.appStatusCode == 1 || this.appStatusCode == 2) {
o = s.taboption('main_settings', form.ListValue, 'proxy_mode',
_('Proxy mode'));
o.value('1', 'Tor');
o.value('2', 'VPN');
};
// PROXY_LOCAL_CLIENTS
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;
// USE_LOGGER
o = s.taboption('main_settings', form.Flag, 'use_logger',
_('Logging events'));
o.rmempty = false;
// DEF_TOTAL_PROXY
o = s.taboption('main_settings', form.Flag, 'def_total_proxy',
_("Enable the 'total-proxy' option at startup"));
o.rmempty = false;
o.default = 0;
o.depends('proxy_local_clients', '0');
// IPSET_CLEAR_SETS
o = s.taboption('main_settings', form.Flag, 'ipset_clear_sets',
_('Clean up ipsets before updating blacklist'));
o.description = _('Reduces RAM consumption during update');
o.rmempty = false;
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'));
// IF_LAN // IF_LAN
o = s.taboption('tor_settings', widgets.DeviceSelect, 'if_lan', o = s.taboption('tor_settings', widgets.DeviceSelect, 'if_lan',
_('LAN interface')); _('LAN interface'));
o.multiple = false; o.multiple = false;
o.noaliases = true; o.noaliases = true;
o.rmempty = false; o.rmempty = false;
o.default = lan_iface; o.default = lan_iface;
// TOR_TRANS_PORT // TOR_TRANS_PORT
o = s.taboption('tor_settings', form.Value, 'tor_trans_port', o = s.taboption('tor_settings', form.Value, 'tor_trans_port',
_('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',
_('Tor configuration file')); _('Tor configuration file'));
o.onclick = () => torrc_edit.show(); o.onclick = () => torrc_edit.show();
o.inputtitle = _('Edit'); o.inputtitle = _('Edit');
o.inputstyle = 'edit btn'; o.inputstyle = 'edit btn';
/* VPN tab */ /* VPN tab */
s.tab('vpn_settings', _('VPN mode')); s.tab('vpn_settings', _('VPN mode'));
// IF_VPN // IF_VPN
o = s.taboption('vpn_settings', widgets.DeviceSelect, 'if_vpn', o = s.taboption('vpn_settings', widgets.DeviceSelect, 'if_vpn',
_('VPN interface')); _('VPN interface'));
o.multiple = false; o.multiple = false;
o.noaliases = true; o.noaliases = true;
o.rmempty = false; o.rmempty = false;
o.default = vpn_iface; o.default = vpn_iface;
}; };
/* Parser settings tab */ /* Parser settings tab */
s.tab('parser_settings', _('Blacklist settings')); s.tab('parser_settings', _('Blacklist settings'));
// 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.datatype = 'uinteger'; o.rmempty = false;
o.default = '0'; o.datatype = 'uinteger';
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:')
+ ' ' + _('net pattern') + ' (' + _('e.g:') + ' 192.168.3.)\n';
};
depends_bllist_module(o); // SUMMARIZE_IP
o = s.taboption('parser_settings', form.Flag, 'summarize_ip',
_("Summarize IP ranges"));
o.rmempty = false;
// SUMMARIZE_IP // SUMMARIZE_CIDR
o = s.taboption('parser_settings', form.Flag, 'summarize_ip', o = s.taboption('parser_settings', form.Flag, 'summarize_cidr',
_("Summarize IP ranges")); _("Summarize '/24' networks"));
o.rmempty = false; o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// SUMMARIZE_CIDR o = s.taboption('parser_settings', this.CBIBlockTitle, '_dummy_fqdn');
o = s.taboption('parser_settings', form.Flag, 'summarize_cidr', o.string = _('FQDN configuration') + ':';
_("Summarize '/24' networks"));
o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
o = s.taboption('parser_settings', CBIBlockTitle, '_dummy_fqdn'); // SD_LIMIT
o.string = _('FQDN configuration') + ':'; o = s.taboption('parser_settings', form.Value, 'sd_limit',
depends_bllist_module(o); _("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';
// SD_LIMIT // OPT_EXCLUDE_SLD
o = s.taboption('parser_settings', form.Value, 'sd_limit', o = s.taboption('parser_settings', form.DynamicList, 'opt_exclude_sld',
_("Subdomains limit")); _('2nd level domains that are excluded from optimization'));
o.description = _('The number of subdomains in the domain, upon reaching which the entire 2nd level domain is added to the list'); o.description = _('e.g:') + ' <code>livejournal.com</code>';
o.datatype = 'uinteger'; o.placeholder = _('e.g:') + ' livejournal.com';
o.default = '16'; o.datatype = "hostname";
depends_bllist_module(o);
// OPT_EXCLUDE_SLD // USE_IDN
o = s.taboption('parser_settings', form.DynamicList, 'opt_exclude_sld', o = s.taboption('parser_settings', form.Flag, 'use_idn',
_('2nd level domains that are excluded from optimization')); _("Convert cyrillic domains to punycode"));
o.datatype = "hostname"; o.rmempty = false;
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 // ALT_NSLOOKUP
o = s.taboption('parser_settings', form.Flag, 'use_idn', o = s.taboption('parser_settings', form.Flag, 'alt_nslookup',
_("Convert cyrillic domains to punycode")); _('Use optional DNS resolver'));
o.rmempty = false; o.rmempty = false;
o.default = 0;
depends_bllist_module(o);
// ALT_NSLOOKUP // ALT_DNS_ADDR
o = s.taboption('parser_settings', form.Flag, 'alt_nslookup', o = s.taboption('parser_settings', form.Value, 'alt_dns_addr',
_('Use optional DNS resolver')); _("Optional DNS resolver"), '<code>ipaddress[#port]</code>');
o.rmempty = false; o.rmempty = false;
o.default = 0; o.validate = this.validateIpPort;
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';
/* 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',
_("IP filter")); _("IP filter"));
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',
_("FQDN filter")); _("FQDN filter"));
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 */
s.tab('user_entries_tab', _('User entries')); s.tab('user_entries_tab', _('User entries'));
// ADD_USER_ENTRIES // ADD_USER_ENTRIES
o = s.taboption('user_entries_tab', form.Flag, 'add_user_entries', o = s.taboption('user_entries_tab', form.Flag, 'add_user_entries',
_('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',
_('User entries')); _('User entries'));
o.onclick = () => user_entries_edit.show(); o.onclick = () => user_entries_edit.show();
o.inputtitle = _('Edit'); o.inputtitle = _('Edit');
o.inputstyle = 'edit btn'; o.inputstyle = 'edit btn';
let map_promise = m.render();
map_promise.then(node => node.classList.add('fade-in'));
return map_promise;
},
let map_promise = m.render(); handleSaveApply: function(ev, mode) {
map_promise.then(node => node.classList.add('fade-in')); return this.handleSave(ev).then(() => {
ui.changes.apply(mode == '0');
return map_promise; if(this.appStatusCode != 1 && this.appStatusCode != 2) {
}, window.setTimeout(() => tools.handleServiceAction(
tools.appName, 'restart'), 3000);
handleSaveApply: function(ev, mode) { };
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);
};
});
},
}); });
@@ -1,252 +1,291 @@
'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'},
` `
.label-status { .label-status {
display: inline; display: inline;
margin: 0px 2px 0px 0 !important; margin: 0px 2px 0px 0 !important;
padding: 1px 4px 2px 4px; padding: 1px 4px 2px 4px;
-webkit-border-radius: 3px; -webkit-border-radius: 3px;
-moz-border-radius: 3px; -moz-border-radius: 3px;
border-radius: 3px; border-radius: 3px;
font-weight: bold; font-weight: bold;
color: #fff !important; color: #fff !important;
} }
.starting { .starting {
background-color: #a7b668 !important; background-color: #a7b668 !important;
} }
.running { .running {
background-color: #2ea256 !important; background-color: #2ea256 !important;
} }
.updating { .updating {
background-color: #1e82ff !important; background-color: #1e82ff !important;
} }
.stopped { .stopped {
background-color: #acacac !important; background-color: #acacac !important;
} }
.error { .error {
background-color: #ff4e54 !important; background-color: #ff4e54 !important;
} }
.total-proxy { .total-proxy {
background-color: #ffb937 !important; background-color: #ffb937 !important;
} }
`)); `));
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({
return (v && typeof(v) === 'string') ? v.trim().replace(/\r?\n/g, '') : v; object: 'luci',
}, method: 'getInitList',
params: [ 'name' ],
expect: { '': {} }
}),
make_status_string: function( callInitAction: rpc.declare({
app_status_code, object: 'luci',
proxy_mode, method: 'setInitAction',
bllist_mode, params: [ 'name', 'action' ],
bllist_module, expect: { result: false }
bllist_source, }),
tp_status_code,
vpn_route_status_code) {
let app_status_label;
let spinning = '';
switch(app_status_code) { getInitStatus: function(name) {
case 0: return this.callInitStatus(name).then(res => {
app_status_label = this.info_label_running; if(res) {
break; return res[name].enabled;
case 2: } else {
app_status_label = this.info_label_stopped; throw _('Command failed');
break; }
case 3: }).catch(e => {
app_status_label = this.info_label_starting; ui.addNotification(null,
spinning = ' spinning'; E('p', _('Failed to get %s init status: %s').format(name, e)));
break; });
case 4: },
app_status_label = this.info_label_updating;
spinning = ' spinning';
break;
default:
app_status_label = this.info_label_error;
return `<div class="table">
<div class="tr">
<div class="td left" style="min-width:33%%">
${_('Status')}:
</div>
<div class="td left">
${app_status_label}
</div>
</div>
</div>`
};
return `<div class="table"> handleServiceAction: function(name, action) {
<div class="tr"> return this.callInitAction(name, action).then(success => {
<div class="td left" style="min-width:33%%"> if(!success) {
${_('Status')}: throw _('Command failed');
</div> };
<div class="td left%s"> return true;
%s %s %s }).catch(e => {
</div> ui.addNotification(null,
</div> E('p', _('Service action failed "%s %s": %s').format(name, action, e)));
<div class="tr"> });
<div class="td left"> },
${_('Proxy mode')}:
</div>
<div class="td left">
%s
</div>
</div>
<div class="tr">
<div class="td left">
${_('Blacklist update mode')}:
</div>
<div class="td left">
%s
</div>
</div>
%s
</div>
`.format(
spinning,
app_status_label,
(tp_status_code == 0) ? '<span class="label-status total-proxy">'
+ _('Total-proxy is on') + '</span>' : '',
(app_status_code != 2 && proxy_mode == 2 && vpn_route_status_code != 0)
? '<span class="label-status error">'
+ _('VPN routing error! Need restart') + '</span>' : '',
(proxy_mode == 1) ? 'Tor' : 'VPN',
(!bllist_module || bllist_module === '') ? _('user entries only') : bllist_mode,
(!bllist_module || bllist_module === '') ? '' :
`<div class="tr">
<div class="td left">
${_('Blacklist source')}:
</div>
<div class="td left">
<span style="cursor:help; border-bottom:1px dotted" data-tooltip="${this.blacklist_sources[bllist_source]}">
${bllist_source}
</span>
</div>
</div>`
);
},
file_edit_dialog: L.Class.extend({ normalizeValue: function(v) {
__init__: function(file, title, description, callback, file_exists=false) { return (v && typeof(v) === 'string') ? v.trim().replace(/\r?\n/g, '') : v;
this.file = file; },
this.title = title;
this.description = description;
this.callback = callback;
this.file_exists = file_exists;
},
load: function() { makeStatusString: function(
return fs.read(this.file); app_status_code,
}, proxy_mode,
bllist_mode,
bllist_module,
bllist_source,
tp_status_code,
vpn_route_status_code) {
let app_status_label;
let spinning = '';
render: function(content) { switch(app_status_code) {
ui.showModal(this.title, [ case 0:
E('div', { 'class': 'cbi-section' }, [ app_status_label = this.infoLabelRunning;
E('div', { 'class': 'cbi-section-descr' }, this.description), break;
E('div', { 'class': 'cbi-section' }, case 2:
E('p', {}, app_status_label = this.infoLabelStopped;
E('textarea', { break;
'id': 'widget.modal_content', case 3:
'class': 'cbi-input-textarea', app_status_label = this.infoLabelStarting;
'style': 'width:100% !important', spinning = ' spinning';
'rows': 10, break;
'wrap': 'off', case 4:
'spellcheck': 'false', app_status_label = this.infoLabelUpdating;
}, spinning = ' spinning';
content || '') break;
) default:
), app_status_label = this.infoLabelError;
]), return `<div class="table">
E('div', { 'class': 'right' }, [ <div class="tr">
E('button', { <div class="td left" style="min-width:33%%">
'class': 'btn', ${_('Status')}:
'click': ui.hideModal, </div>
}, _('Dismiss')), <div class="td left">
' ', ${app_status_label}
E('button', { </div>
'id': 'btn_save', </div>
'class': 'btn cbi-button-positive important', </div>`
'click': ui.createHandlerFn(this, this.handleSave), };
}, _('Save')),
]),
]);
},
handleSave: function(ev) { return `<div class="table">
let textarea = document.getElementById('widget.modal_content'); <div class="tr">
let value = textarea.value.trim().replace(/\r\n/g, '\n') + '\n'; <div class="td left" style="min-width:33%%">
${_('Status')}:
</div>
<div class="td left%s">
%s %s %s
</div>
</div>
<div class="tr">
<div class="td left">
${_('Proxy mode')}:
</div>
<div class="td left">
%s
</div>
</div>
<div class="tr">
<div class="td left">
${_('Blacklist update mode')}:
</div>
<div class="td left">
%s
</div>
</div>
%s
</div>
`.format(
spinning,
app_status_label,
(tp_status_code == 0) ? '<span class="label-status total-proxy">'
+ _('Total-proxy is on') + '</span>' : '',
(app_status_code != 2 && proxy_mode == 2 && vpn_route_status_code != 0)
? '<span class="label-status error">'
+ _('VPN routing error! Need restart') + '</span>' : '',
(proxy_mode == 1) ? 'Tor' : 'VPN',
(!bllist_module || bllist_module === '') ? _('user entries only') : bllist_mode,
(!bllist_module || bllist_module === '') ? '' :
`<div class="tr">
<div class="td left">
${_('Blacklist source')}:
</div>
<div class="td left">
<span style="cursor:help; border-bottom:1px dotted" data-tooltip="${this.blacklistSources[bllist_source]}">
${bllist_source}
</span>
</div>
</div>`
);
},
return fs.write(this.file, value).then(async rc => { fileEditDialog: L.Class.extend({
textarea.value = value; __init__: function(file, title, description, callback, file_exists=false) {
ui.addNotification(null, E('p', _('Contents have been saved.')), this.file = file;
'info'); this.title = title;
if(this.callback) { this.description = description;
return this.callback(rc); this.callback = callback;
}; this.file_exists = file_exists;
}).catch(e => { },
ui.addNotification(null, E('p', _('Unable to save the contents')
+ ': %s'.format(e.message)));
}).finally(() => {
ui.hideModal();
});
},
error: function(e) { load: function() {
if(!this.file_exists && e instanceof Error && e.name === 'NotFoundError') { return fs.read(this.file);
return this.render(); },
} else {
ui.showModal(this.title, [
E('div', { 'class': 'cbi-section' },
E('p', {}, _('Unable to read the contents')
+ ': %s'.format(e.message))
),
E('div', { 'class': 'right' },
E('button', {
'class': 'btn',
'click': ui.hideModal,
}, _('Dismiss'))
),
]);
};
},
show: function() { render: function(content) {
ui.showModal(null, ui.showModal(this.title, [
E('p', { 'class': 'spinning' }, _('Loading')) E('div', { 'class': 'cbi-section' }, [
); E('div', { 'class': 'cbi-section-descr' }, this.description),
this.load().then(content => { E('div', { 'class': 'cbi-section' },
ui.hideModal(); E('p', {},
return this.render(content); E('textarea', {
}).catch(e => { 'id': 'widget.modal_content',
ui.hideModal(); 'class': 'cbi-input-textarea',
return this.error(e); 'style': 'width:100% !important',
}) 'rows': 10,
}, 'wrap': 'off',
}), 'spellcheck': 'false',
},
content || '')
)
),
]),
E('div', { 'class': 'right' }, [
E('button', {
'class': 'btn',
'click': ui.hideModal,
}, _('Dismiss')),
' ',
E('button', {
'id': 'btn_save',
'class': 'btn cbi-button-positive important',
'click': ui.createHandlerFn(this, this.handleSave),
}, _('Save')),
]),
]);
},
handleSave: function(ev) {
let textarea = document.getElementById('widget.modal_content');
let value = textarea.value.trim().replace(/\r\n/g, '\n') + '\n';
return fs.write(this.file, value).then(async rc => {
textarea.value = value;
ui.addNotification(null, E('p', _('Contents have been saved.')),
'info');
if(this.callback) {
return this.callback(rc);
};
}).catch(e => {
ui.addNotification(null, E('p', _('Unable to save the contents')
+ ': %s'.format(e.message)));
}).finally(() => {
ui.hideModal();
});
},
error: function(e) {
if(!this.file_exists && e instanceof Error && e.name === 'NotFoundError') {
return this.render();
} else {
ui.showModal(this.title, [
E('div', { 'class': 'cbi-section' },
E('p', {}, _('Unable to read the contents')
+ ': %s'.format(e.message))
),
E('div', { 'class': 'right' },
E('button', {
'class': 'btn',
'click': ui.hideModal,
}, _('Dismiss'))
),
]);
};
},
show: function() {
ui.showModal(null,
E('p', { 'class': 'spinning' }, _('Loading'))
);
this.load().then(content => {
ui.hideModal();
return this.render(content);
}).catch(e => {
ui.hideModal();
return this.error(e);
})
},
}),
}); });
+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 ""
@@ -1,37 +1,34 @@
{ {
"luci-app-ruantiblock": { "luci-app-ruantiblock": {
"description": "Grant access to ruantiblock procedures", "description": "Grant access to ruantiblock procedures",
"read": { "read": {
"cgi-io": [ "exec" ], "cgi-io": [ "exec" ],
"file": { "file": {
"/usr/bin": [ "list" ], "/usr/bin": [ "list" ],
"/etc/ruantiblock/fqdn_filter": [ "read" ], "/etc/ruantiblock/fqdn_filter": [ "read" ],
"/etc/ruantiblock/ip_filter": [ "read" ], "/etc/ruantiblock/ip_filter": [ "read" ],
"/etc/ruantiblock/user_entries": [ "read" ], "/etc/ruantiblock/user_entries": [ "read" ],
"/var/run/ruantiblock.token": [ "read" ], "/var/run/ruantiblock.token": [ "read" ],
"/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" ], "/sbin/logread -e ruantiblock": [ "exec" ],
"/etc/init.d/tor enabled": [ "exec" ], "/usr/sbin/logread -e ruantiblock": [ "exec" ]
"/etc/init.d/tor restart": [ "exec" ], },
"/etc/init.d/cron enabled": [ "exec" ], "uci": [ "network", "ruantiblock" ],
"/etc/init.d/cron enable": [ "exec" ], "ubus": {
"/etc/init.d/cron restart": [ "exec" ], "luci": [ "getInitList", "setInitAction" ]
"/sbin/logread -e ruantiblock": [ "exec" ], }
"/usr/sbin/logread -e ruantiblock": [ "exec" ] },
}, "write": {
"uci": [ "network", "ruantiblock" ] "file": {
}, "/etc/ruantiblock/fqdn_filter": [ "write" ],
"write": { "/etc/ruantiblock/ip_filter": [ "write" ],
"file": { "/etc/ruantiblock/user_entries": [ "write" ],
"/etc/ruantiblock/fqdn_filter": [ "write" ], "/etc/tor/torrc": [ "write" ],
"/etc/ruantiblock/ip_filter": [ "write" ], "/etc/crontabs/root": [ "write" ]
"/etc/ruantiblock/user_entries": [ "write" ], },
"/etc/tor/torrc": [ "write" ], "uci": [ "ruantiblock" ]
"/etc/crontabs/root": [ "write" ] }
}, }
"uci": [ "ruantiblock" ]
}
}
} }
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+33 -33
View File
@@ -1,35 +1,35 @@
config main 'config' config main 'config'
option proxy_mode '1' option proxy_mode '1'
option proxy_local_clients '1' option proxy_local_clients '1'
option ipset_clear_sets '0' option ipset_clear_sets '0'
option if_lan 'eth0' option if_lan 'eth0'
option if_vpn 'tun0' option if_vpn 'tun0'
option tor_trans_port '9040' option tor_trans_port '9040'
option onion_dns_addr '127.0.0.1#9053' option onion_dns_addr '127.0.0.1#9053'
option add_user_entries '0' option add_user_entries '0'
option use_logger '1' option use_logger '1'
option def_total_proxy '0' option def_total_proxy '0'
option bllist_source 'zapret-info' option bllist_source 'zapret-info'
option bllist_mode 'ip' option bllist_mode 'ip'
option ip_limit '0' option ip_limit '0'
option summarize_ip '0' option summarize_ip '0'
option summarize_cidr '0' option summarize_cidr '0'
option ip_filter '0' option ip_filter '0'
option sd_limit '16' option sd_limit '16'
list opt_exclude_sld 'livejournal.com' list opt_exclude_sld 'livejournal.com'
list opt_exclude_sld 'facebook.com' list opt_exclude_sld 'facebook.com'
list opt_exclude_sld 'vk.com' list opt_exclude_sld 'vk.com'
list opt_exclude_sld 'blog.jp' list opt_exclude_sld 'blog.jp'
list opt_exclude_sld 'msk.ru' list opt_exclude_sld 'msk.ru'
list opt_exclude_sld 'net.ru' list opt_exclude_sld 'net.ru'
list opt_exclude_sld 'org.ru' list opt_exclude_sld 'org.ru'
list opt_exclude_sld 'net.ua' list opt_exclude_sld 'net.ua'
list opt_exclude_sld 'com.ua' list opt_exclude_sld 'com.ua'
list opt_exclude_sld 'org.ua' list opt_exclude_sld 'org.ua'
list opt_exclude_sld 'co.uk' list opt_exclude_sld 'co.uk'
list opt_exclude_sld 'amazonaws.com' list opt_exclude_sld 'amazonaws.com'
option fqdn_filter '0' option fqdn_filter '0'
option use_idn '0' option use_idn '0'
option alt_nslookup '0' option alt_nslookup '0'
option alt_dns_addr '8.8.8.8' option alt_dns_addr '8.8.8.8'
@@ -4,5 +4,5 @@ IF_VPN=`uci get ruantiblock.config.if_vpn`
PROXY_MODE=`uci get ruantiblock.config.proxy_mode` PROXY_MODE=`uci get ruantiblock.config.proxy_mode`
if [ "$ACTION" = "ifup" ] && [ "$PROXY_MODE" = "2" ] && [ "$DEVICE" = "$IF_VPN" ]; then if [ "$ACTION" = "ifup" ] && [ "$PROXY_MODE" = "2" ] && [ "$DEVICE" = "$IF_VPN" ]; then
[ `/usr/bin/ruantiblock raw-status` -ne 2 ] && /usr/bin/ruantiblock reload [ `/usr/bin/ruantiblock raw-status` -ne 2 ] && /usr/bin/ruantiblock reload
fi fi
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