All-in-One Migration Tool
:root {
–bg: #f0f0f1;
–card-bg: #ffffff;
–primary: #2271b1;
–primary-hover: #135e96;
–success: #00a32a;
–danger: #d63638;
–warning: #dba617;
–text: #1d2327;
–text-light: #646970;
–border: #c3c4c7;
–shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06);
–shadow-lg: 0 10px 40px rgba(0, 0, 0, 0.12), 0 4px 12px rgba(0, 0, 0, 0.08);
–radius: 8px;
–radius-sm: 5px;
–transition: 0.2s ease;
–font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, sans-serif;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(–font);
background: var(–bg);
color: var(–text);
min-height: 100vh;
display: flex;
justify-content: center;
padding: 30px 20px;
}
.container {
width: 100%;
max-width: 780px;
display: flex;
flex-direction: column;
gap: 24px;
}
/* Header */
.header {
display: flex;
align-items: center;
gap: 14px;
padding: 0 4px;
}
.header .logo {
width: 44px;
height: 44px;
background: linear-gradient(135deg, #2271b1, #1a5a8b);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 20px;
font-weight: 700;
flex-shrink: 0;
box-shadow: 0 4px 12px rgba(34, 113, 177, 0.3);
}
.header h1 {
font-size: 1.35rem;
font-weight: 700;
letter-spacing: -0.3px;
color: #1d2327;
}
.header .badge {
background: #e9f5ff;
color: #2271b1;
font-size: 0.7rem;
font-weight: 600;
padding: 4px 10px;
border-radius: 20px;
letter-spacing: 0.3px;
text-transform: uppercase;
}
/* Cards */
.card {
background: var(–card-bg);
border-radius: var(–radius);
box-shadow: var(–shadow);
border: 1px solid #e5e7eb;
overflow: hidden;
transition: var(–transition);
}
.card-header {
padding: 18px 22px;
border-bottom: 1px solid #f0f0f1;
display: flex;
align-items: center;
gap: 10px;
font-weight: 600;
font-size: 0.95rem;
}
.card-header .icon {
width: 34px;
height: 34px;
border-radius: 7px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
flex-shrink: 0;
}
.icon-export {
background: #f0f6fc;
color: #2271b1;
}
.icon-import {
background: #f0faf3;
color: #00a32a;
}
.icon-backups {
background: #fef8ee;
color: #dba617;
}
.icon-settings {
background: #f4f4f5;
color: #646970;
}
.card-body {
padding: 22px;
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 11px 20px;
border: none;
border-radius: var(–radius-sm);
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all var(–transition);
font-family: var(–font);
letter-spacing: 0.1px;
text-decoration: none;
white-space: nowrap;
}
.btn-primary {
background: var(–primary);
color: #fff;
}
.btn-primary:hover {
background: var(–primary-hover);
box-shadow: 0 4px 14px rgba(34, 113, 177, 0.35);
transform: translateY(-1px);
}
.btn-success {
background: var(–success);
color: #fff;
}
.btn-success:hover {
background: #008a20;
box-shadow: 0 4px 14px rgba(0, 163, 42, 0.35);
transform: translateY(-1px);
}
.btn-outline {
background: #fff;
color: var(–text);
border: 2px solid #dcdcde;
}
.btn-outline:hover {
border-color: var(–primary);
color: var(–primary);
background: #f9fafe;
}
.btn-danger-outline {
background: #fff;
color: var(–danger);
border: 2px solid #f5c6cb;
}
.btn-danger-outline:hover {
background: #fef2f2;
border-color: var(–danger);
}
.btn-sm {
padding: 7px 14px;
font-size: 0.8rem;
border-radius: 4px;
}
.btn:disabled {
opacity: 0.55;
pointer-events: none;
cursor: not-allowed;
}
.btn .spinner {
width: 16px;
height: 16px;
border: 2px solid transparent;
border-top-color: currentColor;
border-radius: 50%;
animation: spin 0.7s linear infinite;
display: none;
}
.btn.loading .spinner {
display: inline-block;
}
.btn.loading .btn-text {
opacity: 0.7;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Drop zone */
.drop-zone {
border: 2.5px dashed #c3c4c7;
border-radius: var(–radius);
padding: 40px 30px;
text-align: center;
cursor: pointer;
transition: all var(–transition);
background: #fafafa;
position: relative;
}
.drop-zone:hover,
.drop-zone.drag-over {
border-color: var(–primary);
background: #f5f9fd;
box-shadow: inset 0 0 0 4px rgba(34, 113, 177, 0.04);
}
.drop-zone.drag-over {
background: #eef5fb;
border-color: #2271b1;
}
.drop-zone .dz-icon {
font-size: 48px;
margin-bottom: 10px;
display: block;
opacity: 0.7;
}
.drop-zone p {
color: var(–text-light);
font-size: 0.9rem;
margin: 4px 0;
}
.drop-zone .dz-supported {
font-size: 0.75rem;
color: #a7aaad;
margin-top: 8px;
}
.drop-zone input[type="file"] {
position: absolute;
inset: 0;
opacity: 0;
cursor: pointer;
}
/* Progress */
.progress-section {
display: none;
margin-top: 16px;
}
.progress-section.active {
display: block;
}
.progress-bar-outer {
background: #e5e7eb;
border-radius: 20px;
height: 12px;
overflow: hidden;
margin: 8px 0;
}
.progress-bar-inner {
height: 100%;
background: linear-gradient(90deg, #2271b1, #3b8fd4);
border-radius: 20px;
width: 0%;
transition: width 0.3s ease;
position: relative;
}
.progress-bar-inner.striped {
background: linear-gradient(90deg, #2271b1 0%, #3b8fd4 25%, #2271b1 50%, #3b8fd4 75%, #2271b1 100%);
background-size: 40px 100%;
animation: stripe-move 0.8s linear infinite;
}
@keyframes stripe-move {
to {
background-position: 40px 0;
}
}
.progress-text {
font-size: 0.85rem;
color: var(–text-light);
display: flex;
justify-content: space-between;
}
.progress-percentage {
font-weight: 700;
color: var(–primary);
}
/* File info */
.file-info {
display: none;
background: #f9fafb;
border-radius: var(–radius-sm);
padding: 14px 18px;
margin-top: 12px;
align-items: center;
gap: 12px;
border: 1px solid #e5e7eb;
}
.file-info.active {
display: flex;
}
.file-info .fi-icon {
font-size: 28px;
flex-shrink: 0;
}
.file-info .fi-details {
flex: 1;
min-width: 0;
}
.file-info .fi-name {
font-weight: 600;
font-size: 0.9rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.file-info .fi-size {
font-size: 0.78rem;
color: var(–text-light);
}
.file-info .fi-remove {
cursor: pointer;
color: #a7aaad;
font-size: 20px;
padding: 4px 8px;
border-radius: 4px;
transition: var(–transition);
background: none;
border: none;
line-height: 1;
}
.file-info .fi-remove:hover {
color: var(–danger);
background: #fef2f2;
}
/* Backup list */
.backup-list {
list-style: none;
}
.backup-item {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 16px;
border-bottom: 1px solid #f0f0f1;
transition: var(–transition);
border-radius: 6px;
}
.backup-item:last-child {
border-bottom: none;
}
.backup-item:hover {
background: #fafafa;
}
.backup-item .bi-icon {
font-size: 24px;
flex-shrink: 0;
}
.backup-item .bi-info {
flex: 1;
min-width: 0;
}
.backup-item .bi-name {
font-weight: 600;
font-size: 0.88rem;
}
.backup-item .bi-meta {
font-size: 0.75rem;
color: var(–text-light);
}
.backup-item .bi-actions {
display: flex;
gap: 6px;
flex-shrink: 0;
}
.empty-state {
text-align: center;
padding: 30px;
color: var(–text-light);
}
.empty-state .empty-icon {
font-size: 40px;
display: block;
margin-bottom: 8px;
opacity: 0.5;
}
/* Toast */
.toast-container {
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 10px;
pointer-events: none;
}
.toast {
background: #1d2327;
color: #fff;
padding: 13px 20px;
border-radius: 8px;
font-size: 0.88rem;
font-weight: 500;
box-shadow: var(–shadow-lg);
animation: slideIn 0.35s ease, fadeOut 0.35s ease 2.8s forwards;
pointer-events: auto;
max-width: 420px;
display: flex;
align-items: center;
gap: 10px;
}
.toast.success {
border-left: 4px solid #00a32a;
}
.toast.error {
border-left: 4px solid #d63638;
}
.toast.info {
border-left: 4px solid #2271b1;
}
@keyframes slideIn {
from {
transform: translateX(120%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes fadeOut {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(80%);
}
}
/* Advanced options */
.advanced-toggle {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
font-weight: 600;
font-size: 0.85rem;
color: var(–primary);
padding: 8px 0;
user-select: none;
}
.advanced-toggle:hover {
color: var(–primary-hover);
}
.advanced-toggle .arrow {
transition: transform 0.25s ease;
font-size: 0.7rem;
}
.advanced-toggle.open .arrow {
transform: rotate(90deg);
}
.advanced-options {
display: none;
padding: 14px 0 0 0;
border-top: 1px solid #f0f0f1;
margin-top: 10px;
}
.advanced-options.open {
display: block;
}
.checkbox-group {
display: flex;
flex-direction: column;
gap: 10px;
}
.checkbox-group label {
display: flex;
align-items: center;
gap: 10px;
font-size: 0.88rem;
cursor: pointer;
padding: 6px 0;
}
.checkbox-group input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: var(–primary);
cursor: pointer;
flex-shrink: 0;
}
/* Responsive */
@media (max-width: 600px) {
.card-body {
padding: 16px;
}
.drop-zone {
padding: 28px 16px;
}
.btn {
padding: 10px 16px;
font-size: 0.82rem;
}
.backup-item {
flex-wrap: wrap;
}
.backup-item .bi-actions {
width: 100%;
justify-content: flex-end;
margin-top: 4px;
}
}
Create a complete backup of your site. Download the file to your computer.
▶ Advanced Options
Restore a backup by uploading a .wpress or .zip file.
📂
Drag & drop your backup file here
or click to browse
Supported: .wpress, .zip (Max: 512 MB)
Uploading & extracting…
0%
⚠️ Warning: Importing will overwrite your current site data. Ensure you have a recent backup.
📭
No backups found on this device.
Exported files saved to this browser will appear here.
(function() {
// ──────────────────────────────────────
// State
// ──────────────────────────────────────
let selectedFile = null;
let isExporting = false;
let isImporting = false;
const MAX_FILE_SIZE = 512 * 1024 * 1024; // 512 MB
const STORAGE_KEY = 'aio_migration_backups';
// ──────────────────────────────────────
// DOM Elements
// ──────────────────────────────────────
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const fileInfo = document.getElementById('fileInfo');
const fileName = document.getElementById('fileName');
const fileSize = document.getElementById('fileSize');
const importBtn = document.getElementById('importBtn');
const clearBtn = document.getElementById('clearBtn');
const exportBtn = document.getElementById('exportBtn');
const exportProgress = document.getElementById('exportProgress');
const exportBar = document.getElementById('exportBar');
const exportStatus = document.getElementById('exportStatus');
const exportPercent = document.getElementById('exportPercent');
const importProgress = document.getElementById('importProgress');
const importBar = document.getElementById('importBar');
const importStatus = document.getElementById('importStatus');
const importPercent = document.getElementById('importPercent');
const importWarning = document.getElementById('importWarning');
const backupList = document.getElementById('backupList');
const emptyBackups = document.getElementById(’emptyBackups');
const toastContainer = document.getElementById('toastContainer');
// ──────────────────────────────────────
// Toast System
// ──────────────────────────────────────
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `toast ${type}`;
const icons = { success: '✅', error: '❌', info: 'ℹ️' };
toast.innerHTML = `
${icons[type] || 'ℹ️'} ${message}`;
toastContainer.appendChild(toast);
setTimeout(() => {
if (toast.parentNode) toast.remove();
}, 3200);
}
// ──────────────────────────────────────
// Formatting
// ──────────────────────────────────────
function formatBytes(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function formatDate(date) {
const d = new Date(date);
return d.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
// ──────────────────────────────────────
// File Handling
// ──────────────────────────────────────
function handleFileSelect(event) {
const file = event.target.files[0];
if (file) processFile(file);
}
function processFile(file) {
const validExtensions = ['.wpress', '.zip'];
const ext = '.' + file.name.split('.').pop().toLowerCase();
if (!validExtensions.includes(ext)) {
showToast('Invalid file type. Please upload a .wpress or .zip file.', 'error');
fileInput.value = ";
return;
}
if (file.size > MAX_FILE_SIZE) {
showToast('File is too large. Maximum size is 512 MB.', 'error');
fileInput.value = ";
return;
}
selectedFile = file;
fileName.textContent = file.name;
fileSize.textContent = formatBytes(file.size);
fileInfo.classList.add('active');
importBtn.disabled = false;
clearBtn.disabled = false;
importWarning.style.display = 'block';
showToast(`File selected: ${file.name}`, 'info');
}
function clearFile() {
selectedFile = null;
fileInput.value = ";
fileInfo.classList.remove('active');
importBtn.disabled = true;
clearBtn.disabled = true;
fileName.textContent = '—';
fileSize.textContent = '—';
importProgress.classList.remove('active');
importBar.style.width = '0%';
importPercent.textContent = '0%';
importStatus.textContent = 'Uploading & extracting…';
importWarning.style.display = 'block';
}
// ──────────────────────────────────────
// Drag & Drop
// ──────────────────────────────────────
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
e.stopPropagation();
dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', (e) => {
e.preventDefault();
e.stopPropagation();
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
e.stopPropagation();
dropZone.classList.remove('drag-over');
const file = e.dataTransfer.files[0];
if (file) {
fileInput.files = e.dataTransfer.files;
processFile(file);
}
});
dropZone.addEventListener('click', () => {
fileInput.click();
});
// ──────────────────────────────────────
// Export Simulation
// ──────────────────────────────────────
function startExport() {
if (isExporting) return;
isExporting = true;
exportBtn.classList.add('loading');
exportBtn.disabled = true;
exportProgress.classList.add('active');
exportBar.style.width = '0%';
exportBar.classList.add('striped');
exportPercent.textContent = '0%';
exportStatus.textContent = 'Preparing export…';
let progress = 0;
const steps = [
{ pct: 15, msg: 'Compressing database tables…', delay: 600 },
{ pct: 30, msg: 'Packaging media files…', delay: 800 },
{ pct: 50, msg: 'Bundling themes…', delay: 700 },
{ pct: 65, msg: 'Bundling plugins…', delay: 700 },
{ pct: 80, msg: 'Creating archive…', delay: 900 },
{ pct: 92, msg: 'Finalizing…', delay: 600 },
{ pct: 100, msg: 'Export complete!', delay: 500 },
];
let currentStep = 0;
function runStep() {
if (currentStep >= steps.length) {
// Done
exportBar.classList.remove('striped');
exportBar.style.width = '100%';
exportPercent.textContent = '100%';
exportStatus.textContent = '✅ Export complete!';
exportBtn.classList.remove('loading');
exportBtn.disabled = false;
isExporting = false;
// Save to localStorage as a simulated backup
const backupData = {
id: Date.now(),
name: `backup-${new Date().toISOString().slice(0,10)}-${Math.random().toString(36).slice(2,8)}.wpress`,
size: Math.floor(Math.random() * 200 * 1024 * 1024) + 10 * 1024 * 1024,
date: new Date().toISOString(),
};
saveBackup(backupData);
renderBackups();
showToast('Export completed successfully! File saved to backups.', 'success');
// Simulate download
simulateDownload(backupData.name, backupData.size);
// Reset after a moment
setTimeout(() => {
exportProgress.classList.remove('active');
exportBar.style.width = '0%';
exportBar.classList.add('striped');
exportPercent.textContent = '0%';
}, 2000);
return;
}
const step = steps[currentStep];
progress = step.pct;
exportBar.style.width = progress + '%';
exportPercent.textContent = progress + '%';
exportStatus.textContent = step.msg;
currentStep++;
setTimeout(runStep, step.delay);
}
runStep();
}
function simulateDownload(filename, size) {
// Create a blob and trigger download
const content = `SIMULATED BACKUP FILE\nName: ${filename}\nSize: ${formatBytes(size)}\nDate: ${new Date().toISOString()}\nThis is a simulated migration backup for demonstration purposes.\n`;
const blob = new Blob([content], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// ──────────────────────────────────────
// Import Simulation
// ──────────────────────────────────────
function startImport() {
if (isImporting || !selectedFile) return;
if (!confirm(
'⚠️ Are you sure you want to import this backup?\n\nThis will overwrite all current site data. This action cannot be undone.'
)) return;
isImporting = true;
importBtn.classList.add('loading');
importBtn.disabled = true;
clearBtn.disabled = true;
importProgress.classList.add('active');
importBar.style.width = '0%';
importBar.classList.add('striped');
importPercent.textContent = '0%';
importStatus.textContent = 'Uploading file…';
importWarning.style.display = 'none';
dropZone.style.pointerEvents = 'none';
dropZone.style.opacity = '0.5';
let progress = 0;
const steps = [
{ pct: 20, msg: 'Uploading & verifying…', delay: 700 },
{ pct: 35, msg: 'Extracting archive…', delay: 1000 },
{ pct: 50, msg: 'Importing database…', delay: 900 },
{ pct: 65, msg: 'Restoring media files…', delay: 800 },
{ pct: 78, msg: 'Restoring themes…', delay: 700 },
{ pct: 88, msg: 'Restoring plugins…', delay: 700 },
{ pct: 95, msg: 'Cleaning up…', delay: 600 },
{ pct: 100, msg: 'Import complete!', delay: 500 },
];
let currentStep = 0;
function runStep() {
if (currentStep >= steps.length) {
importBar.classList.remove('striped');
importBar.style.width = '100%';
importPercent.textContent = '100%';
importStatus.textContent = '✅ Import complete! Reloading…';
importBtn.classList.remove('loading');
isImporting = false;
dropZone.style.pointerEvents = 'auto';
dropZone.style.opacity = '1';
showToast('Import successful! Site restored from backup.', 'success');
clearFile();
setTimeout(() => {
importProgress.classList.remove('active');
importBar.style.width = '0%';
importBar.classList.add('striped');
importPercent.textContent = '0%';
importWarning.style.display = 'block';
}, 2500);
return;
}
const step = steps[currentStep];
progress = step.pct;
importBar.style.width = progress + '%';
importPercent.textContent = progress + '%';
importStatus.textContent = step.msg;
currentStep++;
setTimeout(runStep, step.delay);
}
runStep();
}
// ──────────────────────────────────────
// Backups (localStorage)
// ──────────────────────────────────────
function getBackups() {
try {
const data = localStorage.getItem(STORAGE_KEY);
return data ? JSON.parse(data) : [];
} catch (e) {
return [];
}
}
function saveBackup(backup) {
const backups = getBackups();
backups.unshift(backup);
// Keep max 10 backups
const trimmed = backups.slice(0, 10);
localStorage.setItem(STORAGE_KEY, JSON.stringify(trimmed));
}
function deleteBackup(id) {
let backups = getBackups();
backups = backups.filter(b => b.id !== id);
localStorage.setItem(STORAGE_KEY, JSON.stringify(backups));
renderBackups();
showToast('Backup deleted.', 'info');
}
function downloadBackup(backup) {
simulateDownload(backup.name, backup.size);
showToast(`Downloading ${backup.name}…`, 'success');
}
function renderBackups() {
const backups = getBackups();
backupList.innerHTML = ";
if (backups.length === 0) {
emptyBackups.style.display = 'block';
backupList.style.display = 'none';
} else {
emptyBackups.style.display = 'none';
backupList.style.display = 'block';
backups.forEach(backup => {
const li = document.createElement('li');
li.className = 'backup-item';
li.innerHTML = `
📦
${escapeHTML(backup.name)}
${formatBytes(backup.size)} • ${formatDate(backup.date)}
`;
backupList.appendChild(li);
});
}
}
function escapeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
// Expose functions for inline onclick
window._deleteBackupById = function(id) {
if (confirm('Delete this backup? This cannot be undone.')) {
deleteBackup(id);
}
};
window._downloadBackupById = function(id) {
const backups = getBackups();
const backup = backups.find(b => b.id === id);
if (backup) downloadBackup(backup);
};
// ──────────────────────────────────────
// Advanced Options Toggle
// ──────────────────────────────────────
window.toggleAdvanced = function(id) {
const panel = document.getElementById(id);
const toggle = document.getElementById(id === 'exportAdvanced' ? 'exportAdvancedToggle' : null);
if (!panel) return;
panel.classList.toggle('open');
// Find the toggle element
const allToggles = document.querySelectorAll('.advanced-toggle');
allToggles.forEach(t => {
const targetId = t.getAttribute('onclick')?.match(/'([^']+)'/)?.[1];
if (targetId === id) {
t.classList.toggle('open', panel.classList.contains('open'));
}
});
};
// Also handle toggle click more robustly
document.querySelectorAll('.advanced-toggle').forEach(toggle => {
toggle.addEventListener('click', function(e) {
const match = this.getAttribute('onclick')?.match(/'([^']+)'/);
if (match) {
const id = match[1];
const panel = document.getElementById(id);
if (panel) {
this.classList.toggle('open', panel.classList.contains('open'));
}
}
});
});
// ──────────────────────────────────────
// Init
// ──────────────────────────────────────
renderBackups();
// Keyboard shortcut: Ctrl+E for export
document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'e') {
e.preventDefault();
if (!isExporting) startExport();
}
if ((e.ctrlKey || e.metaKey) && e.key === 'i') {
e.preventDefault();
if (selectedFile && !isImporting) startImport();
}
});
console.log('🚀 All-in-One Migration Tool Ready');
console.log(' Ctrl+E → Quick Export');
console.log(' Ctrl+I → Quick Import (if file selected)');
console.log(' Drop .wpress or .zip files onto the import zone');
console.log(' Exports are saved to localStorage and auto-downloaded');
})();