Something went wrong. Try again.
A downstream fork of Tranquil PDS for use in margin.cafe
Something went wrong. Try again.
4.9 kB · 149 lines
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150use axum::{ Json, body::Body, extract::State, http::StatusCode, http::header, response::{IntoResponse, Response},};use serde::{Deserialize, Serialize};use tracing::error;use tranquil_pds::api::error::ApiError;use tranquil_pds::api::query::XrpcQuery;use tranquil_pds::state::AppState;use tranquil_pds::sync::util::{RepoAccessLevel, assert_repo_availability};use tranquil_types::{CidLink, Did, Tid};
#[derive(Deserialize)]pub struct GetBlobParams { pub did: Did, pub cid: CidLink,}
pub async fn get_blob( State(state): State<AppState>, XrpcQuery(params): XrpcQuery<GetBlobParams>,) -> Response { let did = params.did; let cid = params.cid;
let _account = match assert_repo_availability(state.repos.repo.as_ref(), &did, RepoAccessLevel::Public) .await { Ok(a) => a, Err(e) => return e.into_response(), };
match state.repos.blob.get_blob_with_takedown(&cid).await { Ok(Some(blob)) if blob.takedown_ref.is_none() => {} Ok(_) => return ApiError::BlobNotFound(Some("Blob not found".into())).into_response(), Err(e) => { error!("DB error checking blob moderation status: {:?}", e); return ApiError::InternalError(None).into_response(); } }
let blob_result = state.repos.blob.get_blob_metadata(&cid).await; match blob_result { Ok(Some(metadata)) => match state.blob_store.get(&metadata.storage_key).await { Ok(data) => Response::builder() .status(StatusCode::OK) .header(header::CONTENT_TYPE, &metadata.mime_type) .header(header::CONTENT_LENGTH, metadata.size_bytes.to_string()) .header( header::CONTENT_DISPOSITION, format!("attachment; filename=\"{}\"", cid), ) .header("x-content-type-options", "nosniff") .header("content-security-policy", "default-src 'none'; sandbox") .body(Body::from(data)) .unwrap_or_else(|_| ApiError::InternalError(None).into_response()), Err(e) => { error!("Failed to fetch blob from storage: {:?}", e); ApiError::BlobNotFound(Some("Blob not found in storage".into())).into_response() } }, Ok(None) => ApiError::BlobNotFound(Some("Blob not found".into())).into_response(), Err(e) => { error!("DB error in get_blob: {:?}", e); ApiError::InternalError(Some(format!("Database error: {}", e))).into_response() } }}
#[derive(Deserialize)]pub struct ListBlobsParams { pub did: Did, pub since: Option<Tid>, pub limit: Option<i64>, pub cursor: Option<String>,}
#[derive(Serialize)]pub struct ListBlobsOutput { #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option<CidLink>, pub cids: Vec<CidLink>,}
pub async fn list_blobs( State(state): State<AppState>, XrpcQuery(params): XrpcQuery<ListBlobsParams>,) -> Response { let did = params.did;
let account = match assert_repo_availability(state.repos.repo.as_ref(), &did, RepoAccessLevel::Public) .await { Ok(a) => a, Err(e) => return e.into_response(), };
let limit = params.limit.unwrap_or(500).clamp(1, 1000); let cursor_cid = params.cursor.as_deref().unwrap_or(""); let user_id = account.user_id;
let cids_result: Result<Vec<CidLink>, _> = if let Some(since) = ¶ms.since { state .repos .blob .list_blobs_since_rev(&did, since) .await .map(|cids| { let mut cids = cids; cids.sort(); cids.into_iter() .filter(|c| c.as_str() > cursor_cid) .take(usize::try_from(limit + 1).unwrap_or(0)) .collect() }) } else { state .repos .blob .list_blobs_by_user(user_id, Some(cursor_cid), limit + 1) .await }; match cids_result { Ok(cids) => { let limit_usize = usize::try_from(limit).unwrap_or(0); let has_more = cids.len() > limit_usize; let cids: Vec<CidLink> = cids.into_iter().take(limit_usize).collect(); let next_cursor = if has_more { cids.last().cloned() } else { None }; ( StatusCode::OK, Json(ListBlobsOutput { cursor: next_cursor, cids, }), ) .into_response() } Err(e) => { error!("DB error in list_blobs: {:?}", e); ApiError::InternalError(Some(format!("Database error: {}", e))).into_response() } }}