mirror of
https://github.com/gSpotx2f/ruantiblock_openwrt.git
synced 2026-05-14 14:40:58 +00:00
v2.0. Multi proxy for user entries. TProxy support.
This commit is contained in:
@@ -4,7 +4,9 @@
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_VERSION:=1.6.0-r1
|
||||
PKG_NAME:=luci-app-ruantiblock
|
||||
PKG_VERSION:=2.0.0
|
||||
PKG_RELEASE:=1
|
||||
LUCI_TITLE:=LuCI support for ruantiblock
|
||||
LUCI_DEPENDS:=+ruantiblock
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
@@ -10,26 +10,26 @@ return view.extend({
|
||||
|
||||
currentCrontabLines: [],
|
||||
|
||||
toDD: function(n){
|
||||
toDD(n){
|
||||
return String(n).replace(/^(\d)$/, "0$1");
|
||||
},
|
||||
|
||||
cronStatusString: function(s) {
|
||||
cronStatusString(s) {
|
||||
return s || _('No Sсhedule');
|
||||
},
|
||||
|
||||
stringifyRuabTasks: function(str_array) {
|
||||
stringifyRuabTasks(str_array) {
|
||||
let current_tasks = str_array.filter(s => s.match(this.crontabRegexp));
|
||||
return current_tasks.join('\n');
|
||||
},
|
||||
|
||||
setCronStatus: function(value) {
|
||||
setCronStatus(value) {
|
||||
document.getElementById('cron_status').value = this.cronStatusString(value);
|
||||
document.getElementById("btn_cron_del").style.visibility = (value) ?
|
||||
'visible' : 'hidden';
|
||||
},
|
||||
|
||||
writeCronFile: function() {
|
||||
writeCronFile() {
|
||||
let btn_cron_add = document.getElementById('btn_cron_add');
|
||||
let btn_cron_del = document.getElementById('btn_cron_del');
|
||||
let crontab_string = this.currentCrontabLines.join('\n');
|
||||
@@ -53,17 +53,17 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
delRuabShedules: function() {
|
||||
delRuabShedules() {
|
||||
this.currentCrontabLines = this.currentCrontabLines.filter(
|
||||
s => s.match(this.crontabRegexp) ? false : true);
|
||||
},
|
||||
|
||||
delCronSchedule: function(ev) {
|
||||
delCronSchedule(ev) {
|
||||
this.delRuabShedules();
|
||||
return this.writeCronFile();
|
||||
},
|
||||
|
||||
setCronSchedule: function(ev) {
|
||||
setCronSchedule(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;
|
||||
@@ -88,7 +88,7 @@ return view.extend({
|
||||
return this.writeCronFile();
|
||||
},
|
||||
|
||||
onchangeHourInterval: function(e) {
|
||||
onchangeHourInterval(e) {
|
||||
let value = e.target.value;
|
||||
let bool = (value != '');
|
||||
let cron_hour = document.getElementById('cron_hour');
|
||||
@@ -106,7 +106,7 @@ return view.extend({
|
||||
};
|
||||
},
|
||||
|
||||
load: function() {
|
||||
load() {
|
||||
return fs.lines(tools.crontabFile).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to read the contents')
|
||||
+ ': %s [ %s ]'.format(
|
||||
@@ -115,7 +115,7 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
render: function(content) {
|
||||
render(content) {
|
||||
this.currentCrontabLines = content;
|
||||
let current_task = this.stringifyRuabTasks(content);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ document.head.append(E('style', {'type': 'text/css'},
|
||||
return view.extend({
|
||||
pollInterval : L.env.pollinterval,
|
||||
|
||||
secToTimeString: function(value) {
|
||||
secToTimeString(value) {
|
||||
let string = '';
|
||||
if(/^\d+$/.test(value)) {
|
||||
value = Number(value);
|
||||
@@ -42,50 +42,103 @@ return view.extend({
|
||||
return string;
|
||||
},
|
||||
|
||||
formatNftJson: function(data) {
|
||||
let output = { 'rules': [] };
|
||||
if(data.rules.nftables && data.rules.nftables.length > 1) {
|
||||
for(let i of data.rules.nftables) {
|
||||
if(!i.rule) continue;
|
||||
let set, bytes;
|
||||
i.rule.expr.forEach(e => {
|
||||
if(e.match && e.match.left && e.match.left.payload) {
|
||||
set = e.match.right.replace('@', '');
|
||||
}
|
||||
else if(e.counter) {
|
||||
bytes = e.counter.bytes;
|
||||
};
|
||||
});
|
||||
output.rules.push([ set, bytes ]);
|
||||
};
|
||||
formatNftJson(data) {
|
||||
let output = { 'sink': [] };
|
||||
if(data.sink.nftables && data.sink.nftables.length > 1) {
|
||||
let rules = [];
|
||||
|
||||
function parseDnsmasqData(set) {
|
||||
let sArray = [];
|
||||
if(data[set].nftables && data[set].nftables.length > 1) {
|
||||
data[set].nftables.forEach(e => {
|
||||
if(e.set && e.set.elem) {
|
||||
e.set.elem.forEach(i => {
|
||||
if(i.elem) {
|
||||
sArray.push([ i.elem.val, i.elem.expires ]);
|
||||
};
|
||||
});
|
||||
for(let i of data.sink.nftables) {
|
||||
if(i.rule) {
|
||||
let instance = (i.rule.comment === ' ') ? '-main-' : i.rule.comment;
|
||||
let proto, bytes;
|
||||
i.rule.expr.forEach(e => {
|
||||
if(e.match && e.match.left && e.match.left.meta && e.match.left.meta.key && e.match.left.meta.key == "l4proto") {
|
||||
proto = e.match.right;
|
||||
}
|
||||
else if(e.counter) {
|
||||
bytes = e.counter.bytes;
|
||||
};
|
||||
});
|
||||
rules.push([ instance, proto, bytes ]);
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
return sArray;
|
||||
};
|
||||
|
||||
if(data.dnsmasq) {
|
||||
output.dnsmasq = parseDnsmasqData('dnsmasq');
|
||||
if(rules.length > 0) {
|
||||
output.sink = rules;
|
||||
};
|
||||
if(data.dnsmasq_bypass) {
|
||||
output.dnsmasq_bypass = parseDnsmasqData('dnsmasq_bypass');
|
||||
};
|
||||
|
||||
if(data.sink_local && data.sink_local.nftables && data.sink_local.nftables.length > 1) {
|
||||
output.sink_local = [];
|
||||
let rules = [];
|
||||
|
||||
for(let i of data.sink_local.nftables) {
|
||||
if(i.rule) {
|
||||
let instance = (i.rule.comment === ' ') ? '-main-' : i.rule.comment;
|
||||
let proto, bytes;
|
||||
i.rule.expr.forEach(e => {
|
||||
if(e.match && e.match.left && e.match.left.meta && e.match.left.meta.key && e.match.left.meta.key == "l4proto") {
|
||||
proto = e.match.right;
|
||||
}
|
||||
else if(e.counter) {
|
||||
bytes = e.counter.bytes;
|
||||
};
|
||||
});
|
||||
rules.push([ instance, proto, bytes ]);
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
};
|
||||
|
||||
if(rules.length > 0) {
|
||||
output.sink_local = rules;
|
||||
};
|
||||
};
|
||||
|
||||
function parseDnsmasqData(set) {
|
||||
let sArray = [];
|
||||
if(set.nftables && set.nftables.length > 1) {
|
||||
set.nftables.forEach(e => {
|
||||
if(e.set && e.set.elem) {
|
||||
e.set.elem.forEach(i => {
|
||||
if(i.elem) {
|
||||
sArray.push([ i.elem.val, i.elem.expires ]);
|
||||
};
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
return sArray;
|
||||
};
|
||||
|
||||
if(data.dnsmasq) {
|
||||
output.dnsmasq = parseDnsmasqData(data.dnsmasq);
|
||||
};
|
||||
if(data.dnsmasq_bypass) {
|
||||
output.dnsmasq_bypass = parseDnsmasqData(data.dnsmasq_bypass);
|
||||
};
|
||||
if(data.dnsmasq_user_instances) {
|
||||
output.dnsmasq_user_instances = [];
|
||||
if(data.dnsmasq_user_instances && data.dnsmasq_user_instances.length > 1) {
|
||||
for(let i of data.dnsmasq_user_instances) {
|
||||
if(i.nftables) {
|
||||
let name;
|
||||
i.nftables.forEach(e => {
|
||||
if(e.set) {
|
||||
name = e.set.name;
|
||||
};
|
||||
});
|
||||
output.dnsmasq_user_instances.push([ name, parseDnsmasqData(i) ]);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return output;
|
||||
},
|
||||
|
||||
makeDnsmasqTable: function(ipDataArray) {
|
||||
makeDnsmasqTable(ipDataArray, title) {
|
||||
let lines = `<tr class="tr"><td class="td center">${_('No entries available...')}</td></tr>`;
|
||||
let ipTable = E('table', { 'id': 'ipTable', 'class': 'table' });
|
||||
|
||||
@@ -122,6 +175,7 @@ return view.extend({
|
||||
};
|
||||
|
||||
return E([
|
||||
E('h3', {}, title),
|
||||
E('div', { 'class': 'log-entries-count' },
|
||||
`${_('Entries')}: ${ipDataArray.length}`
|
||||
),
|
||||
@@ -129,7 +183,7 @@ return view.extend({
|
||||
]);
|
||||
},
|
||||
|
||||
pollInfo: function() {
|
||||
pollInfo() {
|
||||
return fs.exec_direct(tools.execPath, [ 'html-info' ], 'json').catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to execute or read contents')
|
||||
+ ': %s [ %s ]'.format(e.message, tools.execPath)
|
||||
@@ -174,11 +228,20 @@ return view.extend({
|
||||
|
||||
let nft_data = this.formatNftJson(data);
|
||||
|
||||
if(nft_data.rules.length > 0) {
|
||||
for(let [set, bytes] of nft_data.rules) {
|
||||
let elem = document.getElementById('rules.' + set);
|
||||
if(nft_data.sink.length > 0) {
|
||||
for(let i of nft_data.sink) {
|
||||
let elem = document.getElementById('sink.' + i[0] + '.' + (i[1] || 'all'));
|
||||
if(elem) {
|
||||
elem.textContent = bytes;
|
||||
elem.textContent = i[2];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if(nft_data.sink_local && nft_data.sink_local.length > 0) {
|
||||
for(let i of nft_data.sink_local) {
|
||||
let elem = document.getElementById('sink_local.' + i[0] + '.' + (i[1] || 'all'));
|
||||
if(elem) {
|
||||
elem.textContent = i[2];
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -186,15 +249,16 @@ return view.extend({
|
||||
let rdTableWrapper = document.getElementById('rdTableWrapper');
|
||||
if(rdTableWrapper) {
|
||||
rdTableWrapper.innerHTML = '';
|
||||
rdTableWrapper.append(this.makeDnsmasqTable(nft_data.dnsmasq));
|
||||
rdTableWrapper.append(this.makeDnsmasqTable(nft_data.dnsmasq, _('Dnsmasq')));
|
||||
};
|
||||
|
||||
let rdbTableWrapper = document.getElementById('rdbTableWrapper');
|
||||
if(rdbTableWrapper) {
|
||||
rdbTableWrapper.innerHTML = '';
|
||||
rdbTableWrapper.append(this.makeDnsmasqTable(nft_data.dnsmasq_bypass));
|
||||
let rdsTableWrapper = document.getElementById('rdsTableWrapper');
|
||||
if(rdsTableWrapper) {
|
||||
rdsTableWrapper.innerHTML = '';
|
||||
for(let i of nft_data.dnsmasq_user_instances) {
|
||||
rdsTableWrapper.append(this.makeDnsmasqTable(i[1], _('Dnsmasq') + ' ' + i[0]));
|
||||
};
|
||||
};
|
||||
|
||||
} else {
|
||||
if(poll.active()) {
|
||||
poll.stop();
|
||||
@@ -203,7 +267,7 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
load: function() {
|
||||
load() {
|
||||
return fs.exec_direct(tools.execPath, [ 'html-info' ], 'json').catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to execute or read contents')
|
||||
+ ': %s [ %s ]'.format(e.message, tools.execPath)
|
||||
@@ -211,7 +275,7 @@ return view.extend({
|
||||
})
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
render(data) {
|
||||
if(!data) {
|
||||
return;
|
||||
};
|
||||
@@ -220,11 +284,12 @@ return view.extend({
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {};
|
||||
|
||||
let update_status = null,
|
||||
user_entries = null,
|
||||
rules = null,
|
||||
dnsmasq = null,
|
||||
dnsmasqBypass = null;
|
||||
let update_status = null,
|
||||
user_entries = null,
|
||||
sink = null,
|
||||
sink_local = null,
|
||||
dnsmasq = null,
|
||||
dnsmasqUserInstances = null;
|
||||
|
||||
if(data) {
|
||||
if(data.status === 'enabled') {
|
||||
@@ -285,39 +350,87 @@ return view.extend({
|
||||
|
||||
let nft_data = this.formatNftJson(data);
|
||||
|
||||
if(nft_data.rules) {
|
||||
let table_rules = E('table', { 'class': 'table' }, [
|
||||
if(nft_data.sink) {
|
||||
let table = E('table', { 'class': 'table' }, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th left', 'style': 'min-width:33%' },
|
||||
_('Match-set')),
|
||||
_('Instance')),
|
||||
E('th', { 'class': 'th left' }, _('Protocol')),
|
||||
E('th', { 'class': 'th left' }, _('Bytes')),
|
||||
]),
|
||||
]);
|
||||
for(let i of nft_data.sink) {
|
||||
let instance = i[0];
|
||||
let proto = (i[1] === undefined) ? _('all') : i[1];
|
||||
let bytes = i[2];
|
||||
|
||||
for(let [set, bytes] of nft_data.rules) {
|
||||
if(!set) {
|
||||
if(!instance) {
|
||||
continue;
|
||||
};
|
||||
table_rules.append(
|
||||
table.append(
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td',{
|
||||
'class' : 'td left',
|
||||
'data-title': _('Match-set'),
|
||||
}, set + ((set.length >= 1) ? (
|
||||
' (' + set.replace(/^c/, 'CIDR').replace(/^i/, 'IP').replace(/^d/, 'dnsmasq').replace(/^bi/, 'bypass IP').replace(/^bd/, 'bypass dnsmasq').replace(/^fproxy/, 'full proxy') + ')'
|
||||
) : '')),
|
||||
E('td', {
|
||||
'class' : 'td left',
|
||||
'id' : 'rules.' + set,
|
||||
'data-title': _('Instance'),
|
||||
}, instance),
|
||||
E('td', {
|
||||
'class' : 'td left',
|
||||
'data-title': _('Protocol'),
|
||||
}, proto),
|
||||
E('td', {
|
||||
'class' : 'td left',
|
||||
'id' : 'sink.' + instance + '.' + (i[1] || 'all'),
|
||||
'data-title': _('Bytes'),
|
||||
}, bytes),
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
rules = E([
|
||||
E('h3', {}, _('Nftables rules')),
|
||||
table_rules,
|
||||
};
|
||||
sink = E([
|
||||
E('h3', {}, _('Transit traffic')),
|
||||
table,
|
||||
]);
|
||||
};
|
||||
|
||||
if(nft_data.sink_local) {
|
||||
let table = E('table', { 'class': 'table' }, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th left', 'style': 'min-width:33%' },
|
||||
_('Instance')),
|
||||
E('th', { 'class': 'th left' }, _('Protocol')),
|
||||
E('th', { 'class': 'th left' }, _('Bytes')),
|
||||
]),
|
||||
]);
|
||||
for(let i of nft_data.sink_local) {
|
||||
let instance = i[0];
|
||||
let proto = (i[1] === undefined) ? _('all') : i[1];
|
||||
let bytes = i[2];
|
||||
|
||||
if(!instance) {
|
||||
continue;
|
||||
};
|
||||
table.append(
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', {
|
||||
'class' : 'td left',
|
||||
'data-title': _('Instance'),
|
||||
}, instance),
|
||||
E('td', {
|
||||
'class' : 'td left',
|
||||
'data-title': _('Protocol'),
|
||||
}, proto),
|
||||
E('td', {
|
||||
'class' : 'td left',
|
||||
'id' : 'sink_local.' + instance + '.' + (i[1] || 'all'),
|
||||
'data-title': _('Bytes'),
|
||||
}, bytes),
|
||||
])
|
||||
);
|
||||
|
||||
};
|
||||
sink_local = E([
|
||||
E('h3', {}, _('Local traffic')),
|
||||
table,
|
||||
]);
|
||||
};
|
||||
|
||||
@@ -325,24 +438,28 @@ return view.extend({
|
||||
let rdTableWrapper = E('div', {
|
||||
'id' : 'rdTableWrapper',
|
||||
'style': 'width:100%'
|
||||
}, this.makeDnsmasqTable(nft_data.dnsmasq));
|
||||
}, this.makeDnsmasqTable(nft_data.dnsmasq, _('Dnsmasq')));
|
||||
|
||||
dnsmasq = E([
|
||||
E('h3', {}, _('Dnsmasq')),
|
||||
rdTableWrapper,
|
||||
]);
|
||||
};
|
||||
|
||||
if(nft_data.dnsmasq_bypass) {
|
||||
let rdbTableWrapper = E('div', {
|
||||
'id' : 'rdbTableWrapper',
|
||||
if(nft_data.dnsmasq_user_instances) {
|
||||
let rdsTableWrapper = E('div', {
|
||||
'id' : 'rdsTableWrapper',
|
||||
'style': 'width:100%'
|
||||
}, this.makeDnsmasqTable(nft_data.dnsmasq_bypass));
|
||||
});
|
||||
|
||||
dnsmasqBypass = E([
|
||||
E('h3', {}, _('Dnsmasq bypass')),
|
||||
rdbTableWrapper,
|
||||
]);
|
||||
for(let i of nft_data.dnsmasq_user_instances) {
|
||||
rdsTableWrapper.append(this.makeDnsmasqTable(i[1], _('Dnsmasq') + ' ' + i[0]));
|
||||
};
|
||||
|
||||
if(nft_data.dnsmasq_user_instances.length > 0) {
|
||||
dnsmasqUserInstances = E([
|
||||
rdsTableWrapper,
|
||||
]);
|
||||
};
|
||||
};
|
||||
|
||||
poll.add(L.bind(this.pollInfo, this), this.pollInterval);
|
||||
@@ -360,7 +477,7 @@ return view.extend({
|
||||
E('div', { 'class': 'cbi-section-node' }, update_status)
|
||||
),
|
||||
E('div', { 'class': 'cbi-section fade-in' },
|
||||
E('div', { 'class': 'cbi-section-node' }, rules)
|
||||
E('div', { 'class': 'cbi-section-node' }, sink)
|
||||
),
|
||||
];
|
||||
|
||||
@@ -373,16 +490,24 @@ return view.extend({
|
||||
);
|
||||
}
|
||||
|
||||
if(dnsmasqBypass) {
|
||||
if(sink_local) {
|
||||
layout.splice(5, 0,
|
||||
E('div', { 'class': 'cbi-section fade-in' },
|
||||
E('div', { 'class': 'cbi-section-node' }, dnsmasqBypass)
|
||||
E('div', { 'class': 'cbi-section-node' }, sink_local)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
if(dnsmasqUserInstances) {
|
||||
layout.splice(6, 0,
|
||||
E('div', { 'class': 'cbi-section fade-in' },
|
||||
E('div', { 'class': 'cbi-section-node' }, dnsmasqUserInstances)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
if(dnsmasq) {
|
||||
layout.splice(6, 0,
|
||||
layout.splice(7, 0,
|
||||
E('div', { 'class': 'cbi-section fade-in' },
|
||||
E('div', { 'class': 'cbi-section-node' }, dnsmasq)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require view.ruantiblock.log-widget as widget';
|
||||
|
||||
return baseclass.extend({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require fs';
|
||||
'require poll';
|
||||
'require uci';
|
||||
@@ -15,7 +16,121 @@ const btn_style_warning = 'btn cbi-button-negative important'
|
||||
return view.extend({
|
||||
statusTokenValue: null,
|
||||
|
||||
disableButtons: function(bool, btn, elems=[]) {
|
||||
dialogDestroy: baseclass.extend({
|
||||
__init__(context) {
|
||||
this.context = context;
|
||||
},
|
||||
|
||||
currentDnsmasqCfgDir: null,
|
||||
|
||||
dnsmasqCfgDirsSelect: null,
|
||||
|
||||
cancelButton : E('button', {
|
||||
'id' : 'btn_cancel',
|
||||
'class': btn_style_neutral,
|
||||
'click': ui.hideModal,
|
||||
}, _('Cancel')),
|
||||
|
||||
load() {
|
||||
return L.resolveDefault(fs.list(tools.dnsmasqCfgDirsRoot), null);
|
||||
},
|
||||
|
||||
render(data) {
|
||||
let section = uci.get(tools.appName, 'config');
|
||||
this.currentDnsmasqCfgDir = section.dnsmasq_cfg_dir;
|
||||
let available_cfg_dirs = [];
|
||||
|
||||
let dnsmasq_cfg_dirs_arr = data;
|
||||
if(dnsmasq_cfg_dirs_arr) {
|
||||
dnsmasq_cfg_dirs_arr.forEach(e => {
|
||||
let fname = e.name;
|
||||
if(fname.startsWith('dnsmasq')) {
|
||||
available_cfg_dirs.push([ fname, tools.dnsmasqCfgDirsRoot + '/' + fname ]);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
this.dnsmasqCfgDirsSelect = E('select', {
|
||||
'id' : 'dnsmasq_cfg_dirs_list',
|
||||
'class': "cbi-input-select",
|
||||
}),
|
||||
|
||||
available_cfg_dirs.forEach(e => {
|
||||
this.dnsmasqCfgDirsSelect.append(
|
||||
E('option', { 'value': e[1] }, e[0]));
|
||||
});
|
||||
this.dnsmasqCfgDirsSelect.value = this.currentDnsmasqCfgDir;
|
||||
|
||||
ui.showModal(this.title, [
|
||||
E('h4', _('The service will be disabled and all blacklist data will be deleted. Continue?')),
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title' },
|
||||
_('Dnsmasq config directory')),
|
||||
E('div', { 'class': 'cbi-value-field' }, [
|
||||
this.dnsmasqCfgDirsSelect,
|
||||
E('div', { 'class': 'cbi-value-description' },
|
||||
_('Change dnsmasq config directory')),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
E('div', { 'class': 'right' }, [
|
||||
this.cancelButton,
|
||||
' ',
|
||||
E('button', {
|
||||
'id' : 'btn_apply',
|
||||
'class': btn_style_warning,
|
||||
'click': ui.createHandlerFn(this, this.handleApply),
|
||||
}, _('Shutdown')),
|
||||
]),
|
||||
], 'cbi-modal');
|
||||
},
|
||||
|
||||
handleApply(ev) {
|
||||
this.cancelButton.disabled = true;
|
||||
return this.context.appAction('destroy').then(() => {
|
||||
if(this.dnsmasqCfgDirsSelect.value !== this.currentDnsmasqCfgDir) {
|
||||
uci.set(tools.appName, 'config', 'dnsmasq_cfg_dir',
|
||||
this.dnsmasqCfgDirsSelect.value);
|
||||
uci.save();
|
||||
uci.apply();
|
||||
};
|
||||
}).finally(() => {
|
||||
this.cancelButton.disabled = false;
|
||||
ui.hideModal();
|
||||
});
|
||||
},
|
||||
|
||||
error(e) {
|
||||
ui.showModal(this.title, [
|
||||
E('div', { 'class': 'cbi-section' },
|
||||
E('p', {}, _('An error occurred')
|
||||
+ ': %s'.format(e.message))
|
||||
),
|
||||
E('div', { 'class': 'right' },
|
||||
E('button', {
|
||||
'class': btn_style_neutral,
|
||||
'click': ui.hideModal,
|
||||
}, _('Dismiss'))
|
||||
),
|
||||
]);
|
||||
},
|
||||
|
||||
show() {
|
||||
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);
|
||||
})
|
||||
},
|
||||
}),
|
||||
|
||||
disableButtons(bool, btn, elems=[]) {
|
||||
let btn_start = elems[1] || document.getElementById("btn_start");
|
||||
let btn_destroy = elems[4] || document.getElementById("btn_destroy");
|
||||
let btn_enable = elems[2] || document.getElementById("btn_enable");
|
||||
@@ -31,7 +146,7 @@ return view.extend({
|
||||
};
|
||||
},
|
||||
|
||||
getAppStatus: function() {
|
||||
getAppStatus() {
|
||||
return Promise.all([
|
||||
fs.exec(tools.execPath, [ 'raw-status' ]),
|
||||
fs.exec(tools.execPath, [ 'vpn-route-status' ]),
|
||||
@@ -46,7 +161,7 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
setAppStatus: function(status_array, elems=[], force_app_code) {
|
||||
setAppStatus(status_array, elems=[], force_app_code) {
|
||||
let section = uci.get(tools.appName, 'config');
|
||||
if(!status_array || typeof(section) !== 'object') {
|
||||
(elems[0] || document.getElementById("status")).innerHTML = tools.makeStatusString(1);
|
||||
@@ -59,8 +174,7 @@ return view.extend({
|
||||
let app_status_code = (force_app_code) ? force_app_code : status_array[0].code;
|
||||
let vpn_route_status_code = status_array[1].code;
|
||||
let enabled_flag = status_array[2];
|
||||
let proxy_local_clients = section.proxy_local_clients;
|
||||
let proxy_mode = section.proxy_mode;
|
||||
let dnsmasq_cfg_dir = section.dnsmasq_cfg_dir;
|
||||
let bllist_preset = section.bllist_preset;
|
||||
let bllist_module = section.bllist_module;
|
||||
|
||||
@@ -122,7 +236,6 @@ return view.extend({
|
||||
|
||||
(elems[0] || document.getElementById("status")).innerHTML = tools.makeStatusString(
|
||||
app_status_code,
|
||||
proxy_mode,
|
||||
bllist_preset,
|
||||
bllist_module,
|
||||
vpn_route_status_code);
|
||||
@@ -132,7 +245,7 @@ return view.extend({
|
||||
};
|
||||
},
|
||||
|
||||
serviceAction: function(action, button) {
|
||||
serviceAction(action, button) {
|
||||
if(button) {
|
||||
let elem = document.getElementById(button);
|
||||
this.disableButtons(true, elem);
|
||||
@@ -149,7 +262,7 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
appAction: function(action, button) {
|
||||
appAction(action, button) {
|
||||
if(button) {
|
||||
let elem = document.getElementById(button);
|
||||
this.disableButtons(true, elem);
|
||||
@@ -167,13 +280,12 @@ return view.extend({
|
||||
return this.getAppStatus().then(
|
||||
(status_array) => {
|
||||
this.setAppStatus(status_array);
|
||||
ui.hideModal();
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
statusPoll: function() {
|
||||
statusPoll() {
|
||||
return fs.read(tools.tokenFile).then(v => {
|
||||
v = tools.normalizeValue(v);
|
||||
if(v != this.statusTokenValue) {
|
||||
@@ -187,48 +299,21 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
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() {
|
||||
load() {
|
||||
return this.getAppStatus();
|
||||
},
|
||||
|
||||
render: function(status_array) {
|
||||
render(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 dialog_destroy = new this.dialogDestroy(this);
|
||||
|
||||
let status_string = E('div', {
|
||||
'id' : 'status',
|
||||
'name' : 'status',
|
||||
@@ -281,7 +366,7 @@ return view.extend({
|
||||
'name' : 'btn_destroy',
|
||||
'class': btn_style_negative,
|
||||
}, _('Shutdown'));
|
||||
btn_destroy.onclick = L.bind(this.dialogDestroy, this);
|
||||
btn_destroy.onclick = () => dialog_destroy.show();
|
||||
|
||||
layout_append(btn_destroy, _('Shutdown'),
|
||||
_('Complete service shutdown, as well as deleting nftsets and blacklist data'));
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
'require view.ruantiblock.tools as tools';
|
||||
|
||||
return view.extend({
|
||||
parsers : {},
|
||||
parsers : {},
|
||||
|
||||
appStatusCode: null,
|
||||
appStatusCode : null,
|
||||
|
||||
depends : function(elem, key, array, empty=true) {
|
||||
depends(elem, key, array, empty=true) {
|
||||
if(empty && array.length === 0) {
|
||||
elem.depends(key, '_dummy');
|
||||
} else {
|
||||
@@ -20,20 +20,82 @@ return view.extend({
|
||||
};
|
||||
},
|
||||
|
||||
validateIpPort: function(section, value) {
|
||||
validateIpPort(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`;
|
||||
},
|
||||
|
||||
validateUrl: function(section, value) {
|
||||
validateUrl(section, value) {
|
||||
return (/^$|^https?:\/\/[\w.-]+(:[0-9]{2,5})?[\w\/~.&?+=-]*$/.test(value)) ? true : _('Expecting:')
|
||||
+ ` ${_('valid URL')}\n`;
|
||||
},
|
||||
|
||||
load: function() {
|
||||
CBIBlockFileEdit: form.Value.extend({
|
||||
__name__ : 'CBI.BlockFileEdit',
|
||||
|
||||
__init__(map, section, ctx, id, file, title, description, callback) {
|
||||
this.map = map;
|
||||
this.section = section;
|
||||
this.ctx = ctx;
|
||||
this.id = id,
|
||||
this.optional = true;
|
||||
this.rmempty = true;
|
||||
this.file = file;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.callback = callback;
|
||||
this.content = '';
|
||||
},
|
||||
|
||||
cfgvalue(section_id, option) {
|
||||
return this.content;
|
||||
},
|
||||
|
||||
formvalue(section_id) {
|
||||
let value = this.content;
|
||||
let textarea = document.getElementById('widget.file_edit.content.' + this.id);
|
||||
if(textarea) {
|
||||
value = textarea.value.trim().replace(/\r\n/g, '\n') + '\n';
|
||||
};
|
||||
return value;
|
||||
},
|
||||
|
||||
write(section_id, formvalue) {
|
||||
return fs.write(this.file, formvalue).then(rc => {
|
||||
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)));
|
||||
});
|
||||
},
|
||||
|
||||
load() {
|
||||
return L.resolveDefault(fs.read(this.file), '').then(c => {
|
||||
this.content = c;
|
||||
});
|
||||
},
|
||||
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
return E('textarea', {
|
||||
'id' : 'widget.file_edit.content.' + this.id,
|
||||
'class' : 'cbi-input-textarea',
|
||||
'style' : 'width:100% !important;resize:vertical !important',
|
||||
'rows' : 10,
|
||||
'wrap' : 'off',
|
||||
'spellcheck': 'false',
|
||||
}, cfgvalue);
|
||||
},
|
||||
}),
|
||||
|
||||
load() {
|
||||
return Promise.all([
|
||||
L.resolveDefault(fs.exec(tools.execPath, [ 'raw-status' ]), 1),
|
||||
L.resolveDefault(fs.list(tools.parsersDir), null),
|
||||
L.resolveDefault(fs.list(tools.dnsmasqCfgDirsRoot), null),
|
||||
uci.load(tools.appName),
|
||||
]).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to read the contents')
|
||||
@@ -43,14 +105,14 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
render(data) {
|
||||
if(!data) {
|
||||
return;
|
||||
};
|
||||
this.appStatusCode = data[0].code;
|
||||
let p_dir_arr = data[1];
|
||||
let curent_module = uci.get(tools.appName, 'config', 'bllist_module');
|
||||
let curent_preset = uci.get(tools.appName, 'config', 'bllist_preset');
|
||||
this.appStatusCode = data[0].code;
|
||||
let p_dir_arr = data[1];
|
||||
let curent_module = uci.get(tools.appName, 'config', 'bllist_module');
|
||||
let curent_preset = uci.get(tools.appName, 'config', 'bllist_preset');
|
||||
|
||||
if(p_dir_arr) {
|
||||
p_dir_arr.forEach(e => {
|
||||
@@ -129,7 +191,7 @@ return view.extend({
|
||||
'<br /><code>#comment<br />domain.net<br />anotherdomain.com</code>'
|
||||
);
|
||||
|
||||
let m, s, o;
|
||||
let m, s, o, ss;
|
||||
|
||||
m = new form.Map(tools.appName, _('Ruantiblock') + ' - ' + _('Settings'));
|
||||
|
||||
@@ -140,39 +202,32 @@ return view.extend({
|
||||
|
||||
/* Main settings tab */
|
||||
|
||||
s.tab('main_settings', _('Main settings'));
|
||||
|
||||
// PROXY_MODE
|
||||
o = s.taboption('main_settings', form.ListValue, 'proxy_mode',
|
||||
_('Proxy mode'));
|
||||
o.value('1', 'Tor');
|
||||
o.value('2', 'VPN');
|
||||
o.value('3', _('Transparent proxy'));
|
||||
|
||||
// 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;
|
||||
s.tab('main_tab', _('Main settings'));
|
||||
|
||||
// ENABLE_LOGGING
|
||||
o = s.taboption('main_settings', form.Flag, 'enable_logging',
|
||||
o = s.taboption('main_tab', form.Flag, 'enable_logging',
|
||||
_('Logging events'));
|
||||
o.rmempty = false;
|
||||
|
||||
// update_at_startup
|
||||
o = s.taboption('main_settings', form.Flag, 'update_at_startup',
|
||||
o = s.taboption('main_tab', form.Flag, 'update_at_startup',
|
||||
_('Update at startup'));
|
||||
o.description = _('Update blacklist after system startup');
|
||||
o.rmempty = false;
|
||||
|
||||
// PROXY_LOCAL_CLIENTS
|
||||
o = s.taboption('main_tab', form.Flag, 'proxy_local_clients',
|
||||
_('Apply proxy rules to router application traffic'));
|
||||
o.rmempty = false;
|
||||
|
||||
// NFTSET_CLEAR_SETS
|
||||
o = s.taboption('main_settings', form.Flag, 'nftset_clear_sets',
|
||||
o = s.taboption('main_tab', form.Flag, 'nftset_clear_sets',
|
||||
_('Clean up nftsets before updating blacklist'));
|
||||
o.description = _('Reduces RAM consumption during update');
|
||||
o.rmempty = false;
|
||||
|
||||
// ALLOWED_HOSTS_MODE
|
||||
o = s.taboption('main_settings', form.ListValue, 'allowed_hosts_mode',
|
||||
o = s.taboption('main_tab', form.ListValue, 'allowed_hosts_mode',
|
||||
_('Host filter'));
|
||||
o.value('0', _('Disabled'));
|
||||
o.value('1', _('Only listed hosts'));
|
||||
@@ -180,40 +235,29 @@ return view.extend({
|
||||
o.description = _('Restriction of hosts that are allowed to bypass blocking');
|
||||
|
||||
// ALLOWED_HOSTS_LIST
|
||||
o = s.taboption('main_settings', form.DynamicList, 'allowed_hosts_list',
|
||||
o = s.taboption('main_tab', form.DynamicList, 'allowed_hosts_list',
|
||||
_('IP addresses for host filter'));
|
||||
o.datatype = 'ip4addr';
|
||||
|
||||
// ENABLE_FPROXY
|
||||
o = s.taboption('main_settings', form.Flag, 'enable_fproxy',
|
||||
_('Enable full proxy mode'));
|
||||
o.description = _('All traffic of the specified hosts passes through the proxy, without a blacklist');
|
||||
o.rmempty = false;
|
||||
|
||||
// FPROXY_LIST
|
||||
o = s.taboption('main_settings', form.DynamicList, 'fproxy_list',
|
||||
_('IP addresses for full proxy mode'));
|
||||
o.datatype = 'ip4addr';
|
||||
|
||||
|
||||
/* Tor tab */
|
||||
|
||||
s.tab('tor_settings', _('Tor mode'));
|
||||
s.tab('tor_tab', _('Tor mode'));
|
||||
|
||||
// TOR_TRANS_PORT
|
||||
o = s.taboption('tor_settings', form.Value, 'tor_trans_port',
|
||||
o = s.taboption('tor_tab', form.Value, 'tor_trans_port',
|
||||
_('Transparent proxy port'));
|
||||
o.rmempty = false;
|
||||
o.datatype = 'port';
|
||||
|
||||
// ONION_DNS_ADDR
|
||||
o = s.taboption('tor_settings', form.Value, 'onion_dns_addr',
|
||||
o = s.taboption('tor_tab', form.Value, 'onion_dns_addr',
|
||||
_("Optional DNS resolver for '.onion' zone"), '<code>ipaddress#port</code>');
|
||||
o.rmempty = false;
|
||||
o.validate = this.validateIpPort;
|
||||
|
||||
// Torrc edit dialog
|
||||
o = s.taboption('tor_settings', form.Button, '_torrc_btn',
|
||||
o = s.taboption('tor_tab', form.Button, '_torrc_btn',
|
||||
_('Tor configuration file'));
|
||||
o.onclick = () => torrc_edit.show();
|
||||
o.inputtitle = _('Edit');
|
||||
@@ -222,10 +266,10 @@ return view.extend({
|
||||
|
||||
/* VPN tab */
|
||||
|
||||
s.tab('vpn_settings', _('VPN mode'));
|
||||
s.tab('vpn_tab', _('VPN mode'));
|
||||
|
||||
// IF_VPN
|
||||
o = s.taboption('vpn_settings', widgets.DeviceSelect, 'if_vpn',
|
||||
o = s.taboption('vpn_tab', widgets.DeviceSelect, 'if_vpn',
|
||||
_('VPN interface'));
|
||||
o.multiple = false;
|
||||
o.noaliases = true;
|
||||
@@ -233,13 +277,13 @@ return view.extend({
|
||||
o.default = 'tun0';
|
||||
|
||||
// VPN_GW_IP
|
||||
o = s.taboption('vpn_settings', form.Value, 'vpn_gw_ip',
|
||||
o = s.taboption('vpn_tab', form.Value, 'vpn_gw_ip',
|
||||
_('VPN gateway IP address'),
|
||||
_('If not specified, the VPN interface address is used (or peer address for PPP protocols)'));
|
||||
o.datatype = 'ip4addr(1)';
|
||||
|
||||
// VPN_ROUTE_CHECK
|
||||
o = s.taboption('vpn_settings', form.ListValue, 'vpn_route_check',
|
||||
o = s.taboption('vpn_tab', form.ListValue, 'vpn_route_check',
|
||||
_('Type of adding a VPN rule to the routing table'));
|
||||
o.value('0', 'hotplug.d');
|
||||
o.value('1', 'ruab_route_check');
|
||||
@@ -248,32 +292,46 @@ return view.extend({
|
||||
_('ruab_route_check - script that regularly checks an entry in the routing table.');
|
||||
|
||||
|
||||
/* Proxy tab */
|
||||
/* Tproxy tab */
|
||||
|
||||
s.tab('proxy_settings', _('Transparent proxy mode'));
|
||||
s.tab('tproxy_tab', _('Transparent proxy mode'));
|
||||
|
||||
// T_PROXY_TYPE
|
||||
o = s.taboption('tproxy_tab', form.ListValue, 't_proxy_type',
|
||||
_('Proxy type'));
|
||||
o.value('0', _('redirect'));
|
||||
o.value('1', _('tproxy'));
|
||||
o.description = _('Statement in nftables rules');
|
||||
|
||||
// T_PROXY_PORT_TCP
|
||||
o = s.taboption('proxy_settings', form.Value, 't_proxy_port_tcp',
|
||||
o = s.taboption('tproxy_tab', form.Value, 't_proxy_port_tcp',
|
||||
_('Transparent proxy TCP port'));
|
||||
o.rmempty = false;
|
||||
o.datatype = 'port';
|
||||
|
||||
// T_PROXY_ALLOW_UDP
|
||||
o = s.taboption('proxy_settings', form.Flag, 't_proxy_allow_udp',
|
||||
o = s.taboption('tproxy_tab', form.Flag, 't_proxy_allow_udp',
|
||||
_('Send UDP traffic to transparent proxy'));
|
||||
o.rmempty = false;
|
||||
|
||||
// T_PROXY_PORT_UDP
|
||||
o = s.taboption('proxy_settings', form.Value, 't_proxy_port_udp',
|
||||
o = s.taboption('tproxy_tab', form.Value, 't_proxy_port_udp',
|
||||
_('Transparent proxy UDP port'));
|
||||
o.rmempty = false;
|
||||
o.datatype = 'port';
|
||||
|
||||
|
||||
/* Blacklist module tab */
|
||||
/* Blacklist tab */
|
||||
|
||||
s.tab('blacklist_tab', _('Blacklist settings'));
|
||||
|
||||
// PROXY_MODE
|
||||
o = s.taboption('blacklist_tab', form.ListValue, 'proxy_mode',
|
||||
_('Proxy mode'));
|
||||
o.value('1', 'Tor');
|
||||
o.value('2', 'VPN');
|
||||
o.value('3', _('Transparent proxy'));
|
||||
|
||||
// BLLIST_PRESET
|
||||
let bllist_preset = s.taboption('blacklist_tab', form.ListValue,
|
||||
'bllist_preset', _('Blacklist update mode'));
|
||||
@@ -312,29 +370,16 @@ return view.extend({
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
|
||||
// ADD_USER_ENTRIES
|
||||
o = s.taboption('blacklist_tab', form.Flag, 'add_user_entries',
|
||||
_('Enable user entries'), _('Add user entries to the blacklist when updating'));
|
||||
// ENABLE_FPROXY
|
||||
o = s.taboption('blacklist_tab', form.Flag, 'enable_fproxy',
|
||||
_('Enable full proxy mode'));
|
||||
o.description = _('All traffic of the specified hosts passes through the proxy, without a blacklist');
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
o.depends({ bllist_preset: '', '!reverse': true });
|
||||
|
||||
// USER_ENTRIES edit dialog
|
||||
o = s.taboption('blacklist_tab', form.Button, '_user_entries_btn',
|
||||
_('User entries'));
|
||||
o.onclick = () => user_entries_edit.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
|
||||
// USER_ENTRIES_REMOTE
|
||||
o = s.taboption('blacklist_tab', form.DynamicList, 'user_entries_remote',
|
||||
_('URLs of remote user entries file'));
|
||||
o.validate = this.validateUrl;
|
||||
|
||||
// USER_ENTRIES_DNS
|
||||
o = s.taboption('blacklist_tab', form.Value, 'user_entries_dns',
|
||||
_("DNS server that is used for the user's FQDN entries"), '<code>ipaddress[#port]</code>');
|
||||
o.validate = this.validateIpPort;
|
||||
// FPROXY_LIST
|
||||
o = s.taboption('blacklist_tab', form.DynamicList, 'fproxy_list',
|
||||
_('IP addresses for full proxy mode'));
|
||||
o.datatype = 'ip4addr';
|
||||
|
||||
// BYPASS_MODE
|
||||
o = s.taboption('blacklist_tab', form.Flag, 'bypass_mode',
|
||||
@@ -354,6 +399,7 @@ return view.extend({
|
||||
_('DNS server that is used for the FQDN entries of exclusion list'), '<code>ipaddress[#port]</code>');
|
||||
o.validate = this.validateIpPort;
|
||||
|
||||
|
||||
if(availableParsers) {
|
||||
bllist_preset.description += '<br /> ( * - ' + _('requires installed blacklist module') + ' )';
|
||||
|
||||
@@ -398,10 +444,9 @@ return view.extend({
|
||||
// BLLIST_GR_EXCLUDED_SLD_FILE edit dialog
|
||||
o = s.taboption('parser_settings_tab', form.Button, '_gr_excluded_sld_btn',
|
||||
_('2nd level domains that are excluded from optimization'));
|
||||
o.onclick = () => gr_excluded_sld_edit.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
//o.description = _('e.g:') + ' <code>livejournal.com</code>';
|
||||
o.onclick = () => gr_excluded_sld_edit.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
|
||||
// BLLIST_ENABLE_IDN
|
||||
o = s.taboption('parser_settings_tab', form.Flag, 'bllist_enable_idn',
|
||||
@@ -447,10 +492,9 @@ return view.extend({
|
||||
// BLLIST_GR_EXCLUDED_NETS_FILE edit dialog
|
||||
o = s.taboption('parser_settings_tab', form.Button, '_gr_excluded_nets_btn',
|
||||
_('IP subnet patterns (/24) that are excluded from optimization'));
|
||||
o.onclick = () => gr_excluded_nets_edit.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
//o.description = _('e.g:') + ' <code>192.168.1.</code>';
|
||||
o.onclick = () => gr_excluded_nets_edit.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
|
||||
// BLLIST_SUMMARIZE_IP
|
||||
o = s.taboption('parser_settings_tab', form.Flag, 'bllist_summarize_ip',
|
||||
@@ -461,7 +505,180 @@ return view.extend({
|
||||
o = s.taboption('parser_settings_tab', form.Flag, 'bllist_summarize_cidr',
|
||||
_("Summarize '/24' networks"));
|
||||
o.rmempty = false;
|
||||
};
|
||||
|
||||
|
||||
/* User entries tab */
|
||||
|
||||
s.tab('user_entries_tab', _('User entries'));
|
||||
|
||||
o = s.taboption('user_entries_tab', form.SectionValue, 'user_instance', form.GridSection,
|
||||
'user_instance');
|
||||
ss = o.subsection;
|
||||
ss.addremove = false;
|
||||
ss.sortable = false;
|
||||
ss.nodescriptions = true;
|
||||
ss.modaltitle = `${_('User entries')} - %s`;
|
||||
ss.max_cols = 2;
|
||||
|
||||
|
||||
/* User entries main settings tab */
|
||||
|
||||
ss.tab('u_main_tab', _('Main settings'));
|
||||
|
||||
// U_ENABLED
|
||||
o = ss.taboption('u_main_tab', form.Flag, 'u_enabled',
|
||||
_('Enabled'),
|
||||
);
|
||||
o.rmempty = false;
|
||||
o.default = '1';
|
||||
o.editable = true;
|
||||
o.modalonly = false;
|
||||
|
||||
// description
|
||||
o = ss.taboption('u_main_tab', form.Value, 'u_description',
|
||||
_("Description"));
|
||||
o.datatype = 'maxlength(100)';
|
||||
o.modalonly = null;
|
||||
|
||||
// U_PROXY_MODE
|
||||
o = ss.taboption('u_main_tab', form.ListValue, 'u_proxy_mode',
|
||||
_('Proxy mode'));
|
||||
o.value('1', 'Tor');
|
||||
o.value('2', 'VPN');
|
||||
o.value('3', _('Transparent proxy'));
|
||||
o.default = '2';
|
||||
o.modalonly = true;
|
||||
|
||||
// U_SKIP_MARKED_PACKETS
|
||||
o = ss.taboption('u_main_tab', form.Flag, 'u_skip_marked_packets',
|
||||
_('Lowest priority'));
|
||||
o.description = _('This proxy will receive traffic last, even after the main blacklist');
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
// U_ENABLE_FPROXY
|
||||
o = ss.taboption('u_main_tab', form.Flag, 'u_enable_fproxy',
|
||||
_('Enable full proxy mode'));
|
||||
o.description = _('All traffic of the specified hosts passes through the proxy, without a blacklist');
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
// U_FPROXY_LIST
|
||||
o = ss.taboption('u_main_tab', form.DynamicList, 'u_fproxy_list',
|
||||
_('IP addresses for full proxy mode'));
|
||||
o.datatype = 'ip4addr';
|
||||
o.modalonly = true;
|
||||
|
||||
|
||||
/* User entries tor tab */
|
||||
|
||||
ss.tab('u_tor_tab', _('Tor mode'));
|
||||
|
||||
// U_TOR_TRANS_PORT
|
||||
o = ss.taboption('u_tor_tab', form.Value, 'u_tor_trans_port',
|
||||
_('Transparent proxy port'));
|
||||
o.rmempty = false;
|
||||
o.datatype = 'port';
|
||||
o.modalonly = true;
|
||||
|
||||
// U_ONION_DNS_ADDR
|
||||
o = ss.taboption('u_tor_tab', form.Value, 'u_onion_dns_addr',
|
||||
_("Optional DNS resolver for '.onion' zone"), '<code>ipaddress#port</code>');
|
||||
o.rmempty = false;
|
||||
o.validate = this.validateIpPort;
|
||||
o.modalonly = true;
|
||||
|
||||
/* User entries VPN tab */
|
||||
|
||||
ss.tab('u_vpn_tab', _('VPN mode'));
|
||||
|
||||
// U_IF_VPN
|
||||
o = ss.taboption('u_vpn_tab', widgets.DeviceSelect, 'u_if_vpn',
|
||||
_('VPN interface'));
|
||||
o.multiple = false;
|
||||
o.noaliases = true;
|
||||
o.rmempty = false;
|
||||
o.default = 'tun0';
|
||||
o.modalonly = true;
|
||||
|
||||
// U_VPN_GW_IP
|
||||
o = ss.taboption('u_vpn_tab', form.Value, 'u_vpn_gw_ip',
|
||||
_('VPN gateway IP address'),
|
||||
_('If not specified, the VPN interface address is used (or peer address for PPP protocols)'));
|
||||
o.datatype = 'ip4addr(1)';
|
||||
o.modalonly = true;
|
||||
|
||||
|
||||
/* User entries tproxy tab */
|
||||
|
||||
ss.tab('u_tproxy_tab', _('Transparent proxy mode'));
|
||||
|
||||
// U_T_PROXY_TYPE
|
||||
o = ss.taboption('u_tproxy_tab', form.ListValue, 'u_t_proxy_type',
|
||||
_('Proxy type'));
|
||||
o.value('0', _('redirect'));
|
||||
o.value('1', _('tproxy'));
|
||||
o.description = _('Statement in nftables rules');
|
||||
|
||||
// U_T_PROXY_PORT_TCP
|
||||
o = ss.taboption('u_tproxy_tab', form.Value, 'u_t_proxy_port_tcp',
|
||||
_('Transparent proxy TCP port'));
|
||||
o.rmempty = false;
|
||||
o.datatype = 'port';
|
||||
o.modalonly = true;
|
||||
|
||||
// U_T_PROXY_ALLOW_UDP
|
||||
o = ss.taboption('u_tproxy_tab', form.Flag, 'u_t_proxy_allow_udp',
|
||||
_('Send UDP traffic to transparent proxy'));
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
// U_T_PROXY_PORT_UDP
|
||||
o = ss.taboption('u_tproxy_tab', form.Value, 'u_t_proxy_port_udp',
|
||||
_('Transparent proxy UDP port'));
|
||||
o.rmempty = false;
|
||||
o.datatype = 'port';
|
||||
o.modalonly = true;
|
||||
|
||||
|
||||
/* User entries items tab */
|
||||
|
||||
ss.tab('u_entries_tab', _('Entries'));
|
||||
|
||||
ss.addModalOptions = (s, section_id, ev) => {
|
||||
|
||||
// user entries edit dialog
|
||||
o = s.taboption('u_entries_tab', this.CBIBlockFileEdit, this,
|
||||
'user-entries',
|
||||
tools.userListsDir + '/' + s.section,
|
||||
_('Edit 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>'
|
||||
);
|
||||
|
||||
// DEBUG
|
||||
console.log(tools.userListsDir + '/' + s.section);
|
||||
|
||||
o.modalonly = true;
|
||||
|
||||
// U_ENTRIES_REMOTE
|
||||
o = s.taboption('u_entries_tab', form.DynamicList, 'u_entries_remote',
|
||||
_('URLs of remote user entries file'));
|
||||
o.validate = this.validateUrl;
|
||||
o.modalonly = true;
|
||||
|
||||
// U_ENABLE_ENTRIES_REMOTE_PROXY
|
||||
o = s.taboption('u_entries_tab', form.Flag, 'u_enable_entries_remote_proxy',
|
||||
_('Downloading files via proxy'), _('Turn on if files are blocked'));
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
|
||||
// U_ENTRIES_DNS
|
||||
o = s.taboption('u_entries_tab', form.Value, 'u_entries_dns',
|
||||
_("DNS server that is used for the user's FQDN entries"), '<code>ipaddress[#port]</code>');
|
||||
o.validate = this.validateIpPort;
|
||||
o.modalonly = true;
|
||||
};
|
||||
|
||||
let map_promise = m.render();
|
||||
@@ -469,12 +686,28 @@ return view.extend({
|
||||
return map_promise;
|
||||
},
|
||||
|
||||
handleSaveApply: function(ev, mode) {
|
||||
return this.handleSave(ev).then(() => {
|
||||
handleSave(ev, restart) {
|
||||
let tasks = [];
|
||||
document.getElementById('maincontent')
|
||||
.querySelectorAll('.cbi-map').forEach((map, i, a) => {
|
||||
let res = DOM.callClassMethod(map, 'save');
|
||||
if(restart && i == a.length - 1 && this.appStatusCode != 1 && this.appStatusCode != 2) {
|
||||
res.then(() => {
|
||||
window.setTimeout(() => {
|
||||
fs.exec_direct(tools.execPath, [ 'restart' ]).then(
|
||||
() => console.log(tools.execPath + ' restarted...')
|
||||
);
|
||||
}, 1000);
|
||||
});
|
||||
};
|
||||
tasks.push(res);
|
||||
});
|
||||
return Promise.all(tasks);
|
||||
},
|
||||
|
||||
handleSaveApply(ev, mode) {
|
||||
return this.handleSave(ev, true).then(() => {
|
||||
ui.changes.apply(mode == '0');
|
||||
if(this.appStatusCode != 1 && this.appStatusCode != 2) {
|
||||
window.setTimeout(() => fs.exec(tools.execPath, [ 'restart' ]), 3000);
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -38,8 +38,10 @@ return baseclass.extend({
|
||||
execPath : '/usr/bin/ruantiblock',
|
||||
tokenFile : '/var/run/ruantiblock.token',
|
||||
parsersDir : '/usr/libexec/ruantiblock',
|
||||
dnsmasqCfgDirsRoot: '/tmp',
|
||||
torrcFile : '/etc/tor/torrc',
|
||||
userEntriesFile : '/etc/ruantiblock/user_entries',
|
||||
userListsDir : '/etc/ruantiblock/user_lists',
|
||||
bypassEntriesFile : '/etc/ruantiblock/bypass_entries',
|
||||
fqdnFilterFile : '/etc/ruantiblock/fqdn_filter',
|
||||
ipFilterFile : '/etc/ruantiblock/ip_filter',
|
||||
@@ -76,7 +78,7 @@ return baseclass.extend({
|
||||
expect: { result: false }
|
||||
}),
|
||||
|
||||
getInitStatus: function(name) {
|
||||
getInitStatus(name) {
|
||||
return this.callInitStatus(name).then(res => {
|
||||
if(res) {
|
||||
return res[name].enabled;
|
||||
@@ -89,7 +91,7 @@ return baseclass.extend({
|
||||
});
|
||||
},
|
||||
|
||||
handleServiceAction: function(name, action) {
|
||||
handleServiceAction(name, action) {
|
||||
return this.callInitAction(name, action).then(success => {
|
||||
if(!success) {
|
||||
throw _('Command failed');
|
||||
@@ -101,13 +103,12 @@ return baseclass.extend({
|
||||
});
|
||||
},
|
||||
|
||||
normalizeValue: function(v) {
|
||||
normalizeValue(v) {
|
||||
return (v && typeof(v) === 'string') ? v.trim().replace(/\r?\n/g, '') : v;
|
||||
},
|
||||
|
||||
makeStatusString: function(
|
||||
app_status_code,
|
||||
proxy_mode,
|
||||
bllist_preset,
|
||||
bllist_module,
|
||||
vpn_route_status_code) {
|
||||
@@ -152,14 +153,6 @@ return baseclass.extend({
|
||||
%s %s
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tr">
|
||||
<td class="td left">
|
||||
${_('Proxy mode')}:
|
||||
</td>
|
||||
<td class="td left">
|
||||
%s
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tr">
|
||||
<td class="td left">
|
||||
${_('Blacklist update mode')}:
|
||||
@@ -172,10 +165,9 @@ return baseclass.extend({
|
||||
`.format(
|
||||
spinning,
|
||||
app_status_label,
|
||||
(app_status_code != 2 && proxy_mode == 2 && vpn_route_status_code != 0)
|
||||
(app_status_code != 2 && vpn_route_status_code != 0)
|
||||
? '<span class="label-status error">'
|
||||
+ _('VPN routing error! Need restart') + '</span>' : '',
|
||||
(proxy_mode == 3) ? _('Transparent proxy') : (proxy_mode == 2) ? 'VPN' : 'Tor',
|
||||
(!bllist_preset || bllist_preset === '') ? _('user entries only') :
|
||||
(this.blacklistPresets[bllist_preset]) ?
|
||||
`<span style="cursor:help; border-bottom:1px dotted" data-tooltip="${this.blacklistPresets[bllist_preset][2]}">
|
||||
@@ -185,7 +177,7 @@ return baseclass.extend({
|
||||
},
|
||||
|
||||
fileEditDialog: baseclass.extend({
|
||||
__init__: function(file, title, description, callback, file_exists=false) {
|
||||
__init__(file, title, description, callback, file_exists=false) {
|
||||
this.file = file;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
@@ -193,11 +185,11 @@ return baseclass.extend({
|
||||
this.file_exists = file_exists;
|
||||
},
|
||||
|
||||
load: function() {
|
||||
load() {
|
||||
return L.resolveDefault(fs.read(this.file), '');
|
||||
},
|
||||
|
||||
render: function(content) {
|
||||
render(content) {
|
||||
ui.showModal(this.title, [
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('div', { 'class': 'cbi-section-descr' }, this.description),
|
||||
@@ -230,7 +222,7 @@ return baseclass.extend({
|
||||
]);
|
||||
},
|
||||
|
||||
handleSave: function(ev) {
|
||||
handleSave(ev) {
|
||||
let textarea = document.getElementById('widget.modal_content');
|
||||
let value = textarea.value.trim().replace(/\r\n/g, '\n') + '\n';
|
||||
|
||||
@@ -249,7 +241,7 @@ return baseclass.extend({
|
||||
});
|
||||
},
|
||||
|
||||
error: function(e) {
|
||||
error(e) {
|
||||
if(!this.file_exists && e instanceof Error && e.name === 'NotFoundError') {
|
||||
return this.render();
|
||||
} else {
|
||||
@@ -268,7 +260,7 @@ return baseclass.extend({
|
||||
};
|
||||
},
|
||||
|
||||
show: function() {
|
||||
show() {
|
||||
ui.showModal(null,
|
||||
E('p', { 'class': 'spinning' }, _('Loading'))
|
||||
);
|
||||
|
||||
@@ -70,6 +70,9 @@ msgstr "Байты"
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
msgid "Change dnsmasq config directory"
|
||||
msgstr "Изменить директорию конфигов dnsmasq"
|
||||
|
||||
msgid "Changes have been saved."
|
||||
msgstr "Изменения сохранены."
|
||||
|
||||
@@ -110,12 +113,18 @@ msgstr "День"
|
||||
msgid "Debug"
|
||||
msgstr "Отладка"
|
||||
|
||||
msgid "Description"
|
||||
msgstr "Описание"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "Отключено"
|
||||
|
||||
msgid "Dismiss"
|
||||
msgstr "Закрыть"
|
||||
|
||||
msgid "Dnsmasq config directory"
|
||||
msgstr "Директория конфигов dnsmasq"
|
||||
|
||||
msgid "Download error"
|
||||
msgstr "Ошибка загрузки"
|
||||
|
||||
@@ -125,9 +134,15 @@ msgstr "Скачать журнал"
|
||||
msgid "Downloading a blacklist via proxy"
|
||||
msgstr "Скачивать блэклист через прокси"
|
||||
|
||||
msgid "Downloading files via proxy"
|
||||
msgstr "Скачивать файлы через прокси"
|
||||
|
||||
msgid "Edit"
|
||||
msgstr "Изменить"
|
||||
|
||||
msgid "Edit entries"
|
||||
msgstr "Изменить записи"
|
||||
|
||||
msgid "Emergency"
|
||||
msgstr "Чрезвычайная ситуация"
|
||||
|
||||
@@ -233,15 +248,15 @@ msgstr "Шаблоны IP подсетей (/24) не подлежащих оп
|
||||
msgid "Info"
|
||||
msgstr "Информация"
|
||||
|
||||
msgid "Instance"
|
||||
msgstr "Экземпляр"
|
||||
|
||||
msgid "Interval"
|
||||
msgstr "Интервал"
|
||||
|
||||
msgid "Invalid regular expression"
|
||||
msgstr "Неправильное регулярное выражение"
|
||||
|
||||
msgid "Nftables rules"
|
||||
msgstr "Правила nftables"
|
||||
|
||||
msgid "Last blacklist update"
|
||||
msgstr "Последнее обновление блэклиста"
|
||||
|
||||
@@ -257,6 +272,9 @@ msgstr "Список хостов, которые исключаются из о
|
||||
msgid "Loading"
|
||||
msgstr "Загрузка"
|
||||
|
||||
msgid "Local traffic"
|
||||
msgstr "Локальный трафик"
|
||||
|
||||
msgid "Log"
|
||||
msgstr "Лог"
|
||||
|
||||
@@ -269,6 +287,9 @@ msgstr "Уровни логирования"
|
||||
msgid "Logread not found"
|
||||
msgstr "Logread не найден"
|
||||
|
||||
msgid "Lowest priority"
|
||||
msgstr "Самый низкий приоритет"
|
||||
|
||||
msgid "Main settings"
|
||||
msgstr "Основные настройки"
|
||||
|
||||
@@ -373,9 +394,15 @@ msgstr "Отбор IP адресов из блэклиста по шаблона
|
||||
msgid "Pick domains from blacklist by FQDN filter patterns"
|
||||
msgstr "Отбор доменов из блэклиста по шаблонам фильтра FQDN"
|
||||
|
||||
msgid "Protocol"
|
||||
msgstr "Протокол"
|
||||
|
||||
msgid "Proxy mode"
|
||||
msgstr "Режим прокси"
|
||||
|
||||
msgid "Proxy type"
|
||||
msgstr "Тип прокси"
|
||||
|
||||
msgid "Reduces RAM consumption during update"
|
||||
msgstr "Уменьшает потребление оперативной памяти при обновлении"
|
||||
|
||||
@@ -418,8 +445,11 @@ msgstr "Настройки"
|
||||
msgid "Shutdown"
|
||||
msgstr "Выключение"
|
||||
|
||||
msgid "Size in memory"
|
||||
msgstr "Размер в памяти"
|
||||
msgid "Skip marked packets"
|
||||
msgstr "Пропускать помеченные пакеты"
|
||||
|
||||
msgid "Skip packets that have already been marked in previous rules"
|
||||
msgstr "Пропускать пакеты, которые уже были помечены в предыдущих правилах"
|
||||
|
||||
msgid "Sorting entries"
|
||||
msgstr "Сортировка записей"
|
||||
@@ -430,6 +460,9 @@ msgstr "Запускается"
|
||||
msgid "Statistics"
|
||||
msgstr "Статистика"
|
||||
|
||||
msgid "Statement in nftables rules"
|
||||
msgstr "Действие в правилах nftables"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "Статус"
|
||||
|
||||
@@ -469,6 +502,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Служба будет выключена и все данные блэклиста будут удалены. Продолжить?"
|
||||
|
||||
msgid "This proxy will receive traffic last, even after the main blacklist"
|
||||
msgstr "В этот прокси трафик будет попадать в последнюю очередь, даже после основного блэклиста"
|
||||
|
||||
msgid "Time"
|
||||
msgstr "Время"
|
||||
|
||||
@@ -487,6 +523,9 @@ msgstr "Конфигурационный файл Tor"
|
||||
msgid "Tor mode"
|
||||
msgstr "Режим Tor"
|
||||
|
||||
msgid "Transit traffic"
|
||||
msgstr "Транзитный трафик"
|
||||
|
||||
msgid "Transparent proxy"
|
||||
msgstr "Прозрачный прокси"
|
||||
|
||||
@@ -505,6 +544,9 @@ msgstr "UDP порт прозрачного прокси"
|
||||
msgid "Turn on if blacklist source is blocked"
|
||||
msgstr "Включите, если источник блэклиста заблокирован"
|
||||
|
||||
msgid "Turn on if files are blocked"
|
||||
msgstr "Включите, если файлы заблокированы"
|
||||
|
||||
msgid "Type a search pattern..."
|
||||
msgstr "Введите шаблон для поиска"
|
||||
|
||||
@@ -568,6 +610,9 @@ msgstr "Ошибка маршрутизации VPN! Необходим пере
|
||||
msgid "Warning"
|
||||
msgstr "Внимание"
|
||||
|
||||
msgid "all"
|
||||
msgstr "все"
|
||||
|
||||
msgid "ascending"
|
||||
msgstr "по возрастанию"
|
||||
|
||||
|
||||
@@ -55,6 +55,9 @@ msgstr ""
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change dnsmasq config directory"
|
||||
msgstr ""
|
||||
|
||||
msgid "Changes have been saved."
|
||||
msgstr ""
|
||||
|
||||
@@ -95,12 +98,18 @@ msgstr ""
|
||||
msgid "Debug"
|
||||
msgstr ""
|
||||
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dismiss"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dnsmasq config directory"
|
||||
msgstr ""
|
||||
|
||||
msgid "Download error"
|
||||
msgstr ""
|
||||
|
||||
@@ -110,9 +119,15 @@ msgstr ""
|
||||
msgid "Downloading a blacklist via proxy"
|
||||
msgstr ""
|
||||
|
||||
msgid "Downloading files via proxy"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit entries"
|
||||
msgstr ""
|
||||
|
||||
msgid "Emergency"
|
||||
msgstr ""
|
||||
msgid "Enable"
|
||||
@@ -214,15 +229,15 @@ msgstr ""
|
||||
msgid "Info"
|
||||
msgstr ""
|
||||
|
||||
msgid "Instance"
|
||||
msgstr ""
|
||||
|
||||
msgid "Interval"
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid regular expression"
|
||||
msgstr ""
|
||||
|
||||
msgid "Nftables rules"
|
||||
msgstr ""
|
||||
|
||||
msgid "Last blacklist update"
|
||||
msgstr ""
|
||||
|
||||
@@ -238,6 +253,9 @@ msgstr ""
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
||||
msgid "Local traffic"
|
||||
msgstr ""
|
||||
|
||||
msgid "Log"
|
||||
msgstr ""
|
||||
|
||||
@@ -250,6 +268,9 @@ msgstr ""
|
||||
msgid "Logread not found"
|
||||
msgstr ""
|
||||
|
||||
msgid "Lowest priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Main settings"
|
||||
msgstr ""
|
||||
|
||||
@@ -342,9 +363,15 @@ msgstr ""
|
||||
msgid "Pick domains from blacklist by FQDN filter patterns"
|
||||
msgstr ""
|
||||
|
||||
msgid "Protocol"
|
||||
msgstr ""
|
||||
|
||||
msgid "Proxy mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Proxy type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reduces RAM consumption during update"
|
||||
msgstr ""
|
||||
|
||||
@@ -386,9 +413,6 @@ msgstr ""
|
||||
msgid "Shutdown"
|
||||
msgstr ""
|
||||
|
||||
msgid "Size in memory"
|
||||
msgstr ""
|
||||
|
||||
msgid "Sorting entries"
|
||||
msgstr ""
|
||||
|
||||
@@ -397,6 +421,10 @@ msgstr ""
|
||||
|
||||
msgid "Statistics"
|
||||
msgstr ""
|
||||
|
||||
msgid "Statement in nftables rules"
|
||||
msgstr ""
|
||||
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
@@ -429,6 +457,9 @@ msgid ""
|
||||
"Continue?"
|
||||
msgstr ""
|
||||
|
||||
msgid "This proxy will receive traffic last, even after the main blacklist"
|
||||
msgstr ""
|
||||
|
||||
msgid "Time"
|
||||
msgstr ""
|
||||
|
||||
@@ -447,6 +478,9 @@ msgstr ""
|
||||
msgid "Tor mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Transit traffic"
|
||||
msgstr ""
|
||||
|
||||
msgid "Transparent proxy"
|
||||
msgstr ""
|
||||
|
||||
@@ -465,6 +499,9 @@ msgstr ""
|
||||
msgid "Turn on if blacklist source is blocked"
|
||||
msgstr ""
|
||||
|
||||
msgid "Turn on if files are blocked"
|
||||
msgstr ""
|
||||
|
||||
msgid "Type a search pattern..."
|
||||
msgstr ""
|
||||
|
||||
@@ -509,6 +546,7 @@ msgid "URLs of remote user entries file"
|
||||
|
||||
msgid "Use optional DNS resolver"
|
||||
msgstr ""
|
||||
|
||||
msgid "User entries"
|
||||
msgstr ""
|
||||
|
||||
@@ -527,6 +565,9 @@ msgstr ""
|
||||
msgid "Warning"
|
||||
msgstr ""
|
||||
|
||||
msgid "all"
|
||||
msgstr "все"
|
||||
|
||||
msgid "ascending"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"/usr/libexec/ruantiblock": [ "list" ],
|
||||
"/etc/ruantiblock/fqdn_filter": [ "read" ],
|
||||
"/etc/ruantiblock/ip_filter": [ "read" ],
|
||||
"/etc/ruantiblock/user_entries": [ "read" ],
|
||||
"/etc/ruantiblock/user_lists/*": [ "read" ],
|
||||
"/etc/ruantiblock/bypass_entries": [ "read" ],
|
||||
"/etc/ruantiblock/gr_excluded_nets": [ "read" ],
|
||||
"/etc/ruantiblock/gr_excluded_sld": [ "read" ],
|
||||
@@ -16,7 +16,8 @@
|
||||
"/etc/crontabs/root": [ "read" ],
|
||||
"/usr/bin/ruantiblock*": [ "exec" ],
|
||||
"/sbin/logread -e ruantiblock:": [ "exec" ],
|
||||
"/usr/sbin/logread -e ruantiblock:": [ "exec" ]
|
||||
"/usr/sbin/logread -e ruantiblock:": [ "exec" ],
|
||||
"/tmp": [ "list" ]
|
||||
},
|
||||
"uci": [ "network", "ruantiblock" ],
|
||||
"ubus": {
|
||||
@@ -27,7 +28,7 @@
|
||||
"file": {
|
||||
"/etc/ruantiblock/fqdn_filter": [ "write" ],
|
||||
"/etc/ruantiblock/ip_filter": [ "write" ],
|
||||
"/etc/ruantiblock/user_entries": [ "write" ],
|
||||
"/etc/ruantiblock/user_lists/*": [ "write" ],
|
||||
"/etc/ruantiblock/bypass_entries": [ "write" ],
|
||||
"/etc/ruantiblock/gr_excluded_nets": [ "write" ],
|
||||
"/etc/ruantiblock/gr_excluded_sld": [ "write" ],
|
||||
|
||||
Reference in New Issue
Block a user