2020-06-19 20:43:08 +03:00
|
|
|
'use strict';
|
2021-12-05 19:18:32 +03:00
|
|
|
'require baseclass';
|
2020-06-19 20:43:08 +03:00
|
|
|
'require fs';
|
2021-11-04 18:57:08 +03:00
|
|
|
'require rpc';
|
2020-06-19 20:43:08 +03:00
|
|
|
'require ui';
|
|
|
|
|
|
2021-03-26 23:49:38 +03:00
|
|
|
document.head.append(E('style', {'type': 'text/css'},
|
|
|
|
|
`
|
2020-06-19 20:43:08 +03:00
|
|
|
.label-status {
|
2025-10-16 23:32:07 +03:00
|
|
|
display: inline-block;
|
|
|
|
|
margin: 2px !important;
|
2022-05-30 17:49:26 +03:00
|
|
|
padding: 2px 4px;
|
2021-11-04 18:57:08 +03:00
|
|
|
-webkit-border-radius: 3px;
|
|
|
|
|
-moz-border-radius: 3px;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
color: #fff !important;
|
2025-10-16 23:32:07 +03:00
|
|
|
word-wrap: break-word !important;
|
2020-06-19 20:43:08 +03:00
|
|
|
}
|
|
|
|
|
.starting {
|
2022-09-20 16:33:28 +03:00
|
|
|
background-color: #9c994c !important;
|
2020-06-19 20:43:08 +03:00
|
|
|
}
|
|
|
|
|
.running {
|
2021-11-04 18:57:08 +03:00
|
|
|
background-color: #2ea256 !important;
|
2020-06-19 20:43:08 +03:00
|
|
|
}
|
|
|
|
|
.updating {
|
2021-11-04 18:57:08 +03:00
|
|
|
background-color: #1e82ff !important;
|
2020-06-19 20:43:08 +03:00
|
|
|
}
|
|
|
|
|
.stopped {
|
2022-09-20 16:33:28 +03:00
|
|
|
background-color: #8a8a8a !important;
|
2020-06-19 20:43:08 +03:00
|
|
|
}
|
|
|
|
|
.error {
|
2021-11-04 18:57:08 +03:00
|
|
|
background-color: #ff4e54 !important;
|
2020-06-19 20:43:08 +03:00
|
|
|
}
|
2021-03-26 23:49:38 +03:00
|
|
|
`));
|
|
|
|
|
|
2021-12-05 19:18:32 +03:00
|
|
|
return baseclass.extend({
|
2024-11-06 15:30:03 +03:00
|
|
|
appName : 'ruantiblock',
|
|
|
|
|
execPath : '/usr/bin/ruantiblock',
|
|
|
|
|
tokenFile : '/var/run/ruantiblock.token',
|
|
|
|
|
parsersDir : '/usr/libexec/ruantiblock',
|
|
|
|
|
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',
|
|
|
|
|
grExcludedNetsFile : '/etc/ruantiblock/gr_excluded_nets',
|
|
|
|
|
grExcludedSldFile : '/etc/ruantiblock/gr_excluded_sld',
|
|
|
|
|
crontabFile : '/etc/crontabs/root',
|
2025-10-16 23:32:07 +03:00
|
|
|
infoLabelStarting : E('span', { 'class': 'label-status starting' }, _('Starting')),
|
|
|
|
|
infoLabelRunning : E('span', { 'class': 'label-status running' }, _('Enabled')),
|
|
|
|
|
infoLabelUpdating : E('span', { 'class': 'label-status updating' }, _('Updating')),
|
|
|
|
|
infoLabelStopped : E('span', { 'class': 'label-status stopped' }, _('Disabled')),
|
|
|
|
|
infoLabelError : E('span', { 'class': 'label-status error' }, _('Error')),
|
2021-11-04 18:57:08 +03:00
|
|
|
|
2022-05-05 18:28:32 +03:00
|
|
|
blacklistPresets: {
|
2025-11-13 18:14:06 +03:00
|
|
|
'ruantiblock-fqdn' : [ 'ruantiblock', 'fqdn', 'https://github.com/gSpotx2f/ruantiblock_blacklist' ],
|
|
|
|
|
'ruantiblock-ip' : [ 'ruantiblock', 'ip', 'https://github.com/gSpotx2f/ruantiblock_blacklist' ],
|
|
|
|
|
'zapret-info-fqdn' : [ '*zapret-info', 'fqdn', 'https://github.com/zapret-info/z-i' ],
|
|
|
|
|
'zapret-info-fqdn-only': [ '*zapret-info', 'fqdn-only', 'https://github.com/zapret-info/z-i' ],
|
|
|
|
|
'zapret-info-ip' : [ '*zapret-info', 'ip', 'https://github.com/zapret-info/z-i' ],
|
|
|
|
|
'rublacklist-fqdn' : [ '*rublacklist', 'fqdn', 'https://rublacklist.net' ],
|
|
|
|
|
'rublacklist-fqdn-only': [ '*rublacklist', 'fqdn-only', 'https://rublacklist.net' ],
|
|
|
|
|
'rublacklist-ip' : [ '*rublacklist', 'ip', 'https://rublacklist.net' ],
|
|
|
|
|
'antifilter-fqdn' : [ '*antifilter', 'fqdn', 'https://antifilter.download' ],
|
|
|
|
|
'antifilter-fqdn-only' : [ '*antifilter', 'fqdn-only', 'https://antifilter.download' ],
|
|
|
|
|
'antifilter-ip' : [ '*antifilter', 'ip', 'https://antifilter.download' ],
|
2021-11-04 18:57:08 +03:00
|
|
|
},
|
|
|
|
|
|
2024-11-06 15:30:03 +03:00
|
|
|
defaultConfig: {
|
|
|
|
|
'proxy_mode' : '2',
|
|
|
|
|
'tor_trans_port' : '9040',
|
|
|
|
|
'onion_dns_addr' : '127.0.0.1#9053',
|
|
|
|
|
'if_vpn' : 'tun0',
|
|
|
|
|
't_proxy_port_tcp': '1100',
|
|
|
|
|
't_proxy_port_udp': '1100',
|
|
|
|
|
},
|
|
|
|
|
|
2021-11-04 18:57:08 +03:00
|
|
|
callInitStatus: rpc.declare({
|
|
|
|
|
object: 'luci',
|
|
|
|
|
method: 'getInitList',
|
|
|
|
|
params: [ 'name' ],
|
|
|
|
|
expect: { '': {} }
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
callInitAction: rpc.declare({
|
|
|
|
|
object: 'luci',
|
|
|
|
|
method: 'setInitAction',
|
|
|
|
|
params: [ 'name', 'action' ],
|
|
|
|
|
expect: { result: false }
|
|
|
|
|
}),
|
|
|
|
|
|
2024-11-03 02:20:45 +03:00
|
|
|
getInitStatus(name) {
|
2021-11-04 18:57:08 +03:00
|
|
|
return this.callInitStatus(name).then(res => {
|
|
|
|
|
if(res) {
|
|
|
|
|
return res[name].enabled;
|
|
|
|
|
} else {
|
|
|
|
|
throw _('Command failed');
|
|
|
|
|
}
|
|
|
|
|
}).catch(e => {
|
|
|
|
|
ui.addNotification(null,
|
|
|
|
|
E('p', _('Failed to get %s init status: %s').format(name, e)));
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2024-11-03 02:20:45 +03:00
|
|
|
handleServiceAction(name, action) {
|
2021-11-04 18:57:08 +03:00
|
|
|
return this.callInitAction(name, action).then(success => {
|
|
|
|
|
if(!success) {
|
|
|
|
|
throw _('Command failed');
|
|
|
|
|
};
|
|
|
|
|
return true;
|
|
|
|
|
}).catch(e => {
|
|
|
|
|
ui.addNotification(null,
|
|
|
|
|
E('p', _('Service action failed "%s %s": %s').format(name, action, e)));
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2024-11-03 02:20:45 +03:00
|
|
|
normalizeValue(v) {
|
2021-11-04 18:57:08 +03:00
|
|
|
return (v && typeof(v) === 'string') ? v.trim().replace(/\r?\n/g, '') : v;
|
|
|
|
|
},
|
|
|
|
|
|
2024-11-06 15:30:03 +03:00
|
|
|
makeStatusString(
|
|
|
|
|
app_status_code,
|
|
|
|
|
bllist_preset,
|
|
|
|
|
bllist_module,
|
|
|
|
|
vpn_route_status_code) {
|
2021-11-04 18:57:08 +03:00
|
|
|
let app_status_label;
|
|
|
|
|
let spinning = '';
|
|
|
|
|
|
|
|
|
|
switch(app_status_code) {
|
|
|
|
|
case 0:
|
|
|
|
|
app_status_label = this.infoLabelRunning;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
app_status_label = this.infoLabelStopped;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
app_status_label = this.infoLabelStarting;
|
|
|
|
|
spinning = ' spinning';
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
app_status_label = this.infoLabelUpdating;
|
|
|
|
|
spinning = ' spinning';
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
app_status_label = this.infoLabelError;
|
2025-10-16 23:32:07 +03:00
|
|
|
return E('table', { 'class': 'table' }, [
|
|
|
|
|
E('tr', { 'class': 'tr' }, [
|
|
|
|
|
E('td', { 'class': 'td left', 'style': 'width:33%' }, _('Status')),
|
|
|
|
|
E('td', { 'class': 'td left' }, app_status_label),
|
|
|
|
|
]),
|
|
|
|
|
]);
|
2021-11-04 18:57:08 +03:00
|
|
|
};
|
|
|
|
|
|
2025-10-16 23:32:07 +03:00
|
|
|
return E('table', { 'class': 'table' }, [
|
|
|
|
|
E('tr', { 'class': 'tr' }, [
|
|
|
|
|
E('td', { 'class': 'td left', 'style': 'width:33%' }, _('Status')),
|
|
|
|
|
E('td', { 'class': 'td left' + spinning }, [
|
|
|
|
|
app_status_label,
|
|
|
|
|
(app_status_code != 2 && vpn_route_status_code != 0)
|
|
|
|
|
? E('span', { 'class': 'label-status error' },
|
|
|
|
|
_('VPN routing error! Need restart'))
|
|
|
|
|
: '',
|
|
|
|
|
]),
|
|
|
|
|
]),
|
|
|
|
|
E('tr', { 'class': 'tr' }, [
|
|
|
|
|
E('td', { 'class': 'td left' }, _('Blacklist update mode')),
|
|
|
|
|
E('td', { 'class': 'td left' },
|
|
|
|
|
(!bllist_preset || bllist_preset === '') ? _('user entries only') :
|
|
|
|
|
(this.blacklistPresets[bllist_preset]) ?
|
|
|
|
|
[
|
|
|
|
|
E('span', {
|
|
|
|
|
'style' : 'cursor:help; border-bottom:1px dotted',
|
|
|
|
|
'data-tooltip': this.blacklistPresets[bllist_preset][2],
|
|
|
|
|
}, this.blacklistPresets[bllist_preset][0]),
|
|
|
|
|
' - ',
|
|
|
|
|
this.blacklistPresets[bllist_preset][1],
|
|
|
|
|
]
|
|
|
|
|
:
|
|
|
|
|
_('Error') + '!'
|
|
|
|
|
),
|
|
|
|
|
]),
|
|
|
|
|
]);
|
2021-11-04 18:57:08 +03:00
|
|
|
},
|
|
|
|
|
|
2021-12-05 19:18:32 +03:00
|
|
|
fileEditDialog: baseclass.extend({
|
2024-11-03 02:20:45 +03:00
|
|
|
__init__(file, title, description, callback, file_exists=false) {
|
2021-11-04 18:57:08 +03:00
|
|
|
this.file = file;
|
|
|
|
|
this.title = title;
|
|
|
|
|
this.description = description;
|
|
|
|
|
this.callback = callback;
|
|
|
|
|
this.file_exists = file_exists;
|
|
|
|
|
},
|
|
|
|
|
|
2024-11-03 02:20:45 +03:00
|
|
|
load() {
|
2022-01-06 00:49:07 +03:00
|
|
|
return L.resolveDefault(fs.read(this.file), '');
|
2021-11-04 18:57:08 +03:00
|
|
|
},
|
|
|
|
|
|
2024-11-03 02:20:45 +03:00
|
|
|
render(content) {
|
2021-11-04 18:57:08 +03:00
|
|
|
ui.showModal(this.title, [
|
|
|
|
|
E('div', { 'class': 'cbi-section' }, [
|
|
|
|
|
E('div', { 'class': 'cbi-section-descr' }, this.description),
|
|
|
|
|
E('div', { 'class': 'cbi-section' },
|
|
|
|
|
E('p', {},
|
|
|
|
|
E('textarea', {
|
2025-02-02 16:46:56 +03:00
|
|
|
'id' : 'widget.modal_content',
|
|
|
|
|
'class' : 'cbi-input-textarea',
|
|
|
|
|
'style' : 'width:100% !important',
|
|
|
|
|
'rows' : 10,
|
|
|
|
|
'wrap' : 'off',
|
2021-11-04 18:57:08 +03:00
|
|
|
'spellcheck': 'false',
|
|
|
|
|
},
|
2022-01-06 00:49:07 +03:00
|
|
|
content)
|
2021-11-04 18:57:08 +03:00
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
]),
|
2025-02-02 16:46:56 +03:00
|
|
|
E('div', { 'class': 'right button-row' }, [
|
2021-11-04 18:57:08 +03:00
|
|
|
E('button', {
|
|
|
|
|
'class': 'btn',
|
|
|
|
|
'click': ui.hideModal,
|
|
|
|
|
}, _('Dismiss')),
|
|
|
|
|
' ',
|
|
|
|
|
E('button', {
|
2025-02-02 16:46:56 +03:00
|
|
|
'id' : 'btn_save',
|
2021-11-04 18:57:08 +03:00
|
|
|
'class': 'btn cbi-button-positive important',
|
|
|
|
|
'click': ui.createHandlerFn(this, this.handleSave),
|
|
|
|
|
}, _('Save')),
|
|
|
|
|
]),
|
|
|
|
|
]);
|
|
|
|
|
},
|
|
|
|
|
|
2024-11-03 02:20:45 +03:00
|
|
|
handleSave(ev) {
|
2021-11-04 18:57:08 +03:00
|
|
|
let textarea = document.getElementById('widget.modal_content');
|
|
|
|
|
let value = textarea.value.trim().replace(/\r\n/g, '\n') + '\n';
|
|
|
|
|
|
2024-11-06 15:30:03 +03:00
|
|
|
return fs.write(this.file, value).then(rc => {
|
2021-11-04 18:57:08 +03:00
|
|
|
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();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2024-11-03 02:20:45 +03:00
|
|
|
error(e) {
|
2021-11-04 18:57:08 +03:00
|
|
|
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'))
|
|
|
|
|
),
|
|
|
|
|
]);
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
|
2024-11-03 02:20:45 +03:00
|
|
|
show() {
|
2021-11-04 18:57:08 +03:00
|
|
|
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);
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
}),
|
2020-06-19 20:43:08 +03:00
|
|
|
});
|