diff options
| author | Doan Luu Duc Tai <doan@luuduct.ai> | 2026-10-31 07:39:18 -0500 |
|---|---|---|
| committer | Doan Luu Duc Tai <doan@luuduct.ai> | 2026-10-31 07:39:18 -0500 |
| commit | d809d86182ab802472ae7d5e74cab66ba7bfa397 (patch) | |
| tree | 36718e8d91b13b2714cbfe7877353ac7625f2f44 /src/archive.rs | |
| parent | dd1d5863ef865ebe29dc93d03ca6abc0aaf48bee (diff) | |
Package private chat for HTTPS and verify portable encrypted recovery
Diffstat (limited to 'src/archive.rs')
| -rw-r--r-- | src/archive.rs | 25 |
1 files changed, 18 insertions, 7 deletions
diff --git a/src/archive.rs b/src/archive.rs index 4d2c01c..1e58992 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -84,7 +84,8 @@ fn snapshot(project: &Project, records: &[Value], stage: &Path) -> Result<()> { ids: Vec::new(), }; for record in records { - if field(record, &["State", "Running"])?.as_bool() == Some(true) + if field(record, &["Config", "Labels", "io.stack.service"])?.as_str() != Some("chat") + && field(record, &["State", "Running"])?.as_bool() == Some(true) && field(record, &["State", "Paused"])?.as_bool() == Some(false) { let id = field(record, &["Id"])? @@ -100,12 +101,12 @@ fn snapshot(project: &Project, records: &[Value], stage: &Path) -> Result<()> { resume } -fn snapshot_chat(root: &Path, stage: &Path) -> Result<()> { - let source = root.join("var/data/chat/chat.sqlite"); +fn snapshot_chat(root: &Path, stage: &Path, name: &str) -> Result<()> { + let source = root.join("var/data").join(name).join("chat.sqlite"); if !source.is_file() { return Ok(()); } - let directory = stage.join("var/data/chat"); + let directory = stage.join("var/data").join(name); files::private_dir(&directory)?; let database = rusqlite::Connection::open_with_flags(source, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY)?; @@ -128,14 +129,19 @@ fn snapshot_files(project: &Project, records: &[Value], stage: &Path) -> Result< let data = project.root.join("var/data"); for item in fs::read_dir(&data)? { let entry = item?; - if entry.file_name() != "caddy" && entry.file_name() != "chat" { + if !["caddy", "chat", "chat-local"] + .iter() + .any(|name| entry.file_name() == *name) + { files::copy_tree( &entry.path(), &stage.join("var/data").join(entry.file_name()), )?; } } - snapshot_chat(&project.root, stage)?; + for name in ["chat", "chat-local"] { + snapshot_chat(&project.root, stage, name)?; + } let caddy = records .iter() .find(|record| { @@ -167,6 +173,7 @@ fn snapshot_files(project: &Project, records: &[Value], stage: &Path) -> Result< .find(|path| path.is_file()) .context("Build a release locally before exporting so the backup includes a portable binary")?; fs::copy(executable, stage.join("bin/stack"))?; + crate::chat::copy_artifacts(project, stage)?; Ok(()) } @@ -310,6 +317,10 @@ fn backup_inner( !output.exists(), "An export with this timestamp already exists" ); + fs::set_permissions( + &encrypted, + std::os::unix::fs::PermissionsExt::from_mode(0o600), + )?; fs::rename(&encrypted, &output)?; let digest = format!("{} {name}\n", files::hash_file(&output)?); files::write( @@ -397,7 +408,7 @@ mod tests { )?; let transaction = connection.unchecked_transaction()?; transaction.execute("INSERT INTO messages VALUES ('uncommitted')", [])?; - super::snapshot_chat(&root, &stage)?; + super::snapshot_chat(&root, &stage, "chat")?; let snapshot = stage.join("var/data/chat/chat.sqlite"); let restored = rusqlite::Connection::open(&snapshot)?; let count: i64 = |
