luci-app-attendedsysupgrade: LuCIfy codebase
[project/luci.git] / applications / luci-app-attendedsysupgrade / htdocs / luci-static / resources / view / attendedsysupgrade / overview.js
index b7cbdc2231fbfad83991160a598713105e6a9bf8..8c1a5a84b181240a2a80c67378c5d83f91eee364 100644 (file)
@@ -7,6 +7,7 @@
 'require poll';
 'require request';
 'require dom';
+'require fs';
 
 var callPackagelist = rpc.declare({
        object: 'rpc-sys',
@@ -21,432 +22,422 @@ var callSystemBoard = rpc.declare({
 var callUpgradeStart = rpc.declare({
        object: 'rpc-sys',
        method: 'upgrade_start',
-       params: [ 'keep' ],
+       params: ['keep'],
 });
 
+/**
+ * Returns the branch of a given version. This helps to offer upgrades
+ * for point releases (aka within the branch).
+ * 
+ * Logic:
+ * SNAPSHOT -> SNAPSHOT
+ * 21.02-SNAPSHOT -> 21.02
+ * 21.02.0-rc1 -> 21.02
+ * 19.07.8 -> 19.07
+ * 
+ * @param {string} version 
+ * Input version from which to determine the branch
+ * @returns {string}
+ * The determined branch
+ */
 function get_branch(version) {
-       // determine branch of a version
-       // SNAPSHOT -> SNAPSHOT
-       // 21.02-SNAPSHOT -> 21.02
-       // 21.02.0-rc1 -> 21.02
-       // 19.07.8 -> 19.07
        return version.replace('-SNAPSHOT', '').split('.').slice(0, 2).join('.');
 }
 
+/**
+ * The OpenWrt revision string contains both a hash as well as the number 
+ * commits since the OpenWrt/LEDE reboot. It helps to determine if a 
+ * snapshot is newer than another.
+ * 
+ * @param {string} revision
+ * Revision string of a OpenWrt device
+ * @returns {integer}
+ * The number of commits since OpenWrt/LEDE reboot
+ */
 function get_revision_count(revision) {
        return parseInt(revision.substring(1).split('-')[0]);
 }
 
-function error_api_connect(response) {
-       ui.showModal(_('Error connecting to upgrade server'), [
-               E('p', {},
-                       _('Could not reach API at "%s". Please try again later.')
-               .format(response.url)),
-               E('pre', {}, response.responseText),
-               E('div', {
-                       class: 'right',
-               },
-                       [
-       E('div', {
-               class: 'btn',
-               click: ui.hideModal,
+return view.extend({
+       steps: {
+               init: _('10% Received build request'),
+               download_imagebuilder: _('20% Downloading ImageBuilder archive'),
+               unpack_imagebuilder: _('40% Setup ImageBuilder'),
+               calculate_packages_hash: _('60% Validate package selection'),
+               building_image: _('80% Generating firmware image')
        },
-               _('Close')),
-                       ]),
-       ]);
-}
 
-function install_sysupgrade(url, keep, sha256) {
-       displayStatus('notice spinning',
-               E('p', _('Downloading firmware from server to browser')));
-       request
-                       .get(url, {
-       headers: {
-               'Content-Type': 'application/x-www-form-urlencoded',
+       data: {
+               url: '',
+               revision: '',
+               advanced_mode: 0,
        },
-       responseType: 'blob',
-                       })
-                       .then((response) => {
-       var form_data = new FormData();
-       form_data.append('sessionid', rpc.getSessionID());
-       form_data.append('filename', '/tmp/firmware.bin');
-       form_data.append('filemode', 600);
-       form_data.append('filedata', response.blob());
-
-       displayStatus('notice spinning',
-                                       E('p', _('Uploading firmware from browser to device')));
-       request
-                       .get(`${L.env.cgi_base}/cgi-upload`, {
-                               method: 'PUT',
-                               content: form_data,
-                       })
-                       .then((response) => response.json())
-                       .then((response) => {
-                               if (response.sha256sum != sha256) {
-               displayStatus('warning', [
-                       E('b', _('Wrong checksum')),
-                       E('p',
-                               _('Error during download of firmware. Please try again')),
-                       E('div', {
-                               class: 'btn',
-                               click: ui.hideModal,
-                       },
-                               _('Close')),
-               ]);
+
+       firmware: {
+               profile: '',
+               target: '',
+               version: '',
+               packages: [],
+               diff_packages: true,
+       },
+
+       handle200: function (response) {
+               res = response.json();
+               var image;
+               for (image of res.images) {
+                       if (this.data.rootfs_type == image.filesystem) {
+                               if (this.data.efi) {
+                                       if (image.type == 'combined-efi') {
+                                               break;
+                                       }
                                } else {
-               displayStatus(
-                               'warning spinning',
-                               E('p',
-                                       _('Installing the sysupgrade. Do not unpower device!')));
-               L.resolveDefault(callUpgradeStart(keep), {})
-                               .then((response) => {
-                                       if (keep) {
-                       ui.awaitReconnect(window.location.host);
-                                       } else {
-                       ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
+                                       if (image.type == 'sysupgrade' || image.type == 'combined') {
+                                               break;
                                        }
-                               });
                                }
-                       });
-                       });
-}
-
-function request_sysupgrade(server_url, data) {
-       var res, req;
-
-       if (data.request_hash) {
-               req = request.get(`${server_url}/api/v1/build/${data.request_hash}`);
-       } else {
-               req = request.post(`${server_url}/api/v1/build`, {
-                       profile: data.board_name,
-                       target: data.target,
-                       version: data.version,
-                       packages: data.packages,
-                       diff_packages: true,
-               });
-       }
-
-       req.then((response) => {
-               switch (response.status) {
-               case 200:
-                       res = response.json();
-                       var image;
-                       for (image of res.images) {
-       if (image.type == 'sysupgrade') {
-               break;
-       }
                        }
-                       if (image.name != undefined) {
-       var sysupgrade_url = `${server_url}/store/${res.bin_dir}/${image.name}`;
-
-       var keep = E('input', {
-               type: 'checkbox',
-       });
-       keep.checked = true;
-
-       var fields = [
-               _('Version'),
-               `${res.version_number} ${res.version_code}`,
-               _('File'),
-               E('a', {
-                       href: sysupgrade_url,
-               },
-                       image.name),
-               _('SHA256'),
-               image.sha256,
-               _('Build Date'),
-               res.build_at,
-               _('Target'),
-               res.target,
-       ];
-
-       var table = E('div', {
-               class: 'table',
-       });
-
-       for (var i = 0; i < fields.length; i += 2) {
-               table.appendChild(E('tr', {
-                       class: 'tr',
-               },
-                                               [
-                               E('td', {
-                                       class: 'td left',
-                                       width: '33%',
-                               },
-                                       [ fields[i] ]),
-                               E('td', {
-                                       class: 'td left',
-                               },
-                                       [ fields[i + 1] ]),
-                                               ]));
-       }
-
-       var modal_body = [
-               table,
-               E('p', {class: 'mt-2'},
-                       E('label', {
-                               class: 'btn',
-                       },
-                               [
-               keep, ' ',
-               _('Keep settings and retain the current configuration')
-                               ])),
-               E('div', {
-                       class: 'right',
-               },
-                       [
-                               E('div', {
-               class: 'btn',
-               click: ui.hideModal,
-                               },
-               _('Cancel')),
-                               ' ',
-                               E('div', {
-               class: 'btn cbi-button-action',
-               click: function() {
-                       install_sysupgrade(sysupgrade_url, keep.checked,
-                                                image.sha256);
-               },
-                               },
-               _('Install Sysupgrade')),
-                       ]),
-       ];
+               }
 
-       ui.showModal(_('Successfully created sysupgrade image'), modal_body);
-                       }
+               if (image.name != undefined) {
+                       var sysupgrade_url = `${this.data.url}/store/${res.bin_dir}/${image.name}`;
+
+                       var keep = E('input', { type: 'checkbox' });
+                       keep.checked = true;
 
-                       break;
-               case 202:
-                       res = response.json();
-                       data.request_hash = res.request_hash;
-
-                       if ('queue_position' in res)
-       displayStatus('notice spinning',
-                                       E('p', _('Request in build queue position %s')
-                                .format(res.queue_position)));
-                       else
-       displayStatus('notice spinning',
-                                       E('p', _('Building firmware sysupgrade image')));
-
-                       setTimeout(function() { request_sysupgrade(server_url, data); }, 5000);
-                       break;
-               case 400: // bad request
-               case 422: // bad package
-               case 500: // build failed
-                       res = response.json();
-                       var body = [
-       E('p', {}, res.detail),
-       E('p', {}, _('Please report the error message and request')),
-       E('b', {}, _('Request to server:')),
-       E('pre', {}, JSON.stringify(data, null, 4)),
+                       var fields = [
+                               _('Version'), `${res.version_number} ${res.version_code}`,
+                               _('SHA256'), image.sha256,
                        ];
 
-                       if (res.stdout) {
-       body.push(E('b', {}, 'STDOUT:'));
-       body.push(E('pre', {}, res.stdout));
+                       if (this.data.advanced_mode == 1) {
+                               fields.push(
+                                       _('Profile'), res.id,
+                                       _('Target'), res.target,
+                                       _('Build Date'), res.build_at,
+                                       _('Filename'), image.name,
+                                       _('Filesystem'), image.filesystem,
+                               )
                        }
 
-                       if (res.stderr) {
-       body.push(E('b', {}, 'STDERR:'));
-       body.push(E('pre', {}, res.stderr));
+                       fields.push('', E('a', { href: sysupgrade_url }, _('Download firmware image')))
+
+                       var table = E('div', { class: 'table' });
+
+                       for (var i = 0; i < fields.length; i += 2) {
+                               table.appendChild(E('tr', { class: 'tr' }, [
+                                       E('td', { class: 'td left', width: '33%' }, [fields[i]]),
+                                       E('td', { class: 'td left' }, [fields[i + 1]]),
+                               ]));
                        }
 
-                       body = body.concat([
-       E('div', {
-               class: 'right',
-       },
-               [
-                       E('div', {
-                               class: 'btn',
-                               click: ui.hideModal,
-                       },
-                               _('Close')),
-               ]),
-                       ]);
-                       ui.showModal(_('Error building the sysupgrade'), body);
-                       break;
+                       var modal_body = [
+                               table,
+                               E('p', { class: 'mt-2' },
+                                       E('label', { class: 'btn' }, [
+                                               keep, ' ',
+                                               _('Keep settings and retain the current configuration')
+                                       ])),
+                               E('div', { class: 'right' }, [
+                                       E('div', { class: 'btn', click: ui.hideModal }, _('Cancel')), ' ',
+                                       E('button', {
+                                               'class': 'btn cbi-button cbi-button-positive important',
+                                               'click': ui.createHandlerFn(this, function () {
+                                                       this.handleInstall(sysupgrade_url, keep.checked, image.sha256)
+                                               })
+                                       }, _('Install firmware image')),
+                               ]),
+                       ];
+
+                       ui.showModal(_('Successfully created firmware image'), modal_body);
                }
-       });
-}
+       },
+
+       handle202: function (response) {
+               response = response.json();
+               this.data.request_hash = res.request_hash;
 
-async function check_sysupgrade(server_url, system_board, packages) {
-       var {board_name} = system_board;
-       var {target, version, revision} = system_board.release;
-       var current_branch = get_branch(version);
-       var advanced_mode =
-                       uci.get_first('attendedsysupgrade', 'client', 'advanced_mode') || 0;
-       var candidates = [];
-       var response;
-
-       displayStatus('notice spinning',
-               E('p', _('Searching for an available sysupgrade of %s - %s')
-                                .format(version, revision)));
-
-       if (version.endsWith('SNAPSHOT')) {
-               response =
-       await request.get(`${server_url}/api/v1/revision/${version}/${target}`);
-               if (!response.ok) {
-                       error_api_connect(response);
-                       return;
+               if ('queue_position' in response) {
+                       ui.showModal(_('Queued...'), [
+                               E('p', { 'class': 'spinning' }, _('Request in build queue position %s').format(response.queue_position))
+                       ]);
+               } else {
+                       ui.showModal(_('Building Firmware...'), [
+                               E('p', { 'class': 'spinning' }, _('Progress: %s').format(this.steps[response.imagebuilder_status]))
+                       ]);
                }
+       },
 
-               const remote_revision = response.json().revision;
+       handleError: function (response) {
+               response = response.json();
+               var body = [
+                       E('p', {}, _('Server response: %s').format(response.detail)),
+                       E('a', { href: 'https://github.com/openwrt/asu/issues' }, _('Please report the error message and request')),
+                       E('p', {}, _('Request Data:')),
+                       E('pre', {}, JSON.stringify({ ...this.data, ...this.firmware }, null, 4)),
+               ];
 
-               if (get_revision_count(revision) < get_revision_count(remote_revision)) {
-                       candidates.push(version);
+               if (response.stdout) {
+                       body.push(E('b', {}, 'STDOUT:'));
+                       body.push(E('pre', {}, response.stdout));
                }
-       } else {
-               response = await request.get(`${server_url}/api/overview`, {
-                       timeout: 8000,
-               });
-
-               if (!response.ok) {
-                       error_api_connect(response);
-                       return;
+
+               if (response.stderr) {
+                       body.push(E('b', {}, 'STDERR:'));
+                       body.push(E('pre', {}, response.stderr));
                }
 
-               const latest = response.json().latest;
+               body = body.concat([
+                       E('div', { class: 'right' }, [
+                               E('div', { class: 'btn', click: ui.hideModal }, _('Close')),
+                       ]),
+               ]);
 
-               for (let remote_version of latest) {
-                       var remote_branch = get_branch(remote_version);
+               ui.showModal(_('Error building the firmware image'), body);
+       },
 
-                       // already latest version installed
-                       if (version == remote_version) {
-       break;
-                       }
+       handleRequest: function () {
+               var request_url = `${this.data.url}/api/v1/build`;
+               var method = "POST"
+               var content = this.firmware;
+
+               /**
+                * If `request_hash` is available use a GET request instead of 
+                * sending the entire object.
+                */
+               if (this.data.request_hash) {
+                       request_url += `/${this.data.request_hash}`;
+                       content = {};
+                       method = "GET"
+               }
 
-                       // skip branch upgrades outside the advanced mode
-                       if (current_branch != remote_branch && advanced_mode == 0) {
-       continue;
-                       }
+               request.request(request_url, { method: method, content: content })
+                       .then((response) => {
+                               switch (response.status) {
+                                       case 202:
+                                               this.handle202(response);
+                                               break;
+                                       case 200:
+                                               poll.stop();
+                                               this.handle200(response);
+                                               break;
+                                       case 400: // bad request
+                                       case 422: // bad package
+                                       case 500: // build failed
+                                               poll.stop();
+                                               this.handleError(response);
+                                               break;
+                               }
+                       });
+       },
 
-                       candidates.unshift(remote_version);
+       handleInstall: function (url, keep, sha256) {
+               ui.showModal(_('Downloading...'), [
+                       E('p', { 'class': 'spinning' }, _('Downloading firmware from server to browser'))
+               ]);
 
-                       // don't offer branches older than the current
-                       if (current_branch == remote_branch) {
-       break;
-                       }
+               request.get(url, {
+                       headers: {
+                               'Content-Type': 'application/x-www-form-urlencoded',
+                       },
+                       responseType: 'blob',
+               })
+                       .then((response) => {
+                               var form_data = new FormData();
+                               form_data.append('sessionid', rpc.getSessionID());
+                               form_data.append('filename', '/tmp/firmware.bin');
+                               form_data.append('filemode', 600);
+                               form_data.append('filedata', response.blob());
+
+                               ui.showModal(_('Uploading...'), [
+                                       E('p', { 'class': 'spinning' }, _('Uploading firmware from browser to device'))
+                               ]);
+
+                               request
+                                       .get(`${L.env.cgi_base}/cgi-upload`, {
+                                               method: 'PUT',
+                                               content: form_data,
+                                       })
+                                       .then((response) => response.json())
+                                       .then((response) => {
+                                               if (response.sha256sum != sha256) {
+
+                                                       ui.showModal(_('Wrong checksum'), [
+                                                               E('p', _('Error during download of firmware. Please try again')),
+                                                               E('div', { class: 'btn', click: ui.hideModal }, _('Close'))
+                                                       ]);
+                                               } else {
+                                                       ui.showModal(_('Installing...'), [
+                                                               E('p', { class: 'spinning' }, _('Installing the sysupgrade. Do not unpower device!'))
+                                                       ]);
+
+                                                       L.resolveDefault(callUpgradeStart(keep), {})
+                                                               .then((response) => {
+                                                                       if (keep) {
+                                                                               ui.awaitReconnect(window.location.host);
+                                                                       } else {
+                                                                               ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
+                                                                       }
+                                                               });
+                                               }
+                                       });
+                       });
+       },
+
+       handleCheck: function () {
+               var { url, revision } = this.data
+               var { version, target } = this.firmware
+               var candidates = [];
+               var response;
+               var request_url = `${url}/api/overview`;
+               if (version.endsWith('SNAPSHOT')) {
+                       request_url = `${url}/api/v1/revision/${version}/${target}`;
                }
-       }
 
-       if (candidates.length) {
-               var m, s, o;
+               ui.showModal(_('Searching...'), [
+                       E('p', { 'class': 'spinning' },
+                               _('Searching for an available sysupgrade of %s - %s').format(version, revision))
+               ]);
 
-               var mapdata = {
-                       request: {
-       board_name: board_name,
-       target: target,
-       version: candidates[0],
-       packages: Object.keys(packages).sort(),
-                       },
-               };
+               L.resolveDefault(request.get(request_url))
+                       .then(response => {
+                               if (!response.ok) {
+                                       ui.showModal(_('Error connecting to upgrade server'), [
+                                               E('p', {}, _('Could not reach API at "%s". Please try again later.').format(response.url)),
+                                               E('pre', {}, response.responseText),
+                                               E('div', { class: 'right' }, [
+                                                       E('div', { class: 'btn', click: ui.hideModal }, _('Close'))
+                                               ]),
+                                       ]);
+                                       return;
+                               }
+                               if (version.endsWith('SNAPSHOT')) {
+                                       const remote_revision = response.json().revision;
+                                       if (get_revision_count(revision) < get_revision_count(remote_revision)) {
+                                               candidates.push([version, remote_revision]);
+                                       }
+                               } else {
+                                       const latest = response.json().latest;
 
-               m = new form.JSONMap(mapdata, '');
+                                       for (let remote_version of latest) {
+                                               var remote_branch = get_branch(remote_version);
 
-               s = m.section(form.NamedSection, 'request', 'example', '',
-                       'Use defaults for the safest update');
-               o = s.option(form.ListValue, 'version', 'Select firmware version');
-               for (let candidate of candidates) {
-                       o.value(candidate, candidate);
-               }
+                                               // already latest version installed
+                                               if (version == remote_version) {
+                                                       break;
+                                               }
 
-               if (advanced_mode == 1) {
-                       o = s.option(form.Value, 'board_name', 'Board Name / Profile');
-                       o = s.option(form.DynamicList, 'packages', 'Packages');
-               }
+                                               // skip branch upgrades outside the advanced mode
+                                               if (this.data.branch != remote_branch && this.data.advanced_mode == 0) {
+                                                       continue;
+                                               }
 
-               m.render().then(function(form_rendered) {
-                       ui.showModal(_('New upgrade available'), [
-       form_rendered,
-       E('div', {
-               class: 'right',
-       },
-               [
-                       E('div', {
-                               class: 'btn',
-                               click: ui.hideModal,
-                       },
-                               _('Cancel')),
-                       ' ',
-                       E('div', {
-                               class: 'btn cbi-button-action',
-                               click: function() {
-               m.save().then((foo) => {
-                       request_sysupgrade(server_url, mapdata.request);
-               });
-                               },
-                       },
-                               _('Request Sysupgrade')),
-               ]),
-                       ]);
-               });
-       } else {
-               ui.showModal(_('No upgrade available'), [
-                       E('p', {},
-       _('The device runs the latest firmware version %s - %s')
-                       .format(version, revision)),
-                       E('div', {
-       class: 'right',
-                       },
-       [
-               E('div', {
-                       class: 'btn',
-                       click: ui.hideModal,
-               },
-                       _('Close')),
-       ]),
-               ]);
-       }
-}
+                                               candidates.unshift([remote_version, null]);
 
-function displayStatus(type, content) {
-       if (type) {
-               var message = ui.showModal('', '');
+                                               // don't offer branches older than the current
+                                               if (this.data.branch == remote_branch) {
+                                                       break;
+                                               }
+                                       }
+                               }
 
-               message.classList.add('alert-message');
-               DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));
+                               if (candidates.length) {
+                                       var m, s, o;
 
-               if (content)
-                       dom.content(message, content);
-       } else {
-               ui.hideModal();
-       }
-}
+                                       var mapdata = {
+                                               request: {
+                                                       profile: this.firmware.profile,
+                                                       version: candidates[0][0],
+                                                       packages: Object.keys(this.firmware.packages).sort(),
+                                               },
+                                       };
 
-return view.extend({
-       load: function() {
+                                       var map = new form.JSONMap(mapdata, '');
+
+                                       s = map.section(form.NamedSection, 'request', '', '', 'Use defaults for the safest update');
+                                       o = s.option(form.ListValue, 'version', 'Select firmware version');
+                                       for (let candidate of candidates) {
+                                               o.value(candidate[0], candidate[1] ? `${candidate[0]} - ${candidate[1]}` : candidate[0]);
+                                       }
+
+                                       if (this.data.advanced_mode == 1) {
+                                               o = s.option(form.Value, 'profile', _('Board Name / Profile'));
+                                               o = s.option(form.DynamicList, 'packages', _('Packages'));
+                                       }
+
+                                       L.resolveDefault(map.render()).
+                                               then(form_rendered => {
+                                                       ui.showModal(_('New firmware upgrade available'), [
+                                                               E('p', _('Currently running: %s - %s').format(this.firmware.version, this.data.revision)),
+                                                               form_rendered,
+                                                               E('div', { class: 'right' }, [
+                                                                       E('div', { class: 'btn', click: ui.hideModal }, _('Cancel')), ' ',
+                                                                       E('button', {
+                                                                               'class': 'btn cbi-button cbi-button-positive important',
+                                                                               'click': ui.createHandlerFn(this, function () {
+                                                                                       map.save().then(() => {
+                                                                                               this.firmware.packages = mapdata.request.packages;
+                                                                                               this.firmware.version = mapdata.request.version;
+                                                                                               this.firmware.profile = mapdata.request.profile;
+                                                                                               poll.add(L.bind(this.handleRequest, this), 5);
+                                                                                       });
+                                                                               })
+                                                                       }, _('Request firmware image')),
+                                                               ]),
+                                                       ]);
+                                               });
+                               } else {
+                                       ui.showModal(_('No upgrade available'), [
+                                               E('p', _('The device runs the latest firmware version %s - %s').format(version, revision)),
+                                               E('div', { class: 'right' }, [
+                                                       E('div', { class: 'btn', click: ui.hideModal }, _('Close')),
+                                               ]),
+                                       ]);
+                               }
+
+                       });
+       },
+
+       load: function () {
                return Promise.all([
                        L.resolveDefault(callPackagelist(), {}),
                        L.resolveDefault(callSystemBoard(), {}),
+                       fs.stat("/sys/firmware/efi"),
+                       fs.read("/proc/mounts"),
                        uci.load('attendedsysupgrade'),
                ]);
        },
-       render: function(res) {
-               var packages = res[0].packages;
-               var system_board = res[1];
-               var auto_search =
-       uci.get_first('attendedsysupgrade', 'client', 'auto_search') || 1;
-               var server_url = uci.get_first('attendedsysupgrade', 'server', 'url');
-
-               var view = [
-                       E('h2', _('Attended Sysupgrade')),
-                       E('p',
-       _('The attended sysupgrade service allows to easily upgrade vanilla and custom firmware images.')),
-                       E('p',
-       _('This is done by building a new firmware on demand via an online service.')),
-               ];
 
-               if (auto_search == 1) {
-                       check_sysupgrade(server_url, system_board, packages);
+       render: function (res) {
+               this.data.app_version = res[0].packages['luci-app-attendedsysupgrade'];
+               this.firmware.packages = res[0].packages;
+
+               this.firmware.profile = res[1].board_name;
+               this.firmware.target = res[1].release.target;
+               this.firmware.version = res[1].release.version;
+               this.data.branch = get_branch(res[1].release.version);
+               this.data.revision = res[1].release.revision;
+               this.data.efi = res[2];
+               if (res[1].rootfs_type) {
+                       this.data.rootfs_type = res[1].rootfs_type;
+               } else {
+                       this.data.rootfs_type = res[3].split(/\r?\n/)[0].split(' ')[2]
                }
 
-               view.push(E('p', {
-                       class: 'btn cbi-button-positive',
-                       click:
-               function() { check_sysupgrade(server_url, system_board, packages); },
-               },
-               _('Search for sysupgrade')));
+               this.data.url = uci.get_first('attendedsysupgrade', 'server', 'url');
+               this.data.advanced_mode = uci.get_first('attendedsysupgrade', 'client', 'advanced_mode') || 0
 
-               return view;
+               return E('p', [
+                       E('h2', _('Attended Sysupgrade')),
+                       E('p', _('The attended sysupgrade service allows to easily upgrade vanilla and custom firmware images.')),
+                       E('p', _('This is done by building a new firmware on demand via an online service.')),
+                       E('p', _('Currently running: %s - %s').format(this.firmware.version, this.data.revision)),
+                       E('button', {
+                               'class': 'btn cbi-button cbi-button-positive important',
+                               'click': ui.createHandlerFn(this, this.handleCheck)
+                       }, _('Search for firmware upgrade'))
+               ]);
        },
+       handleSaveApply: null,
+       handleSave: null,
+       handleReset: null
 });