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) {
41 ui.showModal(_('Error connecting to upgrade server'), [
43 _('Could not reach API at "%s". Please try again later.')
44 .format(response.url)),
45 E('pre', {}, response.responseText),
59 function install_sysupgrade(url, keep, sha256) {
60 displayStatus('notice spinning',
61 E('p', _('Downloading firmware from server to browser')));
65 'Content-Type': 'application/x-www-form-urlencoded',
70 var form_data = new FormData();
71 form_data.append('sessionid', rpc.getSessionID());
72 form_data.append('filename', '/tmp/firmware.bin');
73 form_data.append('filemode', 600);
74 form_data.append('filedata', response.blob());
76 displayStatus('notice spinning',
77 E('p', _('Uploading firmware from browser to device')));
79 .get(`${L.env.cgi_base}/cgi-upload`, {
83 .then((response) => response.json())
85 if (response.sha256sum != sha256) {
86 displayStatus('warning', [
87 E('b', _('Wrong checksum')),
89 _('Error during download of firmware. Please try again')),
100 _('Installing the sysupgrade. Do not unpower device!')));
101 L.resolveDefault(callUpgradeStart(keep), {})
102 .then((response) => {
104 ui.awaitReconnect(window.location.host);
106 ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
114 function request_sysupgrade(server_url, data) {
117 if (data.request_hash) {
118 req = request.get(`${server_url}/api/v1/build/${data.request_hash}`);
120 req = request.post(`${server_url}/api/v1/build`, {
121 profile: data.board_name,
123 version: data.version,
124 packages: data.packages,
129 req.then((response) => {
130 switch (response.status) {
132 res = response.json();
134 for (image of res.images) {
135 if (image.type == 'sysupgrade') {
139 if (image.name != undefined) {
140 var sysupgrade_url = `${server_url}/store/${res.bin_dir}/${image.name}`;
142 var keep = E('input', {
149 `${res.version_number} ${res.version_code}`,
152 href: sysupgrade_url,
163 var table = E('div', {
167 for (var i = 0; i < fields.length; i += 2) {
168 table.appendChild(E('tr', {
186 E('p', {class: 'mt-2'},
192 _('Keep settings and retain the current configuration')
205 class: 'btn cbi-button-action',
207 install_sysupgrade(sysupgrade_url, keep.checked,
211 _('Install Sysupgrade')),
215 ui.showModal(_('Successfully created sysupgrade image'), modal_body);
220 res = response.json();
221 data.request_hash = res.request_hash;
223 if ('queue_position' in res)
224 displayStatus('notice spinning',
225 E('p', _('Request in build queue position %s')
226 .format(res.queue_position)));
228 displayStatus('notice spinning',
229 E('p', _('Building firmware sysupgrade image')));
231 setTimeout(function() { request_sysupgrade(server_url, data); }, 5000);
233 case 400: // bad request
234 case 422: // bad package
235 case 500: // build failed
236 res = response.json();
238 E('p', {}, res.detail),
239 E('p', {}, _('Please report the error message and request')),
240 E('b', {}, _('Request to server:')),
241 E('pre', {}, JSON.stringify(data, null, 4)),
245 body.push(E('b', {}, 'STDOUT:'));
246 body.push(E('pre', {}, res.stdout));
250 body.push(E('b', {}, 'STDERR:'));
251 body.push(E('pre', {}, res.stderr));
266 ui.showModal(_('Error building the sysupgrade'), body);
272 async function check_sysupgrade(server_url, system_board, packages) {
273 var {board_name} = system_board;
274 var {target, version, revision} = system_board.release;
275 var current_branch = get_branch(version);
277 uci.get_first('attendedsysupgrade', 'client', 'advanced_mode') || 0;
281 displayStatus('notice spinning',
282 E('p', _('Searching for an available sysupgrade of %s - %s')
283 .format(version, revision)));
285 if (version.endsWith('SNAPSHOT')) {
287 await request.get(`${server_url}/api/v1/revision/${version}/${target}`);
289 error_api_connect(response);
293 const remote_revision = response.json().revision;
295 if (get_revision_count(revision) < get_revision_count(remote_revision)) {
296 candidates.push(version);
299 response = await request.get(`${server_url}/api/overview`, {
304 error_api_connect(response);
308 const latest = response.json().latest;
310 for (let remote_version of latest) {
311 var remote_branch = get_branch(remote_version);
313 // already latest version installed
314 if (version == remote_version) {
318 // skip branch upgrades outside the advanced mode
319 if (current_branch != remote_branch && advanced_mode == 0) {
323 candidates.unshift(remote_version);
325 // don't offer branches older than the current
326 if (current_branch == remote_branch) {
332 if (candidates.length) {
337 board_name: board_name,
339 version: candidates[0],
340 packages: Object.keys(packages).sort(),
344 m = new form.JSONMap(mapdata, '');
346 s = m.section(form.NamedSection, 'request', 'example', '',
347 'Use defaults for the safest update');
348 o = s.option(form.ListValue, 'version', 'Select firmware version');
349 for (let candidate of candidates) {
350 o.value(candidate, candidate);
353 if (advanced_mode == 1) {
354 o = s.option(form.Value, 'board_name', 'Board Name / Profile');
355 o = s.option(form.DynamicList, 'packages', 'Packages');
358 m.render().then(function(form_rendered) {
359 ui.showModal(_('New upgrade available'), [
372 class: 'btn cbi-button-action',
374 m.save().then((foo) => {
375 request_sysupgrade(server_url, mapdata.request);
379 _('Request Sysupgrade')),
384 ui.showModal(_('No upgrade available'), [
386 _('The device runs the latest firmware version %s - %s')
387 .format(version, revision)),
402 function displayStatus(type, content) {
404 var message = ui.showModal('', '');
406 message.classList.add('alert-message');
407 DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));
410 dom.content(message, content);
419 L.resolveDefault(callPackagelist(), {}),
420 L.resolveDefault(callSystemBoard(), {}),
421 uci.load('attendedsysupgrade'),
424 render: function(res) {
425 var packages = res[0].packages;
426 var system_board = res[1];
428 uci.get_first('attendedsysupgrade', 'client', 'auto_search') || 1;
429 var server_url = uci.get_first('attendedsysupgrade', 'server', 'url');
432 E('h2', _('Attended Sysupgrade')),
434 _('The attended sysupgrade service allows to easily upgrade vanilla and custom firmware images.')),
436 _('This is done by building a new firmware on demand via an online service.')),
439 if (auto_search == 1) {
440 check_sysupgrade(server_url, system_board, packages);
444 class: 'btn cbi-button-positive',
446 function() { check_sysupgrade(server_url, system_board, packages); },
448 _('Search for sysupgrade')));