Add compression support to backup workflow
BackupScheduler Enhancement: - copy_file() now compresses files using ZSTD or LZ4 - min_size threshold: 1024 bytes (smaller files not compressed) - compression level: 3 (balanced speed/compression) BackupConfigResponse Updated: - Added compress, encrypt, include_checksums fields - compress: 'none' | 'lz4' | 'zstd' - Default: 'zstd' REST API Enhancement: - GET /api/v2/backup/config returns full config - POST /api/v2/backup/config accepts compression settings Test Results: - Set compress='lz4': ✅ Config updated - Set compress='zstd': ✅ Config updated - Compression applied via run_backup() (scheduled backup) Note: Direct create_snapshot API doesn't use compression (scheduler.run_backup() is the primary backup mechanism) Build: 495 tests pass
This commit is contained in:
BIN
data/auth.sqlite
BIN
data/auth.sqlite
Binary file not shown.
@@ -2750,6 +2750,9 @@ pub struct BackupConfigResponse {
|
|||||||
pub interval_hours: u64,
|
pub interval_hours: u64,
|
||||||
pub max_snapshots: usize,
|
pub max_snapshots: usize,
|
||||||
pub auto_cleanup: bool,
|
pub auto_cleanup: bool,
|
||||||
|
pub compress: String,
|
||||||
|
pub encrypt: bool,
|
||||||
|
pub include_checksums: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
@@ -2790,24 +2793,37 @@ async fn get_backup_stats_handler() -> Json<BackupStatsResponse> {
|
|||||||
async fn get_backup_config_handler() -> Json<BackupConfigResponse> {
|
async fn get_backup_config_handler() -> Json<BackupConfigResponse> {
|
||||||
let scheduler = BACKUP_SCHEDULER.lock().unwrap();
|
let scheduler = BACKUP_SCHEDULER.lock().unwrap();
|
||||||
let config = scheduler.get_config();
|
let config = scheduler.get_config();
|
||||||
|
let compress_name = match config.compress {
|
||||||
|
crate::vfs::VfsCompression::None => "none",
|
||||||
|
crate::vfs::VfsCompression::Lz4 => "lz4",
|
||||||
|
crate::vfs::VfsCompression::Zstd => "zstd",
|
||||||
|
};
|
||||||
Json(BackupConfigResponse {
|
Json(BackupConfigResponse {
|
||||||
enabled: config.enabled,
|
enabled: config.enabled,
|
||||||
interval_hours: config.interval_hours,
|
interval_hours: config.interval_hours,
|
||||||
max_snapshots: config.max_snapshots,
|
max_snapshots: config.max_snapshots,
|
||||||
auto_cleanup: config.auto_cleanup,
|
auto_cleanup: config.auto_cleanup,
|
||||||
|
compress: compress_name.to_string(),
|
||||||
|
encrypt: config.encrypt,
|
||||||
|
include_checksums: config.include_checksums,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_backup_config_handler(Json(config): Json<BackupConfigResponse>) -> Json<serde_json::Value> {
|
async fn set_backup_config_handler(Json(config): Json<BackupConfigResponse>) -> Json<serde_json::Value> {
|
||||||
let mut scheduler = BACKUP_SCHEDULER.lock().unwrap();
|
let mut scheduler = BACKUP_SCHEDULER.lock().unwrap();
|
||||||
|
let compress = match config.compress.as_str() {
|
||||||
|
"lz4" => crate::vfs::VfsCompression::Lz4,
|
||||||
|
"zstd" => crate::vfs::VfsCompression::Zstd,
|
||||||
|
_ => crate::vfs::VfsCompression::None,
|
||||||
|
};
|
||||||
let new_config = BackupScheduleConfig {
|
let new_config = BackupScheduleConfig {
|
||||||
enabled: config.enabled,
|
enabled: config.enabled,
|
||||||
interval_hours: config.interval_hours,
|
interval_hours: config.interval_hours,
|
||||||
max_snapshots: config.max_snapshots,
|
max_snapshots: config.max_snapshots,
|
||||||
auto_cleanup: config.auto_cleanup,
|
auto_cleanup: config.auto_cleanup,
|
||||||
compress: scheduler.get_config().compress.clone(),
|
compress,
|
||||||
encrypt: scheduler.get_config().encrypt,
|
encrypt: config.encrypt,
|
||||||
include_checksums: scheduler.get_config().include_checksums,
|
include_checksums: config.include_checksums,
|
||||||
};
|
};
|
||||||
scheduler.set_config(new_config);
|
scheduler.set_config(new_config);
|
||||||
Json(serde_json::json!({"success": true, "message": "Backup config updated"}))
|
Json(serde_json::json!({"success": true, "message": "Backup config updated"}))
|
||||||
|
|||||||
@@ -180,14 +180,28 @@ impl BackupScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn copy_file(&self, src: &PathBuf, dst: &PathBuf) -> Result<(), VfsError> {
|
fn copy_file(&self, src: &PathBuf, dst: &PathBuf) -> Result<(), VfsError> {
|
||||||
|
use super::compression::Compressor;
|
||||||
|
use super::VfsCompressionConfig;
|
||||||
|
|
||||||
let mut src_file = self.backend.open_file(src, &super::open_flags::OpenFlags::new().read())?;
|
let mut src_file = self.backend.open_file(src, &super::open_flags::OpenFlags::new().read())?;
|
||||||
let data = src_file.read_all()?;
|
let data = src_file.read_all()?;
|
||||||
|
|
||||||
|
let final_data = if self.config.compress != super::VfsCompression::None {
|
||||||
|
let compressor = Compressor::new(VfsCompressionConfig {
|
||||||
|
algorithm: self.config.compress.clone(),
|
||||||
|
min_size: 1024,
|
||||||
|
level: 3,
|
||||||
|
});
|
||||||
|
compressor.compress(&data)?
|
||||||
|
} else {
|
||||||
|
data
|
||||||
|
};
|
||||||
|
|
||||||
let mut dst_file = self.backend.open_file(
|
let mut dst_file = self.backend.open_file(
|
||||||
dst,
|
dst,
|
||||||
&super::open_flags::OpenFlags::new().write().create().truncate(),
|
&super::open_flags::OpenFlags::new().write().create().truncate(),
|
||||||
)?;
|
)?;
|
||||||
dst_file.write_all(&data)?;
|
dst_file.write_all(&final_data)?;
|
||||||
dst_file.flush()?;
|
dst_file.flush()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user