luci-app-ddns: rework with new ddns changes

This commit rework the app with the new ddns changes. DDns services are now stored in a dedicated list and the service specific data is stored in a dedicated json. This json can both preinstalled with a companion package or be downloaded on demand. The new app now check if the script is present and give a button to install it if not present in the system.
The app now will search for all the available service in the services directory and optionally if present will include in the list the service not installed from a static list. Special service that use a separate script (for example cloudflare-v4) will install directly in the services directory and will be included automatically.
The app now reset the ddns rule settings on service change.
Also rework the app to drop any global function and rework the function to use more default way to get data.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
This commit is contained in:
Ansuel Smith 2020-09-30 13:40:06 +02:00
parent 1da9df8376
commit e478875c6c
No known key found for this signature in database
GPG key ID: AC001D09ADBFEAD7
3 changed files with 780 additions and 583 deletions

View file

@ -1,4 +1,5 @@
'use strict'; 'use strict';
'require ui';
'require view'; 'require view';
'require dom'; 'require dom';
'require poll'; 'require poll';
@ -8,43 +9,40 @@
'require form'; 'require form';
'require tools.widgets as widgets'; 'require tools.widgets as widgets';
var callGetLogServices, callInitAction, callDDnsGetStatus; return view.extend({
var NextUpdateStrings = {}; NextUpdateStrings : {
NextUpdateStrings = {
'Verify' : _("Verify"), 'Verify' : _("Verify"),
'Run once' : _("Run once"), 'Run once' : _("Run once"),
'Disabled' : _("Disabled"), 'Disabled' : _("Disabled"),
'Stopped' : _("Stopped") 'Stopped' : _("Stopped")
} },
var time_res = {}; time_res : {
time_res['seconds'] = 1; seconds : 1,
time_res['minutes'] = 60; minutes : 60,
time_res['hours'] = 3600; hours : 3600,
},
callGetLogServices = rpc.declare({ callGetLogServices: rpc.declare({
object: 'luci.ddns', object: 'luci.ddns',
method: 'get_services_log', method: 'get_services_log',
params: [ 'service_name' ], params: [ 'service_name' ],
expect: { }, expect: { },
}); }),
callInitAction = rpc.declare({ callInitAction: rpc.declare({
object: 'luci', object: 'luci',
method: 'setInitAction', method: 'setInitAction',
params: [ 'name', 'action' ], params: [ 'name', 'action' ],
expect: { result: false } expect: { result: false }
}); }),
callDDnsGetStatus = rpc.declare({ callDDnsGetStatus: rpc.declare({
object: 'luci.ddns', object: 'luci.ddns',
method: 'get_ddns_state', method: 'get_ddns_state',
expect: { } expect: { }
}); }),
return view.extend({
callDDnsGetEnv: rpc.declare({ callDDnsGetEnv: rpc.declare({
object: 'luci.ddns', object: 'luci.ddns',
@ -58,14 +56,137 @@ return view.extend({
expect: { } expect: { }
}), }),
services: {},
/*
* Services list is gen by 3 different source:
* 1. /usr/share/ddns/default contains the service installed by opkg
* 2. /usr/share/ddns/custom contains any service installed by the
* user or the ddns script (for example when service are
* downloaded)
* 3. /usr/share/ddns/list contains all the service that can be
* downloaded by using the ddns script ('service on demand' feature)
*
* (Special services that requires a dedicated package ARE NOT
* supported by the 'service on demand' feature)
*/
callGenServiceList: function(m, ev) {
return Promise.all([
L.resolveDefault(fs.list('/usr/share/ddns/default'), []),
L.resolveDefault(fs.list('/usr/share/ddns/custom'), []),
L.resolveDefault(fs.read('/usr/share/ddns/list'), null)
]).then(L.bind(function (data) {
var default_service = data[0],
custom_service = data[1],
list_service = data[2] && data[2].split("\n") || [],
_this = this;
this.services = {};
default_service.forEach(function (service) {
_this.services[service.name.replace('.json','')] = true
});
custom_service.forEach(function (service) {
_this.services[service.name.replace('.json','')] = true
});
list_service.forEach(function (service) {
if (!_this.services[service])
_this.services[service] = false;
});
}, this))
},
/*
* Check if the service is supported.
* If the script doesn't find any json assume a 'service on demand' install.
* If a json is found check if the ip type is supported.
* Invalidate the service_name if is not supported.
*/
handleCheckService : function(s, service_name, ipv6, ev, section_id) {
var value = service_name.formvalue(section_id);
s.service_supported = null;
service_name.triggerValidation(section_id);
return this.handleGetServiceData(value)
.then(L.bind(function (service_data) {
if (value != '-' && service_data) {
service_data = JSON.parse(service_data);
if (ipv6.formvalue(section_id) == "1" && !service_data.ipv6) {
s.service_supported = false;
return;
}
}
s.service_supported = true;
}, service_name))
.then(L.bind(service_name.triggerValidation, service_name, section_id))
},
handleGetServiceData: function(service) {
return Promise.all([
L.resolveDefault(fs.read('/usr/share/ddns/custom/'+service+'.json'), null),
L.resolveDefault(fs.read('/usr/share/ddns/default/'+service+'.json'), null)
]).then(function(data) {
return data[0] || data[1] || null;
})
},
handleInstallService: function(m, service_name, section_id, section, _this, ev) {
var service = service_name.formvalue(section_id)
return fs.exec('/usr/bin/ddns', ['service', 'install', service])
.then(L.bind(_this.callGenServiceList, _this))
.then(L.bind(m.render, m))
.then(L.bind(this.renderMoreOptionsModal, this, section))
.catch(function(e) { ui.addNotification(null, E('p', e.message)) });
},
handleRefreshServicesList: function(m, ev) {
return fs.exec('/usr/bin/ddns', ['service', 'update'])
.then(L.bind(this.load, this))
.then(L.bind(this.render, this))
.catch(function(e) { ui.addNotification(null, E('p', e.message)) });
},
handleReloadDDnsRule: function(m, section_id, ev) {
return fs.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
[ '-S', section_id, '--', 'start' ])
.then(L.bind(m.load, m))
.then(L.bind(m.render, m))
.catch(function(e) { ui.addNotification(null, E('p', e.message)) });
},
HandleStopDDnsRule: function(m, section_id, ev) {
return fs.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
[ '-S', section_id, '--', 'start' ])
.then(L.bind(m.render, m))
.catch(function(e) { ui.addNotification(null, E('p', e.message)) });
},
handleToggleDDns: function(m, ev) {
return this.callInitAction('ddns', 'enabled')
.then(L.bind(function (action) { return this.callInitAction('ddns', action ? 'disable' : 'enable')}, this))
.then(L.bind(function (action) { return this.callInitAction('ddns', action ? 'stop' : 'start')}, this))
.then(L.bind(m.render, m))
.catch(function(e) { ui.addNotification(null, E('p', e.message)) });
},
handleRestartDDns: function(m, ev) {
return this.callInitAction('ddns', 'restart')
.then(L.bind(m.render, m));
},
poll_status: function(map, data) { poll_status: function(map, data) {
var status = data[1] || [], service = data[0] || [], rows = map.querySelectorAll('.cbi-section-table-row[data-sid]'), var status = data[1] || [], service = data[0] || [], rows = map.querySelectorAll('.cbi-section-table-row[data-sid]'),
section_id, cfg_detail_ip, cfg_update, cfg_status, host, ip, last_update, section_id, cfg_detail_ip, cfg_update, cfg_status, host, ip, last_update,
next_update, service_status, reload, cfg_enabled, stop, next_update, service_status, reload, cfg_enabled, stop,
ddns_enabled = map.querySelector('[data-name="_enabled"]').querySelector('.cbi-value-field'), ddns_enabled = map.querySelector('[data-name="_enabled"]').querySelector('.cbi-value-field'),
ddns_toggle = map.querySelector('[data-name="_toggle"]').querySelector('button'); ddns_toggle = map.querySelector('[data-name="_toggle"]').querySelector('button'),
services_list = map.querySelector('[data-name="_services_list"]').querySelector('.cbi-value-field');
ddns_toggle.innerHTML = status['_enabled'] ? _('Stop DDNS') : _('Start DDNS') ddns_toggle.innerHTML = status['_enabled'] ? _('Stop DDNS') : _('Start DDNS')
services_list.innerHTML = status['_services_list'];
dom.content(ddns_enabled, function() { dom.content(ddns_enabled, function() {
return E([], [ return E([], [
@ -101,7 +222,7 @@ return view.extend({
if (service[section_id].last_update) if (service[section_id].last_update)
last_update = service[section_id].last_update; last_update = service[section_id].last_update;
if (service[section_id].next_update) if (service[section_id].next_update)
next_update = NextUpdateStrings[service[section_id].next_update] || service[section_id].next_update; next_update = this.NextUpdateStrings[service[section_id].next_update] || service[section_id].next_update;
if (service[section_id].pid) if (service[section_id].pid)
service_status = '<b>' + _('Running') + '</b> : ' + service[section_id].pid; service_status = '<b>' + _('Running') + '</b> : ' + service[section_id].pid;
} }
@ -117,10 +238,9 @@ return view.extend({
load: function() { load: function() {
return Promise.all([ return Promise.all([
this.callDDnsGetServicesStatus(), this.callDDnsGetServicesStatus(),
callDDnsGetStatus(), this.callDDnsGetStatus(),
this.callDDnsGetEnv(), this.callDDnsGetEnv(),
fs.lines('/etc/ddns/services'), this.callGenServiceList(),
fs.lines('/etc/ddns/services_ipv6'),
uci.load('ddns') uci.load('ddns')
]); ]);
}, },
@ -131,20 +251,7 @@ return view.extend({
var env = data[2] || []; var env = data[2] || [];
var logdir = uci.get('ddns', 'global', 'ddns_logdir') || "/var/log/ddns"; var logdir = uci.get('ddns', 'global', 'ddns_logdir') || "/var/log/ddns";
var services4 = []; var _this = this;
var services6 = [];
data[3].forEach(function(item) {
if (!item.startsWith("#")) {
services4.push(item.split('\t')[0].slice(1,-1));
}
});
data[4].forEach(function(item) {
if (!item.startsWith("#")) {
services6.push(item.split('\t')[0].slice(1,-1));
}
});
var m, s, o; var m, s, o;
@ -170,32 +277,28 @@ return view.extend({
return res ? _('DDNS Autostart enabled') : _('DDNS Autostart disabled') return res ? _('DDNS Autostart enabled') : _('DDNS Autostart disabled')
}; };
o = s.taboption('info', form.DummyValue, '_toggle', '&#160;'); o = s.taboption('info', form.Button, '_toggle');
o.title = '&#160;';
o.inputtitle = _((status['_enabled'] ? 'stop' : 'start').toUpperCase() + ' DDns');
o.inputstyle = 'apply';
o.onclick = L.bind(this.handleToggleDDns, this, m);
o = s.taboption('info', form.Button, '_restart');
o.title = '&#160;';
o.inputtitle = _('Restart DDns');
o.inputstyle = 'apply';
o.onclick = L.bind(this.handleRestartDDns, this, m);
o = s.taboption('info', form.DummyValue, '_services_list', _('Services list last update'));
o.cfgvalue = function() { o.cfgvalue = function() {
var action = status['_enabled'] ? 'stop' : 'start'; return status[this.option];
return E([], [
E('button', {
'class': 'cbi-button cbi-button-apply',
'click': L.ui.createHandlerFn(this, function() {
return callDDnsGetStatus().then(L.bind(function(data) {
return callInitAction('ddns', action == 'stop' ? 'disable' : 'enable').then(function() {
return callInitAction('ddns', action);
});
}, this)).then(L.bind(m.render, m));
})
}, _(action.toUpperCase() + ' DDns'))]);
}; };
o = s.taboption('info', form.DummyValue, '_restart', '&#160;'); o = s.taboption('info', form.Button, '_refresh_services');
o.cfgvalue = function() { o.title = '&#160;';
return E([], [ o.inputtitle = _('Update DDns Services List');
E('button', { o.inputstyle = 'apply';
'class': 'cbi-button cbi-button-apply', o.onclick = L.bind(this.handleRefreshServicesList, this, m);
'click': L.ui.createHandlerFn(this, function() {
return callInitAction('ddns', 'restart').then(L.bind(m.render, m));
})
}, _('Restart DDns'))]);
};
// DDns hints // DDns hints
@ -344,6 +447,14 @@ return view.extend({
} }
o = s.taboption('global', form.Value, 'cacert', _('Ca Certs path'));
o.description = _('Ca Certs path that will be used to download services data. Set IGNORE to skip certificate validation.');
o.placeholder = 'IGNORE';
o = s.taboption('global', form.Value, 'services_url', _('Services URL Download'));
o.description = _('Url used to download services file. By default is the master openwrt ddns package repo.');
o.placeholder = 'https://raw.githubusercontent.com/openwrt/packages/master/net/ddns-scripts/files';
// DDns services // DDns services
s = m.section(form.GridSection, 'service', _('Services')); s = m.section(form.GridSection, 'service', _('Services'));
s.anonymous = true; s.anonymous = true;
@ -354,9 +465,25 @@ return view.extend({
s.addremove = true; s.addremove = true;
s.sortable = true; s.sortable = true;
s.handleCreateDDnsRule = function(m, name, service_name, ipv6, ev) {
var section_id = name.isValid('_new_') ? name.formvalue('_new_') : null,
service_value = service_name.isValid('_new_') ? service_name.formvalue('_new_') : null,
ipv6_value = ipv6.isValid('_new_') ? ipv6.formvalue('_new_') : null;
if (section_id == null || section_id == '' || service_value == null || section_id == '' || ipv6_value == null || ipv6_value == '')
return;
return m.save(function() {
uci.add('ddns', 'service', section_id);
uci.set('ddns', section_id, 'service_name', service_value);
uci.set('ddns', section_id, 'use_ipv6', ipv6_value);
}).then(L.bind(m.children[1].renderMoreOptionsModal, m.children[1], section_id));
};
s.handleAdd = function(ev) { s.handleAdd = function(ev) {
var m2 = new form.Map('ddns'), var m2 = new form.Map('ddns'),
s2 = m2.section(form.NamedSection, '_new_'); s2 = m2.section(form.NamedSection, '_new_'),
name, ipv6, service_name;
s2.render = function() { s2.render = function() {
return Promise.all([ return Promise.all([
@ -376,26 +503,41 @@ return view.extend({
return true; return true;
}; };
ipv6 = s2.option( form.ListValue, 'use_ipv6',
_("IP address version"),
_("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider"));
ipv6.default = '0';
ipv6.value("0", _("IPv4-Address"))
if (env["has_ipv6"]) {
ipv6.value("1", _("IPv6-Address"))
}
service_name = s2.option(form.ListValue, 'service_name',
String.format('%s', _("DDNS Service provider")));
service_name.value('-',"-- " + _("custom") + " --");
for (var elem in _this.services)
service_name.value(elem);
service_name.validate = function(section_id, value) {
if (value == '') return _("Select a service");
if (s2.service_supported == null) return _("Checking the service support...");
if (!s2.service_supported) return _("Service doesn't support this ip type");
return true;
};
ipv6.onchange = L.bind(_this.handleCheckService, _this, s2, service_name, ipv6);
service_name.onchange = L.bind(_this.handleCheckService, _this, s2, service_name, ipv6);
m2.render().then(L.bind(function(nodes) { m2.render().then(L.bind(function(nodes) {
L.ui.showModal(_('Add new services...'), [ ui.showModal(_('Add new services...'), [
nodes, nodes,
E('div', { 'class': 'right' }, [ E('div', { 'class': 'right' }, [
E('button', { E('button', {
'class': 'btn', 'class': 'btn',
'click': L.ui.hideModal 'click': ui.hideModal
}, _('Cancel')), ' ', }, _('Cancel')), ' ',
E('button', { E('button', {
'class': 'cbi-button cbi-button-positive important', 'class': 'cbi-button cbi-button-positive important',
'click': L.ui.createHandlerFn(this, function(ev) { 'click': ui.createHandlerFn(this, 'handleCreateDDnsRule', m, name, service_name, ipv6)
var nameval = name.isValid('_new_') ? name.formvalue('_new_') : null;
if (nameval == null || nameval == '')
return;
return m.save(function() {
uci.add('ddns', 'service', nameval);
}).then(L.bind(m.children[1].renderMoreOptionsModal, m.children[1], nameval));
})
}, _('Create service')) }, _('Create service'))
]) ])
], 'cbi-modal'); ], 'cbi-modal');
@ -409,18 +551,12 @@ return view.extend({
cfg_enabled = uci.get('ddns', section_id, 'enabled'), cfg_enabled = uci.get('ddns', section_id, 'enabled'),
reload_opt = { reload_opt = {
'class': 'cbi-button cbi-button-neutral reload', 'class': 'cbi-button cbi-button-neutral reload',
'click': L.ui.createHandlerFn(this, function() { 'click': ui.createHandlerFn(_this, 'handleReloadDDnsRule', m, section_id),
return fs.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
[ '-S', section_id, '--', 'start' ]).then(L.bind(m.render, m));
}),
'title': _('Reload this service'), 'title': _('Reload this service'),
}, },
stop_opt = { stop_opt = {
'class': 'cbi-button cbi-button-neutral stop', 'class': 'cbi-button cbi-button-neutral stop',
'click': L.ui.createHandlerFn(this, function() { 'click': ui.createHandlerFn(_this, 'HandleStopDDnsRule', m, section_id),
return fs.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
[ '-S', section_id, '--', 'start' ]).then(L.bind(m.render, m));
}),
'title': _('Stop this service'), 'title': _('Stop this service'),
}; };
@ -442,59 +578,28 @@ return view.extend({
return tdEl; return tdEl;
}; };
o = s.option(form.DummyValue, '_cfg_name', _('Name'));
o.modalonly = false;
o.textvalue = function(section_id) {
return '<b>' + section_id + '</b>';
}
o = s.option(form.DummyValue, '_cfg_detail_ip', _('Lookup Hostname') + "<br />" + _('Registered IP'));
o.rawhtml = true;
o.modalonly = false;
o.textvalue = function(section_id) {
var host = uci.get('ddns', section_id, 'lookup_host') || _('Configuration Error'),
ip = _('No Data');
if (resolved[section_id] && resolved[section_id].ip)
ip = resolved[section_id].ip;
return host + '<br />' + ip;
};
o = s.option(form.Flag, 'enabled', _('Enabled'));
o.rmempty = false;
o.editable = true;
o.modalonly = false;
o = s.option(form.DummyValue, '_cfg_update', _('Last Update') + "<br />" + _('Next Update'));
o.rawhtml = true;
o.modalonly = false;
o.textvalue = function(section_id) {
var last_update = _('Never'), next_update = _('Unknown');
if (resolved[section_id]) {
if (resolved[section_id].last_update)
last_update = resolved[section_id].last_update;
if (resolved[section_id].next_update)
next_update = NextUpdateStrings[resolved[section_id].next_update] || resolved[section_id].next_update;
}
return last_update + '<br />' + next_update;
};
s.modaltitle = function(section_id) { s.modaltitle = function(section_id) {
return _('DDns Service') + ' » ' + section_id; return _('DDns Service') + ' » ' + section_id;
}; };
o = s.option(form.DummyValue, '_cfg_status', _('Status')); s.addModalOptions = function(s, section_id) {
o.modalonly = false;
o.textvalue = function(section_id) {
var text = '<b>' + _('Not Running') + '</b>';
if (resolved[section_id] && resolved[section_id].pid) var service = uci.get('ddns', section_id, 'service_name') || '-',
text = '<b>' + _('Running') + '</b> : ' + resolved[section_id].pid; ipv6 = uci.get('ddns', section_id, 'use_ipv6'), service_name, use_ipv6;
return text; return _this.handleGetServiceData(service).then(L.bind(function (service_data) {
s.service_available = true;
s.service_supported = true;
if (service != '-') {
if (!service_data)
s.service_available = false;
else {
service_data = JSON.parse(service_data);
if (ipv6 == "1" && !service_data.ipv6)
s.service_supported = false;
}
} }
s.tab('basic', _('Basic Settings')); s.tab('basic', _('Basic Settings'));
s.tab('advanced', _('Advanced Settings')); s.tab('advanced', _('Advanced Settings'));
@ -518,62 +623,77 @@ return view.extend({
o.datatype = 'and(minlength(3),hostname("strict"))'; o.datatype = 'and(minlength(3),hostname("strict"))';
o.modalonly = true; o.modalonly = true;
o = s.taboption('basic', form.ListValue, 'use_ipv6', use_ipv6 = s.taboption('basic', form.ListValue, 'use_ipv6',
_("IP address version"), _("IP address version"),
_("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider")); _("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider"));
o.default = '0'; use_ipv6.default = '0';
o.modalonly = true; use_ipv6.modalonly = true;
o.rmempty = false; use_ipv6.rmempty = false;
o.value("0", _("IPv4-Address")) use_ipv6.value("0", _("IPv4-Address"))
if (env["has_ipv6"]) { if (env["has_ipv6"]) {
o.value("1", _("IPv6-Address")) use_ipv6.value("1", _("IPv6-Address"))
} }
o = s.taboption('basic', form.ListValue, 'ipv4_service_name', service_name = s.taboption('basic', form.ListValue, 'service_name',
String.format('%s %s', _("DDNS Service provider"), "[IPv4]")); String.format('%s', _("DDNS Service provider")));
o.depends("use_ipv6", "0") service_name.modalonly = true;
o.modalonly = true; service_name.value('-',"-- " + _("custom") + " --");
o.value('-',"-- " + _("custom") + " --"); for (var elem in _this.services)
for (var i = 0; i < services4.length; i++) service_name.value(elem);
o.value(services4[i]); service_name.cfgvalue = function(section_id) {
o.cfgvalue = function(section_id) {
return uci.get('ddns', section_id, 'service_name') || '-'; return uci.get('ddns', section_id, 'service_name') || '-';
}; };
o.write = function(section_id, formvalue) { service_name.write = function(section_id, service) {
if (formvalue != '-') { if (service != '-') {
uci.set('ddns', section_id, 'update_url', null); uci.set('ddns', section_id, 'update_url', null);
uci.set('ddns', section_id, 'update_script', null); uci.set('ddns', section_id, 'update_script', null);
return uci.set('ddns', section_id, 'service_name', formvalue); return uci.set('ddns', section_id, 'service_name', service);
} }
return uci.set('ddns', section_id, 'service_name', null); return uci.set('ddns', section_id, 'service_name', null);
}; };
service_name.validate = function(section_id, value) {
if (value == '') return _("Select a service");
if (s.service_available == null) return _("Checking the service support...");
if (!s.service_available) return _('Service not installed');
if (!s.service_supported) return _("Service doesn't support this ip type");
return true;
};
o = s.taboption('basic', form.ListValue, 'ipv6_service_name', service_name.onchange = L.bind(_this.handleCheckService, _this, s, service_name, use_ipv6);
String.format('%s %s', _("DDNS Service provider"), "[IPv6]")); use_ipv6.onchange = L.bind(_this.handleCheckService, _this, s, service_name, use_ipv6);
o.depends("use_ipv6", "1")
if (!s.service_available) {
o = s.taboption('basic', form.Button, '_download_service');
o.modalonly = true; o.modalonly = true;
o.value('-',"-- " + _("custom") + " --"); o.title = _('Service not installed');
for (var i = 0; i < services6.length; i++) { o.inputtitle = _('Install Service');
o.value(services6[i]); o.inputstyle = 'apply';
o.onclick = L.bind(_this.handleInstallService,
this, m, service_name, section_id, s.section, _this)
} }
o.cfgvalue = function(section_id) {
var service = uci.get('ddns', section_id, 'service_name'),
update_script = uci.get('ddns', section_id, 'update_script'),
update_url = uci.get('ddns', section_id, 'update_url');
if (!service && (update_script || update_url)) if (!s.service_supported) {
return "-"; o = s.taboption('basic', form.DummyValue, '_not_supported', '&nbsp');
o.cfgvalue = function () {
return service; return _("Service doesn't support this ip type")
}; };
o.write = function(section_id, formvalue) {
if (formvalue != '-') {
uci.set('ddns', section_id, 'update_url', null);
uci.set('ddns', section_id, 'update_script', null);
return uci.set('ddns', section_id, 'service_name', formvalue);
} }
return uci.set('ddns', section_id, 'service_name', null);
}; var service_switch = s.taboption('basic', form.Button, '_switch_proto');
service_switch.modalonly = true;
service_switch.title = _('Really switch service?');
service_switch.inputtitle = _('Switch service');
service_switch.inputstyle = 'apply';
service_switch.onclick = L.bind(function(ev) {
if (!s.service_supported) return;
return s.map.save()
.then(L.bind(m.load, m))
.then(L.bind(m.render, m))
.then(L.bind(this.renderMoreOptionsModal, this, s.section));
}, this);
if (s.service_available && s.service_supported) {
o = s.taboption('basic', form.Value, 'update_url', o = s.taboption('basic', form.Value, 'update_url',
_("Custom update-URL"), _("Custom update-URL"),
@ -583,8 +703,7 @@ return view.extend({
o.modalonly = true; o.modalonly = true;
o.rmempty = true; o.rmempty = true;
o.optional = true; o.optional = true;
o.depends("ipv6_service_name","-"); o.depends("service_name","-");
o.depends("ipv4_service_name","-");
o.validate = function(section_id, value) { o.validate = function(section_id, value) {
var other = this.section.children.filter(function(o) { return o.option == 'update_script' })[0].formvalue(section_id); var other = this.section.children.filter(function(o) { return o.option == 'update_script' })[0].formvalue(section_id);
@ -601,8 +720,7 @@ return view.extend({
o.modalonly = true; o.modalonly = true;
o.rmempty = true; o.rmempty = true;
o.optional = true; o.optional = true;
o.depends("ipv6_service_name","-"); o.depends("service_name","-");
o.depends("ipv4_service_name","-");
o.validate = function(section_id, value) { o.validate = function(section_id, value) {
var other = this.section.children.filter(function(o) { return o.option == 'update_url' })[0].formvalue(section_id); var other = this.section.children.filter(function(o) { return o.option == 'update_url' })[0].formvalue(section_id);
@ -847,7 +965,7 @@ return view.extend({
o.datatype = 'uinteger'; o.datatype = 'uinteger';
o.validate = function(section_id, formvalue) { o.validate = function(section_id, formvalue) {
var unit = this.section.children.filter(function(o) { return o.option == 'check_unit' })[0].formvalue(section_id), var unit = this.section.children.filter(function(o) { return o.option == 'check_unit' })[0].formvalue(section_id),
time_to_sec = time_res[unit || 'minutes'] * formvalue; time_to_sec = _this.time_res[unit || 'minutes'] * formvalue;
if (formvalue && time_to_sec < 300) if (formvalue && time_to_sec < 300)
return _('Values below 5 minutes == 300 seconds are not supported'); return _('Values below 5 minutes == 300 seconds are not supported');
@ -881,8 +999,8 @@ return view.extend({
var check_unit = this.section.children.filter(function(o) { return o.option == 'check_unit' })[0].formvalue(section_id), var check_unit = this.section.children.filter(function(o) { return o.option == 'check_unit' })[0].formvalue(section_id),
check_val = this.section.children.filter(function(o) { return o.option == 'check_interval' })[0].formvalue(section_id), check_val = this.section.children.filter(function(o) { return o.option == 'check_interval' })[0].formvalue(section_id),
force_unit = this.section.children.filter(function(o) { return o.option == 'force_unit' })[0].formvalue(section_id), force_unit = this.section.children.filter(function(o) { return o.option == 'force_unit' })[0].formvalue(section_id),
check_to_sec = time_res[check_unit || 'minutes'] * ( check_val || '30'), check_to_sec = _this.time_res[check_unit || 'minutes'] * ( check_val || '30'),
force_to_sec = time_res[force_unit || 'minutes'] * formvalue; force_to_sec = _this.time_res[force_unit || 'minutes'] * formvalue;
if (force_to_sec != 0 && force_to_sec < check_to_sec) if (force_to_sec != 0 && force_to_sec < check_to_sec)
return _("Values lower 'Check Interval' except '0' are not supported"); return _("Values lower 'Check Interval' except '0' are not supported");
@ -929,41 +1047,113 @@ return view.extend({
o.value("seconds", _("seconds")); o.value("seconds", _("seconds"));
o.value("minutes", _("minutes")); o.value("minutes", _("minutes"));
o = s.taboption('logview', form.Button, '_read_log');
o = s.taboption("logview", form.DummyValue, '_read_log', ''); o.title = '';
o.depends('use_logfile','1'); o.depends('use_logfile','1');
o.modalonly = true; o.modalonly = true;
o.cfgvalue = function(section_id) { o.inputtitle = _('Read / Reread log file');
return E([], [ o.inputstyle = 'apply';
E('button', { o.onclick = L.bind(function(ev, section_id) {
'class': 'cbi-button cbi-button-apply', return _this.callGetLogServices(section_id).then(L.bind(log_box.update_log, log_box));
'click': L.ui.createHandlerFn(this, function() { }, this);
var o = this.section.children.filter(function(o) { return o.option == '_logview' })[0];
return callGetLogServices(section_id).then(L.bind(o.update_log, o));
})
}, _('Read / Reread log file'))]);
};
o = s.taboption("logview", form.DummyValue, "_logview"); var log_box = s.taboption("logview", form.DummyValue, "_logview");
o.depends('use_logfile','1'); log_box.depends('use_logfile','1');
o.modalonly = true; log_box.modalonly = true;
o.update_log = L.bind(function(view, log_data) { log_box.update_log = L.bind(function(view, log_data) {
return document.getElementById('log_area').textContent = log_data.result; return document.getElementById('log_area').textContent = log_data.result;
}, o, this) }, o, this);
o.render = L.bind(function() { log_box.render = L.bind(function() {
return E([ return E([
E('p', {}, _('This is the current content of the log file in ') + logdir + ' for this service.'), E('p', {}, _('This is the current content of the log file in ') + logdir + ' for this service.'),
E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 20, 'readonly' : 'readonly', 'id' : 'log_area' }, _('Please press [Read] button') )) E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 20, 'readonly' : 'readonly', 'id' : 'log_area' }, _('Please press [Read] button') ))
]); ]);
}, o, this) }, o, this);
}
for (var i = 0; i < s.children.length; i++) {
o = s.children[i];
switch (o.option) {
case '_switch_proto':
o.depends({ service_name : service, use_ipv6: ipv6, "!reverse": true })
continue;
case 'enabled':
case 'service_name':
case 'use_ipv6':
case 'update_script':
case 'update_url':
case 'lookup_host':
continue;
default:
if (o.deps.length)
for (var j = 0; j < o.deps.length; j++) {
o.deps[j].service_name = service;
o.deps[j].use_ipv6 = ipv6;
}
else
o.depends({service_name: service, use_ipv6: ipv6 });
}
}
}, this)
)};
o = s.option(form.DummyValue, '_cfg_status', _('Status'));
o.modalonly = false;
o.textvalue = function(section_id) {
var text = '<b>' + _('Not Running') + '</b>';
if (resolved[section_id] && resolved[section_id].pid)
text = '<b>' + _('Running') + '</b> : ' + resolved[section_id].pid;
return text;
};
o = s.option(form.DummyValue, '_cfg_name', _('Name'));
o.modalonly = false;
o.textvalue = function(section_id) {
return '<b>' + section_id + '</b>';
};
o = s.option(form.DummyValue, '_cfg_detail_ip', _('Lookup Hostname') + "<br />" + _('Registered IP'));
o.rawhtml = true;
o.modalonly = false;
o.textvalue = function(section_id) {
var host = uci.get('ddns', section_id, 'lookup_host') || _('Configuration Error'),
ip = _('No Data');
if (resolved[section_id] && resolved[section_id].ip)
ip = resolved[section_id].ip;
return host + '<br />' + ip;
};
o = s.option(form.Flag, 'enabled', _('Enabled'));
o.rmempty = false;
o.editable = true;
o.modalonly = false;
o = s.option(form.DummyValue, '_cfg_update', _('Last Update') + "<br />" + _('Next Update'));
o.rawhtml = true;
o.modalonly = false;
o.textvalue = function(section_id) {
var last_update = _('Never'), next_update = _('Unknown');
if (resolved[section_id]) {
if (resolved[section_id].last_update)
last_update = resolved[section_id].last_update;
if (resolved[section_id].next_update)
next_update = _this.NextUpdateStrings[resolved[section_id].next_update] || resolved[section_id].next_update;
}
return last_update + '<br />' + next_update;
};
return m.render().then(L.bind(function(m, nodes) { return m.render().then(L.bind(function(m, nodes) {
poll.add(L.bind(function() { poll.add(L.bind(function() {
return Promise.all([ return Promise.all([
this.callDDnsGetServicesStatus(), this.callDDnsGetServicesStatus(),
callDDnsGetStatus() this.callDDnsGetStatus()
]).then(L.bind(this.poll_status, this, nodes)); ]).then(L.bind(this.poll_status, this, nodes));
}, this), 5); }, this), 5);
return nodes; return nodes;

View file

@ -7,6 +7,7 @@ local UCI = require "luci.model.uci"
local sys = require "luci.sys" local sys = require "luci.sys"
local util = require "luci.util" local util = require "luci.util"
local ddns_package_path = "/usr/share/ddns"
local luci_helper = "/usr/lib/ddns/dynamic_dns_lucihelper.sh" local luci_helper = "/usr/lib/ddns/dynamic_dns_lucihelper.sh"
local srv_name = "ddns-scripts" local srv_name = "ddns-scripts"
@ -155,6 +156,7 @@ local methods = {
local ipkg = require "luci.model.ipkg" local ipkg = require "luci.model.ipkg"
local uci = UCI.cursor() local uci = UCI.cursor()
local dateformat = uci:get("ddns", "global", "ddns_dateformat") or "%F %R" local dateformat = uci:get("ddns", "global", "ddns_dateformat") or "%F %R"
local services_mtime = fs.stat(ddns_package_path .. "/list", 'mtime')
uci:unload("ddns") uci:unload("ddns")
local ver, srv_ver_cmd local ver, srv_ver_cmd
local res = {} local res = {}
@ -169,6 +171,7 @@ local methods = {
res['_version'] = ver and #ver > 0 and ver or nil res['_version'] = ver and #ver > 0 and ver or nil
res['_enabled'] = sys.init.enabled("ddns") res['_enabled'] = sys.init.enabled("ddns")
res['_curr_dateformat'] = os.date(dateformat) res['_curr_dateformat'] = os.date(dateformat)
res['_services_list'] = services_mtime and os.date(dateformat, services_mtime) or 'NO_LIST'
return res return res
end end

View file

@ -7,8 +7,12 @@
"luci": [ "setInitAction" ] "luci": [ "setInitAction" ]
}, },
"file": { "file": {
"/etc/ddns/services": [ "read" ], "/usr/share/ddns/default": [ "list" ],
"/etc/ddns/services_ipv6": [ "read" ], "/usr/share/ddns/default/*": [ "read" ],
"/usr/share/ddns/custom": [ "list" ],
"/usr/share/ddns/custom/*": [ "read" ],
"/usr/share/ddns/list": [ "read" ],
"/usr/bin/ddns": [ "exec" ],
"/usr/lib/ddns/dynamic_dns_lucihelper.sh": [ "exec" ] "/usr/lib/ddns/dynamic_dns_lucihelper.sh": [ "exec" ]
}, },
"uci": [ "ddns" ] "uci": [ "ddns" ]