this repo has no description
1use crate::api::repo::record::utils::{commit_and_log, RecordOp};
2use crate::api::repo::record::write::prepare_repo_write;
3use crate::repo::tracking::TrackingBlockStore;
4use crate::state::AppState;
5use axum::{
6 extract::State,
7 http::{HeaderMap, StatusCode},
8 response::{IntoResponse, Response},
9 Json,
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 Json(input): Json<DeleteRecordInput>,
35) -> Response {
36 let (did, user_id, current_root_cid) =
37 match prepare_repo_write(&state, &headers, &input.repo).await {
38 Ok(res) => res,
39 Err(err_res) => return err_res,
40 };
41 if let Some(swap_commit) = &input.swap_commit {
42 if Cid::from_str(swap_commit).ok() != Some(current_root_cid) {
43 return (
44 StatusCode::CONFLICT,
45 Json(json!({"error": "InvalidSwap", "message": "Repo has been modified"})),
46 )
47 .into_response();
48 }
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 _ => return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Commit block not found"}))).into_response(),
54 };
55 let commit = match Commit::from_cbor(&commit_bytes) {
56 Ok(c) => c,
57 _ => return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Failed to parse commit"}))).into_response(),
58 };
59 let mst = Mst::load(
60 Arc::new(tracking_store.clone()),
61 commit.data,
62 None,
63 );
64 let collection_nsid = match input.collection.parse::<Nsid>() {
65 Ok(n) => n,
66 Err(_) => return (StatusCode::BAD_REQUEST, Json(json!({"error": "InvalidCollection"}))).into_response(),
67 };
68 let key = format!("{}/{}", collection_nsid, input.rkey);
69 if let Some(swap_record_str) = &input.swap_record {
70 let expected_cid = Cid::from_str(swap_record_str).ok();
71 let actual_cid = mst.get(&key).await.ok().flatten();
72 if expected_cid != actual_cid {
73 return (StatusCode::CONFLICT, Json(json!({"error": "InvalidSwap", "message": "Record has been modified or does not exist"}))).into_response();
74 }
75 }
76 let prev_record_cid = mst.get(&key).await.ok().flatten();
77 if prev_record_cid.is_none() {
78 return (StatusCode::OK, Json(json!({}))).into_response();
79 }
80 let new_mst = match mst.delete(&key).await {
81 Ok(m) => m,
82 Err(e) => {
83 error!("Failed to delete from MST: {:?}", e);
84 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": format!("Failed to delete from MST: {:?}", e)}))).into_response();
85 }
86 };
87 let new_mst_root = match new_mst.persist().await {
88 Ok(c) => c,
89 Err(e) => {
90 error!("Failed to persist MST: {:?}", e);
91 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Failed to persist MST"}))).into_response();
92 }
93 };
94 let op = RecordOp::Delete { collection: input.collection, rkey: input.rkey, prev: prev_record_cid };
95 let mut relevant_blocks = std::collections::BTreeMap::new();
96 if let Err(_) = new_mst.blocks_for_path(&key, &mut relevant_blocks).await {
97 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Failed to get new MST blocks for path"}))).into_response();
98 }
99 if let Err(_) = mst.blocks_for_path(&key, &mut relevant_blocks).await {
100 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Failed to get old MST blocks for path"}))).into_response();
101 }
102 let mut written_cids = tracking_store.get_all_relevant_cids();
103 for cid in relevant_blocks.keys() {
104 if !written_cids.contains(cid) {
105 written_cids.push(*cid);
106 }
107 }
108 let written_cids_str = written_cids.iter().map(|c| c.to_string()).collect::<Vec<_>>();
109 if let Err(e) = commit_and_log(&state, &did, user_id, Some(current_root_cid), Some(commit.data), new_mst_root, vec![op], &written_cids_str).await {
110 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": e}))).into_response();
111 };
112 (StatusCode::OK, Json(json!({}))).into_response()
113}