|
| 1 | +--- a/feeds/luci/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/overview.js |
| 2 | ++++ b/feeds/luci/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/overview.js |
| 3 | +@@ -51,6 +51,12 @@ |
| 4 | + params: ['keep'], |
| 5 | + }); |
| 6 | + |
| 7 | ++const callFileExec = rpc.declare({ |
| 8 | ++ object: 'file', |
| 9 | ++ method: 'exec', |
| 10 | ++ params: ['command', 'params'], |
| 11 | ++}); |
| 12 | ++ |
| 13 | + /** |
| 14 | + * Returns the branch of a given version. This helps to offer upgrades |
| 15 | + * for point releases (aka within the branch). |
| 16 | +@@ -694,7 +700,7 @@ |
| 17 | + const data = { |
| 18 | + system_board: promises[1], |
| 19 | + advanced_mode: uci.get_first('attendedsysupgrade', 'client', 'advanced_mode') || 0, |
| 20 | +- url: uci.get_first('attendedsysupgrade', 'server', 'url').replace(/\/+$/, ''), |
| 21 | ++ url: (uci.get_first('attendedsysupgrade', 'server', 'url') || '').replace(/\/+$/, ''), |
| 22 | + branch: get_branch(promises[1].release.version), |
| 23 | + revision: promises[1].release.revision, |
| 24 | + efi: promises[2], |
| 25 | +@@ -745,13 +751,218 @@ |
| 26 | + E( |
| 27 | + 'button', |
| 28 | + { |
| 29 | +- class: 'btn cbi-button cbi-button-positive important', |
| 30 | ++ class: 'btn cbi-button cbi-button-action important', |
| 31 | + click: ui.createHandlerFn(this, this.handleCheck, data, firmware), |
| 32 | + }, |
| 33 | +- _('Search for firmware upgrade') |
| 34 | ++ _('Search for official firmware upgrade') |
| 35 | ++ ), |
| 36 | ++ ' ', |
| 37 | ++ E( |
| 38 | ++ 'button', |
| 39 | ++ { |
| 40 | ++ class: 'btn cbi-button cbi-button-positive important', |
| 41 | ++ click: ui.createHandlerFn(this, this.handleGithubFirmware, firmware), |
| 42 | ++ }, |
| 43 | ++ _('Check for GitHub firmware') |
| 44 | ++ ), |
| 45 | ++ ]); |
| 46 | ++ }, |
| 47 | ++ |
| 48 | ++ handleGithubFirmware: function(firmware) { |
| 49 | ++ ui.showModal(_('Fetching GitHub firmware info...'), [ |
| 50 | ++ E( |
| 51 | ++ 'p', |
| 52 | ++ { class: 'spinning' }, |
| 53 | ++ _('Fetching available firmware releases from GitHub') |
| 54 | ++ ), |
| 55 | ++ ]); |
| 56 | ++ |
| 57 | ++ // Call the CGI script to get the list of available firmware releases |
| 58 | ++ fetch('/cgi-bin/github_check') |
| 59 | ++ .then(response => response.json()) |
| 60 | ++ .then(data => { |
| 61 | ++ if (data.error) { |
| 62 | ++ ui.showModal(_('Error'), [ |
| 63 | ++ E('p', _('Error fetching firmware info: %s').format(data.error)), |
| 64 | ++ E('div', { class: 'right' }, [ |
| 65 | ++ E('div', { class: 'btn', click: ui.hideModal }, _('Close')), |
| 66 | ++ ]), |
| 67 | ++ ]); |
| 68 | ++ return; |
| 69 | ++ } |
| 70 | ++ |
| 71 | ++ // Check if data is an array (multiple releases) or single object |
| 72 | ++ let releases = Array.isArray(data) ? data : [data]; |
| 73 | ++ |
| 74 | ++ if (releases.length === 0) { |
| 75 | ++ ui.showModal(_('No releases found'), [ |
| 76 | ++ E('p', _('No firmware releases found on GitHub')), |
| 77 | ++ E('div', { class: 'right' }, [ |
| 78 | ++ E('div', { class: 'btn', click: ui.hideModal }, _('Close')), |
| 79 | ++ ]), |
| 80 | ++ ]); |
| 81 | ++ return; |
| 82 | ++ } |
| 83 | ++ |
| 84 | ++ // Create a dropdown to select the release |
| 85 | ++ let releaseSelect = E('select', { 'class': 'cbi-input-select' }); |
| 86 | ++ |
| 87 | ++ // Populate the dropdown with available releases |
| 88 | ++ releases.forEach((release, index) => { |
| 89 | ++ let optionText = release.tag ? `${release.tag} (${release.version})` : release.version; |
| 90 | ++ releaseSelect.appendChild(E('option', { 'value': index }, optionText)); |
| 91 | ++ }); |
| 92 | ++ |
| 93 | ++ let keep = E('input', { type: 'checkbox' }); |
| 94 | ++ keep.checked = true; |
| 95 | ++ |
| 96 | ++ // Create element for the URL |
| 97 | ++ const urlElement = E('td', { class: 'td left' }); |
| 98 | ++ |
| 99 | ++ // Function to update the URL based on selected release |
| 100 | ++ const updateDetails = function() { |
| 101 | ++ const selectedIndex = releaseSelect.value; |
| 102 | ++ const selectedRelease = releases[selectedIndex]; |
| 103 | ++ |
| 104 | ++ if (selectedRelease) { |
| 105 | ++ // Clear previous link and create a new one |
| 106 | ++ urlElement.innerHTML = ''; |
| 107 | ++ urlElement.appendChild(E('a', { href: 'https://github.com/XXXXXX/YYYYYY/releases/tag/' + selectedRelease.tag, target: '_blank' }, _('View on GitHub'))); |
| 108 | ++ } |
| 109 | ++ }; |
| 110 | ++ |
| 111 | ++ // Add event listener to update details when selection changes |
| 112 | ++ releaseSelect.addEventListener('change', updateDetails); |
| 113 | ++ |
| 114 | ++ let modal_body = [ |
| 115 | ++ E('p', _('Select a firmware release to install:')), |
| 116 | ++ E('div', { class: 'table' }, [ |
| 117 | ++ E('tr', { class: 'tr' }, [ |
| 118 | ++ E('td', { class: 'td left', width: '33%' }, _('Release')), |
| 119 | ++ E('td', { class: 'td left' }, [releaseSelect]) |
| 120 | ++ ]) |
| 121 | ++ ]), |
| 122 | ++ E('div', { class: 'table mt-2' }, [ |
| 123 | ++ E('tr', { class: 'tr' }, [ |
| 124 | ++ E('td', { class: 'td left', width: '33%' }, _('URL')), |
| 125 | ++ urlElement |
| 126 | ++ ]) |
| 127 | ++ ]), |
| 128 | ++ E( |
| 129 | ++ 'p', |
| 130 | ++ { class: 'mt-2' }, |
| 131 | ++ E('label', { class: 'btn' }, [ |
| 132 | ++ keep, |
| 133 | ++ ' ', |
| 134 | ++ _('Keep settings and retain the current configuration'), |
| 135 | ++ ]) |
| 136 | ++ ), |
| 137 | ++ E('div', { class: 'right' }, [ |
| 138 | ++ E('div', { class: 'btn', click: ui.hideModal }, _('Cancel')), |
| 139 | ++ ' ', |
| 140 | ++ E( |
| 141 | ++ 'button', |
| 142 | ++ { |
| 143 | ++ class: 'btn cbi-button cbi-button-positive important', |
| 144 | ++ click: ui.createHandlerFn(this, function () { |
| 145 | ++ const selectedIndex = releaseSelect.value; |
| 146 | ++ const selectedRelease = releases[selectedIndex]; |
| 147 | ++ |
| 148 | ++ if (selectedRelease && selectedRelease.tag) { |
| 149 | ++ this.handleGithubInstall(selectedRelease.tag, keep.checked); |
| 150 | ++ } else { |
| 151 | ++ ui.showModal(_('Error'), [ |
| 152 | ++ E('p', _('Invalid release selected')), |
| 153 | ++ E('div', { class: 'right' }, [ |
| 154 | ++ E('div', { class: 'btn', click: ui.hideModal }, _('Close')), |
| 155 | ++ ]), |
| 156 | ++ ]); |
| 157 | ++ } |
| 158 | ++ }), |
| 159 | ++ }, |
| 160 | ++ _('Install selected firmware') |
| 161 | ++ ), |
| 162 | ++ ]), |
| 163 | ++ ]; |
| 164 | ++ |
| 165 | ++ // Initialize details for the initially selected release (first option) |
| 166 | ++ updateDetails(); |
| 167 | ++ |
| 168 | ++ ui.showModal(_('Available GitHub Releases'), modal_body); |
| 169 | ++ }) |
| 170 | ++ .catch(error => { |
| 171 | ++ ui.showModal(_('Error'), [ |
| 172 | ++ E('p', _('Error fetching firmware info: %s').format(error.message)), |
| 173 | ++ E('div', { class: 'right' }, [ |
| 174 | ++ E('div', { class: 'btn', click: ui.hideModal }, _('Close')), |
| 175 | ++ ]), |
| 176 | ++ ]); |
| 177 | ++ }); |
| 178 | ++ }, |
| 179 | ++ |
| 180 | ++ handleGithubInstall: function(tag, keep) { |
| 181 | ++ ui.showModal(_('Downloading from GitHub...'), [ |
| 182 | ++ E( |
| 183 | ++ 'p', |
| 184 | ++ { class: 'spinning' }, |
| 185 | ++ _('Downloading firmware from GitHub to device') |
| 186 | + ), |
| 187 | + ]); |
| 188 | ++ |
| 189 | ++ // Call the CGI script to download the firmware to /tmp/firmware.bin |
| 190 | ++ // Pass the selected tag as a parameter to the github_fetch script |
| 191 | ++ fetch(`/cgi-bin/github_fetch?tag=${encodeURIComponent(tag)}`) |
| 192 | ++ .then(response => response.json()) |
| 193 | ++ .then(data => { |
| 194 | ++ if (data.error) { |
| 195 | ++ ui.showModal(_('Error'), [ |
| 196 | ++ E('p', _('Error downloading firmware: %s').format(data.error)), |
| 197 | ++ E('div', { class: 'right' }, [ |
| 198 | ++ E('div', { class: 'btn', click: ui.hideModal }, _('Close')), |
| 199 | ++ ]), |
| 200 | ++ ]); |
| 201 | ++ return; |
| 202 | ++ } |
| 203 | ++ |
| 204 | ++ if (data.success) { |
| 205 | ++ // Firmware is already downloaded to the device and SHA256 verified by the CGI script |
| 206 | ++ // Path returned by the CGI script: ${data.path || '/tmp/firmware.bin'} |
| 207 | ++ ui.showModal(_('Installing...'), [ |
| 208 | ++ E('div', { class: 'spinning' }, [ |
| 209 | ++ E('p', _('Installing the sysupgrade image...')), |
| 210 | ++ E('p', |
| 211 | ++ _('Once the image is written, the system will reboot.') |
| 212 | ++ + ' ' + |
| 213 | ++ _('This should take at least a minute, so please wait for the login screen.') |
| 214 | ++ ), |
| 215 | ++ E('b', _('While you are waiting, do not unpower device!')), |
| 216 | ++ ]), |
| 217 | ++ ]); |
| 218 | ++ |
| 219 | ++ L.resolveDefault(callUpgradeStart(keep), {}).then((response) => { |
| 220 | ++ // Wait 10 seconds before we try to reconnect... |
| 221 | ++ let hosts = keep ? [] : ['192.168.1.1', 'openwrt.lan']; |
| 222 | ++ setTimeout(() => { ui.awaitReconnect(...hosts); }, 10000); |
| 223 | ++ }); |
| 224 | ++ } else { |
| 225 | ++ ui.showModal(_('Error'), [ |
| 226 | ++ E('p', _('Unexpected response from firmware download script')), |
| 227 | ++ E('div', { class: 'right' }, [ |
| 228 | ++ E('div', { class: 'btn', click: ui.hideModal }, _('Close')), |
| 229 | ++ ]), |
| 230 | ++ ]); |
| 231 | ++ } |
| 232 | ++ }) |
| 233 | ++ .catch(error => { |
| 234 | ++ ui.showModal(_('Download Error'), [ |
| 235 | ++ E('p', _('Error downloading firmware from GitHub: %s').format(error.message)), |
| 236 | ++ E('div', { class: 'right' }, [ |
| 237 | ++ E('div', { class: 'btn', click: ui.hideModal }, _('Close')), |
| 238 | ++ ]), |
| 239 | ++ ]); |
| 240 | ++ }); |
| 241 | + }, |
| 242 | ++ |
| 243 | + handleSaveApply: null, |
| 244 | + handleSave: null, |
| 245 | + handleReset: null, |
0 commit comments