b148e793b276ec1253930dd995ba78f21f4ea73a
[openwrt-luci.git] /
1 'use strict';
2 'require view';
3 'require form';
4 'require uci';
5 'require rpc';
6 'require ui';
7 'require poll';
8 'require request';
9 'require dom';
10
11 var callPackagelist = rpc.declare({
12         object: 'rpc-sys',
13         method: 'packagelist',
14 });
15
16 var callSystemBoard = rpc.declare({
17         object: 'system',
18         method: 'board'
19 });
20
21 var callUpgradeStart = rpc.declare({
22         object: 'rpc-sys',
23         method: 'upgrade_start',
24         params: ["keep"]
25 });
26
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
32         // 19.07.8 -> 19.07
33         return version.replace("-SNAPSHOT", "").split(".").slice(0, 2).join(".");
34 }
35
36 function get_revision_count(revision) {
37         return parseInt(revision.substring(1).split("-")[0])
38 }
39
40 function error_api_connect(response) {
41         console.log(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),
45                 E('div', {
46                         'class': 'right'
47                 }, [
48                         E('div', {
49                                 'class': 'btn',
50                                 'click': ui.hideModal
51                         }, _('Close'))
52                 ])
53         ]);
54 }
55
56 function install_sysupgrade(url, keep, sha256) {
57         displayStatus("notice spinning", E('p', _('Downloading firmware from server to browser')));
58         request.get(url, {
59                         headers: {
60                                 'Content-Type': 'application/x-www-form-urlencoded'
61                         },
62                         responseType: 'blob'
63                 })
64                 .then(response => {
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());
70
71                         displayStatus("notice spinning", E('p', _('Uploading firmware from browser to device')));
72                         request.get(`${L.env.cgi_base}/cgi-upload`, {
73                                         method: 'PUT',
74                                         content: form_data
75                                 })
76                                 .then(response => response.json())
77                                 .then(response => {
78                                         if (response.sha256sum != sha256) {
79                                                 displayStatus("warning", [
80                                                         E('b', _('Wrong checksum')),
81                                                         E('p', _('Error during download of firmware. Please try again')),
82                                                         E('div', {
83                                                                 'class': 'btn',
84                                                                 'click': ui.hideModal
85                                                         }, _('Close'))
86                                                 ]);
87                                         } else {
88                                                 displayStatus('warning spinning', E('p', _('Installing the sysupgrade. Do not unpower device!')));
89                                                 L.resolveDefault(callUpgradeStart(keep), {}).then(response => {
90                                                         if (keep) {
91                                                                 ui.awaitReconnect(window.location.host);
92                                                         } else {
93                                                                 ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
94                                                         }
95                                                 });
96                                         }
97                                 });
98                 });
99 }
100
101 function request_sysupgrade(server_url, data) {
102         var res, req;
103
104         if (data.request_hash) {
105                 req = request.get(`${server_url}/api/v1/build/${data.request_hash}`)
106         } else {
107                 req = request.post(`${server_url}/api/v1/build`, {
108                         profile: data.board_name,
109                         target: data.target,
110                         version: data.version,
111                         packages: data.packages,
112                         diff_packages: true,
113                 })
114         }
115
116         req.then(response => {
117                 switch (response.status) {
118                         case 200:
119                                 res = response.json()
120                                 var image;
121                                 for (image of res.images) {
122                                         if (image.type == "sysupgrade") {
123                                                 break;
124                                         }
125                                 }
126                                 if (image.name != undefined) {
127                                         var sysupgrade_url = `${server_url}/store/${res.bin_dir}/${image.name}`;
128
129                                         var keep = E('input', {
130                                                 type: 'checkbox'
131                                         })
132                                         keep.checked = true;
133
134                                         var fields = [
135                                                 _('Version'), `${res.version_number} ${res.version_code}`,
136                                                 _('File'), E('a', {
137                                                         'href': sysupgrade_url
138                                                 }, image.name),
139                                                 _('SHA256'), image.sha256,
140                                                 _('Build Date'), res.build_at,
141                                                 _('Target'), res.target,
142                                         ];
143
144                                         var table = E('div', {
145                                                 'class': 'table'
146                                         });
147
148                                         for (var i = 0; i < fields.length; i += 2) {
149                                                 table.appendChild(E('div', {
150                                                         'class': 'tr'
151                                                 }, [
152                                                         E('div', {
153                                                                 'class': 'td left',
154                                                                 'width': '33%'
155                                                         }, [fields[i]]),
156                                                         E('div', {
157                                                                 'class': 'td left'
158                                                         }, [(fields[i + 1] != null) ? fields[i + 1] : '?'])
159                                                 ]));
160                                         }
161
162                                         var modal_body = [
163                                                 table,
164                                                 E('p', {}, E('label', {
165                                                         'class': 'btn'
166                                                 }, [
167                                                         keep, ' ', _('Keep settings and retain the current configuration')
168                                                 ])),
169                                                 E('div', {
170                                                         'class': 'right'
171                                                 }, [
172                                                         E('div', {
173                                                                 'class': 'btn',
174                                                                 'click': ui.hideModal
175                                                         }, _('Cancel')),
176                                                         ' ',
177                                                         E('div', {
178                                                                 'class': 'btn cbi-button-action',
179                                                                 'click': function() {
180                                                                         install_sysupgrade(sysupgrade_url, keep.checked, image.sha256)
181                                                                 }
182                                                         }, _('Install Sysupgrade'))
183                                                 ])
184                                         ]
185
186                                         ui.showModal(_('Successfully created sysupgrade image'), modal_body);
187                                 }
188
189                                 break;
190                         case 202:
191                                 res = response.json()
192                                 data.request_hash = res.request_hash;
193
194                                 if ("queue_position" in res)
195                                         displayStatus("notice spinning", E('p', _('Request in build queue position %d'.format(res.queue_position))));
196                                 else
197                                         displayStatus("notice spinning", E('p', _('Building firmware sysupgrade image')));
198
199                                 setTimeout(function() {
200                                         request_sysupgrade(server_url, data);
201                                 }, 5000);
202                                 break;
203                         case 400: // bad request
204                         case 422: // bad package
205                         case 500: // build failed
206                                 res = response.json()
207                                 var body = [
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)),
212
213                                 ]
214
215                                 if (res.stdout) {
216                                         body.push(E('b', {}, "STDOUT:"))
217                                         body.push(E('pre', {}, res.stdout))
218
219                                 }
220
221                                 if (res.stderr) {
222                                         body.push(E('b', {}, "STDERR:"))
223                                         body.push(E('pre', {}, res.stderr))
224
225                                 }
226
227                                 body = body.concat([
228                                         E('div', {
229                                                 'class': 'right'
230                                         }, [
231                                                 E('div', {
232                                                         'class': 'btn',
233                                                         'click': ui.hideModal
234                                                 }, _('Close'))
235                                         ])
236                                 ]);
237                                 ui.showModal(_('Error building the sysupgrade'), body);
238                                 break;
239                 }
240         });
241 }
242
243 async function check_sysupgrade(server_url, system_board, packages) {
244         var {
245                 board_name
246         } = system_board;
247         var {
248                 target,
249                 version,
250                 revision
251         } = system_board.release;
252         var current_branch = get_branch(version);
253         var advanced_mode = uci.get_first('attendedsysupgrade', 'client', 'advanced_mode') || 0;
254         var candidates = [];
255         var response;
256
257         displayStatus("notice spinning", E('p', _(`Searching for an available sysupgrade of ${version} - ${revision}`)));
258
259         if (version.endsWith("SNAPSHOT")) {
260                 response = await request.get(`${server_url}/api/v1/revision/${version}/${target}`)
261                 if (!response.ok) {
262                         error_api_connect(response);
263                         return;
264                 }
265
266                 const remote_revision = response.json().revision;
267
268                 if (get_revision_count(revision) < get_revision_count(remote_revision)) {
269                         candidates.push(version);
270                 }
271         } else {
272                 response = await request.get(`${server_url}/api/overview`, {
273                         timeout: 8000
274                 });
275
276                 if (!response.ok) {
277                         error_api_connect(response);
278                         return;
279                 }
280
281                 const latest = response.json().latest
282
283                 for (let remote_version of latest) {
284                         var remote_branch = get_branch(remote_version);
285
286                         // already latest version installed
287                         if (version == remote_version) {
288                                 break;
289                         }
290
291                         // skip branch upgrades outside the advanced mode
292                         if (current_branch != remote_branch && advanced_mode == 0) {
293                                 continue;
294                         }
295
296                         candidates.unshift(remote_version);
297
298                         // don't offer branches older than the current
299                         if (current_branch == remote_branch) {
300                                 break;
301                         }
302                 }
303         }
304
305         if (candidates.length) {
306                 var m, s, o;
307
308                 var mapdata = {
309                         request: {
310                                 board_name: board_name,
311                                 target: target,
312                                 version: candidates[0],
313                                 packages: Object.keys(packages).sort(),
314                         }
315                 }
316
317                 m = new form.JSONMap(mapdata, '');
318
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);
324                 }
325
326                 if (advanced_mode == 1) {
327                         o = s.option(form.Value, 'board_name', 'Board Name / Profile');
328                         o = s.option(form.DynamicList, 'packages', 'Packages');
329                 }
330
331
332                 m.render()
333                         .then(function(form_rendered) {
334                                 ui.showModal(_('New upgrade available'), [
335                                         form_rendered,
336                                         E('div', {
337                                                 'class': 'right'
338                                         }, [
339                                                 E('div', {
340                                                         'class': 'btn',
341                                                         'click': ui.hideModal
342                                                 }, _('Cancel')),
343                                                 ' ',
344                                                 E('div', {
345                                                         'class': 'btn cbi-button-action',
346                                                         'click': function() {
347                                                                 m.save().then(foo => {
348                                                                         request_sysupgrade(
349                                                                                 server_url, mapdata.request
350                                                                         )
351                                                                 });
352                                                         }
353                                                 }, _('Request Sysupgrade'))
354                                         ])
355                                 ]);
356                         });
357         } else {
358                 ui.showModal(_('No upgrade available'), [
359                         E('p', {}, _(`The device runs the latest firmware version ${version} - ${revision}`)),
360                         E('div', {
361                                 'class': 'right'
362                         }, [
363                                 E('div', {
364                                         'class': 'btn',
365                                         'click': ui.hideModal
366                                 }, _('Close'))
367                         ])
368                 ]);
369         }
370 }
371
372 function displayStatus(type, content) {
373         if (type) {
374                 var message = ui.showModal('', '');
375
376                 message.classList.add('alert-message');
377                 DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));
378
379                 if (content)
380                         dom.content(message, content);
381         } else {
382                 ui.hideModal();
383         }
384 }
385
386 return view.extend({
387         load: function() {
388                 return Promise.all([
389                         L.resolveDefault(callPackagelist(), {}),
390                         L.resolveDefault(callSystemBoard(), {}),
391                         uci.load('attendedsysupgrade')
392                 ]);
393         },
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');
399
400                 var view = [
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.'))
404                 ];
405
406                 if (auto_search == 1) {
407                         check_sysupgrade(server_url, system_board, packages)
408                 }
409
410                 view.push(E('p', {
411                         'class': 'btn cbi-button-positive',
412                         'click': function() {
413                                 check_sysupgrade(server_url, system_board, packages)
414                         }
415                 }, _('Search for sysupgrade')));
416
417                 return view;
418         },
419
420 });
git clone https://git.99rst.org/PROJECT