11 var callPackagelist = rpc.declare({
13 method: 'packagelist',
16 var callSystemBoard = rpc.declare({
21 var callUpgradeStart = rpc.declare({
23 method: 'upgrade_start',
27 function get_branch(version) {
28 // determine branch of a version
29 // SNAPSHOT -> SNAPSHOT
30 // 21.02-SNAPSHOT -> 21.02
31 // 21.02.0-rc1 -> 21.02
33 return version.replace("-SNAPSHOT", "").split(".").slice(0, 2).join(".");
36 function get_revision_count(revision) {
37 return parseInt(revision.substring(1).split("-")[0])
40 function error_api_connect(response) {
42 ui.showModal(_('Error connecting to upgrade server'), [
43 E('p', {}, _(`Could not reach API at "${response.url}. Please try again later.`)),
44 E('pre', {}, response.responseText),
56 function install_sysupgrade(url, keep, sha256) {
57 displayStatus("notice spinning", E('p', _('Downloading firmware from server to browser')));
60 'Content-Type': 'application/x-www-form-urlencoded'
65 var form_data = new FormData();
66 form_data.append("sessionid", rpc.getSessionID());
67 form_data.append("filename", "/tmp/firmware.bin");
68 form_data.append("filemode", 600);
69 form_data.append("filedata", response.blob());
71 displayStatus("notice spinning", E('p', _('Uploading firmware from browser to device')));
72 request.get(`${L.env.cgi_base}/cgi-upload`, {
76 .then(response => response.json())
78 if (response.sha256sum != sha256) {
79 displayStatus("warning", [
80 E('b', _('Wrong checksum')),
81 E('p', _('Error during download of firmware. Please try again')),
88 displayStatus('warning spinning', E('p', _('Installing the sysupgrade. Do not unpower device!')));
89 L.resolveDefault(callUpgradeStart(keep), {}).then(response => {
91 ui.awaitReconnect(window.location.host);
93 ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
101 function request_sysupgrade(server_url, data) {
104 if (data.request_hash) {
105 req = request.get(`${server_url}/api/v1/build/${data.request_hash}`)
107 req = request.post(`${server_url}/api/v1/build`, {
108 profile: data.board_name,
110 version: data.version,
111 packages: data.packages,
116 req.then(response => {
117 switch (response.status) {
119 res = response.json()
121 for (image of res.images) {
122 if (image.type == "sysupgrade") {
126 if (image.name != undefined) {
127 var sysupgrade_url = `${server_url}/store/${res.bin_dir}/${image.name}`;
129 var keep = E('input', {
135 _('Version'), `${res.version_number} ${res.version_code}`,
137 'href': sysupgrade_url
139 _('SHA256'), image.sha256,
140 _('Build Date'), res.build_at,
141 _('Target'), res.target,
144 var table = E('div', {
148 for (var i = 0; i < fields.length; i += 2) {
149 table.appendChild(E('div', {
158 }, [(fields[i + 1] != null) ? fields[i + 1] : '?'])
164 E('p', {}, E('label', {
167 keep, ' ', _('Keep settings and retain the current configuration')
174 'click': ui.hideModal
178 'class': 'btn cbi-button-action',
179 'click': function() {
180 install_sysupgrade(sysupgrade_url, keep.checked, image.sha256)
182 }, _('Install Sysupgrade'))
186 ui.showModal(_('Successfully created sysupgrade image'), modal_body);
191 res = response.json()
192 data.request_hash = res.request_hash;
194 if ("queue_position" in res)
195 displayStatus("notice spinning", E('p', _('Request in build queue position %d'.format(res.queue_position))));
197 displayStatus("notice spinning", E('p', _('Building firmware sysupgrade image')));
199 setTimeout(function() {
200 request_sysupgrade(server_url, data);
203 case 400: // bad request
204 case 422: // bad package
205 case 500: // build failed
206 res = response.json()
208 E('p', {}, res.detail),
209 E('p', {}, _("Please report the error message and request")),
210 E('b', {}, _("Request to server:")),
211 E('pre', {}, JSON.stringify(data, null, 4)),
216 body.push(E('b', {}, "STDOUT:"))
217 body.push(E('pre', {}, res.stdout))
222 body.push(E('b', {}, "STDERR:"))
223 body.push(E('pre', {}, res.stderr))
233 'click': ui.hideModal
237 ui.showModal(_('Error building the sysupgrade'), body);
243 async function check_sysupgrade(server_url, system_board, packages) {
251 } = system_board.release;
252 var current_branch = get_branch(version);
253 var advanced_mode = uci.get_first('attendedsysupgrade', 'client', 'advanced_mode') || 0;
257 displayStatus("notice spinning", E('p', _(`Searching for an available sysupgrade of ${version} - ${revision}`)));
259 if (version.endsWith("SNAPSHOT")) {
260 response = await request.get(`${server_url}/api/v1/revision/${version}/${target}`)
262 error_api_connect(response);
266 const remote_revision = response.json().revision;
268 if (get_revision_count(revision) < get_revision_count(remote_revision)) {
269 candidates.push(version);
272 response = await request.get(`${server_url}/api/overview`, {
277 error_api_connect(response);
281 const latest = response.json().latest
283 for (let remote_version of latest) {
284 var remote_branch = get_branch(remote_version);
286 // already latest version installed
287 if (version == remote_version) {
291 // skip branch upgrades outside the advanced mode
292 if (current_branch != remote_branch && advanced_mode == 0) {
296 candidates.unshift(remote_version);
298 // don't offer branches older than the current
299 if (current_branch == remote_branch) {
305 if (candidates.length) {
310 board_name: board_name,
312 version: candidates[0],
313 packages: Object.keys(packages).sort(),
317 m = new form.JSONMap(mapdata, '');
319 s = m.section(form.NamedSection, 'request', 'example', '',
320 'Use defaults for the safest update');
321 o = s.option(form.ListValue, 'version', 'Select firmware version');
322 for (let candidate of candidates) {
323 o.value(candidate, candidate);
326 if (advanced_mode == 1) {
327 o = s.option(form.Value, 'board_name', 'Board Name / Profile');
328 o = s.option(form.DynamicList, 'packages', 'Packages');
333 .then(function(form_rendered) {
334 ui.showModal(_('New upgrade available'), [
341 'click': ui.hideModal
345 'class': 'btn cbi-button-action',
346 'click': function() {
347 m.save().then(foo => {
349 server_url, mapdata.request
353 }, _('Request Sysupgrade'))
358 ui.showModal(_('No upgrade available'), [
359 E('p', {}, _(`The device runs the latest firmware version ${version} - ${revision}`)),
365 'click': ui.hideModal
372 function displayStatus(type, content) {
374 var message = ui.showModal('', '');
376 message.classList.add('alert-message');
377 DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));
380 dom.content(message, content);
389 L.resolveDefault(callPackagelist(), {}),
390 L.resolveDefault(callSystemBoard(), {}),
391 uci.load('attendedsysupgrade')
394 render: function(res) {
395 var packages = res[0].packages;
396 var system_board = res[1];
397 var auto_search = uci.get_first('attendedsysupgrade', 'client', 'auto_search') || 1;
398 var server_url = uci.get_first('attendedsysupgrade', 'server', 'url');
401 E('h2', _("Attended Sysupgrade")),
402 E('p', _('The attended sysupgrade service allows to easily upgrade vanilla and custom firmware images.')),
403 E('p', _('This is done by building a new firmware on demand via an online service.'))
406 if (auto_search == 1) {
407 check_sysupgrade(server_url, system_board, packages)
411 'class': 'btn cbi-button-positive',
412 'click': function() {
413 check_sysupgrade(server_url, system_board, packages)
415 }, _('Search for sysupgrade')));