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