PDS software with bells & whistles you didn’t even know you needed. will move this to its own account when ready.

feat: Proxy `getRecord` requests to the user's PDS after DID resolution instead of a central AppView endpoint.

Changed files
+37 -9
src
api
repo
record
+37 -9
src/api/repo/record/read.rs
··· 206 206 let record_cid_str: String = match record_row { 207 207 Ok(Some(row)) => row.record_cid, 208 208 _ => { 209 - let appview_endpoint = std::env::var("BSKY_APPVIEW_ENDPOINT") 210 - .unwrap_or_else(|_| "https://api.bsky.app".to_string()); 209 + let did = if input.repo.starts_with("did:") { 210 + input.repo.clone() 211 + } else { 212 + info!("Resolving handle {} to DID for P2P proxy", input.repo); 213 + match crate::handle::resolve_handle(&input.repo).await { 214 + Ok(d) => d, 215 + Err(e) => { 216 + error!("Failed to resolve handle {}: {}", input.repo, e); 217 + return ( 218 + StatusCode::NOT_FOUND, 219 + Json(json!({"error": "RepoNotFound", "message": "Could not resolve handle"})), 220 + ) 221 + .into_response(); 222 + } 223 + } 224 + }; 225 + 226 + let resolved_service = match state.did_resolver.resolve_did(&did).await { 227 + Some(s) => s, 228 + None => { 229 + error!("Failed to resolve PDS for DID {}", did); 230 + return ( 231 + StatusCode::NOT_FOUND, 232 + Json(json!({"error": "RepoNotFound", "message": "Could not resolve DID service endpoint"})), 233 + ) 234 + .into_response(); 235 + } 236 + }; 237 + 211 238 let mut url = format!( 212 239 "{}/xrpc/com.atproto.repo.getRecord?repo={}&collection={}&rkey={}", 213 - appview_endpoint.trim_end_matches('/'), 240 + resolved_service.url.trim_end_matches('/'), 214 241 urlencoding::encode(&input.repo), 215 242 urlencoding::encode(&input.collection), 216 243 urlencoding::encode(&input.rkey) ··· 219 246 url.push_str(&format!("&cid={}", urlencoding::encode(cid))); 220 247 } 221 248 info!( 222 - "Record not found locally (user exists). Proxying getRecord for {} to AppView: {}", 223 - input.repo, url 249 + "Record not found locally. Proxying getRecord for {} (DID: {}) to PDS: {}", 250 + input.repo, did, url 224 251 ); 252 + 225 253 match proxy_client().get(&url).send().await { 226 254 Ok(resp) => { 227 255 let status = resp.status(); 228 256 let body = match resp.bytes().await { 229 257 Ok(b) => b, 230 258 Err(e) => { 231 - error!("Error reading AppView proxy response: {:?}", e); 259 + error!("Error reading upstream PDS response: {:?}", e); 232 260 return ( 233 261 StatusCode::BAD_GATEWAY, 234 262 Json(json!({ 235 263 "error": "UpstreamFailure", 236 - "message": "Error reading upstream response from AppView" 264 + "message": "Error reading upstream response" 237 265 })), 238 266 ) 239 267 .into_response(); ··· 248 276 }); 249 277 } 250 278 Err(e) => { 251 - error!("Error proxying request to AppView: {:?}", e); 279 + error!("Error proxying request to upstream PDS: {:?}", e); 252 280 return ( 253 281 StatusCode::BAD_GATEWAY, 254 282 Json(json!({ 255 283 "error": "UpstreamFailure", 256 - "message": "Failed to reach AppView" 284 + "message": "Failed to reach upstream PDS" 257 285 })), 258 286 ) 259 287 .into_response();