3ee99684da11f3ee34632c7619c81e6092ed2a3e
[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                                 switch (res.status) {
174                                         case "queued":
175                                                 displayStatus("notice spinning", E('p', _('Request in build queue')));
176                                                 break;
177                                         case "started":
178                                                 displayStatus("notice spinning", E('p', _('Building the sysupgrade image')));
179                                                 break;
180                                 }
181                                 setTimeout(function() {
182                                         request_sysupgrade(server_url, data);
183                                 }, 5000);
184                                 break;
185                         case 400: // bad request
186                         case 422: // bad package
187                         case 500: // build failed
188                                 res = response.json()
189                                 var body = [
190                                         E('p', {}, _(res.message)),
191                                         E('p', {}, _("Please report the error message and request")),
192                                         E('b', {}, _("Request to server:")),
193                                         E('pre', {}, JSON.stringify(data, null, 4)),
194
195                                 ]
196
197                                 if (res.stdout) {
198                                         body.push(E('b', {}, "STDOUT:"))
199                                         body.push(E('pre', {}, res.stdout))
200
201                                 }
202
203                                 if (res.stderr) {
204                                         body.push(E('b', {}, "STDERR:"))
205                                         body.push(E('pre', {}, res.stderr))
206
207                                 }
208
209                                 body = body.concat([
210                                         E('div', {
211                                                 'class': 'right'
212                                         }, [
213                                                 E('div', {
214                                                         'class': 'btn',
215                                                         'click': ui.hideModal
216                                                 }, _('Close'))
217                                         ])
218                                 ]);
219                                 ui.showModal(_('Error building the sysupgrade'), body);
220                                 break;
221                 }
222         });
223 }
224
225 function check_sysupgrade(server_url, current_version, target, board_name, packages) {
226         displayStatus("notice spinning", E('p', _('Searching for an available sysupgrade')));
227         var current_branch = get_branch(current_version);
228         var advanced_mode = uci.get_first('attendedsysupgrade', 'client', 'advanced_mode') || 0;
229         var candidates = [];
230
231         fetch(server_url + "/api/latest")
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 }
325
326 function displayStatus(type, content) {
327         if (type) {
328                 var message = ui.showModal('', '');
329
330                 message.classList.add('alert-message');
331                 DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));
332
333                 if (content)
334                         dom.content(message, content);
335         } else {
336                 ui.hideModal();
337         }
338 }
339
340 return view.extend({
341         load: function() {
342                 return Promise.all([
343                         L.resolveDefault(callPackagelist(), {}),
344                         L.resolveDefault(callSystemBoard(), {}),
345                         uci.load('attendedsysupgrade')
346                 ]);
347         },
348         render: function(res) {
349                 var packages = res[0].packages;
350                 var current_version = res[1].release.version;
351                 var target = res[1].release.target;
352                 var board_name = res[1].board_name;
353                 var auto_search = uci.get_first('attendedsysupgrade', 'client', 'auto_search') || 1;
354                 var server_url = uci.get_first('attendedsysupgrade', 'server', 'url');
355
356                 var view = [
357                         E('h2', _("Attended Sysupgrade")),
358                         E('p', _('The attended sysupgrade service allows to easily upgrade vanilla and custom firmware images.')),
359                         E('p', _('This is done by building a new firmware on demand via an online service.'))
360                 ];
361
362                 if (auto_search == 1) {
363                         check_sysupgrade(server_url, current_version, target, board_name, packages)
364                 }
365
366                 view.push(E('p', {
367                         'class': 'btn cbi-button-positive',
368                         'click': function() {
369                                 check_sysupgrade(server_url, current_version, target, board_name, packages)
370                         }
371                 }, _('Search for sysupgrade')));
372
373                 return view;
374         },
375
376 });
git clone https://git.99rst.org/PROJECT