this repo has no description
1use crate::api::repo::record::utils::{CommitParams, RecordOp, commit_and_log}; 2use crate::api::repo::record::write::prepare_repo_write; 3use crate::repo::tracking::TrackingBlockStore; 4use crate::state::AppState; 5use axum::{ 6 Json, 7 extract::State, 8 http::{HeaderMap, StatusCode}, 9 response::{IntoResponse, Response}, 10}; 11use cid::Cid; 12use jacquard::types::string::Nsid; 13use jacquard_repo::{commit::Commit, mst::Mst, storage::BlockStore}; 14use serde::Deserialize; 15use serde_json::json; 16use std::str::FromStr; 17use std::sync::Arc; 18use tracing::error; 19 20#[derive(Deserialize)] 21pub struct DeleteRecordInput { 22 pub repo: String, 23 pub collection: String, 24 pub rkey: String, 25 #[serde(rename = "swapRecord")] 26 pub swap_record: Option<String>, 27 #[serde(rename = "swapCommit")] 28 pub swap_commit: Option<String>, 29} 30 31pub async fn delete_record( 32 State(state): State<AppState>, 33 headers: HeaderMap, 34 axum::extract::OriginalUri(uri): axum::extract::OriginalUri, 35 Json(input): Json<DeleteRecordInput>, 36) -> Response { 37 let auth = 38 match prepare_repo_write(&state, &headers, &input.repo, "POST", &uri.to_string()).await { 39 Ok(res) => res, 40 Err(err_res) => return err_res, 41 }; 42 43 if let Err(e) = crate::auth::scope_check::check_repo_scope( 44 auth.is_oauth, 45 auth.scope.as_deref(), 46 crate::oauth::RepoAction::Delete, 47 &input.collection, 48 ) { 49 return e; 50 } 51 52 let did = auth.did; 53 let user_id = auth.user_id; 54 let current_root_cid = auth.current_root_cid; 55 56 if let Some(swap_commit) = &input.swap_commit 57 && Cid::from_str(swap_commit).ok() != Some(current_root_cid) 58 { 59 return ( 60 StatusCode::CONFLICT, 61 Json(json!({"error": "InvalidSwap", "message": "Repo has been modified"})), 62 ) 63 .into_response(); 64 } 65 let tracking_store = TrackingBlockStore::new(state.block_store.clone()); 66 let commit_bytes = match tracking_store.get(&current_root_cid).await { 67 Ok(Some(b)) => b, 68 _ => { 69 return ( 70 StatusCode::INTERNAL_SERVER_ERROR, 71 Json(json!({"error": "InternalError", "message": "Commit block not found"})), 72 ) 73 .into_response(); 74 } 75 }; 76 let commit = match Commit::from_cbor(&commit_bytes) { 77 Ok(c) => c, 78 _ => { 79 return ( 80 StatusCode::INTERNAL_SERVER_ERROR, 81 Json(json!({"error": "InternalError", "message": "Failed to parse commit"})), 82 ) 83 .into_response(); 84 } 85 }; 86 let mst = Mst::load(Arc::new(tracking_store.clone()), commit.data, None); 87 let collection_nsid = match input.collection.parse::<Nsid>() { 88 Ok(n) => n, 89 Err(_) => { 90 return ( 91 StatusCode::BAD_REQUEST, 92 Json(json!({"error": "InvalidCollection"})), 93 ) 94 .into_response(); 95 } 96 }; 97 let key = format!("{}/{}", collection_nsid, input.rkey); 98 if let Some(swap_record_str) = &input.swap_record { 99 let expected_cid = Cid::from_str(swap_record_str).ok(); 100 let actual_cid = mst.get(&key).await.ok().flatten(); 101 if expected_cid != actual_cid { 102 return (StatusCode::CONFLICT, Json(json!({"error": "InvalidSwap", "message": "Record has been modified or does not exist"}))).into_response(); 103 } 104 } 105 let prev_record_cid = mst.get(&key).await.ok().flatten(); 106 if prev_record_cid.is_none() { 107 return (StatusCode::OK, Json(json!({}))).into_response(); 108 } 109 let new_mst = match mst.delete(&key).await { 110 Ok(m) => m, 111 Err(e) => { 112 error!("Failed to delete from MST: {:?}", e); 113 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": format!("Failed to delete from MST: {:?}", e)}))).into_response(); 114 } 115 }; 116 let new_mst_root = match new_mst.persist().await { 117 Ok(c) => c, 118 Err(e) => { 119 error!("Failed to persist MST: {:?}", e); 120 return ( 121 StatusCode::INTERNAL_SERVER_ERROR, 122 Json(json!({"error": "InternalError", "message": "Failed to persist MST"})), 123 ) 124 .into_response(); 125 } 126 }; 127 let op = RecordOp::Delete { 128 collection: input.collection, 129 rkey: input.rkey, 130 prev: prev_record_cid, 131 }; 132 let mut relevant_blocks = std::collections::BTreeMap::new(); 133 if new_mst 134 .blocks_for_path(&key, &mut relevant_blocks) 135 .await 136 .is_err() 137 { 138 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Failed to get new MST blocks for path"}))).into_response(); 139 } 140 if mst 141 .blocks_for_path(&key, &mut relevant_blocks) 142 .await 143 .is_err() 144 { 145 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Failed to get old MST blocks for path"}))).into_response(); 146 } 147 let mut written_cids = tracking_store.get_all_relevant_cids(); 148 for cid in relevant_blocks.keys() { 149 if !written_cids.contains(cid) { 150 written_cids.push(*cid); 151 } 152 } 153 let written_cids_str = written_cids 154 .iter() 155 .map(|c| c.to_string()) 156 .collect::<Vec<_>>(); 157 if let Err(e) = commit_and_log( 158 &state, 159 CommitParams { 160 did: &did, 161 user_id, 162 current_root_cid: Some(current_root_cid), 163 prev_data_cid: Some(commit.data), 164 new_mst_root, 165 ops: vec![op], 166 blocks_cids: &written_cids_str, 167 }, 168 ) 169 .await 170 { 171 return ( 172 StatusCode::INTERNAL_SERVER_ERROR, 173 Json(json!({"error": "InternalError", "message": e})), 174 ) 175 .into_response(); 176 }; 177 (StatusCode::OK, Json(json!({}))).into_response() 178}