This document describes the new real-time backup progress events available via WebSocket connections.
- Event:
backup progress - Type: Real-time progress updates
- Frequency: ~2 updates per second maximum (intelligent throttling)
{
"type": "create|restore",
"percentage": 0-100,
"bytes_written": 1234567,
"bytes_total": 10000000
}| Field | Type | Description |
|---|---|---|
type |
string | Type of operation: "create" for backup creation, "restore" for backup restoration |
percentage |
int | Progress percentage (0-100). For restore operations, this is typically 0 |
bytes_written |
int64 | Number of bytes processed so far |
bytes_total |
int64 | Total bytes to process (for create operations) |
Special Values:
percentage: 100= Operation completed successfullypercentage: -1= Operation failed/erroredpercentage: 0withbytes_written > 0= Restore operation in progress (no percentage available)
const ws = new WebSocket('wss://your-wings-instance/api/servers/{server}/ws');
ws.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.event === 'backup progress') {
const progress = data.args[0];
switch (progress.type) {
case 'create':
handleBackupProgress(progress);
break;
case 'restore':
handleRestoreProgress(progress);
break;
}
}
});
function handleBackupProgress(progress) {
if (progress.percentage === -1) {
console.error('Backup failed!');
return;
}
if (progress.percentage === 100) {
console.log('Backup completed successfully!');
return;
}
const percent = progress.percentage;
const mb_written = Math.round(progress.bytes_written / 1024 / 1024);
const mb_total = Math.round(progress.bytes_total / 1024 / 1024);
console.log(`Backup progress: ${percent}% (${mb_written}MB / ${mb_total}MB)`);
// Update UI progress bar
document.getElementById('progress-bar').style.width = `${percent}%`;
document.getElementById('progress-text').textContent =
`${percent}% - ${mb_written}MB of ${mb_total}MB`;
}
function handleRestoreProgress(progress) {
if (progress.percentage === -1) {
console.error('Restore failed!');
return;
}
if (progress.percentage === 100) {
console.log('Restore completed successfully!');
return;
}
const files_processed = progress.bytes_written;
console.log(`Restore progress: ${files_processed} files processed`);
// Update UI with file count
document.getElementById('restore-status').textContent =
`Processing... ${files_processed} files restored`;
}import { useState, useEffect } from 'react';
function useBackupProgress(websocket) {
const [progress, setProgress] = useState(null);
useEffect(() => {
if (!websocket) return;
const handleMessage = (event) => {
const data = JSON.parse(event.data);
if (data.event === 'backup progress') {
setProgress(data.args[0]);
}
};
websocket.addEventListener('message', handleMessage);
return () => websocket.removeEventListener('message', handleMessage);
}, [websocket]);
return progress;
}
function BackupProgressComponent({ websocket }) {
const progress = useBackupProgress(websocket);
if (!progress) return null;
if (progress.percentage === -1) {
return <div className="error">❌ Operation failed</div>;
}
if (progress.percentage === 100) {
return <div className="success">✅ Operation completed</div>;
}
if (progress.type === 'create') {
const percent = progress.percentage;
const sizeMB = Math.round(progress.bytes_written / 1024 / 1024);
const totalMB = Math.round(progress.bytes_total / 1024 / 1024);
return (
<div className="progress">
<div className="progress-bar" style={{ width: `${percent}%` }} />
<span>Creating backup: {percent}% ({sizeMB}MB / {totalMB}MB)</span>
</div>
);
}
if (progress.type === 'restore') {
const files = progress.bytes_written;
return (
<div className="progress">
<div className="spinner" />
<span>Restoring backup: {files} files processed</span>
</div>
);
}
return null;
}1. { "type": "create", "percentage": 0, "bytes_written": 0, "bytes_total": 104857600 }
2. { "type": "create", "percentage": 15, "bytes_written": 15728640, "bytes_total": 104857600 }
3. { "type": "create", "percentage": 32, "bytes_written": 33554432, "bytes_total": 104857600 }
4. { "type": "create", "percentage": 58, "bytes_written": 60817408, "bytes_total": 104857600 }
5. { "type": "create", "percentage": 89, "bytes_written": 93323264, "bytes_total": 104857600 }
6. { "type": "create", "percentage": 100, "bytes_written": 104857600, "bytes_total": 104857600 }
1. { "type": "restore", "percentage": 0, "bytes_written": 0, "bytes_total": 0 }
2. { "type": "restore", "percentage": 0, "bytes_written": 10, "bytes_total": 0 }
3. { "type": "restore", "percentage": 0, "bytes_written": 20, "bytes_total": 0 }
4. { "type": "restore", "percentage": 0, "bytes_written": 30, "bytes_total": 0 }
5. { "type": "restore", "percentage": 100, "bytes_written": 35, "bytes_total": 0 }
1. { "type": "create", "percentage": 0, "bytes_written": 0, "bytes_total": 104857600 }
2. { "type": "create", "percentage": 25, "bytes_written": 26214400, "bytes_total": 104857600 }
3. { "type": "create", "percentage": -1, "bytes_written": 26214400, "bytes_total": 104857600 }
- Update Frequency: Maximum ~2 updates per second per operation
- Bandwidth Usage: ~100 bytes per update
- CPU Overhead: <0.00001% of backup operation time
- Memory Usage: ~80 bytes per active backup operation
- Progress updates are throttled to prevent WebSocket spam
- Updates only sent when percentage increases by ≥1%
- Minimum 500ms interval between updates
- No throttling for initial (0%) and final (100%/-1%) updates
- Progress events are sent asynchronously and will never block backup operations
- If WebSocket publishing fails, backup operations continue normally
- All progress callbacks include panic recovery to ensure backup stability
- Local Backups: Full progress tracking with accurate percentages and byte counts
- S3 Backups: Limited progress tracking (archive creation phase only)
- Restore Operations: File-count based progress (no percentage estimates)
This feature is fully backwards compatible. Existing backup operations will work identically with zero changes required. Progress events are additive functionality only.