<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DB Storage Dump/Restore Client</title>
<style>
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 700px; margin: 40px auto; padding: 20px; border: 1px solid #e0e0e0; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.05); }
h1 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; margin-bottom: 20px; }
h2 { color: #007bff; margin-top: 30px; border-left: 5px solid #007bff; padding-left: 10px; }
.section { margin-bottom: 30px; padding: 15px; background-color: #f9f9f9; border-radius: 8px; }
label { display: block; margin-bottom: 8px; font-weight: 600; color: #555; }
input[type="file"], select { padding: 10px; border: 1px solid #ccc; border-radius: 4px; width: 100%; box-sizing: border-box; margin-top: 5px; }
button {
padding: 10px 15px;
background-color: #28a745;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
font-size: 16px;
transition: background-color 0.3s;
}
button:hover { background-color: #218838; }
#status-box {
margin-top: 20px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #fff;
white-space: pre-wrap;
font-family: Consolas, monospace;
font-size: 14px;
min-height: 50px;
}
.error { border-color: red !important; background-color: #ffebeb; color: #a94442; }
.success { border-color: green !important; background-color: #dff0d8; color: #3c763d; }
</style>
</head>
<body>
<h1>💾 DB Storage Operations</h1>
<p>API Endpoint: <code>/storage-dump</code></p>
<div class="section">
<h2>📥 Database Dump (GET)</h2>
<p>This will initiate a <b>GET</b> request to download the full database state as a JSON file.</p>
<button onclick="handleDump()">Download DB Dump</button>
</div>
<div class="section">
<h2>⬆️ Database Restore (POST)</h2>
<form id="restoreForm">
<label for="dumpFile">Select JSON Dump File:</label>
<input type="file" id="dumpFile" name="dumpFile" accept=".json" required>
<label for="restoreMode">Restore Mode:</label>
<select id="restoreMode" name="restoreMode">
<option value="merge">Merge (UPSERT)</option>
<option value="override">Override (DELETE ALL + UPSERT)</option>
</select>
<button type="submit">Upload and Restore</button>
</form>
</div>
<div id="status-box">Status: Ready.</div>
<script>
const API_ENDPOINT = "/storage-dump";
const statusBox = document.getElementById('status-box');
/**
* Clears status box and updates with new message.
* @param {string} message - The message to display.
* @param {boolean} isError - Whether the message indicates an error.
*/
function updateStatus(message, isError = false) {
statusBox.className = '';
statusBox.textContent = message;
if (isError) {
statusBox.classList.add('error');
} else {
statusBox.classList.add('success');
}
}
/**
* Handles the GET request for downloading the DB dump.
*/
function handleDump() {
updateStatus("Dumping database... Please wait.", false);
fetch(API_ENDPOINT)
.then(response => {
if (!response.ok) {
return response.text().then(text => {
throw new Error(`HTTP Error ${response.status}: ${text}`);
});
}
// Extract filename from Content-Disposition header, or use a default
const contentDisp = response.headers.get('Content-Disposition');
let filename = 'db_dump.json';
if (contentDisp) {
const match = contentDisp.match(/filename="(.+?)"/);
if (match && match[1]) {
filename = match[1];
}
}
return response.blob().then(blob => ({ blob, filename }));
})
.then(({ blob, filename }) => {
// Create a link element to trigger the download
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
updateStatus(`Database dump downloaded successfully as **${filename}**.`, false);
})
.catch(error => {
updateStatus(`Dump failed: ${error.message}`, true);
console.error('Dump Error:', error);
});
}
/**
* Handles the POST request for restoring the DB from an uploaded file.
*/
document.getElementById('restoreForm').addEventListener('submit', function(e) {
e.preventDefault();
const fileInput = document.getElementById('dumpFile');
const mode = document.getElementById('restoreMode').value;
const file = fileInput.files[0];
if (!file) {
updateStatus("Please select a dump file to upload.", true);
return;
}
updateStatus(`Restoring database in **${mode}** mode... Uploading **${file.name}**...`, false);
// Use a FormData object to send the file content
const formData = new FormData();
formData.append('data', file); // The server-side should handle reading the file content from the body
// Read file content as text/bytes to send directly in the body
const reader = new FileReader();
reader.onload = function(event) {
fetch(`${API_ENDPOINT}?mode=${mode}`, {
method: 'POST',
headers: {
// The file content is the body, so set Content-Type to JSON (or leave default if server reads raw body)
'Content-Type': 'application/json'
},
body: event.target.result // Send the raw file content (JSON string/bytes)
})
.then(response => {
// Check if response has content before trying to parse JSON
const contentType = response.headers.get("content-type");
if (contentType && contentType.indexOf("application/json") !== -1) {
return response.json().then(data => ({ response, data }));
} else {
return response.text().then(text => ({ response, data: text }));
}
})
.then(({ response, data }) => {
if (!response.ok) {
const errorMessage = typeof data === 'object' && data.message ? data.message : data.toString();
throw new Error(`HTTP Error ${response.status}: ${errorMessage}`);
}
const message = typeof data === 'object' && data.message ? data.message : "Restore command acknowledged successfully.";
updateStatus(`Restore successful! ${message}`, false);
})
.catch(error => {
updateStatus(`Restore failed: ${error.message}`, true);
console.error('Restore Error:', error);
});
};
// Read the file as a text string (assuming the dump file is readable JSON)
reader.readAsText(file);
});
</script>
</body>
</html>