fix(streamlit): Add Re-upload and Gitea Sync buttons to Apps table

Restores missing functionality in the Streamlit dashboard:
- Re-upload button: Upload new .py/.zip to replace existing app code
- Gitea Sync button: Pull latest changes from Gitea repository

The buttons appear in the Apps Library table for each app.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-02-28 08:15:19 +01:00
parent 07705f458c
commit 356e2814ca

View File

@ -305,6 +305,7 @@ return view.extend({
var rows = apps.map(function(app) {
var id = app.id || app.name;
var hasGitea = app.gitea_repo || app.git_url;
return E('tr', {}, [
E('td', {}, E('strong', {}, app.name)),
E('td', {}, app.path ? app.path.split('/').pop() : '-'),
@ -313,6 +314,16 @@ return view.extend({
'class': 'cbi-button',
'click': function() { self.createInstanceFromApp(id); }
}, _('+ Instance')),
E('button', {
'class': 'cbi-button',
'style': 'margin-left:4px',
'click': function() { self.reuploadApp(id); }
}, _('Re-upload')),
hasGitea ? E('button', {
'class': 'cbi-button cbi-button-action',
'style': 'margin-left:4px',
'click': function() { self.giteaPull(id); }
}, _('Gitea Sync')) : '',
E('button', {
'class': 'cbi-button cbi-button-remove',
'style': 'margin-left:4px',
@ -645,5 +656,85 @@ return view.extend({
}, _('Delete'))
])
]);
},
// Re-upload app code
reuploadApp: function(name) {
var self = this;
ui.showModal(_('Re-upload App: ') + name, [
E('p', {}, _('Select a new .py or .zip file to replace the app code:')),
E('input', { 'type': 'file', 'id': 'reupload-file', 'accept': '.py,.zip' }),
E('div', { 'class': 'right', 'style': 'margin-top:16px' }, [
E('button', { 'class': 'cbi-button', 'click': ui.hideModal }, _('Cancel')),
E('button', {
'class': 'cbi-button cbi-button-positive',
'style': 'margin-left:8px',
'click': function() {
var fileInput = document.getElementById('reupload-file');
if (!fileInput || !fileInput.files.length) {
ui.addNotification(null, E('p', {}, _('Select a file')), 'error');
return;
}
var file = fileInput.files[0];
var isZip = file.name.endsWith('.zip');
var reader = new FileReader();
reader.onload = function(e) {
var bytes = new Uint8Array(e.target.result);
var chunks = [];
for (var i = 0; i < bytes.length; i += 8192) {
chunks.push(String.fromCharCode.apply(null, bytes.slice(i, i + 8192)));
}
var content = btoa(chunks.join(''));
ui.hideModal();
poll.stop();
ui.showModal(_('Uploading'), [
E('p', { 'class': 'spinning' }, _('Uploading app code...'))
]);
api.chunkedUpload(name, content, isZip).then(function(r) {
poll.start();
ui.hideModal();
if (r && r.success) {
ui.addNotification(null, E('p', {}, _('App updated: ') + name), 'success');
self.refresh().then(function() { self.updateStatus(); });
} else {
ui.addNotification(null, E('p', {}, r.message || _('Upload failed')), 'error');
}
}).catch(function(err) {
poll.start();
ui.hideModal();
ui.addNotification(null, E('p', {}, _('Error: ') + (err.message || err)), 'error');
});
};
reader.readAsArrayBuffer(file);
}
}, _('Upload'))
])
]);
},
// Gitea pull/sync
giteaPull: function(name) {
var self = this;
ui.showModal(_('Syncing...'), [
E('p', { 'class': 'spinning' }, _('Pulling from Gitea...'))
]);
api.giteaPull(name).then(function(r) {
ui.hideModal();
if (r && r.success) {
ui.addNotification(null, E('p', {}, _('Synced from Gitea: ') + name), 'success');
self.refresh().then(function() { self.updateStatus(); });
} else {
ui.addNotification(null, E('p', {}, r.message || _('Sync failed')), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', {}, _('Error: ') + (err.message || err)), 'error');
});
}
});