secubox-openwrt/luci-app-system-hub/htdocs/luci-static/resources/view/system-hub/backup.js
CyberMind-FR 5902ac500a release: v0.1.1 - Unified Theme System with Dark/Light Mode Support
Major Features:
• Centralized theme system across SecuBox and System Hub
• Three theme modes: dark (default), light, and system (auto-detect)
• Single theme setting in SecuBox controls both plugins
• Real-time theme switching with OS preference detection

SecuBox Changes:
• Added theme.js manager for centralized theme control
• Implemented CSS variables for dark/light mode (secubox.css)
• Fixed hardcoded colors in dashboard.css, alerts.css, monitoring.css
• Integrated theme.js in all 7 views (dashboard, modules, alerts, monitoring, settings, etc.)
• Added get_theme RPC method to luci.secubox backend
• Updated ACL permissions to include get_theme (read access)
• Version updated to 0.1.1

System Hub Changes:
• Added theme.js manager using SecuBox theme API
• Implemented CSS variables for dark/light mode (dashboard.css)
• Integrated theme.js in all 9 views (overview, health, services, logs, backup, components, remote, settings, diagnostics)
• Version updated to 0.1.1
• README updated with maintainer info

Theme System Architecture:
• Configuration: /etc/config/secubox (option theme: dark|light|system)
• RPCD Backend: luci.secubox/get_theme method
• Frontend: theme.js modules (secubox/theme.js, system-hub/theme.js)
• CSS Variables: --sb-bg, --sb-bg-card, --sb-border, --sb-text, --sb-text-muted, --sb-shadow
• Auto-detection: prefers-color-scheme media query for system mode

Documentation:
• Added LUCI_DEVELOPMENT_REFERENCE.md with comprehensive LuCI development patterns
• Documented ubus/RPC types, baseclass.extend() patterns, ACL structure
• Common errors and solutions from implementation experience

Bug Fixes:
• Fixed SecuBox theme not applying visually (CSS variables now used)
• Fixed missing secubox.css in view imports
• Fixed ACL access denied for get_theme method
• Fixed hardcoded colors preventing theme switching

Testing:
• Verified theme switching works in all SecuBox tabs
• Verified theme switching works in all System Hub tabs
• Verified dark/light/system modes function correctly
• Verified single setting controls both plugins

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 16:09:15 +01:00

197 lines
5.4 KiB
JavaScript

'use strict';
'require view';
'require ui';
'require system-hub/api as API';
'require system-hub/theme as Theme';
// Load CSS
document.head.appendChild(E('link', {
'rel': 'stylesheet',
'type': 'text/css',
'href': L.resource('system-hub/dashboard.css')
}));
// Initialize theme
Theme.init();
return L.view.extend({
load: function() {
return Promise.resolve();
},
render: function() {
var v = E('div', { 'class': 'cbi-map' }, [
E('h2', {}, _('Backup & Restore')),
E('div', { 'class': 'cbi-map-descr' }, _('Backup and restore system configuration'))
]);
// Backup Section
var backupSection = E('div', { 'class': 'cbi-section' }, [
E('h3', {}, _('Create Backup')),
E('p', {}, _('Download a backup of your current system configuration.')),
E('div', { 'style': 'margin-top: 15px;' }, [
E('button', {
'class': 'cbi-button cbi-button-action',
'click': L.bind(this.createBackup, this)
}, _('Download Backup'))
])
]);
v.appendChild(backupSection);
// Restore Section
var restoreSection = E('div', { 'class': 'cbi-section' }, [
E('h3', {}, _('Restore Configuration')),
E('p', {}, _('Upload a previously saved backup file to restore your configuration.')),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title' }, _('Backup File')),
E('div', { 'class': 'cbi-value-field' }, [
E('input', {
'type': 'file',
'id': 'backup-file',
'accept': '.tar.gz,.tgz'
})
])
]),
E('div', { 'style': 'margin-top: 15px;' }, [
E('button', {
'class': 'cbi-button cbi-button-apply',
'click': L.bind(this.restoreBackup, this)
}, _('Restore Backup'))
])
]);
v.appendChild(restoreSection);
// Reboot Section
var rebootSection = E('div', { 'class': 'cbi-section' }, [
E('h3', {}, _('System Reboot')),
E('p', {}, [
E('span', { 'style': 'color: #dc3545; font-weight: bold;' }, _('Warning: ')),
_('This will reboot your router. All active connections will be lost.')
]),
E('div', { 'style': 'margin-top: 15px;' }, [
E('button', {
'class': 'cbi-button cbi-button-negative',
'click': L.bind(this.rebootSystem, this)
}, _('Reboot System'))
])
]);
v.appendChild(rebootSection);
return v;
},
createBackup: function() {
ui.showModal(_('Creating Backup'), [
E('p', { 'class': 'spinning' }, _('Creating backup archive...'))
]);
API.backupConfig().then(function(result) {
ui.hideModal();
if (!result.success) {
ui.addNotification(null, E('p', '✗ ' + result.message), 'error');
return;
}
// Convert base64 to blob and download
var binary = atob(result.data);
var array = new Uint8Array(binary.length);
for (var i = 0; i < binary.length; i++) {
array[i] = binary.charCodeAt(i);
}
var blob = new Blob([array], { type: 'application/gzip' });
// Create download link
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = result.filename || 'backup.tar.gz';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
ui.addNotification(null, E('p', '✓ ' + _('Backup created successfully') + ' (' + (result.size / 1024).toFixed(1) + ' KB)'), 'info');
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', _('Backup failed: ') + err.message), 'error');
});
},
restoreBackup: function() {
var fileInput = document.getElementById('backup-file');
var file = fileInput.files[0];
if (!file) {
ui.addNotification(null, E('p', _('Please select a backup file')), 'warning');
return;
}
if (!confirm(_('Restore configuration from backup? This will overwrite current settings and require a reboot.'))) {
return;
}
ui.showModal(_('Restoring Backup'), [
E('p', { 'class': 'spinning' }, _('Uploading and restoring backup...'))
]);
var reader = new FileReader();
reader.onload = function(e) {
// Convert to base64
var arrayBuffer = e.target.result;
var bytes = new Uint8Array(arrayBuffer);
var binary = '';
for (var i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i]);
}
var base64 = btoa(binary);
API.restoreConfig(base64).then(function(result) {
ui.hideModal();
if (result.success) {
ui.addNotification(null, E('p', '✓ ' + result.message), 'info');
setTimeout(function() {
if (confirm(_('Reboot now to apply changes?'))) {
API.reboot();
}
}, 1000);
} else {
ui.addNotification(null, E('p', '✗ ' + result.message), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', _('Restore failed: ') + err.message), 'error');
});
};
reader.onerror = function() {
ui.hideModal();
ui.addNotification(null, E('p', _('Failed to read backup file')), 'error');
};
reader.readAsArrayBuffer(file);
},
rebootSystem: function() {
if (!confirm(_('Are you sure you want to reboot the system? All active connections will be lost.'))) {
return;
}
ui.showModal(_('Rebooting System'), [
E('p', {}, _('System is rebooting...')),
E('p', {}, _('This page will reload automatically in about 60 seconds.'))
]);
API.reboot().then(function(result) {
// Wait and reload
setTimeout(function() {
window.location.reload();
}, 60000);
});
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});