mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-31 16:55:13 +08:00
🤞 Sync 2026-06-25 00:16:31
This commit is contained in:
1 parent
251e9e9dd1
commit
bbcd3f17c5
14 files changed
+505
-276
No files matched your search
@@ -9,32 +9,48 @@
|
||||
*/
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.resolve();
|
||||
// Format raw firmware filename → "OpenWrt 24.10.7 202606-rev01"
|
||||
fmtVer: function(raw) {
|
||||
if (!raw) return '';
|
||||
// From openwrt-24.10.7-4g-lte-202606-rev01
|
||||
var m = raw.match(/^openwrt-([\d.]+)-.*-(\d{6}-rev\d+)$/);
|
||||
if (m) return 'OpenWrt ' + m[1] + ' ' + m[2];
|
||||
return raw;
|
||||
},
|
||||
|
||||
checkInitialState: function() {
|
||||
load: function() {
|
||||
return fs.read('/usr/lib/os-release')
|
||||
.then(function(content) {
|
||||
// OPENWRT_RELEASE="OpenWrt 24.10.7~4g-lte-202606-rev01"
|
||||
var m = (content || '').match(/OPENWRT_RELEASE="(OpenWrt\s+[\d.]+)~.*-(\d{6}-rev\d+)"/);
|
||||
if (m) return m[1].trim() + ' ' + m[2].trim();
|
||||
return '';
|
||||
})
|
||||
.catch(function() { return ''; });
|
||||
},
|
||||
|
||||
checkInitialState: function(currentVer) {
|
||||
var self = this;
|
||||
this.verifyCheckResult().then(function(success) {
|
||||
// Show current version immediately from fw_rev
|
||||
self.updateVersionBlock(currentVer, '—', false);
|
||||
|
||||
// Restore available version if previous check result still on disk
|
||||
self.verifyCheckResult(currentVer).then(function(success) {
|
||||
if (success) {
|
||||
self.checkButton.disabled = true;
|
||||
self.showUpdateAvailable();
|
||||
self.upgradeButton.disabled = false;
|
||||
}
|
||||
}).catch(function() {});
|
||||
},
|
||||
|
||||
showUpdateAvailable: function() {
|
||||
var self = this;
|
||||
self.statusDiv.innerHTML = '';
|
||||
self.statusDiv.appendChild(E('div', { 'class': 'alert-message success' }, [
|
||||
E('h3', {}, _('Update Available')),
|
||||
E('button', {
|
||||
'class': 'btn cbi-button cbi-button-neutral',
|
||||
'style': 'margin-top: 6px;',
|
||||
'click': function() { self.showChangelog(); }
|
||||
}, _('Show Changelog'))
|
||||
]));
|
||||
updateVersionBlock: function(current, upgrade, hasUpdate) {
|
||||
this.verCurrentVal.textContent = current || '—';
|
||||
this.verAvailVal.textContent = upgrade || '—';
|
||||
|
||||
// Changelog link visible only when update is available
|
||||
this.verChangelogLink.style.display = hasUpdate ? '' : 'none';
|
||||
|
||||
// Colour: important (green) = update found, warning (yellow) = no update / unknown
|
||||
this.versionBlock.className = 'alert-message ' + (hasUpdate ? 'success' : 'warning');
|
||||
},
|
||||
|
||||
showChangelog: function() {
|
||||
@@ -52,7 +68,7 @@ return view.extend({
|
||||
]);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render: function(data) {
|
||||
var self = this;
|
||||
|
||||
this.checkButton = E('button', {
|
||||
@@ -66,7 +82,29 @@ return view.extend({
|
||||
'click': ui.createHandlerFn(this, 'handleUpgrade')
|
||||
}, _('Upgrade'));
|
||||
|
||||
this.statusDiv = E('div', { 'class': 'cbi-section' });
|
||||
// Version info rows — always visible, updated after check
|
||||
this.verCurrentVal = E('span', {}, '—');
|
||||
this.verAvailVal = E('span', {}, '—');
|
||||
this.verChangelogLink = E('button', {
|
||||
'class': 'btn cbi-button cbi-button-neutral',
|
||||
'style': 'display: none; margin-left: 10px;',
|
||||
'click': function() { self.showChangelog(); }
|
||||
}, _('Show Changelog'));
|
||||
|
||||
this.versionBlock = E('div', { 'class': 'alert-message', 'style': 'margin-top: 1.5em;' }, [
|
||||
E('table', { 'style': 'border: none; background: none;' }, [
|
||||
E('tr', {}, [
|
||||
E('td', { 'style': 'padding: 2px 10px 2px 0; font-weight: bold; white-space: nowrap;' },
|
||||
_('Current version:')),
|
||||
E('td', {}, this.verCurrentVal)
|
||||
]),
|
||||
E('tr', {}, [
|
||||
E('td', { 'style': 'padding: 2px 10px 2px 0; font-weight: bold; white-space: nowrap;' },
|
||||
_('Available version:')),
|
||||
E('td', {}, [ this.verAvailVal, this.verChangelogLink ])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
var container = E('div', { 'class': 'cbi-map' }, [
|
||||
E('h2', {}, _('OTA System Update')),
|
||||
@@ -75,10 +113,10 @@ return view.extend({
|
||||
' ',
|
||||
this.upgradeButton
|
||||
]),
|
||||
this.statusDiv
|
||||
this.versionBlock
|
||||
]);
|
||||
|
||||
this.checkInitialState();
|
||||
this.checkInitialState(data);
|
||||
|
||||
return container;
|
||||
},
|
||||
@@ -87,33 +125,23 @@ return view.extend({
|
||||
var self = this;
|
||||
|
||||
this.checkButton.disabled = true;
|
||||
this.statusDiv.innerHTML = '';
|
||||
this.statusDiv.appendChild(E('div', { 'class': 'spinner' }, _('Checking for updates...')));
|
||||
this.upgradeButton.disabled = true;
|
||||
|
||||
return fs.exec('/usr/share/ota.sh', ['check'])
|
||||
.then(function() {
|
||||
return self.verifyCheckResult();
|
||||
return self.verifyCheckResult(self.verCurrentVal.textContent);
|
||||
})
|
||||
.then(function(success) {
|
||||
if (success) {
|
||||
self.upgradeButton.disabled = false;
|
||||
self.showUpdateAvailable();
|
||||
} else {
|
||||
self.statusDiv.innerHTML = '';
|
||||
self.statusDiv.appendChild(E('div', { 'class': 'alert-message warning' },
|
||||
_('No updates available or check failed')));
|
||||
}
|
||||
self.upgradeButton.disabled = !success;
|
||||
self.checkButton.disabled = false;
|
||||
})
|
||||
.catch(function(err) {
|
||||
self.statusDiv.innerHTML = '';
|
||||
self.statusDiv.appendChild(E('div', { 'class': 'alert-message error' },
|
||||
_('Check failed: ') + (err.message || err)));
|
||||
self.updateVersionBlock('', _('Check failed: ') + (err.message || err), false);
|
||||
self.checkButton.disabled = false;
|
||||
});
|
||||
},
|
||||
|
||||
verifyCheckResult: function() {
|
||||
verifyCheckResult: function(currentVer) {
|
||||
var self = this;
|
||||
|
||||
return Promise.all([
|
||||
@@ -124,11 +152,33 @@ return view.extend({
|
||||
self.changelog = content;
|
||||
return content && content.length > 0;
|
||||
})
|
||||
.catch(function() { return false; })
|
||||
.catch(function() { return false; }),
|
||||
fs.read('/tmp/ota_version')
|
||||
.then(function(content) { return content || ''; })
|
||||
.catch(function() { return ''; })
|
||||
]).then(function(results) {
|
||||
return results[0] !== null &&
|
||||
results[1] !== null &&
|
||||
results[2] === true;
|
||||
var hasProfiles = results[0] !== null;
|
||||
var hasLock = results[1] !== null;
|
||||
var hasChangelog = results[2];
|
||||
var verContent = results[3];
|
||||
|
||||
// both current= and upgrade= are full firmware names written by ota.sh
|
||||
var current = '', upgrade = '';
|
||||
verContent.split('\n').forEach(function(line) {
|
||||
var m;
|
||||
if ((m = line.match(/^current=(.+)/))) current = self.fmtVer(m[1].trim());
|
||||
if ((m = line.match(/^upgrade=(.+)/))) upgrade = self.fmtVer(m[1].trim());
|
||||
});
|
||||
// fallback: if ota_version missing, use value from load() (already formatted)
|
||||
if (!current) current = currentVer || '';
|
||||
|
||||
if (hasProfiles && hasLock && hasChangelog) {
|
||||
self.updateVersionBlock(current, upgrade, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
self.updateVersionBlock(current, _('No updates available'), false);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -142,53 +192,46 @@ return view.extend({
|
||||
this.upgradeButton.disabled = true;
|
||||
this.checkButton.disabled = true;
|
||||
|
||||
// Remove stale progress file before starting
|
||||
fs.remove('/tmp/ota_progress').catch(function() {});
|
||||
|
||||
self.openUpgradeModal();
|
||||
|
||||
return fs.exec('/usr/share/ota.sh', ['upgrade'])
|
||||
// ota-launch.sh starts ota.sh in background and exits immediately
|
||||
return fs.exec('/usr/share/ota-launch.sh', [])
|
||||
.then(function() {
|
||||
self.stopProgressPolling();
|
||||
self.setModalProgress(100, _('Upgrade started. Device will reboot...'), 'done');
|
||||
self.finalizeUpgradeModal(false);
|
||||
// launcher exited — polling is running, wait for 'done' or 'error'
|
||||
})
|
||||
.catch(function(err) {
|
||||
var msg = err.message || err.toString();
|
||||
self.stopProgressPolling();
|
||||
|
||||
// XHR timeout = sysupgrade already running, treat as success
|
||||
if (msg.indexOf('timed out') !== -1) {
|
||||
self.setModalProgress(100, _('Upgrade started. Device will reboot...'), 'done');
|
||||
self.finalizeUpgradeModal(false);
|
||||
} else {
|
||||
self.setModalProgress(0, _('Error: ') + msg, 'error');
|
||||
self.finalizeUpgradeModal(true);
|
||||
// XHR timeout is expected — launcher or sysupgrade still running,
|
||||
// polling continues and will finalize via 'flashing'/'error' state
|
||||
if (msg.indexOf('timed out') !== -1 || msg.indexOf('XHR') !== -1) {
|
||||
return;
|
||||
}
|
||||
// Genuine exec failure
|
||||
self.stopProgressPolling();
|
||||
self.setModalProgress(0, _('Launch failed: ') + msg, 'error');
|
||||
self.finalizeUpgradeModal(true);
|
||||
});
|
||||
},
|
||||
|
||||
openUpgradeModal: function() {
|
||||
var self = this;
|
||||
|
||||
// Progress bar element
|
||||
self.modalProgressInner = E('div', { 'style': 'width: 0%; transition: width 0.4s ease;' });
|
||||
self.modalProgressBar = E('div', { 'class': 'cbi-progressbar', 'style': 'margin: 10px 0;' },
|
||||
self.modalProgressInner);
|
||||
self.modalProgressBar = E('div', {
|
||||
'class': 'cbi-progressbar',
|
||||
'style': 'margin: 10px 0;'
|
||||
}, self.modalProgressInner);
|
||||
|
||||
// Status label
|
||||
self.modalStatus = E('div', {
|
||||
'style': 'font-size: 0.9em; color: #666; margin-bottom: 8px;'
|
||||
}, _('Starting upgrade...'));
|
||||
|
||||
// Log area
|
||||
self.modalLog = E('pre', {
|
||||
'style': 'white-space: pre-wrap; max-height: 200px; overflow-y: auto; ' +
|
||||
'background: #f5f5f5; padding: 8px; border-radius: 3px; ' +
|
||||
'color: black; font-size: 0.85em; margin-top: 10px;'
|
||||
}, '');
|
||||
|
||||
// Close button (disabled until done)
|
||||
self.modalCloseBtn = E('button', {
|
||||
'class': 'btn cbi-button',
|
||||
'disabled': true,
|
||||
@@ -209,7 +252,6 @@ return view.extend({
|
||||
])
|
||||
]);
|
||||
|
||||
// Start polling /tmp/ota_progress
|
||||
self.startProgressPolling();
|
||||
},
|
||||
|
||||
@@ -227,7 +269,6 @@ return view.extend({
|
||||
|
||||
if (line.indexOf('downloading') === 0) {
|
||||
var pct = parseInt(line.split(' ')[1], 10) || 0;
|
||||
// downloading maps to 0..70% of bar
|
||||
var barPct = Math.round(pct * 0.70);
|
||||
self.setModalProgress(barPct,
|
||||
_('Downloading firmware: ') + pct + '%', 'progress');
|
||||
@@ -242,18 +283,37 @@ return view.extend({
|
||||
self.appendModalLog(_('Testing firmware image...'));
|
||||
|
||||
} else if (line === 'flashing') {
|
||||
self.stopProgressPolling();
|
||||
self.setModalProgress(95, _('Flashing! DO NOT POWER OFF!'), 'progress');
|
||||
self.appendModalLog(_('Flashing firmware...'));
|
||||
// Simulate reboot wait with countdown
|
||||
var countdown = 90;
|
||||
self.setModalProgress(98, _('Device will be rebooted, please wait... ') + countdown + 's', 'progress');
|
||||
var rebootTimer = setInterval(function() {
|
||||
countdown--;
|
||||
if (countdown <= 0) {
|
||||
clearInterval(rebootTimer);
|
||||
self.setModalProgress(100, _('Device will be rebooted, please wait... '), 'done');
|
||||
self.finalizeUpgradeModal(false);
|
||||
} else {
|
||||
self.setModalProgress(98, _('Device will be rebooted, please wait... ') + countdown + 's', 'progress');
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
} else if (line === 'done') {
|
||||
self.stopProgressPolling();
|
||||
self.setModalProgress(100, _('Upgrade started. Device will reboot...'), 'done');
|
||||
self.appendModalLog(_('Done. Waiting for reboot...'));
|
||||
self.finalizeUpgradeModal(false);
|
||||
|
||||
} else if (line.indexOf('error') === 0) {
|
||||
var reason = line.replace('error', '').trim();
|
||||
self.stopProgressPolling();
|
||||
self.setModalProgress(0, _('Error: ') + reason, 'error');
|
||||
self.appendModalLog(_('FAILED: ') + reason);
|
||||
self.stopProgressPolling();
|
||||
self.finalizeUpgradeModal(true);
|
||||
}
|
||||
}).catch(function() {
|
||||
// file not yet created — ignore
|
||||
});
|
||||
}).catch(function() {});
|
||||
}, 1500);
|
||||
},
|
||||
|
||||
@@ -269,7 +329,6 @@ return view.extend({
|
||||
var pct = Math.min(100, Math.max(0, percent));
|
||||
this.modalProgressInner.style.width = pct + '%';
|
||||
this.modalStatus.textContent = text;
|
||||
|
||||
if (state === 'error') {
|
||||
this.modalStatus.style.color = '#c00';
|
||||
} else if (state === 'done') {
|
||||
@@ -282,7 +341,6 @@ return view.extend({
|
||||
appendModalLog: function(line) {
|
||||
if (!this.modalLog) return;
|
||||
this.modalLog.textContent += line + '\n';
|
||||
// Auto-scroll to bottom
|
||||
this.modalLog.scrollTop = this.modalLog.scrollHeight;
|
||||
},
|
||||
|
||||
@@ -291,11 +349,9 @@ return view.extend({
|
||||
this.modalCloseBtn.disabled = false;
|
||||
}
|
||||
if (!isError) {
|
||||
// For successful flash — don't re-enable buttons,
|
||||
// device is about to reboot
|
||||
this.statusDiv.innerHTML = '';
|
||||
this.statusDiv.appendChild(E('div', { 'class': 'alert-message warning' },
|
||||
_('Upgrade started! DO NOT POWER OFF THIS DEVICE! System will reboot after upgrade.')));
|
||||
this.versionBlock.className = 'alert-message warning';
|
||||
this.verAvailVal.textContent = _('Upgrade started — device will reboot');
|
||||
this.verChangelogLink.style.display = 'none';
|
||||
} else {
|
||||
this.upgradeButton.disabled = false;
|
||||
this.checkButton.disabled = false;
|
||||
|
||||
Reference in new issue
Block a user