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:
parent
07705f458c
commit
356e2814ca
@ -305,6 +305,7 @@ return view.extend({
|
|||||||
|
|
||||||
var rows = apps.map(function(app) {
|
var rows = apps.map(function(app) {
|
||||||
var id = app.id || app.name;
|
var id = app.id || app.name;
|
||||||
|
var hasGitea = app.gitea_repo || app.git_url;
|
||||||
return E('tr', {}, [
|
return E('tr', {}, [
|
||||||
E('td', {}, E('strong', {}, app.name)),
|
E('td', {}, E('strong', {}, app.name)),
|
||||||
E('td', {}, app.path ? app.path.split('/').pop() : '-'),
|
E('td', {}, app.path ? app.path.split('/').pop() : '-'),
|
||||||
@ -313,6 +314,16 @@ return view.extend({
|
|||||||
'class': 'cbi-button',
|
'class': 'cbi-button',
|
||||||
'click': function() { self.createInstanceFromApp(id); }
|
'click': function() { self.createInstanceFromApp(id); }
|
||||||
}, _('+ Instance')),
|
}, _('+ 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', {
|
E('button', {
|
||||||
'class': 'cbi-button cbi-button-remove',
|
'class': 'cbi-button cbi-button-remove',
|
||||||
'style': 'margin-left:4px',
|
'style': 'margin-left:4px',
|
||||||
@ -645,5 +656,85 @@ return view.extend({
|
|||||||
}, _('Delete'))
|
}, _('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');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user