From 733ee9a7b6312e9b5fe2a4aa8107baf5379602ab Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Fri, 6 May 2022 13:39:36 +0200 Subject: luci-base: ui.js: warn about connectivity loss on changing iface settings If specific settings such as the protocol, IP address or netmask of an interface the user is connected to are changed, the apply/rollback mechanism might interfere. Display an additional warning dialog in this case, instructing the user to manually reconnect and offering to continue with a less safe unchecked apply mechanism. Signed-off-by: Jo-Philipp Wich --- .../luci-base/htdocs/luci-static/resources/ui.js | 104 +++++++++++++++------ 1 file changed, 77 insertions(+), 27 deletions(-) (limited to 'modules/luci-base/htdocs') diff --git a/modules/luci-base/htdocs/luci-static/resources/ui.js b/modules/luci-base/htdocs/luci-static/resources/ui.js index a46330f294..447bcbae09 100644 --- a/modules/luci-base/htdocs/luci-static/resources/ui.js +++ b/modules/luci-base/htdocs/luci-static/resources/ui.js @@ -4477,6 +4477,26 @@ var UI = baseclass.extend(/** @lends LuCI.ui.prototype */ { } }, + /** @private */ + checkConnectivityAffected: function() { + return L.resolveDefault(fs.exec_direct('/usr/libexec/luci-peeraddr', null, 'json')).then(L.bind(function(info) { + if (L.isObject(info) && Array.isArray(info.inbound_interfaces)) { + for (var i = 0; i < info.inbound_interfaces.length; i++) { + var iif = info.inbound_interfaces[i]; + + for (var j = 0; this.changes && this.changes.network && j < this.changes.network.length; j++) { + var chg = this.changes.network[j]; + + if (chg[0] == 'set' && chg[1] == iif && (chg[2] == 'proto' || chg[2] == 'ipaddr' || chg[2] == 'netmask')) + return iif; + } + } + } + + return null; + }, this)); + }, + /** @private */ rollback: function(checked) { if (checked) { @@ -4614,35 +4634,65 @@ var UI = baseclass.extend(/** @lends LuCI.ui.prototype */ { this.displayStatus('notice spinning', E('p', _('Starting configuration apply…'))); - request.request(L.url('admin/uci', checked ? 'apply_rollback' : 'apply_unchecked'), { - method: 'post', - query: { sid: L.env.sessionid, token: L.env.token } - }).then(function(r) { - if (r.status === (checked ? 200 : 204)) { - var tok = null; try { tok = r.json(); } catch(e) {} - if (checked && tok !== null && typeof(tok) === 'object' && typeof(tok.token) === 'string') - UI.prototype.changes.confirm_auth = tok; - - UI.prototype.changes.confirm(checked, Date.now() + L.env.apply_rollback * 1000); - } - else if (checked && r.status === 204) { - UI.prototype.changes.displayStatus('notice', - E('p', _('There are no changes to apply'))); + (new Promise(function(resolveFn, rejectFn) { + if (!checked) + return resolveFn(false); + + UI.prototype.changes.checkConnectivityAffected().then(function(affected) { + if (!affected) + return resolveFn(true); + + UI.prototype.changes.displayStatus('warning', [ + E('h4', _('Connectivity change')), + E('p', _('The network access to this device could be interrupted by changing settings of the "%h" interface.').format(affected)), + E('p', _('If the IP address used to access LuCI changes, a manual reconnect to the new IP is required within %d seconds to confirm the settings, otherwise modifications will be reverted.').format(L.env.apply_rollback)), + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'btn', + 'click': rejectFn, + }, [ _('Cancel') ]), ' ', + E('button', { + 'class': 'btn cbi-button-action important', + 'click': resolveFn.bind(null, true) + }, [ _('Apply and revert on connectivity loss') ]), ' ', + E('button', { + 'class': 'btn cbi-button-negative important', + 'click': resolveFn.bind(null, false) + }, [ _('Apply and keep settings') ]) + ]) + ]); + }); + })).then(function(checked) { + request.request(L.url('admin/uci', checked ? 'apply_rollback' : 'apply_unchecked'), { + method: 'post', + query: { sid: L.env.sessionid, token: L.env.token } + }).then(function(r) { + if (r.status === (checked ? 200 : 204)) { + var tok = null; try { tok = r.json(); } catch(e) {} + if (checked && tok !== null && typeof(tok) === 'object' && typeof(tok.token) === 'string') + UI.prototype.changes.confirm_auth = tok; + + UI.prototype.changes.confirm(checked, Date.now() + L.env.apply_rollback * 1000); + } + else if (checked && r.status === 204) { + UI.prototype.changes.displayStatus('notice', + E('p', _('There are no changes to apply'))); - window.setTimeout(function() { - UI.prototype.changes.displayStatus(false); - }, L.env.apply_display * 1000); - } - else { - UI.prototype.changes.displayStatus('warning', - E('p', _('Apply request failed with status %h') - .format(r.responseText || r.statusText || r.status))); + window.setTimeout(function() { + UI.prototype.changes.displayStatus(false); + }, L.env.apply_display * 1000); + } + else { + UI.prototype.changes.displayStatus('warning', + E('p', _('Apply request failed with status %h') + .format(r.responseText || r.statusText || r.status))); - window.setTimeout(function() { - UI.prototype.changes.displayStatus(false); - }, L.env.apply_display * 1000); - } - }); + window.setTimeout(function() { + UI.prototype.changes.displayStatus(false); + }, L.env.apply_display * 1000); + } + }); + }, this.displayStatus.bind(this, false)); }, /** -- cgit v1.2.3