Release v0.5.1: namespace integrity, dashboard parity and QR hardening
This commit is contained in:
1 parent
0295b4bd7c
commit
115f6e9a23
45 files changed
+5851
-1765
No files matched your search
+83
-22
@@ -15,34 +15,95 @@ pub fn perform_restore(
|
||||
let tar_gz = GzDecoder::new(f);
|
||||
let mut archive = Archive::new(tar_gz);
|
||||
|
||||
// 2. Validate that the archive contains the expected BZOD database files
|
||||
let mut has_admin = false;
|
||||
let mut has_content = false;
|
||||
let mut has_analytics = false;
|
||||
let mut has_system = false;
|
||||
// 2. Unpack to temporary directory first
|
||||
let temp_dir =
|
||||
std::env::temp_dir().join(format!("bzod_system_restore_{}", uuid::Uuid::new_v4()));
|
||||
std::fs::create_dir_all(&temp_dir)?;
|
||||
|
||||
for entry_res in archive.entries()? {
|
||||
let entry = entry_res?;
|
||||
let path = entry.path()?;
|
||||
let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
|
||||
match file_name {
|
||||
"admin.db" => has_admin = true,
|
||||
"content.db" => has_content = true,
|
||||
"analytics.db" => has_analytics = true,
|
||||
"system.db" => has_system = true,
|
||||
_ => {}
|
||||
if let Err(e) = archive.unpack(&temp_dir) {
|
||||
let _ = std::fs::remove_dir_all(&temp_dir);
|
||||
return Err(e.into());
|
||||
}
|
||||
|
||||
// 3. Run validation on temp_dir
|
||||
let mut temp_config = Config::load();
|
||||
temp_config.data_dir = temp_dir.clone();
|
||||
|
||||
// Namespace audit
|
||||
match crate::db::users::audit_slug_namespace(&temp_config) {
|
||||
Ok(report) => {
|
||||
if !report.duplicates.is_empty() {
|
||||
let _ = std::fs::remove_dir_all(&temp_dir);
|
||||
return Err(
|
||||
format!("Slug conflicts detected in backup: {:?}", report.duplicates).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = std::fs::remove_dir_all(&temp_dir);
|
||||
return Err(format!("Failed to audit slug namespace in backup: {}", e).into());
|
||||
}
|
||||
}
|
||||
|
||||
if !has_admin || !has_content || !has_analytics || !has_system {
|
||||
return Err("Archive is missing one or more required database files (admin.db, content.db, analytics.db, system.db)".into());
|
||||
// Registry integrity check
|
||||
let system_db_path = if temp_dir.join("admin/system.db").exists() {
|
||||
temp_dir.join("admin/system.db")
|
||||
} else {
|
||||
temp_dir.join("system.db")
|
||||
};
|
||||
let users_db_path = if temp_dir.join("admin/users.db").exists() {
|
||||
temp_dir.join("admin/users.db")
|
||||
} else {
|
||||
temp_dir.join("users.db")
|
||||
};
|
||||
|
||||
if system_db_path.exists() && users_db_path.exists() {
|
||||
let system_conn = rusqlite::Connection::open(&system_db_path)?;
|
||||
let users_conn = rusqlite::Connection::open(&users_db_path)?;
|
||||
match crate::db::users::verify_global_slug_registry_integrity(
|
||||
&system_conn,
|
||||
&users_conn,
|
||||
&temp_dir,
|
||||
) {
|
||||
Ok((errors, _warnings)) => {
|
||||
if !errors.is_empty() {
|
||||
let _ = std::fs::remove_dir_all(&temp_dir);
|
||||
return Err(format!("Registry integrity errors in backup: {:?}", errors).into());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = std::fs::remove_dir_all(&temp_dir);
|
||||
return Err(format!("Failed to verify registry integrity in backup: {}", e).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Unpack archive to data_dir
|
||||
let f2 = File::open(file_path)?;
|
||||
let tar_gz2 = GzDecoder::new(f2);
|
||||
let mut archive2 = Archive::new(tar_gz2);
|
||||
archive2.unpack(data_dir)?;
|
||||
// 4. If validation succeeds, copy temp_dir contents to data_dir
|
||||
if data_dir.exists() {
|
||||
let _ = std::fs::remove_dir_all(data_dir);
|
||||
}
|
||||
std::fs::create_dir_all(data_dir)?;
|
||||
|
||||
fn copy_dir_all(src: &std::path::Path, dst: &std::path::Path) -> std::io::Result<()> {
|
||||
std::fs::create_dir_all(dst)?;
|
||||
for entry in std::fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let ty = entry.file_type()?;
|
||||
if ty.is_dir() {
|
||||
copy_dir_all(&entry.path(), &dst.join(entry.file_name()))?;
|
||||
} else {
|
||||
std::fs::copy(entry.path(), dst.join(entry.file_name()))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
if let Err(e) = copy_dir_all(&temp_dir, data_dir) {
|
||||
let _ = std::fs::remove_dir_all(&temp_dir);
|
||||
return Err(format!("Failed to copy restored files: {}", e).into());
|
||||
}
|
||||
|
||||
let _ = std::fs::remove_dir_all(&temp_dir);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user