secubox-openwrt/package/secubox/luci-app-gitea/htdocs/luci-static/resources/view/gitea/settings.js
CyberMind-FR d43a02a397 feat(gitea): Add self-hosted Git platform for OpenWrt
Add secubox-app-gitea and luci-app-gitea packages:

secubox-app-gitea:
- LXC container with Alpine 3.21 rootfs
- Gitea 1.22.6 binary (auto-detect amd64/arm64/armv7)
- HTTP (3000) and SSH (2222) ports
- SQLite database (embedded)
- giteactl: install/uninstall/update/backup/restore

luci-app-gitea:
- Cyberpunk themed dashboard
- Repository browser with clone URLs
- User management interface
- Server and security settings
- Backup/restore functionality
- 18 RPCD methods

Resource requirements: 256MB RAM minimum, ~100MB storage

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 10:03:31 +01:00

352 lines
11 KiB
JavaScript

'use strict';
'require view';
'require ui';
'require dom';
'require gitea.api as api';
return view.extend({
configData: null,
statusData: null,
backupsData: null,
load: function() {
var self = this;
return Promise.all([
api.getStatus(),
api.getConfig(),
api.listBackups()
]).then(function(results) {
self.statusData = results[0] || {};
self.configData = results[1] || {};
self.backupsData = results[2] || [];
return results;
});
},
render: function() {
var self = this;
// Inject CSS
var cssLink = E('link', {
'rel': 'stylesheet',
'type': 'text/css',
'href': L.resource('gitea/dashboard.css')
});
var container = E('div', { 'class': 'gitea-dashboard' }, [
cssLink,
this.renderHeader(),
this.renderContent()
]);
return container;
},
renderHeader: function() {
return E('div', { 'class': 'gt-header' }, [
E('div', { 'class': 'gt-header-content' }, [
E('div', { 'class': 'gt-logo' }, '\u2699\uFE0F'),
E('div', {}, [
E('h1', { 'class': 'gt-title' }, _('SETTINGS')),
E('p', { 'class': 'gt-subtitle' }, _('Gitea Platform Configuration'))
])
])
]);
},
renderContent: function() {
var status = this.statusData;
if (!status.installed) {
return E('div', { 'class': 'gt-card' }, [
E('div', { 'class': 'gt-card-body' }, [
E('div', { 'class': 'gt-empty' }, [
E('div', { 'class': 'gt-empty-icon' }, '\u2699\uFE0F'),
E('div', {}, _('Gitea is not installed')),
E('p', {}, _('Install Gitea from the Overview page to configure settings.'))
])
])
]);
}
return E('div', { 'class': 'gt-main-grid' }, [
this.renderServerSettings(),
this.renderSecuritySettings(),
this.renderBackupCard()
]);
},
renderServerSettings: function() {
var self = this;
var config = this.configData;
var main = config.main || {};
return E('div', { 'class': 'gt-card' }, [
E('div', { 'class': 'gt-card-header' }, [
E('div', { 'class': 'gt-card-title' }, [
E('span', {}, '\uD83D\uDDA5\uFE0F'),
' ' + _('Server Settings')
])
]),
E('div', { 'class': 'gt-card-body' }, [
E('div', { 'class': 'gt-form-group' }, [
E('label', { 'class': 'gt-form-label' }, _('App Name')),
E('input', {
'type': 'text',
'class': 'gt-form-input',
'id': 'cfg-app-name',
'value': main.app_name || 'SecuBox Git'
}),
E('div', { 'class': 'gt-form-hint' }, _('Display name for the Gitea instance'))
]),
E('div', { 'class': 'gt-form-group' }, [
E('label', { 'class': 'gt-form-label' }, _('Domain')),
E('input', {
'type': 'text',
'class': 'gt-form-input',
'id': 'cfg-domain',
'value': main.domain || 'git.local'
}),
E('div', { 'class': 'gt-form-hint' }, _('Domain name for URLs'))
]),
E('div', { 'class': 'gt-form-group' }, [
E('label', { 'class': 'gt-form-label' }, _('HTTP Port')),
E('input', {
'type': 'number',
'class': 'gt-form-input',
'id': 'cfg-http-port',
'value': main.http_port || 3000
})
]),
E('div', { 'class': 'gt-form-group' }, [
E('label', { 'class': 'gt-form-label' }, _('SSH Port')),
E('input', {
'type': 'number',
'class': 'gt-form-input',
'id': 'cfg-ssh-port',
'value': main.ssh_port || 2222
})
]),
E('div', { 'class': 'gt-form-group' }, [
E('label', { 'class': 'gt-form-label' }, _('Memory Limit')),
E('select', { 'class': 'gt-form-select', 'id': 'cfg-memory' }, [
E('option', { 'value': '256M', 'selected': main.memory_limit === '256M' }, '256 MB'),
E('option', { 'value': '512M', 'selected': main.memory_limit === '512M' || !main.memory_limit }, '512 MB'),
E('option', { 'value': '1G', 'selected': main.memory_limit === '1G' }, '1 GB'),
E('option', { 'value': '2G', 'selected': main.memory_limit === '2G' }, '2 GB')
])
]),
E('div', { 'class': 'gt-form-group' }, [
E('label', { 'class': 'gt-form-checkbox' }, [
E('input', {
'type': 'checkbox',
'id': 'cfg-enabled',
'checked': main.enabled
}),
_('Enable service on boot')
])
]),
E('button', {
'class': 'gt-btn gt-btn-primary',
'click': function() { self.handleSaveConfig(); }
}, [E('span', {}, '\uD83D\uDCBE'), ' ' + _('Save Settings')])
])
]);
},
renderSecuritySettings: function() {
var self = this;
var config = this.configData;
var server = config.server || {};
return E('div', { 'class': 'gt-card' }, [
E('div', { 'class': 'gt-card-header' }, [
E('div', { 'class': 'gt-card-title' }, [
E('span', {}, '\uD83D\uDD12'),
' ' + _('Security Settings')
])
]),
E('div', { 'class': 'gt-card-body' }, [
E('div', { 'class': 'gt-form-group' }, [
E('label', { 'class': 'gt-form-checkbox' }, [
E('input', {
'type': 'checkbox',
'id': 'cfg-disable-reg',
'checked': server.disable_registration
}),
_('Disable user registration')
]),
E('div', { 'class': 'gt-form-hint' }, _('Prevent new users from signing up'))
]),
E('div', { 'class': 'gt-form-group' }, [
E('label', { 'class': 'gt-form-checkbox' }, [
E('input', {
'type': 'checkbox',
'id': 'cfg-require-signin',
'checked': server.require_signin
}),
_('Require sign-in to view')
]),
E('div', { 'class': 'gt-form-hint' }, _('Require authentication to browse repositories'))
]),
E('div', { 'class': 'gt-form-group' }, [
E('label', { 'class': 'gt-form-label' }, _('Landing Page')),
E('select', { 'class': 'gt-form-select', 'id': 'cfg-landing' }, [
E('option', { 'value': 'explore', 'selected': server.landing_page === 'explore' }, _('Explore')),
E('option', { 'value': 'home', 'selected': server.landing_page === 'home' }, _('Home')),
E('option', { 'value': 'organizations', 'selected': server.landing_page === 'organizations' }, _('Organizations')),
E('option', { 'value': 'login', 'selected': server.landing_page === 'login' }, _('Login'))
])
]),
E('p', { 'class': 'gt-form-hint', 'style': 'margin-top: 15px; color: #ff5f1f;' },
_('Note: Changes to security settings require a service restart to take effect.'))
])
]);
},
renderBackupCard: function() {
var self = this;
var backups = this.backupsData || [];
return E('div', { 'class': 'gt-card' }, [
E('div', { 'class': 'gt-card-header' }, [
E('div', { 'class': 'gt-card-title' }, [
E('span', {}, '\uD83D\uDCBE'),
' ' + _('Backup & Restore')
])
]),
E('div', { 'class': 'gt-card-body' }, [
E('div', { 'class': 'gt-btn-group' }, [
E('button', {
'class': 'gt-btn gt-btn-primary',
'click': function() { self.handleBackup(); }
}, [E('span', {}, '\uD83D\uDCBE'), ' ' + _('Create Backup')])
]),
backups.length > 0 ?
E('div', { 'style': 'margin-top: 20px' }, [
E('h4', { 'style': 'color: #888; font-size: 12px; margin-bottom: 10px;' }, _('Available Backups')),
E('table', { 'class': 'gt-table' }, [
E('thead', {}, [
E('tr', {}, [
E('th', {}, _('Filename')),
E('th', {}, _('Size')),
E('th', {}, _('Date')),
E('th', {}, _('Actions'))
])
]),
E('tbody', {},
backups.map(function(backup) {
var date = backup.mtime ? new Date(backup.mtime * 1000).toLocaleString() : '-';
return E('tr', {}, [
E('td', {}, backup.name),
E('td', {}, backup.size || '-'),
E('td', {}, date),
E('td', {}, [
E('button', {
'class': 'gt-btn gt-btn-warning',
'style': 'padding: 4px 8px; font-size: 10px;',
'click': function() { self.handleRestore(backup.path); }
}, _('Restore'))
])
]);
})
)
])
]) :
E('div', { 'class': 'gt-empty', 'style': 'padding: 20px' }, [
E('div', {}, _('No backups found'))
])
])
]);
},
handleSaveConfig: function() {
var self = this;
var config = {
app_name: document.getElementById('cfg-app-name').value,
domain: document.getElementById('cfg-domain').value,
http_port: parseInt(document.getElementById('cfg-http-port').value) || 3000,
ssh_port: parseInt(document.getElementById('cfg-ssh-port').value) || 2222,
memory_limit: document.getElementById('cfg-memory').value,
enabled: document.getElementById('cfg-enabled').checked ? '1' : '0',
disable_registration: document.getElementById('cfg-disable-reg').checked ? 'true' : 'false',
require_signin: document.getElementById('cfg-require-signin').checked ? 'true' : 'false',
landing_page: document.getElementById('cfg-landing').value
};
api.saveConfig(config).then(function(result) {
if (result && result.success) {
ui.addNotification(null, E('p', {}, _('Configuration saved. Restart the service for changes to take effect.')), 'success');
} else {
ui.addNotification(null, E('p', {}, result.message || _('Failed to save configuration')), 'error');
}
}).catch(function(err) {
ui.addNotification(null, E('p', {}, _('Failed to save configuration: ') + err.message), 'error');
});
},
handleBackup: function() {
var self = this;
ui.showModal(_('Creating Backup'), [
E('p', {}, _('Backing up repositories and database...')),
E('div', { 'class': 'spinning' })
]);
api.createBackup().then(function(result) {
ui.hideModal();
if (result && result.success) {
ui.addNotification(null, E('p', {}, _('Backup created successfully')), 'success');
// Refresh backup list
return api.listBackups().then(function(data) {
self.backupsData = data;
location.reload();
});
} else {
ui.addNotification(null, E('p', {}, result.message || _('Backup failed')), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', {}, _('Backup failed: ') + err.message), 'error');
});
},
handleRestore: function(file) {
var self = this;
ui.showModal(_('Confirm Restore'), [
E('p', {}, _('Are you sure you want to restore from this backup? This will overwrite current data.')),
E('p', { 'style': 'color: #ffaa00' }, file),
E('div', { 'class': 'right' }, [
E('button', {
'class': 'btn',
'click': ui.hideModal
}, _('Cancel')),
E('button', {
'class': 'btn cbi-button-negative',
'click': function() {
ui.hideModal();
ui.showModal(_('Restoring Backup'), [
E('p', {}, _('Restoring data...')),
E('div', { 'class': 'spinning' })
]);
api.restoreBackup(file).then(function(result) {
ui.hideModal();
if (result && result.success) {
ui.addNotification(null, E('p', {}, _('Restore completed successfully')), 'success');
} else {
ui.addNotification(null, E('p', {}, result.message || _('Restore failed')), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', {}, _('Restore failed: ') + err.message), 'error');
});
}
}, _('Restore'))
])
]);
}
});