--- /dev/null
+'use strict';
+'require view';
+'require form';
+'require tools.widgets as widgets';
+
+return view.extend({
+ render: function () {
+ const m = new form.Map('antiblock', _('AntiBlock'));
+
+ const s = m.section(form.NamedSection, 'config', 'main', _('AntiBlock'));
+ s.addremove = true;
+
+ let o = s.option(form.Flag, 'enabled', _('Enabled'));
+
+ o = s.option(form.DynamicList, 'blacklist', _('Blacklist'), _('Prevent adding IP from these subnets to the routing table, optional parameter'));
+ o.depends('enabled', '1');
+
+ o = s.option(form.Flag, 'log', _('Log'), _('Show operations log, optional parameter'));
+ o.depends('enabled', '1');
+
+ o = s.option(form.Flag, 'stat', _('Statistics'), _('Show statistics data, optional parameter'));
+ o.depends('enabled', '1');
+
+ return m.render();
+ },
+});
'use strict';
'require ui';
+'require uci';
+'require fs';
'require form';
-'require rpc';
'require view';
-const read_domains = rpc.declare({
- object: 'luci.antiblock',
- method: 'read_domains'
-});
+let section_routes;
+let section_data;
+let domains_textarea;
-const write_domains = rpc.declare({
- object: 'luci.antiblock',
- method: 'write_domains',
- params: ['domains']
-});
+async function write_domains_handler() {
+ ui.showModal(null, [E('p', { class: 'spinning' }, _('Write domains'))]);
+ const lines = domains_textarea.value.split(/\r?\n/).filter(Boolean);
+ let write_data = '';
+ lines.forEach(function (element) { write_data += element + '\n' });
+ const domains_path = section_routes.selectedOptions[0].label;
+ try {
+ await fs.write(domains_path, write_data);
+ await fs.exec('/etc/init.d/antiblock', ['restart']);
+ } catch (err) {
+ ui.addNotification(null, E('p', {}, _('Unable to write to domains file') + ' ' + domains_path + ' "' + err.message + '"'));
+ }
+ ui.hideModal();
+ select_handler();
+}
-return view.extend({
- generic_failure: function (message) {
- return E('div', {
- 'class': 'error'
- }, ['RPC call failure: ', message]);
- },
- load: function () {
- return Promise.all([
- read_domains()
- ]);
- },
- render: function (data) {
- const main_div = E('div');
+function read_domains_handler(data) {
+ const text_data = data.split(/\r?\n/).filter(Boolean);
+ const section_descr_div = E('div', { class: 'cbi-section-descr' }, _('Domain count in file:') + ' ' + text_data.length);
+
+ domains_textarea = E('textarea', { class: 'cbi-input-textarea' },);
+ domains_textarea.value = '';
+ text_data.forEach(function (element) { domains_textarea.value += element + '\n' });
- const header = E('h2', {}, _('AntiBlock'));
+ const btn_write_domains = E('button', { class: 'cbi-button cbi-button-apply', click: write_domains_handler }, _('Write domains'));
+ const div_for_btn = E('div', { style: 'padding-top: 20px' });
+ div_for_btn.appendChild(btn_write_domains);
- const section_descr_div = E(
- 'div',
- {
- class: 'cbi-section-descr',
- },
- _('Domains count in file: ')
- );
+ section_data.innerHTML = '';
+ section_data.appendChild(section_descr_div);
+ section_data.appendChild(domains_textarea);
+ section_data.appendChild(div_for_btn);
+}
- const section_div = E(
- 'div',
- {
- class: 'cbi-section',
+function select_handler() {
+ section_data.innerHTML = '';
+ const domains_path = section_routes.selectedOptions[0].label;
+ fs.read_direct(domains_path).then(
+ read_domains_handler
+ ).catch(
+ function (err) {
+ if (err.message == 'Failed to stat requested path') {
+ fs.exec('/bin/mkdir', ['/etc/antiblock']).then(
+ fs.write(domains_path, '').then(
+ read_domains_handler("")
+ ).catch(
+ function (err) {
+ section_data.appendChild(E('p', {}, _('Unable to create domains file') + ' ' + domains_path + ' "' + err.message + '"'));
+ }
+ )
+ );
+ } else {
+ section_data.appendChild(E('p', {}, _('Unable to read domains file') + ' ' + domains_path + ' "' + err.message + '"'));
}
- );
+ }
+ );
+}
- main_div.appendChild(header);
- main_div.appendChild(section_div);
- section_div.appendChild(section_descr_div);
+return view.extend({
+ handleSaveApply: null,
+ handleSave: null,
+ handleReset: null,
+ load: async function () {
+ return await uci.load('antiblock');
+ },
+ render: function () {
+ const uci_routes = uci.sections('antiblock', 'route');
- if (typeof data[0].domains !== 'undefined') {
- const domains_textarea = E(
- 'textarea',
- {
- class: 'cbi-input-textarea',
- },
- );
+ section_routes = E('select', { class: 'cbi-input-select', change: select_handler });
+ uci_routes.forEach(function (route) {
+ if (route.domains_path.substring(0, 4) != 'http') {
+ const routes_option = E('option', { value: route.domains_path }, route.domains_path);
+ section_routes.appendChild(routes_option);
+ }
+ });
- section_descr_div.innerHTML += data[0].domains.length;
+ const main_div = E([]);
+ main_div.appendChild(E('h2', _('Domains')));
- domains_textarea.value = '';
- data[0].domains.forEach((element) => domains_textarea.value += element + '\n');
+ if (section_routes.innerHTML != '') {
+ const routes_div = E('div', { class: 'cbi-section' });
+ routes_div.appendChild(E('div', { class: 'cbi-section-descr' }, _('Domains path:')));
+ routes_div.appendChild(section_routes);
+ main_div.appendChild(routes_div);
- const btn_write_domains = E(
- 'button',
- {
- class: 'btn cbi-button cbi-button-apply',
- click: function (ev) {
- ui.showModal(null, [
- E(
- 'p',
- { class: 'spinning' },
- _('Write domains')
- ),
- ]);
- const lines = domains_textarea.value.split(/\r?\n/).filter(Boolean);
- const write_domains_res = Promise.all([write_domains(lines)]);
- write_domains_res.then(
- function (value) { location.reload(); },
- function (error) { /* code if some error */ }
- );
- },
- },
- _('Write domains')
- );
+ section_data = E('div', { class: 'cbi-section' });
+ main_div.appendChild(section_data);
- section_div.appendChild(domains_textarea);
- section_div.appendChild(btn_write_domains);
+ select_handler();
} else {
- const error_div = E(
- 'div',
- {
- },
- _('The File argument was not specified.')
- );
-
- section_div.appendChild(error_div);
+ const routes_div = E('div', { class: 'cbi-section' });
+ routes_div.appendChild(E('div', { class: 'cbi-section-descr' }, _('Path to file in "Domains path" is not set.')));
+ main_div.appendChild(routes_div);
}
return main_div;
- },
- handleSave: null,
- handleSaveApply: null,
- handleReset: null
+ }
});
+++ /dev/null
-'use strict';
-'require view';
-'require form';
-'require tools.widgets as widgets';
-
-return view.extend({
- render: function () {
- const m = new form.Map('antiblock', _('AntiBlock'));
-
- const s = m.section(form.NamedSection, 'config', 'antiblock', _('AntiBlock'));
- s.addremove = true;
-
- let o = s.option(form.Flag, 'enabled', _('Enabled'));
-
- o = s.option(form.Value, 'url', _('URL'), _('Domains file URL, either File or URL or both'));
- o.default = 'https://antifilter.download/list/domains.lst';
- o.depends('enabled', '1');
-
- o = s.option(form.Value, 'file', _('File'), _('Domains file path, either File or URL or both'));
- o.depends('enabled', '1');
-
- o = s.option(form.Value, 'DNS', _('DNS'), _('DNS address, required parameter'));
- o.default = '1.1.1.1:53';
- o.depends('enabled', '1');
-
- o = s.option(form.Value, 'listen', _('Listen'), _('Listen address, required parameter'));
- o.default = '192.168.1.1:5053';
- o.depends('enabled', '1');
-
- o = s.option(widgets.DeviceSelect, 'VPN_name', _('VPN name'), _('Interface name, required parameter'));
- o.depends('enabled', '1');
-
- o = s.option(form.Value, 'output', _('Output'), _('Log or statistics output folder, optional parameter'));
- o.depends('enabled', '1');
-
- o = s.option(form.Flag, 'log', _('Log'), _('Show operations log, optional parameter'));
- o.depends({ output: '/', '!contains': true });
-
- o = s.option(form.Flag, 'stat', _('Stat'), _('Show statistics data, optional parameter'));
- o.depends({ output: '/', '!contains': true });
-
- return m.render();
- },
-});
--- /dev/null
+'use strict';
+'require view';
+'require fs';
+'require poll';
+'require ui';
+'require uci';
+
+let main_config;
+
+return view.extend({
+ retrieveLog: async function () {
+ return fs.read_direct('/tmp/antiblock/log.txt').then(function (logdata) {
+ const loglines = logdata.trim().split(/\n/).map(function (line) {
+ return line.replace(/^<\d+>/, '');
+ });
+ return { value: loglines.join('\n'), rows: loglines.length + 1 };
+ }).catch(function (err) {
+ ui.addNotification(null, E('p', {}, _('Unable to load log data:') + ' ' + err.message));
+ return '';
+ });
+ },
+
+ pollLog: async function () {
+ const element = document.getElementById('syslog');
+ if (element) {
+ const log = await this.retrieveLog();
+ element.value = log.value;
+ element.rows = log.rows;
+ }
+ },
+
+ load: async function () {
+ await uci.load('antiblock');
+
+ main_config = uci.sections('antiblock', 'main');
+ if (!main_config[0]?.log || main_config[0]?.log === '0') {
+ return;
+ }
+
+ poll.add(this.pollLog.bind(this), 10);
+
+ return await this.retrieveLog();
+ },
+
+ render: function (loglines) {
+ const main_div = E([]);
+ main_div.appendChild(E('h2', _('Log')));
+ const routes_div = E('div', { class: 'cbi-section' });
+ routes_div.appendChild(E('div', { class: 'cbi-section-descr' }, _('Log is not enabled.')));
+ main_div.appendChild(routes_div);
+
+ if (!main_config[0]?.log || main_config[0]?.log === '0') {
+ return main_div;
+ }
+
+ const scrollDownButton = E('button', {
+ 'id': 'scrollDownButton',
+ 'class': 'cbi-button cbi-button-neutral',
+ }, _('Scroll to tail', 'scroll to bottom (the tail) of the log file'));
+ scrollDownButton.addEventListener('click', function () {
+ scrollUpButton.scrollIntoView();
+ });
+
+ const scrollUpButton = E('button', {
+ 'id': 'scrollUpButton',
+ 'class': 'cbi-button cbi-button-neutral',
+ }, _('Scroll to head', 'scroll to top (the head) of the log file'));
+ scrollUpButton.addEventListener('click', function () {
+ scrollDownButton.scrollIntoView();
+ });
+
+ return E([], [
+ E('h2', {}, [_('Log')]),
+ E('div', { 'id': 'content_syslog' }, [
+ E('div', { 'style': 'padding-bottom: 20px' }, [scrollDownButton]),
+ E('textarea', {
+ 'id': 'syslog',
+ 'style': 'font-size:12px',
+ 'readonly': 'readonly',
+ 'wrap': 'off',
+ 'rows': loglines.rows
+ }, [loglines.value]),
+ E('div', { 'style': 'padding-top: 20px' }, [scrollUpButton])
+ ])
+ ]);
+ },
+
+ handleSaveApply: null,
+ handleSave: null,
+ handleReset: null
+});
--- /dev/null
+'use strict';
+'require view';
+'require form';
+'require tools.widgets as widgets';
+
+return view.extend({
+ render: function () {
+ const m = new form.Map('antiblock', _('Routes'));
+
+ const s = m.section(form.GridSection, 'route', _('Routes'), _('It is necessary to enter from 1 to 32 values:'));
+ s.optional = false;
+ s.anonymous = true;
+ s.addremove = true;
+ s.nodescriptions = true;
+
+ let o = s.option(widgets.NetworkSelect, 'gateway', _('Gateway'), _('Gateway'));
+ o.loopback = true;
+ o.nocreate = true;
+
+ o = s.option(form.Value, 'domains_path', _('Domains path'), _('Domains path/URL. If you want to add domains via LuCI, specify the files in the /etc/antiblock folder.'));
+ o.default = '/etc/antiblock/';
+
+ return m.render();
+ }
+});
--- /dev/null
+'use strict';
+'require view';
+'require fs';
+'require poll';
+'require ui';
+'require uci';
+
+let main_config;
+
+return view.extend({
+ retrieveLog: async function () {
+ return fs.read_direct('/tmp/antiblock/stat.txt').then(function (logdata) {
+ const loglines = logdata.trim().split(/\n/).map(function (line) {
+ return line.replace(/^<\d+>/, '');
+ });
+ return { value: loglines.join('\n'), rows: loglines.length + 1 };
+ }).catch(function (err) {
+ ui.addNotification(null, E('p', {}, _('Unable to load statistics data:') + ' ' + err.message));
+ return '';
+ });
+ },
+
+ pollLog: async function () {
+ const element = document.getElementById('syslog');
+ if (element) {
+ const log = await this.retrieveLog();
+ element.value = log.value;
+ element.rows = log.rows;
+ }
+ },
+
+ load: async function () {
+ await uci.load('antiblock');
+
+ main_config = uci.sections('antiblock', 'main');
+ if (!main_config[0]?.stat || main_config[0]?.stat === '0') {
+ return;
+ }
+
+ poll.add(this.pollLog.bind(this), 10);
+
+ return await this.retrieveLog();
+ },
+
+ render: function (loglines) {
+ const main_div = E([]);
+ main_div.appendChild(E('h2', _('Statistics')));
+ const routes_div = E('div', { class: 'cbi-section' });
+ routes_div.appendChild(E('div', { class: 'cbi-section-descr' }, _('Statistics are not enabled.')));
+ main_div.appendChild(routes_div);
+
+ if (!main_config[0]?.stat || main_config[0]?.stat === '0') {
+ return main_div;
+ }
+
+ return E([], [
+ E('h2', {}, [_('Statistics')]),
+ E('div', { 'id': 'content_syslog' }, [
+ E('textarea', {
+ 'id': 'syslog',
+ 'style': 'font-size:12px',
+ 'readonly': 'readonly',
+ 'wrap': 'off',
+ 'rows': loglines.rows
+ }, [loglines.value])
+ ])
+ ]);
+ },
+
+ handleSaveApply: null,
+ handleSave: null,
+ handleReset: null
+});
--- /dev/null
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/args.js:8
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/args.js:10
+#: applications/luci-app-antiblock/root/usr/share/luci/menu.d/luci-app-antiblock.json:3
+msgid "AntiBlock"
+msgstr ""
+
+#: applications/luci-app-antiblock/root/usr/share/luci/menu.d/luci-app-antiblock.json:15
+msgid "Args"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/args.js:15
+msgid "Blacklist"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/domains.js:30
+msgid "Domain count in file:"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/domains.js:89
+#: applications/luci-app-antiblock/root/usr/share/luci/menu.d/luci-app-antiblock.json:31
+msgid "Domains"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/routes.js:20
+msgid "Domains path"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/routes.js:20
+msgid ""
+"Domains path/URL. If you want to add domains via LuCI, specify the files in "
+"the /etc/antiblock folder."
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/domains.js:93
+msgid "Domains path:"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/args.js:13
+msgid "Enabled"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/routes.js:16
+msgid "Gateway"
+msgstr ""
+
+#: applications/luci-app-antiblock/root/usr/share/rpcd/acl.d/luci-app-antiblock.json:3
+msgid "Grant UCI and RPC access to LuCI app AntiBlock"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/routes.js:10
+msgid "It is necessary to enter from 1 to 32 values:"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/args.js:18
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/log.js:47
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/log.js:73
+#: applications/luci-app-antiblock/root/usr/share/luci/menu.d/luci-app-antiblock.json:47
+msgid "Log"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/log.js:49
+msgid "Log is not enabled."
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/domains.js:103
+msgid "Path to file in \"Domains path\" is not set."
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/args.js:15
+msgid ""
+"Prevent adding IP from these subnets to the routing table, optional parameter"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/routes.js:8
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/routes.js:10
+#: applications/luci-app-antiblock/root/usr/share/luci/menu.d/luci-app-antiblock.json:23
+msgid "Routes"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/log.js:67
+msgctxt "scroll to top (the head) of the log file"
+msgid "Scroll to head"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/log.js:59
+msgctxt "scroll to bottom (the tail) of the log file"
+msgid "Scroll to tail"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/args.js:18
+msgid "Show operations log, optional parameter"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/args.js:21
+msgid "Show statistics data, optional parameter"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/args.js:21
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/statistics.js:47
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/statistics.js:57
+#: applications/luci-app-antiblock/root/usr/share/luci/menu.d/luci-app-antiblock.json:39
+msgid "Statistics"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/statistics.js:49
+msgid "Statistics are not enabled."
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/domains.js:59
+msgid "Unable to create domains file"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/log.js:18
+msgid "Unable to load log data:"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/statistics.js:18
+msgid "Unable to load statistics data:"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/domains.js:64
+msgid "Unable to read domains file"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/domains.js:22
+msgid "Unable to write to domains file"
+msgstr ""
+
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/domains.js:13
+#: applications/luci-app-antiblock/htdocs/luci-static/resources/view/antiblock/domains.js:36
+msgid "Write domains"
+msgstr ""
+++ /dev/null
-#!/bin/sh
-
-# ubus -v list luci.antiblock
-# ubus -S call luci.antiblock read_domains
-# ubus -S call luci.antiblock write_domains '{"domains":["test0.com","test1.com","test2.com"]}'
-
-. /lib/functions.sh
-. /usr/share/libubox/jshn.sh
-
-get_file_path() {
- file_path="$(uci -q get antiblock.config.file)"
-}
-
-read_domains() {
- get_file_path
-
- json_init
- if [ -n "$file_path" ]; then
- if [ ! -f "$file_path" ]; then
- touch "$file_path"
- fi
-
- json_add_array "domains"
- file_data=$(cat $file_path)
- for domain in $file_data; do
- json_add_string "" "$domain"
- done
- json_close_array
- else
- json_add_array "empty"
- json_close_array
- fi
- json_dump
- json_cleanup
-}
-
-write_domains() {
- get_file_path
-
- if [ -n "$file_path" ]; then
- if [ ! -f "$file_path" ]; then
- touch "$file_path"
- fi
-
- json_load "$1"
- json_get_values values "domains"
- >$file_path
- for key in $values; do
- echo "$key" >>$file_path
- done
- json_cleanup
-
- /etc/init.d/antiblock restart
- fi
-}
-
-case "$1" in
-list)
- json_init
- json_add_object "read_domains"
- json_close_object
- json_add_object "write_domains"
- json_add_string 'domains' "domains"
- json_close_object
- json_dump
- json_cleanup
- ;;
-call)
- case "$2" in
- read_domains)
- read_domains
- ;;
- write_domains)
- read -r input
- write_domains "$input"
- ;;
- *)
- return 0
- ;;
- esac
- ;;
-*)
- return 0
- ;;
-esac
]
}
},
- "admin/services/antiblock/form": {
- "title": "Args View",
+ "admin/services/antiblock/args": {
+ "title": "Args",
"order": 1,
"action": {
"type": "view",
- "path": "antiblock/form"
+ "path": "antiblock/args"
}
},
- "admin/services/antiblock/domains": {
- "title": "Domains file View",
+ "admin/services/antiblock/routes": {
+ "title": "Routes",
"order": 2,
+ "action": {
+ "type": "view",
+ "path": "antiblock/routes"
+ }
+ },
+ "admin/services/antiblock/domains": {
+ "title": "Domains",
+ "order": 3,
"action": {
"type": "view",
"path": "antiblock/domains"
}
+ },
+ "admin/services/antiblock/statistics": {
+ "title": "Statistics",
+ "order": 4,
+ "action": {
+ "type": "view",
+ "path": "antiblock/statistics"
+ }
+ },
+ "admin/services/antiblock/log": {
+ "title": "Log",
+ "order": 5,
+ "action": {
+ "type": "view",
+ "path": "antiblock/log"
+ }
}
}
"luci-app-antiblock": {
"description": "Grant UCI and RPC access to LuCI app AntiBlock",
"read": {
- "ubus": {
- "luci.antiblock": [
- "read_domains",
- "write_domains"
+ "file": {
+ "/tmp/antiblock/*": [
+ "read"
+ ],
+ "/etc/antiblock/*": [
+ "read"
+ ],
+ "/etc/init.d/antiblock restart": [
+ "exec"
+ ],
+ "/bin/mkdir /etc/antiblock": [
+ "exec"
]
},
"uci": [
]
},
"write": {
+ "file": {
+ "/etc/antiblock/*": [
+ "write"
+ ]
+ },
"uci": [
"antiblock"
]