diff --git a/package.json b/package.json
index 8b5231b..1cbdd53 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,8 @@
"dotenv": "^16.3.1",
"cors": "^2.8.5",
"express-session": "^1.17.3",
- "bcryptjs": "^2.4.3"
+ "bcryptjs": "^2.4.3",
+ "multer": "^1.4.5-lts.1"
},
"devDependencies": {
"nodemon": "^3.0.1"
diff --git a/public/assets/app.js b/public/assets/app.js
index 9c66407..62a2b1c 100644
--- a/public/assets/app.js
+++ b/public/assets/app.js
@@ -1342,6 +1342,57 @@
}
}
+ async uploadBackup() {
+ const fileInput = document.getElementById('backupFileInput');
+ const file = fileInput.files[0];
+
+ if (!file) {
+ this.showToast('No file selected', 'error');
+ return;
+ }
+
+ if (!file.name.endsWith('.tar.gz')) {
+ this.showToast('Only .tar.gz files are supported', 'error');
+ fileInput.value = '';
+ return;
+ }
+
+ try {
+ // Show loading state
+ const uploadBtn = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent.includes('Upload archive'));
+ const originalText = uploadBtn.textContent;
+ uploadBtn.disabled = true;
+ uploadBtn.textContent = 'Uploading...';
+
+ const formData = new FormData();
+ formData.append('file', file);
+
+ const response = await fetch('/api/backups/upload', {
+ method: 'POST',
+ body: formData,
+ });
+
+ const result = await response.json();
+
+ if (!response.ok) {
+ throw new Error(result.error || 'Failed to upload backup');
+ }
+
+ this.showToast('Backup uploaded successfully', 'success');
+ fileInput.value = '';
+ await this.loadBackups();
+ } catch (err) {
+ this.showToast(err.message, 'error');
+ fileInput.value = '';
+ } finally {
+ const uploadBtn = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent.includes('Upload') || btn.textContent.includes('Uploading'));
+ if (uploadBtn) {
+ uploadBtn.disabled = false;
+ uploadBtn.textContent = 'Upload archive';
+ }
+ }
+ }
+
async loadSettings() {
try {
const response = await fetch('/api/settings');
diff --git a/public/index.html b/public/index.html
index e41e80e..abf922b 100644
--- a/public/index.html
+++ b/public/index.html
@@ -330,7 +330,11 @@ SELECT * FROM users LIMIT 10;">
Backups
Archives contain the SQL dump and, if enabled, the application snapshot.
-
+
+
+
+
+