feat(cli/org): org rm/restore/purge trash lifecycle (collection-scoped)
This commit is contained in:
@@ -1044,6 +1044,94 @@ pub fn run_edit(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolve a query to (collection, item) with grant enforcement. Used by the
|
||||
/// trash-lifecycle commands.
|
||||
fn open_org_item(
|
||||
vault: &crate::org_session::UnlockedOrgVault,
|
||||
caller: &relicario_core::OrgMember,
|
||||
query: &str,
|
||||
) -> Result<(String, relicario_core::Item)> {
|
||||
let manifest = vault.load_manifest()?;
|
||||
let visible = manifest.filter_for_member(caller);
|
||||
let entry = resolve_org_query(&visible, query)?;
|
||||
let collection = entry.collection.clone();
|
||||
let id = entry.id.clone();
|
||||
crate::org_session::UnlockedOrgVault::ensure_grant(caller, &collection)?;
|
||||
let item = vault.load_item(&collection, &id)?;
|
||||
Ok((collection, item))
|
||||
}
|
||||
|
||||
pub fn run_rm(dir: &Path, query: &str) -> Result<()> {
|
||||
let vault = crate::org_session::open_org_vault(Some(dir))?;
|
||||
let caller = vault.current_member()?;
|
||||
let (collection, mut item) = open_org_item(&vault, &caller, query)?;
|
||||
|
||||
item.soft_delete();
|
||||
let item_rel = vault.save_item(&collection, &item)?;
|
||||
let mut manifest = vault.load_manifest()?;
|
||||
upsert_org_entry(&mut manifest, &item, &collection);
|
||||
vault.save_manifest(&manifest)?;
|
||||
|
||||
let commit_msg = format!(
|
||||
"org trash: {} ({})\n\nRelicario-Actor: {} {}\nRelicario-Action: item-delete\nRelicario-Collection: {}\nRelicario-Item: {}",
|
||||
crate::helpers::sanitize_for_commit(&item.title), item.id.as_str(),
|
||||
caller.display_name, caller.member_id.as_str(), collection, item.id.as_str()
|
||||
);
|
||||
crate::org_session::org_git_run(&vault.root, &["add", &item_rel, "manifest.enc"], "org rm: git add")?;
|
||||
crate::org_session::org_git_run(&vault.root, &["commit", "-m", &commit_msg], "org rm: git commit")?;
|
||||
println!("Moved to trash: {}", item.title);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_restore(dir: &Path, query: &str) -> Result<()> {
|
||||
let vault = crate::org_session::open_org_vault(Some(dir))?;
|
||||
let caller = vault.current_member()?;
|
||||
let (collection, mut item) = open_org_item(&vault, &caller, query)?;
|
||||
|
||||
item.restore();
|
||||
let item_rel = vault.save_item(&collection, &item)?;
|
||||
let mut manifest = vault.load_manifest()?;
|
||||
upsert_org_entry(&mut manifest, &item, &collection);
|
||||
vault.save_manifest(&manifest)?;
|
||||
|
||||
let commit_msg = format!(
|
||||
"org restore: {} ({})\n\nRelicario-Actor: {} {}\nRelicario-Action: item-restore\nRelicario-Collection: {}\nRelicario-Item: {}",
|
||||
crate::helpers::sanitize_for_commit(&item.title), item.id.as_str(),
|
||||
caller.display_name, caller.member_id.as_str(), collection, item.id.as_str()
|
||||
);
|
||||
crate::org_session::org_git_run(&vault.root, &["add", &item_rel, "manifest.enc"], "org restore: git add")?;
|
||||
crate::org_session::org_git_run(&vault.root, &["commit", "-m", &commit_msg], "org restore: git commit")?;
|
||||
println!("Restored: {}", item.title);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_purge(dir: &Path, query: &str) -> Result<()> {
|
||||
let vault = crate::org_session::open_org_vault(Some(dir))?;
|
||||
let caller = vault.current_member()?;
|
||||
let (collection, item) = open_org_item(&vault, &caller, query)?;
|
||||
let title = item.title.clone();
|
||||
let id = item.id.clone();
|
||||
|
||||
// Remove the blob from disk, drop the manifest entry, stage with git rm.
|
||||
vault.remove_item(&collection, &id)?;
|
||||
let mut manifest = vault.load_manifest()?;
|
||||
manifest.entries.retain(|e| e.id != id);
|
||||
vault.save_manifest(&manifest)?;
|
||||
|
||||
let item_rel = format!("items/{}/{}.enc", collection, id.as_str());
|
||||
crate::helpers::git_rm(&vault.root, &[item_rel], "org purge: git rm")?;
|
||||
crate::org_session::org_git_run(&vault.root, &["add", "manifest.enc"], "org purge: git add manifest")?;
|
||||
|
||||
let commit_msg = format!(
|
||||
"org purge: {} ({})\n\nRelicario-Actor: {} {}\nRelicario-Action: item-purge\nRelicario-Collection: {}\nRelicario-Item: {}",
|
||||
crate::helpers::sanitize_for_commit(&title), id.as_str(),
|
||||
caller.display_name, caller.member_id.as_str(), collection, id.as_str()
|
||||
);
|
||||
crate::org_session::org_git_run(&vault.root, &["commit", "-m", &commit_msg], "org purge: git commit")?;
|
||||
println!("Purged: {title}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Insert-or-replace an `OrgManifestEntry` mirroring the personal-vault
|
||||
/// `Manifest::upsert`. Keyed by item id.
|
||||
fn upsert_org_entry(
|
||||
|
||||
@@ -535,8 +535,12 @@ pub(crate) enum OrgCommands {
|
||||
#[arg(long)] phone: Option<String>,
|
||||
#[arg(long)] full_name: Option<String>,
|
||||
},
|
||||
// Item subcommands (Rm/Restore/Purge) are added by
|
||||
// Task B13, which extends this enum.
|
||||
/// Soft-delete an org item (reversible via `org restore`).
|
||||
Rm { query: String },
|
||||
/// Restore a soft-deleted org item.
|
||||
Restore { query: String },
|
||||
/// Permanently purge an org item (deletes the encrypted blob).
|
||||
Purge { query: String },
|
||||
}
|
||||
|
||||
#[derive(clap::Subcommand)]
|
||||
@@ -689,8 +693,18 @@ fn main() -> Result<()> {
|
||||
let d = crate::org_session::org_dir(dir_path)?;
|
||||
commands::org::run_edit(&d, &query, title, username, url, password, body, email, phone, full_name)?;
|
||||
}
|
||||
// Item dispatch arms (Rm/Restore/Purge) added by
|
||||
// Task B13.
|
||||
OrgCommands::Rm { query } => {
|
||||
let d = crate::org_session::org_dir(dir_path)?;
|
||||
commands::org::run_rm(&d, &query)?;
|
||||
}
|
||||
OrgCommands::Restore { query } => {
|
||||
let d = crate::org_session::org_dir(dir_path)?;
|
||||
commands::org::run_restore(&d, &query)?;
|
||||
}
|
||||
OrgCommands::Purge { query } => {
|
||||
let d = crate::org_session::org_dir(dir_path)?;
|
||||
commands::org::run_purge(&d, &query)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user