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')));