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