diff options
6 files changed, 440 insertions, 159 deletions
diff --git a/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/configuration.js b/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/configuration.js new file mode 100644 index 0000000000..a99e646064 --- /dev/null +++ b/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/configuration.js @@ -0,0 +1,34 @@ +'use strict'; +'require view'; +'require form'; + +return view.extend({ + render: function() { + var m, s, o; + + m = new form.Map('attendedsysupgrade', _('Attended Sysupgrade'), + _('Attendedsysupgrade Configuration.') + ); + + s = m.section(form.TypedSection, 'server', _('Server')); + s.anonymous = true; + + s.option(form.Value, 'url', _('Address'), + _('Address of the sysupgrade server')); + + s = m.section(form.TypedSection, 'client', _('Client')); + s.anonymous = true; + + o = s.option(form.Flag, 'auto_search', _('Search on opening'), + _('Search for new sysupgrades on opening the tab')); + o.default = '1'; + o.rmempty = false; + + o = s.option(form.Flag, 'advanced_mode', _('Advances Mode'), + _('Show advanced options like packge list modification')); + o.default = '0'; + o.rmempty = false; + + return m.render(); + } +}); diff --git a/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/overview.js b/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/overview.js new file mode 100644 index 0000000000..a46bb3db44 --- /dev/null +++ b/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/overview.js @@ -0,0 +1,366 @@ +'use strict'; +'require view'; +'require form'; +'require uci'; +'require rpc'; +'require ui'; +'require poll'; +'require request'; +'require dom'; + +var callPackagelist = rpc.declare({ + object: 'rpc-sys', + method: 'packagelist', +}); + +var callSystemBoard = rpc.declare({ + object: 'system', + method: 'board' +}); + +var callUpgradeStart = rpc.declare({ + object: 'rpc-sys', + method: 'upgrade_start', + params: ["keep"] +}); + +function install_sysupgrade(url, keep, sha256) { + displayStatus("notice spinning", E('p', _('Downloading firmware from server to browser'))); + request.get(url, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + responseType: 'blob' + }) + .then(response => { + var form_data = new FormData(); + form_data.append("sessionid", rpc.getSessionID()); + form_data.append("filename", "/tmp/firmware.bin"); + form_data.append("filemode", 600); + form_data.append("filedata", response.blob()); + + displayStatus("notice spinning", E('p', _('Uploading firmware from browser to device'))); + request.get(L.env.cgi_base + "/cgi-upload", { + method: 'PUT', + content: form_data + }) + .then(response => response.json()) + .then(response => { + if (response.sha256sum != sha256) { + displayStatus("warning", [ + E('b', _('Wrong checksum')), + E('p', _('Error during download of firmware. Please try again')), + E('div', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]); + } else { + displayStatus('warning spinning', E('p', _('Installing the sysupgrade. Do not unpower device!'))); + L.resolveDefault(callUpgradeStart(keep), {}).then(response => { + if (keep) { + ui.awaitReconnect(window.location.host); + } else { + ui.awaitReconnect('192.168.1.1', 'openwrt.lan'); + } + }); + } + }); + }); +} + +function request_sysupgrade(server_url, data) { + var res, req; + + if (data.request_hash) { + req = request.get(server_url + "/api/build/" + data.request_hash) + } else { + req = request.post(server_url + "/api/build", { + profile: data.board_name, + version: data.version, + packages: data.packages, + diff_packages: true, + }) + } + + req.then(response => { + switch (response.status) { + case 200: + var res = response.json() + var image; + console.log(res) + for (image of res.images) { + if (image.type == "sysupgrade") { + break; + } + } + if (image.name != undefined) { + var sysupgrade_url = server_url + "/store/" + res.bin_dir + "/" + image.name; + + var keep = E('input', { + type: 'checkbox' + }) + keep.checked = true; + + var fields = [ + _('Version'), res.version_number + ' ' + res.version_code, + _('File'), E('a', { + 'href': sysupgrade_url + }, image.name), + _('SHA256'), image.sha256, + _('Build Date'), res.build_at, + _('Target'), res.target, + ]; + + var table = E('div', { + 'class': 'table' + }); + + for (var i = 0; i < fields.length; i += 2) { + table.appendChild(E('div', { + 'class': 'tr' + }, [ + E('div', { + 'class': 'td left', + 'width': '33%' + }, [fields[i]]), + E('div', { + 'class': 'td left' + }, [(fields[i + 1] != null) ? fields[i + 1] : '?']) + ])); + } + + var modal_body = [ + table, + E('p', {}, E('label', { + 'class': 'btn' + }, [ + keep, ' ', _('Keep settings and retain the current configuration') + ])), + E('div', { + 'class': 'right' + }, [ + E('div', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Cancel')), + ' ', + E('div', { + 'class': 'btn cbi-button-action', + 'click': function() { + install_sysupgrade(sysupgrade_url, keep.checked, image.sha256) + } + }, _('Install Sysupgrade')) + ]) + ] + + ui.showModal(_('Successfully created sysupgrade image'), modal_body); + } + + break; + case 202: + res = response.json() + data.request_hash = res.request_hash; + switch (res.status) { + case "queued": + displayStatus("notice spinning", E('p', _('Request in build queue'))); + break; + case "started": + displayStatus("notice spinning", E('p', _('Building the sysupgrade image'))); + break; + } + setTimeout(function() { + request_sysupgrade(server_url, data); + }, 5000); + break; + case 400: // bad request + case 422: // bad package + case 500: // build failed + res = response.json() + var body = [ + E('p', {}, _(res.message)), + E('p', {}, _("Please report the error message and request")), + E('b', {}, _("Request to server:")), + E('pre', {}, JSON.stringify(data, null, 4)), + + ] + + if (res.stdout) { + body.push(E('b', {}, "STDOUT:")) + body.push(E('pre', {}, res.stdout)) + + } + + if (res.stderr) { + body.push(E('b', {}, "STDERR:")) + body.push(E('pre', {}, res.stderr)) + + } + + body = body.concat([ + E('div', { + 'class': 'right' + }, [ + E('div', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + ui.showModal(_('Error building the sysupgrade'), body); + break; + } + }); +} + +function check_sysupgrade(server_url, current_version, board_name, packages) { + displayStatus("notice spinning", E('p', _('Searching for an available sysupgrade'))); + var current_branch = current_version.split(".").slice(0, 2).join("."); + var candidates = []; + fetch(server_url + "/api/latest") + .then(response => response.json()) + .then(response => { + if (current_version == "SNAPSHOT") { + candidates.push("SNAPSHOT"); + } else { + for (let version of response["latest"]) { + var branch = version.split(".").slice(0, 2).join("."); + + // already latest version installed + if (current_version == version) { + break; + } + + // warn user that a new major release would be installed + //if (current_branch != branch) { + // branch["warn_branch_jump"] = true; + //} + + candidates.unshift(version); + + // don't offer branches older than the current + if (current_branch == branch) { + break; + } + } + } + if (candidates) { + var m, s, o; + var advanced_mode = uci.get_first('attendedsysupgrade', 'client', 'advanced_mode') || 0; + + console.log(candidates); + + var mapdata = { + request: { + board_name: board_name, + version: candidates[0], + packages: Object.keys(packages).sort(), + } + } + + m = new form.JSONMap(mapdata, ''); + + s = m.section(form.NamedSection, 'request', 'example', '', + 'Use defaults for the safest update'); + o = s.option(form.ListValue, 'version', 'Select firmware version'); + for (let candidate of candidates) { + o.value(candidate, candidate); + } + + if (advanced_mode == 1) { + o = s.option(form.Value, 'board_name', 'Board Name / Profile'); + o = s.option(form.DynamicList, 'packages', 'Packages'); + } + + + m.render() + .then(function(form_rendered) { + ui.showModal(_('New upgrade available'), [ + form_rendered, + E('div', { + 'class': 'right' + }, [ + E('div', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Cancel')), + ' ', + E('div', { + 'class': 'btn cbi-button-action', + 'click': function() { + m.save().then(foo => { + request_sysupgrade( + server_url, mapdata.request + ) + }); + } + }, _('Request Sysupgrade')) + ]) + ]); + }); + } else { + ui.showModal(_('No upgrade available'), [ + E('p', {}, _("The device runs the latest firmware version")), + E('div', { + 'class': 'right' + }, [ + E('div', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + } + }); +} + +function displayStatus(type, content) { + if (type) { + var message = ui.showModal('', ''); + + message.classList.add('alert-message'); + DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/)); + + if (content) + dom.content(message, content); + } else { + ui.hideModal(); + } +} + +return view.extend({ + load: function() { + return Promise.all([ + L.resolveDefault(callPackagelist(), {}), + L.resolveDefault(callSystemBoard(), {}), + uci.load('attendedsysupgrade') + ]); + }, + render: function(res) { + var packages = res[0].packages; + var current_version = res[1].release.version; + var board_name = res[1].board_name; + var auto_search = uci.get_first('attendedsysupgrade', 'client', 'auto_search') || 1; + var server_url = uci.get_first('attendedsysupgrade', 'server', 'url'); + + var view = [ + E('h2', _("Attended Sysupgrade")), + E('p', _('The attended sysupgrade service allows to easily upgrade vanilla and custom firmware images.')), + E('p', _('This is done by building a new firmware on demand via an online service.')) + ]; + + if (auto_search == 1) { + check_sysupgrade(server_url, current_version, board_name, packages) + } + + view.push(E('p', { + 'class': 'btn cbi-button-positive', + 'click': function() { + check_sysupgrade(server_url, current_version, board_name, packages) + } + }, _('Search for sysupgrade'))); + + return view; + }, + +}); diff --git a/applications/luci-app-attendedsysupgrade/luasrc/view/attendedsysupgrade.htm b/applications/luci-app-attendedsysupgrade/luasrc/view/attendedsysupgrade.htm deleted file mode 100644 index c9259559be..0000000000 --- a/applications/luci-app-attendedsysupgrade/luasrc/view/attendedsysupgrade.htm +++ /dev/null @@ -1,123 +0,0 @@ -<% --- all lua code provided by https://github.com/jow-/ --- thank you very much! - - function apply_acls(filename, session) - local json = require "luci.jsonc" - local util = require "luci.util" - local fs = require "nixio.fs" - - local grants = { } - - local acl = json.parse(fs.readfile(filename)) - if type(acl) ~= "table" then - return - end - - local group, perms - for group, perms in pairs(acl) do - local perm, scopes - for perm, scopes in pairs(perms) do - if type(scopes) == "table" then - local scope, objects - for scope, objects in pairs(scopes) do - if type(objects) == "table" then - if not grants[scope] then - grants[scope] = { } - end - - if next(objects) == 1 then - local _, object - for _, object in ipairs(objects) do - if not grants[scope][object] then - grants[scope][object] = { } - end - table.insert(grants[scope][object], perm) - end - else - local object, funcs - for object, funcs in pairs(objects) do - if type(funcs) == "table" then - local _, func - for _, func in ipairs(funcs) do - if not grants[scope][object] then - grants[scope][object] = { } - end - table.insert(grants[scope][object], func) - end - end - end - end - end - end - end - end - end - - local _, scope, object, func - for scope, _ in pairs(grants) do - local objects = { } - for object, _ in pairs(_) do - for _, func in ipairs(_) do - table.insert(objects, { object, func }) - end - end - - util.ubus("session", "grant", { - ubus_rpc_session = session, - scope = scope, objects = objects - }) - end - end - - apply_acls("/usr/share/rpcd/acl.d/attendedsysupgrade.json", luci.dispatcher.context.authsession) -%> -<%+header%> -<h2 name="content"><%:Attended Sysupgrade%></h2> -<div class="cbi-map-descr"> - Easily search and install new releases and package upgrades. Sysupgrade firmware are created on demand based on locally installed packages. -</div> -<div style="display: none" id="status_box" class="alert-message info"></div> -<div style="display: none" id="packages" class="alert-message success"></div> -<p> -<textarea style="display: none; width: 100%;" id="edit_packages" rows="15"></textarea> -</p> -<fieldset class="cbi-section"> - <form method="post" action=""> - <div class="cbi-selection-node"> - <div class="cbi-value" id="keep_container" style="display: none"> - <div class="cbi-section-descr"> - Check "Keep settings" to retain the current configuration (requires a compatible firmware). - </div> - <label class="cbi-value-title" for="keep">Keep settings:</label> - <div class="cbi-value-field"> - <input name="keep" id="keep" checked="checked" type="checkbox"> - </div> - </div> - <div class="cbi-value" id="edit_button" style="display: none"> - <div class="cbi-value-field"> - <input class="cbi-button" value="Edit installed packages" onclick="edit_packages()" type="button"> - </div> - </div> - <div class="cbi-value cbi-value" id="server_div" style="display:none"> - <label class="cbi-value-title" for="server">Server:</label> - <div class="cbi-value-field"> - <input onclick="edit_server()" class="cbi-button cbi-button-edit" value="" type="button" id="server" name="server"> - </div> - </div> - <div class="cbi-value cbi-value-last"> - <div class="cbi-value-field"> - <input class="cbi-button cbi-button-apply" value="Search for upgrades" style="display: none" onclick="upgrade_check()" type="button" id="upgrade_button"> - </div> - </div> - </div> - </form> -</fieldset> -<script type="text/javascript"> - data = {}; - data["ubus_rpc_session"] = "<%=luci.dispatcher.context.authsession%>" - origin = document.location.href.replace(location.pathname, "") - ubus_url = origin + "/ubus/" -</script> -<script type="text/javascript" src="<%=resource%>/attendedsysupgrade.js"></script> -<%+footer%> diff --git a/applications/luci-app-attendedsysupgrade/root/usr/share/luci/menu.d/luci-app-attendedsysupgrade.json b/applications/luci-app-attendedsysupgrade/root/usr/share/luci/menu.d/luci-app-attendedsysupgrade.json index 79d82a828f..33d7019fa3 100644 --- a/applications/luci-app-attendedsysupgrade/root/usr/share/luci/menu.d/luci-app-attendedsysupgrade.json +++ b/applications/luci-app-attendedsysupgrade/root/usr/share/luci/menu.d/luci-app-attendedsysupgrade.json @@ -1,13 +1,31 @@ { - "admin/system/attended_sysupgrade": { + "admin/system/attendedsysupgrade": { "title": "Attended Sysupgrade", - "order": 1, + "order": 60, "action": { - "type": "template", - "path": "attendedsysupgrade" + "type": "firstchild" }, "depends": { - "acl": [ "attendedsysupgrade" ] + "acl": [ "luci-app-attendedsysupgrade" ], + "uci": { "attendedsysupgrade": true } + } + }, + + "admin/system/attendedsysupgrade/overview": { + "title": "Overview", + "order": 1, + "action": { + "type": "view", + "path": "attendedsysupgrade/overview" + } + }, + + "admin/system/attendedsysupgrade/configuration": { + "title": "Configuration", + "order": 2, + "action": { + "type": "view", + "path": "attendedsysupgrade/configuration" } } } diff --git a/applications/luci-app-attendedsysupgrade/root/usr/share/rpcd/acl.d/attendedsysupgrade.json b/applications/luci-app-attendedsysupgrade/root/usr/share/rpcd/acl.d/attendedsysupgrade.json deleted file mode 100644 index 7549319260..0000000000 --- a/applications/luci-app-attendedsysupgrade/root/usr/share/rpcd/acl.d/attendedsysupgrade.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "attendedsysupgrade": { - "description": "attended sysupgrade via rpcd and luci", - "read": { - "ubus": { - "rpc-sys": [ - "upgrade_start", - "packagelist" - ], - "system": [ - "board", - "info" - ], - "uci": [ - "get", "set", "commit" - ] - }, - "uci": [ - "attendedsysupgrade" - ] - }, - "write": { - "cgi-io": [ - "upload" - ], - "uci": [ - "attendedsysupgrade" - ] - } - } -} diff --git a/applications/luci-app-attendedsysupgrade/root/usr/share/rpcd/acl.d/luci-app-attendedsysupgrade.json b/applications/luci-app-attendedsysupgrade/root/usr/share/rpcd/acl.d/luci-app-attendedsysupgrade.json new file mode 100644 index 0000000000..e3ceeaa35d --- /dev/null +++ b/applications/luci-app-attendedsysupgrade/root/usr/share/rpcd/acl.d/luci-app-attendedsysupgrade.json @@ -0,0 +1,17 @@ +{ + "luci-app-attendedsysupgrade": { + "description": "Grant UCI access to LuCI app attendedsysupgrade", + "read": { + "uci": ["attendedsysupgrade"], + "ubus": { + "rpc-sys": ["upgrade_start", "packagelist"] + } + }, + "write": { + "uci": ["attendedsysupgrade"], + "ubus": { + "rpc-sys": ["upgrade_start"] + } + } + } +} |