feat: add database restore from uploaded SQL file with RESTORE confirmation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
GuavaPopper
2026-06-04 14:53:36 +07:00
parent 3ecf7d937c
commit b76284d8b7
3 changed files with 104 additions and 0 deletions
+72
View File
@@ -100,6 +100,9 @@
<a href="{{ url('/api/admin/backup') }}" class="btn btn-outline gap-2">
<i class="fa-solid fa-database text-success"></i> Backup Database
</a>
<button onclick="document.getElementById('modal_restore').showModal()" class="btn btn-outline gap-2 border-error text-error hover:bg-error hover:text-white">
<i class="fa-solid fa-upload"></i> Restore DB
</button>
<button onclick="openCreateModal()" class="btn btn-primary shadow-lg gap-2">
<i class="fa-solid fa-user-plus"></i> Tambah Pengguna
</button>
@@ -498,6 +501,75 @@
document.getElementById('toastBox').appendChild(t);
setTimeout(() => t.remove(), 3000);
}
// ── RESTORE ─────────────────────────────────────────────────
function updateRestoreBtn() {
document.getElementById('btnRestoreSubmit').disabled =
document.getElementById('restore_confirm').value !== 'RESTORE';
}
async function submitRestore() {
const fileInput = document.getElementById('restore_file');
if (!fileInput.files.length) { showToast('Pilih file .sql terlebih dahulu', 'error'); return; }
const fd = new FormData();
fd.append('file', fileInput.files[0]);
fd.append('confirm', document.getElementById('restore_confirm').value);
const btn = document.getElementById('btnRestoreSubmit');
btn.disabled = true;
btn.innerHTML = '<span class="loading loading-spinner loading-xs"></span> Memproses...';
const res = await fetch('{{ url('/api/admin/restore') }}', {
method: 'POST',
headers: { 'X-CSRF-TOKEN': csrfToken },
body: fd,
});
const json = await res.json();
document.getElementById('modal_restore').close();
document.getElementById('restore_confirm').value = '';
document.getElementById('restore_file').value = '';
btn.disabled = true;
btn.innerHTML = '<i class="fa-solid fa-triangle-exclamation mr-1"></i> Restore Sekarang';
if (json.success) {
showToast('Restore berhasil. Halaman akan dimuat ulang...', 'success');
setTimeout(() => location.reload(), 2000);
} else {
showToast(json.message || 'Restore gagal', 'error');
}
}
</script>
<!-- Modal Restore -->
<dialog id="modal_restore" class="modal">
<div class="modal-box w-11/12 max-w-lg">
<h3 class="font-bold text-lg text-error mb-2">
<i class="fa-solid fa-triangle-exclamation mr-2"></i>Restore Database
</h3>
<div class="alert alert-error mb-4 text-sm">
<i class="fa-solid fa-circle-exclamation"></i>
<span><strong>PERINGATAN:</strong> Operasi ini akan <strong>menghapus seluruh data saat ini</strong> dan menggantinya dengan isi file backup. Tindakan ini <strong>tidak dapat dibatalkan.</strong></span>
</div>
<div class="form-control mb-3">
<label class="label"><span class="label-text font-bold">File Backup (.sql)</span></label>
<input id="restore_file" type="file" accept=".sql,.txt" class="file-input file-input-bordered file-input-error w-full">
</div>
<div class="form-control mb-4">
<label class="label">
<span class="label-text font-bold">Ketik <code class="bg-base-200 px-1 rounded">RESTORE</code> untuk konfirmasi</span>
</label>
<input id="restore_confirm" type="text" placeholder="RESTORE" class="input input-bordered input-error w-full font-mono tracking-widest" oninput="updateRestoreBtn()">
</div>
<div class="modal-action">
<button onclick="document.getElementById('modal_restore').close()" class="btn btn-ghost">Batal</button>
<button id="btnRestoreSubmit" onclick="submitRestore()" class="btn btn-error text-white" disabled>
<i class="fa-solid fa-triangle-exclamation mr-1"></i> Restore Sekarang
</button>
</div>
</div>
<form method="dialog" class="modal-backdrop"><button>close</button></form>
</dialog>
</body>
</html>