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