at main 73 lines 2.1 kB view raw
1//! XRPC endpoint handlers for the appview. 2 3use jacquard::CowStr; 4use jacquard::IntoStatic; 5use jacquard::cowstr::ToCowStr; 6use jacquard::types::string::AtUri; 7use smol_str::SmolStr; 8 9use crate::server::AppState; 10 11use self::actor::resolve_actor; 12use self::repo::XrpcErrorResponse; 13 14pub mod actor; 15pub mod bsky; 16pub mod collab; 17pub mod domain; 18pub mod edit; 19pub mod identity; 20pub mod notebook; 21pub mod repo; 22 23/// Resolved AT URI components with canonical DID-based URI. 24pub struct ResolvedUri { 25 /// The resolved DID (authority converted from handle if needed) 26 pub did: SmolStr, 27 /// The collection from the URI 28 pub collection: SmolStr, 29 /// The record key from the URI 30 pub rkey: SmolStr, 31 /// Canonical DID-based URI for database lookups 32 pub canonical_uri: String, 33} 34 35/// Parse an AT URI and resolve its authority to a DID. 36/// 37/// This handles the common case where a URI might use a handle as the authority 38/// (e.g. `at://alice.bsky.social/...`) but the database stores URIs with DIDs 39/// (e.g. `at://did:plc:abc123/...`). 40pub async fn resolve_uri( 41 state: &AppState, 42 uri: &AtUri<'_>, 43) -> Result<ResolvedUri, XrpcErrorResponse> { 44 let authority = uri.authority(); 45 let collection = uri 46 .collection() 47 .ok_or_else(|| XrpcErrorResponse::invalid_request("URI must include collection"))?; 48 let rkey = uri 49 .rkey() 50 .ok_or_else(|| XrpcErrorResponse::invalid_request("URI must include rkey"))?; 51 52 // Resolve authority to DID (might be a handle) 53 let did = resolve_actor(state, authority).await?; 54 55 // Construct canonical DID-based URI for DB lookup 56 let canonical_uri = format!("at://{}/{}/{}", did, collection, rkey.as_ref()); 57 58 Ok(ResolvedUri { 59 did: SmolStr::new(&did), 60 collection: SmolStr::new(collection.as_ref()), 61 rkey: SmolStr::new(rkey.as_ref()), 62 canonical_uri, 63 }) 64} 65 66/// Convert SmolStr to Option<CowStr> if non-empty 67pub fn non_empty_str(s: &SmolStr) -> Option<CowStr<'static>> { 68 if s.is_empty() { 69 None 70 } else { 71 Some(s.to_cowstr().into_static()) 72 } 73}