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 (did, user_id, current_root_cid) =
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 if let Some(swap_commit) = &input.swap_commit
43 && Cid::from_str(swap_commit).ok() != Some(current_root_cid) {
44 return (
45 StatusCode::CONFLICT,
46 Json(json!({"error": "InvalidSwap", "message": "Repo has been modified"})),
47 )
48 .into_response();
49 }
50 let tracking_store = TrackingBlockStore::new(state.block_store.clone());
51 let commit_bytes = match tracking_store.get(¤t_root_cid).await {
52 Ok(Some(b)) => b,
53 _ => {
54 return (
55 StatusCode::INTERNAL_SERVER_ERROR,
56 Json(json!({"error": "InternalError", "message": "Commit block not found"})),
57 )
58 .into_response();
59 }
60 };
61 let commit = match Commit::from_cbor(&commit_bytes) {
62 Ok(c) => c,
63 _ => {
64 return (
65 StatusCode::INTERNAL_SERVER_ERROR,
66 Json(json!({"error": "InternalError", "message": "Failed to parse commit"})),
67 )
68 .into_response();
69 }
70 };
71 let mst = Mst::load(Arc::new(tracking_store.clone()), commit.data, None);
72 let collection_nsid = match input.collection.parse::<Nsid>() {
73 Ok(n) => n,
74 Err(_) => {
75 return (
76 StatusCode::BAD_REQUEST,
77 Json(json!({"error": "InvalidCollection"})),
78 )
79 .into_response();
80 }
81 };
82 let key = format!("{}/{}", collection_nsid, input.rkey);
83 if let Some(swap_record_str) = &input.swap_record {
84 let expected_cid = Cid::from_str(swap_record_str).ok();
85 let actual_cid = mst.get(&key).await.ok().flatten();
86 if expected_cid != actual_cid {
87 return (StatusCode::CONFLICT, Json(json!({"error": "InvalidSwap", "message": "Record has been modified or does not exist"}))).into_response();
88 }
89 }
90 let prev_record_cid = mst.get(&key).await.ok().flatten();
91 if prev_record_cid.is_none() {
92 return (StatusCode::OK, Json(json!({}))).into_response();
93 }
94 let new_mst = match mst.delete(&key).await {
95 Ok(m) => m,
96 Err(e) => {
97 error!("Failed to delete from MST: {:?}", e);
98 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": format!("Failed to delete from MST: {:?}", e)}))).into_response();
99 }
100 };
101 let new_mst_root = match new_mst.persist().await {
102 Ok(c) => c,
103 Err(e) => {
104 error!("Failed to persist MST: {:?}", e);
105 return (
106 StatusCode::INTERNAL_SERVER_ERROR,
107 Json(json!({"error": "InternalError", "message": "Failed to persist MST"})),
108 )
109 .into_response();
110 }
111 };
112 let op = RecordOp::Delete {
113 collection: input.collection,
114 rkey: input.rkey,
115 prev: prev_record_cid,
116 };
117 let mut relevant_blocks = std::collections::BTreeMap::new();
118 if new_mst.blocks_for_path(&key, &mut relevant_blocks).await.is_err() {
119 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Failed to get new MST blocks for path"}))).into_response();
120 }
121 if mst.blocks_for_path(&key, &mut relevant_blocks).await.is_err() {
122 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Failed to get old MST blocks for path"}))).into_response();
123 }
124 let mut written_cids = tracking_store.get_all_relevant_cids();
125 for cid in relevant_blocks.keys() {
126 if !written_cids.contains(cid) {
127 written_cids.push(*cid);
128 }
129 }
130 let written_cids_str = written_cids
131 .iter()
132 .map(|c| c.to_string())
133 .collect::<Vec<_>>();
134 if let Err(e) = commit_and_log(
135 &state,
136 CommitParams {
137 did: &did,
138 user_id,
139 current_root_cid: Some(current_root_cid),
140 prev_data_cid: Some(commit.data),
141 new_mst_root,
142 ops: vec![op],
143 blocks_cids: &written_cids_str,
144 },
145 )
146 .await
147 {
148 return (
149 StatusCode::INTERNAL_SERVER_ERROR,
150 Json(json!({"error": "InternalError", "message": e})),
151 )
152 .into_response();
153 };
154 (StatusCode::OK, Json(json!({}))).into_response()
155}