use anyhow::Result; use serde_json::{Value, json}; use sqlx::{FromRow, PgPool}; use uuid::Uuid; use crate::db; use crate::output::OutputMode; #[derive(FromRow)] struct DeleteRow { id: Uuid, version: i64, tags: Vec, metadata: Value, encrypted: Vec, } pub async fn run( pool: &PgPool, namespace: &str, kind: &str, name: &str, output: OutputMode, ) -> Result<()> { tracing::debug!(namespace, kind, name, "deleting record"); let mut tx = pool.begin().await?; let row: Option = sqlx::query_as( "SELECT id, version, tags, metadata, encrypted FROM secrets \ WHERE namespace = $1 AND kind = $2 AND name = $3 \ FOR UPDATE", ) .bind(namespace) .bind(kind) .bind(name) .fetch_optional(&mut *tx) .await?; let Some(row) = row else { tx.rollback().await?; tracing::warn!(namespace, kind, name, "record not found for deletion"); match output { OutputMode::Json => println!( "{}", serde_json::to_string_pretty( &json!({"action":"not_found","namespace":namespace,"kind":kind,"name":name}) )? ), OutputMode::JsonCompact => println!( "{}", serde_json::to_string( &json!({"action":"not_found","namespace":namespace,"kind":kind,"name":name}) )? ), _ => println!("Not found: [{}/{}] {}", namespace, kind, name), } return Ok(()); }; // Snapshot before physical delete so the row can be restored via rollback. if let Err(e) = db::snapshot_history( &mut tx, db::SnapshotParams { secret_id: row.id, namespace, kind, name, version: row.version, action: "delete", tags: &row.tags, metadata: &row.metadata, encrypted: &row.encrypted, }, ) .await { tracing::warn!(error = %e, "failed to snapshot history before delete"); } sqlx::query("DELETE FROM secrets WHERE id = $1") .bind(row.id) .execute(&mut *tx) .await?; crate::audit::log_tx(&mut tx, "delete", namespace, kind, name, json!({})).await; tx.commit().await?; match output { OutputMode::Json => println!( "{}", serde_json::to_string_pretty( &json!({"action":"deleted","namespace":namespace,"kind":kind,"name":name}) )? ), OutputMode::JsonCompact => println!( "{}", serde_json::to_string( &json!({"action":"deleted","namespace":namespace,"kind":kind,"name":name}) )? ), _ => println!("Deleted: [{}/{}] {}", namespace, kind, name), } Ok(()) }