'use strict'; 'require baseclass'; 'require form'; 'require fs'; 'require poll'; 'require rpc'; 'require uci'; 'require ui'; 'require view'; 'require tools.widgets as widgets' document.head.append(E('style', {'type': 'text/css'}, ` :root { --app-id-font-color: #454545; --app-id-font-shadow: #fff; --app-id-connected-color: #6bdebb; --app-id-disconnected-color: #f8aeba; --app-id-undefined-color: #dfdfdf; } :root[data-darkmode="true"] { --app-id-font-color: #f6f6f6; --app-id-font-shadow: #4d4d4d; --app-id-connected-color: #005F20; --app-id-disconnected-color: #a93734; --app-id-undefined-color: #4d4d4d; } .id-connected { --on-color: var(--app-id-font-color); background-color: var(--app-id-connected-color) !important; border-color: var(--app-id-connected-color) !important; color: var(--app-id-font-color) !important; text-shadow: 0 1px 1px var(--app-id-font-shadow); } .id-disconnected { --on-color: var(--app-id-font-color); background-color: var(--app-id-disconnected-color) !important; border-color: var(--app-id-disconnected-color) !important; color: var(--app-id-font-color) !important; text-shadow: 0 1px 1px var(--app-id-font-shadow); } .id-undefined { --on-color: var(--app-id-font-color); background-color: var(--app-id-undefined-color) !important; border-color: var(--app-id-undefined-color) !important; color: var(--app-id-font-color) !important; text-shadow: 0 1px 1px var(--app-id-font-shadow); } .id-label-status { display: inline-block; word-wrap: break-word; margin: 2px !important; padding: 4px 8px; border: 1px solid; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; font-weight: bold; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } `)); const btnStyleEnabled = 'btn cbi-button-save'; const btnStyleDisabled = 'btn cbi-button-reset'; const btnStyleApply = 'btn cbi-button-apply'; var Timefield = ui.Textfield.extend({ secToString(value) { let string = '0'; if(/^\d+$/.test(value)) { value = Number(value); if(value >= 86400 && (value % 86400) == 0) { string = String(value / 86400) + 'd'; } else if(value >= 3600 && (value % 3600) == 0) { string = String(value / 3600) + 'h'; } else if(value >= 60 && (value % 60) == 0) { string = String(value / 60) + 'm'; } else { string = String(value) + 's'; }; }; return string; }, render() { let frameEl = E('div', { 'id': this.options.id }), inputEl = E('input', { 'id' : this.options.id ? 'widget.' + this.options.id : null, 'name' : this.options.name, 'type' : 'text', 'class' : 'cbi-input-text', 'readonly' : this.options.readonly ? '' : null, 'disabled' : this.options.disabled ? '' : null, 'maxlength' : this.options.maxlength, 'placeholder': this.options.placeholder, 'value' : this.secToString(this.value), }); frameEl.appendChild(inputEl); return this.bind(frameEl); }, getValue() { let rawValue = this.node.querySelector('input').value, value = 0, res = rawValue.match(/^(\d+)([dhms]?)$/); if(res) { if(res[2] == 'd') { value = Number(res[1]) * 86400; } else if(res[2] == 'h') { value = Number(res[1]) * 3600; } else if(res[2] == 'm') { value = Number(res[1]) * 60; } else if(!res[2] || res[2] == 's') { value = Number(res[1]); } else { value = 0; }; } else { value = 0; }; return String(value); }, setValue(value) { let inputEl = this.node.querySelector('input'); inputEl.value = this.secToString(value); }, }); var TextfieldButton = ui.Textfield.extend({ render() { let frameEl = E('div', { 'id': this.options.id }), inputEl = E('input', { 'id' : this.options.id ? 'widget.' + this.options.id : null, 'name' : this.options.name, 'type' : 'text', 'class' : 'cbi-input-text', 'readonly' : this.options.readonly ? '' : null, 'disabled' : this.options.disabled ? '' : null, 'maxlength' : this.options.maxlength, 'placeholder': this.options.placeholder, 'value' : this.value, }); frameEl.appendChild(E('div', { 'class': 'control-group' }, [ inputEl, E('button', { 'class' : `cbi-button cbi-button-${this.options.btnstyle || 'neutral'}`, 'title' : this.options.btntitle, 'aria-label': this.options.btntitle, 'click' : this.options.onclick, }, this.options.btntext,) ])); return this.bind(frameEl); }, }); return view.extend({ appName : 'internet-detector', configDir : '/etc/internet-detector', pollInterval : L.env.pollinterval, appStatus : 'stoped', initStatus : null, inetStatus : null, inetStatusArea : E('div', { 'class': 'cbi-value-field', 'id': 'inetStatusArea' }), serviceStatusLabel : E('em', { 'id': 'serviceStatusLabel' }), initButton : null, currentAppMode : '0', defaultHosts : [ '8.8.8.8', '1.1.1.1' ], defaultURLs : [ 'https://www.google.com' ], ledsPath : '/sys/class/leds', ledsPerInstance : 3, leds : [], publicIPProviders : { dns : [ { name: 'opendns1', title: 'opendns1 (DNS)' }, { name: 'opendns2', title: 'opendns2 (DNS)' }, { name: 'opendns3', title: 'opendns3 (DNS)' }, { name: 'opendns4', title: 'opendns4 (DNS)' }, { name: 'google', title: 'google (DNS)' }, { name: 'akamai', title: 'akamai (DNS)' }, ], http: [ { name: 'akamai_http', title: 'akamai (HTTP)' }, { name: 'amazonaws', title: 'amazonaws (HTTP)' }, { name: 'wgetip', title: 'wgetip.com (HTTP)' }, { name: 'ifconfig', title: 'ifconfig.me (HTTP)' }, { name: 'ipecho', title: 'ipecho.net (HTTP)' }, { name: 'canhazip', title: 'canhazip.com (HTTP)' }, { name: 'icanhazip', title: 'icanhazip.com (HTTP)' }, ], }, mm : false, mmInit : false, email : false, emailExec : false, telegram : false, curlExec : false, modRegularScriptNextRun: {}, callInitStatus: rpc.declare({ object: 'luci', method: 'getInitList', params: [ 'name' ], expect: { '': {} } }), callInitAction: rpc.declare({ object: 'luci', method: 'setInitAction', params: [ 'name', 'action' ], expect: { result: false } }), getInitStatus() { return this.callInitStatus(this.appName).then(res => { if(res) { return res[this.appName].enabled; } else { throw _('Command failed'); } }).catch(e => { ui.addNotification(null, E('p', _('Failed to get %s init status: %s').format(this.appName, e))); }); }, handleServiceAction(action) { return this.callInitAction(this.appName, action).then(success => { if(!success) { throw _('Command failed'); }; return true; }).catch(e => { ui.addNotification(null, E('p', _('Service action failed "%s %s": %s').format(this.appName, action, e))); }); }, callInit: rpc.declare({ object: 'luci.internet-detector', method: 'Init', expect: { '': {} } }), getInit() { return this.callInit().then(data => { return data; }); }, callUIPoll: rpc.declare({ object: 'luci.internet-detector', method: 'UIPoll', expect: { '': {} } }), getUIPoll() { return this.callUIPoll().then(data => { return data; }); }, callStatus: rpc.declare({ object: 'luci.internet-detector', method: 'Status', expect: { '': {} } }), getStatus() { return this.callStatus().then(data => { return data; }); }, callInetStatus: rpc.declare({ object: 'luci.internet-detector', method: 'InetStatus', expect: { '': {} } }), getInetStatus() { return this.callInetStatus().then(data => { return data; }); }, callTgChatId: rpc.declare({ object: 'luci.internet-detector-mod-telegram', method: 'GetTgChatId', params: [ 'botToken', 'proxyType', 'proxyHost', 'proxyPort', 'proxyUser', 'proxyPasswd', 'iface' ], expect: { '': {} } }), getTgChatId(botToken, proxyType, proxyHost, proxyPort, proxyUser, proxyPasswd, iface) { return this.callTgChatId( botToken, proxyType, proxyHost, proxyPort, proxyUser, proxyPasswd, iface).then(data => { return data; }); }, setInternetStatus() { this.inetStatusArea.innerHTML = ''; if(!this.inetStatus || !this.inetStatus.instances || this.inetStatus.instances.length == 0) { let label = E('span', { 'class': 'id-label-status id-undefined' }, _('Undefined')); if((this.currentAppMode == '1' && this.appStatus != 'stoped') || this.currentAppMode == '2') { label.classList.add('spinning'); }; this.inetStatusArea.append(label); } else { this.inetStatus.instances.sort((a, b) => a.num - b.num); for(let i of this.inetStatus.instances) { let status = _('Disconnected'); let className = 'id-label-status id-disconnected'; if(i.inet == 0) { status = _('Connected'); className = 'id-label-status id-connected'; } else if(i.inet == -1) { status = _('Undefined'); className = 'id-label-status id-undefined spinning'; }; let publicIp = (i.mod_public_ip !== undefined) ? ' | %s: %s'.format(_('Public IP'), (i.mod_public_ip == '') ? _('Undefined') : _(i.mod_public_ip)) : ''; this.inetStatusArea.append( E('span', { 'class': className }, '%s%s%s'.format( i.instance + ': ', status, publicIp) ) ); if(i.mod_regular_script) { this.modRegularScriptNextRun[i.instance] = i.mod_regular_script; let nextRunLabel = document.getElementById('id_next_run_' + i.instance); if(nextRunLabel) { if(this.appStatus == 'running') { nextRunLabel.innerHTML = this.modRegularScriptNextRun[i.instance]; } else { nextRunLabel.innerHTML = _('Not scheduled'); }; }; }; }; }; if(this.appStatus == 'running') { this.serviceStatusLabel.textContent = _('Running'); } else { this.serviceStatusLabel.textContent = _('Stopped'); }; }, servicePoll() { return Promise.all([ this.getStatus(), this.getInetStatus(), ]).then(stat => { this.appStatus = stat[0].status; this.inetStatus = stat[1]; this.setInternetStatus(); }).catch(e => { this.appStatus = 'stoped'; this.inetStatus = {}; }); }, uiPoll() { return this.getUIPoll().then(status => { this.inetStatus = status; this.setInternetStatus(); }); }, serviceRestart() { return this.handleServiceAction('restart'); }, serviceRestartHandler() { poll.stop(); return this.serviceRestart().then(() => { window.setTimeout(() => this.servicePoll(), 1000); poll.start(); }); }, getTgChatIdHandler(ev, instance) { ev.preventDefault(); let botToken; let botTokenInput = document.getElementById( 'widget.cbid.%s.%s.mod_telegram_api_token'.format(this.appName, instance)); if(botTokenInput) { botToken = botTokenInput.value; }; if(!botTokenInput || !botToken) { alert(_('Bot API token is missing!')); return; }; let proxyType = ''; let proxyTypeInput = document.getElementById( 'widget.cbid.%s.%s.mod_telegram_proxy_type'.format(this.appName, instance)); if(proxyTypeInput) { proxyType = proxyTypeInput.value; }; let proxyHost = ''; let proxyHostInput = document.getElementById( 'widget.cbid.%s.%s.mod_telegram_proxy_host'.format(this.appName, instance)); if(proxyHostInput) { proxyHost = proxyHostInput.value; }; let proxyPort = ''; let proxyPortInput = document.getElementById( 'widget.cbid.%s.%s.mod_telegram_proxy_port'.format(this.appName, instance)); if(proxyPortInput) { proxyPort = proxyPortInput.value; }; let proxyUser = ''; let proxyUserInput = document.getElementById( 'widget.cbid.%s.%s.mod_telegram_proxy_user'.format(this.appName, instance)); if(proxyUserInput) { proxyUser = proxyUserInput.value; }; let proxyPasswd = ''; let proxyPasswdInput = document.getElementById( 'widget.cbid.%s.%s.mod_telegram_proxy_passwd'.format(this.appName, instance)); if(proxyPasswdInput) { proxyPasswd = proxyPasswdInput.value; }; let iface = ''; let ifaceInput = document.getElementById( 'cbid.%s.%s.mod_telegram_iface'.format(this.appName, instance)); if(ifaceInput) { let li = ifaceInput.querySelector('li[selected]'); if(li) { iface = li.dataset.value || ''; }; }; return this.getTgChatId(botToken, proxyType, proxyHost, proxyPort, proxyUser, proxyPasswd, iface).then(r => { if(r.ok) { if(r.chatId) { let tgChatIdInput = document.getElementById( 'widget.cbid.%s.%s.mod_telegram_chat_id'.format(this.appName, instance)); if(tgChatIdInput) { if(r.chatId.length == 0) { alert(_('No messages available. Write something to the bot and try again.')); } else { tgChatIdInput.value = r.chatId[r.chatId.length - 1]; tgChatIdInput.focus(); tgChatIdInput.blur(); }; }; }; } else { let errCode = r.errCode; let errorString = `${_('Error')} ${errCode}.`; if(errCode == 401 || errCode == 404) { errorString += ` ${_('Incorrect bot token?')}`; } else { errorString += ` ${r.errDesc}.`; } alert(errorString); }; }).catch(e => { alert(e.message); throw e; }); }, validateUrl(section, value) { return (/^$|^https?:\/\/([\w.-]+|\[[a-fA-F0-9:.]+\])(:[0-9]{2,5})?((\/|\?).*)?$/.test(value)) ? true : _('Expecting:') + ` ${_('valid URL')}\n`; }, CBITimeInput: form.Value.extend({ __name__ : 'CBI.TimeInput', renderWidget(section_id, option_index, cfgvalue) { let value = (cfgvalue != null) ? cfgvalue : this.default, widget = new Timefield(value, { id : this.cbid(section_id), optional : this.optional || this.rmempty, maxlength : 3, placeholder: _('Type a time string'), validate : L.bind( function(section, value) { return (/^$|^\d+[dhms]?$/.test(value)) ? true : _('Expecting:') + ` ${_('One of the following:')}\n - ${_('days')}: 1d\n - ${_('hours')}: 2h\n - ${_('minutes')}: 10m\n - ${_('seconds')}: 30s\n`; }, this, section_id ), disabled : (this.readonly != null) ? this.readonly : this.map.readonly, }); return widget.render(); }, }), CBITextfieldButtonInput: form.Value.extend({ __name__ : 'CBI.TextfieldButtonInput', renderWidget(section_id, option_index, cfgvalue) { let value = (cfgvalue != null) ? cfgvalue : this.default, widget = new TextfieldButton(value, { id : this.cbid(section_id), optional : this.optional || this.rmempty, datatype : this.datatype, placeholder: this.placeholder, validate : L.bind(this.validate, this, section_id), disabled : (this.readonly != null) ? this.readonly : this.map.readonly, btntext : this.btntext, btntitle : this.btntitle, btnstyle : this.btnstyle, onclick : this.onclick, }); return widget.render(); }, }), CBIBlockInetStatus: form.Value.extend({ __name__ : 'CBI.BlockInetStatus', __init__(map, section, ctx) { this.map = map; this.section = section; this.ctx = ctx; this.optional = true; this.rmempty = true; }, renderWidget(section_id, option_index, cfgvalue) { this.ctx.setInternetStatus(); return E([ E('label', { 'class': 'cbi-value-title', 'for': 'inetStatusArea' }, _('Internet status') ), this.ctx.inetStatusArea ]) }, }), CBIBlockServiceStatus: form.Value.extend({ __name__ : 'CBI.BlockServiceStatus', __init__(map, section, ctx) { this.map = map; this.section = section; this.ctx = ctx; this.optional = true; this.rmempty = true; }, renderWidget(section_id, option_index, cfgvalue) { return E([ E('label', { 'class': 'cbi-value-title', 'for': 'serviceStatusLabel' }, _('Service') ), E('div', { 'class': 'cbi-value-field' }, this.ctx.serviceStatusLabel ), ]); }, }), CBIBlockInitButton: form.Value.extend({ __name__ : 'CBI.BlockInitButton', __init__(map, section, ctx) { this.map = map; this.section = section; this.ctx = ctx; this.optional = true; this.rmempty = true; }, renderWidget(section_id, option_index, cfgvalue) { this.ctx.initButton = E('button', { 'class': (!this.ctx.initStatus) ? btnStyleDisabled : btnStyleEnabled, 'click': ui.createHandlerFn(this, () => { return this.ctx.handleServiceAction( (!this.ctx.initStatus) ? 'enable' : 'disable' ).then(success => { if(!success) { return; }; if(!this.ctx.initStatus) { this.ctx.initButton.textContent = _('Enabled'); this.ctx.initButton.className = btnStyleEnabled; this.ctx.initStatus = true; } else { this.ctx.initButton.textContent = _('Disabled'); this.ctx.initButton.className = btnStyleDisabled; this.ctx.initStatus = false; }; }); }), }, (!this.ctx.initStatus) ? _('Disabled') : _('Enabled')); return E( [ E('label', { 'class': 'cbi-value-title', 'for': 'initButton' }, _('Run service at startup') ), E('div', { 'class': 'cbi-value-field' }, [ E('div', {}, this.ctx.initButton), E('input', { 'id' : 'initButton', 'type': 'hidden', }), ]), ]); }, }), 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', 'rows' : 10, 'wrap' : 'off', 'spellcheck': 'false', }, cfgvalue); }, }), load() { return Promise.all([ this.getStatus(), this.getInitStatus(), L.resolveDefault(fs.list(this.ledsPath), []), this.getInit(), uci.load(this.appName), ]).catch(e => { ui.addNotification( null, E('p', _('An error has occurred') + ': %s'.format(e.message))); }); }, render(data) { if(!data) { return; }; this.appStatus = (data[0].code == 0) ? data[0].stdout.trim() : null; this.initStatus = data[1]; this.leds = data[2]; if(data[3]) { if(data[3].mm_mod) { this.mm = true; }; if(data[3].mm_init) { this.mmInit = true; }; if(data[3].email_mod) { this.email = true; }; if(data[3].email_exec) { this.emailExec = true; }; if(data[3].telegram) { this.telegram = true; }; if(data[3].curl_exec) { this.curlExec = true; }; }; this.currentAppMode = uci.get(this.appName, 'config', 'mode'); let s, o, ss; let m = new form.Map(this.appName, _('Internet Detector'), _('Checking Internet availability.')); /* Status widget */ s = m.section(form.NamedSection, 'config', 'main'); o = s.option(this.CBIBlockInetStatus, this); s = m.section(form.NamedSection, 'config', 'main'); /* Service widget */ if(this.currentAppMode == '1') { o = s.option(this.CBIBlockServiceStatus, this); // restart button o = s.option(form.Button, '_restart_btn', _('Restart service') ); o.onclick = () => this.serviceRestartHandler(); o.inputtitle = _('Restart'); o.inputstyle = btnStyleApply; // init button o = s.option(this.CBIBlockInitButton, this); }; /* Main settings */ // mode let mode = s.option(form.ListValue, 'mode', _('Internet detector mode')); mode.value('0', _('Disabled')); mode.value('1', _('Service')); mode.value('2', _('Web UI only (UI detector)')); mode.description = '%s
%s
%s'.format( _('Disabled: detector is completely off.'), _('Service: detector always runs as a system service.'), _('Web UI only: detector works only when the Web UI is open (UI detector).') ); mode.default = '0'; /* Instances configuration */ if(this.currentAppMode != '2') { // logging_level o = s.option(form.ListValue, 'logging_level', _('Logging'), _('Log event level.') ); o.value(-1, _('Disabled')); o.value(3, _('Error')); o.value(4, _('Warning')); o.value(5, _('Notice')); o.value(6, _('Info')); o.value(7, _('Debug')); o.rmempty = false; o.default = '6'; }; s = m.section(form.GridSection, 'instance'); s.title = _('Instances'); s.addremove = true; s.sortable = true; s.nodescriptions = true; s.addbtntitle = _('Add instance'); s.tab('main', _('Main settings')); function makeIntervalOptions(list) { list.value(2, '2 ' + _('sec')); list.value(5, '5 ' + _('sec')); list.value(10, '10 ' + _('sec')); list.value(15, '15 ' + _('sec')); list.value(20, '20 ' + _('sec')); list.value(25, '25 ' + _('sec')); list.value(30, '30 ' + _('sec')); list.value(60, '1 ' + _('min')); list.value(120, '2 ' + _('min')); list.value(300, '5 ' + _('min')); list.value(600, '10 ' + _('min')); } function makeTimerDelayOptions(list) { list.value(25, '25 ' + _('msec')); list.value(50, '50 ' + _('msec')); list.value(100, '100 ' + _('msec')); list.value(250, '250 ' + _('msec')); list.value(500, '500 ' + _('msec')); list.value(750, '750 ' + _('msec')); list.value(1000, '1 ' + _('sec')); list.value(1500, '1.5 ' + _('sec')); list.value(2000, '2 ' + _('sec')); } // description o = s.taboption('main', form.Value, 'description', _("Description")); o.datatype = 'maxlength(30)'; o.modalonly = null; // hosts o = s.taboption('main', form.DynamicList, 'hosts', _('Hosts'), _('Hosts for checking Internet availability. Hosts are polled (in list order) until at least one responds.') ); o.datatype = 'or(host,hostport,ipaddrport(1))'; o.default = this.defaultHosts; o.depends({ check_type: '0' }); o.depends({ check_type: '1' }); o.rmempty = false; o.modalonly = true; if(this.curlExec) { // urls o = s.taboption('main', form.DynamicList, 'urls', _('URLs'), _('URLs for checking Internet availability. URLs are polled (in list order) until at least one returns HTTP status code 200.') ); o.validate = this.validateUrl; o.default = this.defaultURLs; o.rmempty = false; o.depends({ check_type: '2' }); o.modalonly = true; }; // check_type o = s.taboption('main', form.ListValue, 'check_type', _('Check type'), _('Host availability check type.') + '
' + ((this.curlExec) ? '' : _('To support URL checking, you need to install curl.')) ); o.value(1, _('ICMP-echo request (ping)')); o.value(0, _('TCP port connection')); if(this.curlExec) { o.value(2, _('URL test (HTTP)')); }; o.default = '0'; o.modalonly = true; // tcp_port o = s.taboption('main', form.Value, 'tcp_port', _('TCP port'), _('Default port value for TCP connections.') ); o.datatype = 'port'; o.default = '53'; o.depends({ check_type: '0' }); o.modalonly = true; // icmp_packet_size o = s.taboption('main', form.ListValue, 'icmp_packet_size', _('ICMP packet data size')); o.value(1, _('Small: 1 byte')); o.value(32, _('Windows: 32 bytes')); o.value(56, _('Standard: 56 bytes')); o.value(248, _('Big: 248 bytes')); o.value(1492, _('Huge: 1492 bytes')); o.value(9000, _('Jumbo: 9000 bytes')); o.default = '56'; o.depends({ check_type: '1' }); o.modalonly = true; if(this.curlExec) { // proxy_type o = s.taboption('main', form.ListValue, 'proxy_type', _('Proxy') ); o.value('', _('Disabled')); o.value('http'); o.value('socks4'); o.value('socks4a'); o.value('socks5'); o.value('socks5h'); o.default = ''; o.depends({ check_type: '2' }); o.modalonly = true; // proxy_host o = s.taboption('main', form.Value, 'proxy_host', _('Proxy host') ); o.datatype = 'host'; o.rmempty = false; o.depends({ proxy_type: /.+/ }); o.modalonly = true; // proxy_port o = s.taboption('main', form.Value, 'proxy_port', _('Proxy port') ); o.datatype = 'port'; o.rmempty = false; o.depends({ proxy_type: /.+/ }); o.modalonly = true; // proxy_user o = s.taboption('main', form.Value, 'proxy_user', _('Proxy user') ); o.depends({ proxy_type: /(http|socks5h?)/ }); o.modalonly = true; // proxy_passwd o = s.taboption('main', form.Value, 'proxy_passwd', _('Proxy password') ); o.depends({ proxy_type: /(http|socks5h?)/ }); o.modalonly = true; o.password = true; }; // iface o = s.taboption('main', widgets.DeviceSelect, 'iface', _('Device'), _('Network device for Internet access. If not specified, the default device is used.') ); o.noaliases = true; o.modalonly = true; // interval_up o = s.taboption('main', form.ListValue, 'interval_up', _('Alive interval'), _('Hosts polling interval when the Internet is up.') ); o.default = '30'; o.modalonly = true; makeIntervalOptions(o); // interval_down o = s.taboption('main', form.ListValue, 'interval_down', _('Dead interval'), _('Hosts polling interval when the Internet is down.') ); o.default = '5'; o.modalonly = true; makeIntervalOptions(o); // connection_attempts o = s.taboption('main', form.ListValue, 'connection_attempts', _('Connection attempts'), _('Maximum number of attempts to connect to each host.') ); o.modalonly = true; o.value(1); o.value(2); o.value(3); o.default = '2'; // connection_timeout o = s.taboption('main', form.ListValue, 'connection_timeout', _('Connection timeout'), _('Maximum timeout for waiting for a response from the host.') ); o.modalonly = true; o.value(1, '1 ' + _('sec')); o.value(2, '2 ' + _('sec')); o.value(3, '3 ' + _('sec')); o.value(4, '4 ' + _('sec')); o.value(5, '5 ' + _('sec')); o.value(6, '6 ' + _('sec')); o.value(7, '7 ' + _('sec')); o.value(8, '8 ' + _('sec')); o.value(9, '9 ' + _('sec')); o.value(10, '10 ' + _('sec')); o.default = '2'; // enabled o = s.taboption('main', form.Flag, 'enabled', _('Enabled'), ); o.rmempty = false; o.default = '1'; o.editable = true; o.modalonly = false; /* Modules */ if(this.currentAppMode != '2') { s.tab('led_control', _('LED control')); s.tab('reboot_device', _('Reboot device')); s.tab('restart_network', _('Restart network')); if(this.mm) { s.tab('restart_modem', _('Restart modem')); }; }; s.tab('public_ip', _('Public IP address')); if(this.currentAppMode != '2') { if(this.email) { s.tab('email', _('Email notification')); }; if(this.telegram) { s.tab('telegram', _('Telegram notification')); }; s.tab('user_scripts', _('User scripts')); s.tab('regular_script', _('Regular script')); }; s.addModalOptions = (s, section_id, ev) => { if(section_id == 'config') { s.map.children = []; s.map.readonly = true; s.map.children.push(new form.NamedSection(s.map, '_dummy', '_dummy', _('Error!'), _('Invalid instance name...'))); return; }; if(this.currentAppMode != '2') { // LED control o = s.taboption('led_control', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('LED indicates the Internet status.') + '
'; o.modalonly = true; if(this.leds.length > 0) { this.leds.sort((a, b) => (a.name > b.name) ? 1 : (a.name < b.name) ? -1 : 0); // enabled o = s.taboption('led_control', form.Flag, 'mod_led_control_enabled', _('Enabled')); o.rmempty = false; o.modalonly = true; o = s.taboption('led_control', form.SectionValue, s.section, form.NamedSection, s.section); o.depends({ mod_led_control_enabled: '1' }); ss = o.subsection; for(let i = 1; i <= this.ledsPerInstance; i++) { ss.tab('led' + i + '_tab', _('LED') + ' ' + i); // led_name o = ss.taboption('led' + i + '_tab', form.ListValue, 'mod_led_control_led' + i + '_name', _('LED Name')); o.modalonly = true; if(i > 1) { o.rmempty = true; o.optional = true; }; this.leds.forEach(e => o.value(e.name)); // led_action_1 o = ss.taboption('led' + i + '_tab', form.ListValue, 'mod_led_control_led' + i + '_action_1', _('After connection')); o.depends({ ['mod_led_control_led' + i + '_name']: /.+/ }); o.modalonly = true; o.value(1, _('Off')); o.value(2, _('On')); o.value(3, _('Blinking (kernel: timer)')); o.value(4, _('Network device activity (kernel: netdev)')); o.default = '2'; // blink_on_delay_1 o = ss.taboption('led' + i + '_tab', form.ListValue, 'mod_led_control_led' + i + '_blink_on_delay_1', _('On-state delay'), _('On-state delay for blinking option.')); makeTimerDelayOptions(o); o.depends({ ['mod_led_control_led' + i + '_action_1']: '3' }); o.modalonly = true; o.default = '500'; // blink_off_delay_1 o = ss.taboption('led' + i + '_tab', form.ListValue, 'mod_led_control_led' + i + '_blink_off_delay_1', _('Off-state delay'), _('Off-state delay for blinking option.')); makeTimerDelayOptions(o); o.depends({ ['mod_led_control_led' + i + '_action_1']: '3' }); o.modalonly = true; o.default = '500'; // netdev_device_1 o = ss.taboption('led' + i + '_tab', widgets.DeviceSelect, 'mod_led_control_led' + i + '_netdev_device_1', _('Device'), _('LED will display the link activity of this network device.') ); o.depends({ ['mod_led_control_led' + i + '_action_1']: '4' }); o.modalonly = true; o.rmempty = false; o.noaliases = true; // netdev_mode_1 o = ss.taboption('led' + i + '_tab', form.MultiValue, 'mod_led_control_led' + i + '_netdev_mode_1', _('LED mode') ); o.depends({ ['mod_led_control_led' + i + '_action_1']: '4' }); o.modalonly = true; o.value('link', _('Link On')); o.value('tx', _('Transmit')); o.value('rx', _('Receive')); o.default = 'link'; o.rmempty = false; // led_action_2 o = ss.taboption('led' + i + '_tab', form.ListValue, 'mod_led_control_led' + i + '_action_2', _('After disconnection')); o.depends({ ['mod_led_control_led' + i + '_name']: /.+/ }); o.modalonly = true; o.value(1, _('Off')); o.value(2, _('On')); o.value(3, _('Blinking (kernel: timer)')); o.value(4, _('Network device activity (kernel: netdev)')); o.default = '1'; // blink_on_delay_2 o = ss.taboption('led' + i + '_tab', form.ListValue, 'mod_led_control_led' + i + '_blink_on_delay_2', _('On-state delay'), _('On-state delay for blinking option.')); makeTimerDelayOptions(o); o.depends({ ['mod_led_control_led' + i + '_action_2']: '3' }); o.modalonly = true; o.default = '500'; // blink_off_delay_2 o = ss.taboption('led' + i + '_tab', form.ListValue, 'mod_led_control_led' + i + '_blink_off_delay_2', _('Off-state delay'), _('Off-state delay for blinking option.')); makeTimerDelayOptions(o); o.depends({ ['mod_led_control_led' + i + '_action_2']: '3' }); o.modalonly = true; o.default = '500'; // netdev_device_2 o = ss.taboption('led' + i + '_tab', widgets.DeviceSelect, 'mod_led_control_led' + i + '_netdev_device_2', _('Device'), _('LED will display the link activity of this network device.') ); o.depends({ ['mod_led_control_led' + i + '_action_2']: '4' }); o.modalonly = true; o.rmempty = false; o.noaliases = true; // netdev_mode_2 o = ss.taboption('led' + i + '_tab', form.MultiValue, 'mod_led_control_led' + i + '_netdev_mode_2', _('LED mode') ); o.depends({ ['mod_led_control_led' + i + '_action_2']: '4' }); o.modalonly = true; o.value('link', _('Link On')); o.value('tx', _('Transmit')); o.value('rx', _('Receive')); o.default = 'link'; o.rmempty = false; }; } else { o = s.taboption('led_control', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('No LEDs available...') + '
'; o.modalonly = true; }; // Reboot device o = s.taboption('reboot_device', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('Device will be rebooted when the Internet is disconnected.') + '
'; o.modalonly = true; // enabled o = s.taboption('reboot_device', form.Flag, 'mod_reboot_enabled', _('Enabled')); o.rmempty = false; o.modalonly = true; // dead_period o = s.taboption('reboot_device', this.CBITimeInput, 'mod_reboot_dead_period', _('Dead period'), _('Period of time without Internet access until the device is rebooted.') ); o.default = '3600'; o.rmempty = false; o.modalonly = true; // force_reboot_delay o = s.taboption('reboot_device', form.ListValue, 'mod_reboot_force_reboot_delay', _('Forced reboot delay'), _('Waiting for a reboot to complete before performing a forced reboot.') ); o.modalonly = true; o.value(0, _('Disable forced reboot')); o.value(60, '1 ' + _('min')); o.value(120, '2 ' + _('min')); o.value(300, '5 ' + _('min')); o.value(600, '10 ' + _('min')); o.value(1800, '30 ' + _('min')); o.value(3600, '1 ' + _('hour')); o.default = '300'; // disconnected_at_startup o = s.taboption('reboot_device', form.Flag, 'mod_reboot_disconnected_at_startup', _('On startup'), _('Reboot device if the Internet is disconnected at service startup.') ); o.rmempty = false; o.modalonly = true; // Restart network o = s.taboption('restart_network', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('Network will be restarted when the Internet is disconnected.') + '
'; o.modalonly = true; // enabled o = s.taboption('restart_network', form.Flag, 'mod_network_restart_enabled', _('Enabled')); o.rmempty = false; o.modalonly = true; // dead_period o = s.taboption('restart_network', this.CBITimeInput, 'mod_network_restart_dead_period', _('Dead period'), _('Period of time without Internet access before network restart.') ); o.default = '900'; o.rmempty = false; o.modalonly = true; // ifaces o = s.taboption('restart_network', widgets.DeviceSelect, 'mod_network_restart_ifaces', _('Device'), _('Network device or interface to restart. If not specified, then the network service is restarted.') ); o.modalonly = true; o.multiple = true; o.modalonly = true; // attempts o = s.taboption('restart_network', form.ListValue, 'mod_network_restart_attempts', _('Restart attempts'), _('Maximum number of network restart attempts before Internet access is available.') ); o.modalonly = true; o.value(1); o.value(2); o.value(3); o.value(4); o.value(5); o.value(10); o.value(0, _('infinitely')); o.default = '1'; // attempt_interval o = s.taboption('restart_network', this.CBITimeInput, 'mod_network_restart_attempt_interval', _('Attempt interval'), _('Interval between network restarts.') ); o.default = '60'; o.rmempty = false; o.modalonly = true; // device_timeout o = s.taboption('restart_network', form.ListValue, 'mod_network_restart_device_timeout', _('Device timeout'), _('Timeout between stopping and starting a network device when restarting.') ); o.modalonly = true; o.value(0, '0 ' + _('sec')); o.value(1, '1 ' + _('sec')); o.value(2, '2 ' + _('sec')); o.value(3, '3 ' + _('sec')); o.value(4, '4 ' + _('sec')); o.value(5, '5 ' + _('sec')); o.value(6, '6 ' + _('sec')); o.value(7, '7 ' + _('sec')); o.value(8, '8 ' + _('sec')); o.value(9, '9 ' + _('sec')); o.value(10, '10 ' + _('sec')); o.default = '0'; // disconnected_at_startup o = s.taboption('restart_network', form.Flag, 'mod_network_restart_disconnected_at_startup', _('On startup'), _('Restart network if the Internet is disconnected at service startup.') ); o.rmempty = false; o.modalonly = true; // Restart modem if(this.mm) { if(this.mmInit) { o = s.taboption('restart_modem', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('Modem will be restarted when the Internet is disconnected.') + '
'; o.modalonly = true; // enabled o = s.taboption('restart_modem', form.Flag, 'mod_modem_restart_enabled', _('Enabled'), ); o.rmempty = false; o.modalonly = true; // dead_period o = s.taboption('restart_modem', this.CBITimeInput, 'mod_modem_restart_dead_period', _('Dead period'), _('Period of time without Internet access before modem restart.') ); o.default = '600'; o.rmempty = false; o.modalonly = true; // any_band o = s.taboption('restart_modem', form.Flag, 'mod_modem_restart_any_band', _('Unlock modem bands'), _('Set the modem to be allowed to use any band.') ); o.rmempty = false; o.modalonly = true; // attempts o = s.taboption('restart_modem', form.ListValue, 'mod_modem_restart_attempts', _('Restart attempts'), _('Maximum number of modem restart attempts before Internet access is available.') ); o.modalonly = true; o.value(1); o.value(2); o.value(3); o.value(4); o.value(5); o.value(10); o.value(0, _('infinitely')); o.default = '1'; // attempt_interval o = s.taboption('restart_modem', this.CBITimeInput, 'mod_modem_restart_attempt_interval', _('Attempt interval'), _('Interval between modem restarts.') ); o.default = '60'; o.rmempty = false; o.modalonly = true; // iface o = s.taboption('restart_modem', widgets.NetworkSelect, 'mod_modem_restart_iface', _('Interface'), _('ModemManger interface. If specified, it will be restarted after restarting ModemManager.') ); o.multiple = false; o.nocreate = true; o.modalonly = true; // iface_timeout o = s.taboption('restart_modem', form.ListValue, 'mod_modem_restart_iface_timeout', _('Interface timeout'), _('Timeout between stopping and starting a ModemManger interface when restarting.') ); o.modalonly = true; o.value(0, '0 ' + _('sec')); o.value(1, '1 ' + _('sec')); o.value(2, '2 ' + _('sec')); o.value(3, '3 ' + _('sec')); o.value(4, '4 ' + _('sec')); o.value(5, '5 ' + _('sec')); o.value(6, '6 ' + _('sec')); o.value(7, '7 ' + _('sec')); o.value(8, '8 ' + _('sec')); o.value(9, '9 ' + _('sec')); o.value(10, '10 ' + _('sec')); o.default = '0'; // disconnected_at_startup o = s.taboption('restart_modem', form.Flag, 'mod_modem_restart_disconnected_at_startup', _('On startup'), _('Restart modem if the Internet is disconnected at service startup.') ); o.rmempty = false; o.modalonly = true; } else { o = s.taboption('restart_modem', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('ModemManager is not available...') + '
'; o.modalonly = true; }; }; }; // Public IP address o = s.taboption('public_ip', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('Checking the real public IP address.') + '
'; o.modalonly = true; // enabled o = s.taboption('public_ip', form.Flag, 'mod_public_ip_enabled', _('Enabled')); o.rmempty = false; o.modalonly = true; // provider o = s.taboption('public_ip', form.ListValue, 'mod_public_ip_provider', _('Provider'), _('Service for determining the public IP address.') + '
' + ((this.curlExec) ? '' : _('To support HTTP services, you need to install curl.')) ); o.modalonly = true; for(let i of this.publicIPProviders.dns) { o.value(i.name, i.title); }; if(this.curlExec) { for(let i of this.publicIPProviders.http) { o.value(i.name, i.title); }; }; o.default = this.publicIPProviders.dns[0].name; // ipv6 o = s.taboption('public_ip', form.ListValue, 'mod_public_ip_qtype', _('DNS query type'), _('The type of record requested in the DNS query (if the service supports it).') ); o.modalonly = true; o.value('0', 'A (IPv4)'); o.value('1', 'AAAA (IPv6)'); o.default = '0'; for(let i of this.publicIPProviders.dns) { o.depends({ mod_public_ip_provider: i.name }); }; // interval o = s.taboption('public_ip', form.ListValue, 'mod_public_ip_interval', _('Polling interval'), _('Interval between IP address requests.') ); o.default = '600'; o.modalonly = true; o.value(60, '1' + ' ' + _('min')); o.value(300, '5' + ' ' + _('min')); o.value(600, '10' + ' ' + _('min')); o.value(1800, '30' + ' ' + _('min')); o.value(3600, '1' + ' ' + _('hour')); o.value(10800, '3' + ' ' + _('hour')); // interval_failed o = s.taboption('public_ip', form.ListValue, 'mod_public_ip_interval_failed', _('Failed interval'), _('Interval between IP address requests if the IP address is not defined.') ); o.default = '60'; o.modalonly = true; o.value(30, '30' + ' ' + _('sec')); o.value(60, '1' + ' ' + _('min')); o.value(180, '3' + ' ' + _('min')); o.value(300, '5' + ' ' + _('min')); o.value(600, '10' + ' ' + _('min')); // request_attempts o = s.taboption('public_ip', form.ListValue, 'mod_public_ip_request_attempts', _('Attempts'), _('Number of attempts to request an IP address.') ); o.default = '2' o.modalonly = true; for(let i = 1; i <= 3; i++) { o.value(i); }; // timeout o = s.taboption('public_ip', form.ListValue, 'mod_public_ip_timeout', _('Server response timeout') ); o.default = '3' o.modalonly = true; for(let i = 1; i <= 5; i++) { o.value(i, i + ' ' + _('sec')); }; if(this.currentAppMode != '2') { // enable_ip_script o = s.taboption('public_ip', form.Flag, 'mod_public_ip_enable_ip_script', _('Enable public-ip-script')); o.rmempty = false; o.modalonly = true; // public-ip-script edit dialog o = s.taboption('public_ip', this.CBIBlockFileEdit, this, 'public-ip-script', this.configDir + '/public-ip-script.' + s.section, _('Edit public-ip-script'), _('Shell commands that run when the public IP address changes. New IP is available as value of the $PUBLIC_IP variable (empty string if undefined).') ); o.modalonly = true; // Email notification if(this.email) { if(this.emailExec) { o = s.taboption('email', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('An email will be sent when connected or disconnected from the Internet.') + '
'; o.modalonly = true; // enabled o = s.taboption('email', form.Flag, 'mod_email_enabled', _('Enabled')); o.rmempty = false; o.modalonly = true; // mode o = s.taboption('email', form.ListValue, 'mod_email_mode', _('When email will be sent') ); o.modalonly = true; o.value(0, _('after connection')); o.value(1, _('after disconnection')); o.value(2, _('after connection or disconnection')); o.default = '0'; // alive_period o = s.taboption('email', this.CBITimeInput, 'mod_email_alive_period', _('Alive period'), _('Period of time after connecting to the Internet before sending a message.') ); o.rmempty = false; o.modalonly = true; o.depends({ 'mod_email_mode': '0' }); o.depends({ 'mod_email_mode': '2' }); o.default = '0'; // dead_period o = s.taboption('email', this.CBITimeInput, 'mod_email_dead_period', _('Dead period'), _('Period of time after disconnecting from Internet before sending a message.') ); o.rmempty = false; o.modalonly = true; o.depends({ 'mod_email_mode': '1' }); o.depends({ 'mod_email_mode': '2' }); o.default = '0'; // host_alias o = s.taboption('email', form.Value, 'mod_email_host_alias', _('Host alias'), _('Host identifier in messages. If not specified, hostname will be used.')); o.modalonly = true; // mail_recipient o = s.taboption('email', form.Value, 'mod_email_mail_recipient', _('Recipient')); o.description = _('Email address of the recipient.'); o.modalonly = true; // mail_sender o = s.taboption('email', form.Value, 'mod_email_mail_sender', _('Sender')); o.description = _('Email address of the sender.'); o.modalonly = true; // mail_user o = s.taboption('email', form.Value, 'mod_email_mail_user', _('User')); o.description = _('Username for SMTP authentication.'); o.modalonly = true; // mail_password o = s.taboption('email', form.Value, 'mod_email_mail_password', _('Password')); o.description = _('Password for SMTP authentication.'); o.password = true; o.modalonly = true; // mail_smtp o = s.taboption('email', form.Value, 'mod_email_mail_smtp', _('SMTP server')); o.description = _('Hostname/IP address of the SMTP server.'); o.datatype = 'host'; o.modalonly = true; // mail_smtp_port o = s.taboption('email', form.Value, 'mod_email_mail_smtp_port', _('SMTP server port')); o.datatype = 'port'; o.modalonly = true; // mail_security o = s.taboption('email', form.ListValue, 'mod_email_mail_security', _('Security')); o.description = '%s
%s'.format( _('TLS: use STARTTLS if the server supports it.'), _('SSL: SMTP over SSL.'), ); o.value('tls', 'TLS'); o.value('ssl', 'SSL'); o.default = 'tls'; o.modalonly = true; // message_at_startup o = s.taboption('email', form.Flag, 'mod_email_message_at_startup', _('On startup'), _('Send message on service startup.') ); o.rmempty = false; o.modalonly = true; } else { o = s.taboption('email', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('Mailsend is not available...') + '
'; o.modalonly = true; }; }; // Telegram notification if(this.telegram) { if(this.curlExec) { o = s.taboption('telegram', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('Telegram message will be sent when connected or disconnected from the Internet.') + '
' + _("You need to register a new %sTelegram bot%s. Then get the bot's API token and paste it into the Bot token field. After that, open a chat with the bot, write something (in the Telegram app) and you will be able to get the chat ID using the ID button.").format("", '') + '
'; o.modalonly = true; // enabled o = s.taboption('telegram', form.Flag, 'mod_telegram_enabled', _('Enabled')); o.rmempty = false; o.modalonly = true; // mode o = s.taboption('telegram', form.ListValue, 'mod_telegram_mode', _('When message will be sent') ); o.modalonly = true; o.value(0, _('after connection')); o.value(1, _('after disconnection')); o.value(2, _('after connection or disconnection')); o.default = '0'; // alive_period o = s.taboption('telegram', this.CBITimeInput, 'mod_telegram_alive_period', _('Alive period'), _('Period of time after connecting to the Internet before sending a message.') ); o.rmempty = false; o.modalonly = true; o.depends({ 'mod_telegram_mode': '0' }); o.depends({ 'mod_telegram_mode': '2' }); o.default = '0'; // dead_period o = s.taboption('telegram', this.CBITimeInput, 'mod_telegram_dead_period', _('Dead period'), _('Period of time after disconnecting from Internet before sending a message.') ); o.rmempty = false; o.modalonly = true; o.depends({ 'mod_telegram_mode': '1' }); o.depends({ 'mod_telegram_mode': '2' }); o.default = '0'; // host_alias o = s.taboption('telegram', form.Value, 'mod_telegram_host_alias', _('Host alias'), _('Host identifier in messages. If not specified, hostname will be used.')); o.modalonly = true; // tg_api_token o = s.taboption('telegram', form.Value, 'mod_telegram_api_token', _('Bot token'), _('Telegram bot API token.')); o.password = true; o.modalonly = true; // tg_chat_id o = s.taboption('telegram', this.CBITextfieldButtonInput, 'mod_telegram_chat_id', _('Chat ID'), _('ID of the Telegram chat to which messages will be sent.') ); o.btntext = _('ID'), o.btntitle = _('Request chat ID from bot API'), o.btnstyle = 'action', o.onclick = ui.createHandlerFn(this, (ev) => this.getTgChatIdHandler(ev, s.section)); o.modalonly = true; o.optional = false; o.rmempty = false; o.depends({ 'mod_telegram_api_token': /.+/ }); // proxy_type o = s.taboption('telegram', form.ListValue, 'mod_telegram_proxy_type', _('Proxy') ); o.value('', _('Disabled')); o.value('http'); o.value('socks4'); o.value('socks4a'); o.value('socks5'); o.value('socks5h'); o.default = ''; o.modalonly = true; // proxy_host o = s.taboption('telegram', form.Value, 'mod_telegram_proxy_host', _('Proxy host') ); o.datatype = 'host'; o.rmempty = false; o.depends({ mod_telegram_proxy_type: /.+/ }); o.modalonly = true; // proxy_port o = s.taboption('telegram', form.Value, 'mod_telegram_proxy_port', _('Proxy port') ); o.datatype = 'port'; o.rmempty = false; o.depends({ mod_telegram_proxy_type: /.+/ }); o.modalonly = true; // proxy_user o = s.taboption('telegram', form.Value, 'mod_telegram_proxy_user', _('Proxy user') ); o.depends({ mod_telegram_proxy_type: /(http|socks5h?)/ }); o.modalonly = true; // proxy_passwd o = s.taboption('telegram', form.Value, 'mod_telegram_proxy_passwd', _('Proxy password') ); o.depends({ mod_telegram_proxy_type: /(http|socks5h?)/ }); o.modalonly = true; o.password = true; // iface o = s.taboption('telegram', widgets.DeviceSelect, 'mod_telegram_iface', _('Device'), _('Network device for sending messages. If not specified, the default device is used.') ); o.noaliases = true; o.modalonly = true; // message_at_startup o = s.taboption('telegram', form.Flag, 'mod_telegram_message_at_startup', _('On startup'), _('Send message on service startup.') ); o.rmempty = false; o.modalonly = true; } else { o = s.taboption('telegram', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('Curl is not available...') + '
'; o.modalonly = true; }; }; // User scripts o = s.taboption('user_scripts', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('Shell commands to run when connected or disconnected from the Internet.') + '
'; o.modalonly = true; // enabled o = s.taboption('user_scripts', form.Flag, 'mod_user_scripts_enabled', _('Enabled')); o.rmempty = false; o.modalonly = true; o = s.taboption('user_scripts', form.SectionValue, 'user_scripts_section', form.NamedSection, s.section); ss = o.subsection; // up-script tab ss.tab('user_scripts_up_script', 'up-script'); o = ss.taboption('user_scripts_up_script', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('Shell commands that run when connected to the Internet.') + '
'; o.modalonly = true; // up_script edit o = ss.taboption('user_scripts_up_script', this.CBIBlockFileEdit, this, 'up_script', this.configDir + '/up-script.' + s.section, _('Edit up-script') ); o.modalonly = true; // alive_period o = ss.taboption('user_scripts_up_script', this.CBITimeInput, 'mod_user_scripts_alive_period', _('Alive period'), _('Period of time after connecting to Internet before up-script runs.') ); o.default = '0'; o.rmempty = false; o.modalonly = true; // up_script_attempts o = ss.taboption('user_scripts_up_script', form.ListValue, 'mod_user_scripts_up_script_attempts', _('Attempts'), _('Maximum number of up-script run attempts when connected to the Internet.') ); o.modalonly = true; o.value(1); o.value(2); o.value(3); o.value(4); o.value(5); o.value(10); o.value(0, _('infinitely')); o.default = '1'; // up_script_attempt_interval o = ss.taboption('user_scripts_up_script', this.CBITimeInput, 'mod_user_scripts_up_script_attempt_interval', _('Attempt interval'), _('Interval between up-script runs.') ); o.default = '60'; o.rmempty = false; o.modalonly = true; // connected_at_startup o = ss.taboption('user_scripts_up_script', form.Flag, 'mod_user_scripts_connected_at_startup', _('On startup'), _('Run up-script if the Internet is connected at service startup.') ); o.rmempty = false; o.modalonly = true; // down-script tab ss.tab('user_scripts_down_script', 'down-script'); o = ss.taboption('user_scripts_down_script', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('Shell commands to run when disconnected from the Internet.') + '
'; o.modalonly = true; // down_script edit o = ss.taboption('user_scripts_down_script', this.CBIBlockFileEdit, this, 'down_script', this.configDir + '/down-script.' + s.section, _('Edit down-script') ); o.modalonly = true; // dead_period o = ss.taboption('user_scripts_down_script', this.CBITimeInput, 'mod_user_scripts_dead_period', _('Dead period'), _('Period of time after disconnecting from Internet before down-script runs.') ); o.default = '0'; o.rmempty = false; o.modalonly = true; // down_script_attempts o = ss.taboption('user_scripts_down_script', form.ListValue, 'mod_user_scripts_down_script_attempts', _('Attempts'), _('Maximum number of down-script run attempts before Internet access is available.') ); o.modalonly = true; o.value(1); o.value(2); o.value(3); o.value(4); o.value(5); o.value(10); o.value(0, _('infinitely')); o.default = '1'; // down_script_attempt_interval o = ss.taboption('user_scripts_down_script', this.CBITimeInput, 'mod_user_scripts_down_script_attempt_interval', _('Attempt interval'), _('Interval between down-script runs.') ); o.default = '60'; o.rmempty = false; o.modalonly = true; // disconnected_at_startup o = ss.taboption('user_scripts_down_script', form.Flag, 'mod_user_scripts_disconnected_at_startup', _('On startup'), _('Run down-script if the Internet is disconnected at service startup.') ); o.rmempty = false; o.modalonly = true; // Regular script o = s.taboption('regular_script', form.DummyValue, '_dummy'); o.rawhtml = true; o.default = '
' + _('Shell commands that are run regularly.') + '
'; o.modalonly = true; // enabled o = s.taboption('regular_script', form.Flag, 'mod_regular_script_enabled', _('Enabled')); o.rmempty = false; o.modalonly = true; // next run o = s.taboption('regular_script', form.DummyValue, '_dummy', _('Next run')); o.rawhtml = true; o.default = '' + (this.modRegularScriptNextRun[s.section] || _('Not scheduled')) + ''; o.modalonly = true; // interval o = s.taboption('regular_script', this.CBITimeInput, 'mod_regular_script_interval', _('Run interval') ); o.default = '3600'; o.rmempty = false; o.modalonly = true; // inet_state o = s.taboption('regular_script', form.ListValue, 'mod_regular_script_inet_state', _('Run if Internet state is') ); o.modalonly = true; o.value(0, _('connected')); o.value(1, _('disconnected')); o.value(2, _('connected or disconnected')); o.default = '2'; // regular_script edit o = s.taboption('regular_script', this.CBIBlockFileEdit, this, 'regular_script', this.configDir + '/regular-script.' + s.section, _('Edit regular-script'), _('Shell commands that run regularly at a specified interval. Current state of the Internet is available as value of the $INET_STATE variable (0 - connected, 1 - disconnected).') ); o.modalonly = true; }; }; if(this.currentAppMode != '0') { poll.add( L.bind((this.currentAppMode == '1') ? this.servicePoll : this.uiPoll, this), this.pollInterval ); }; let mapPromise = m.render(); mapPromise.then(node => node.classList.add('fade-in')); return mapPromise; }, });