PDS software with bells & whistles you didn’t even know you needed. will move this to its own account when ready.
at main 4.4 kB view raw
1use crate::api::error::ApiError; 2use crate::auth::{BearerAuth, extract_auth_token_from_header, validate_token_with_dpop}; 3use crate::state::AppState; 4use axum::{ 5 Json, 6 extract::State, 7 http::HeaderMap, 8 response::{IntoResponse, Response}, 9}; 10use cid::Cid; 11use jacquard_repo::storage::BlockStore; 12use serde::{Deserialize, Serialize}; 13use std::str::FromStr; 14 15#[derive(Serialize)] 16#[serde(rename_all = "camelCase")] 17pub struct CheckSignupQueueOutput { 18 pub activated: bool, 19 #[serde(skip_serializing_if = "Option::is_none")] 20 pub place_in_queue: Option<i64>, 21 #[serde(skip_serializing_if = "Option::is_none")] 22 pub estimated_time_ms: Option<i64>, 23} 24 25pub async fn check_signup_queue(State(state): State<AppState>, headers: HeaderMap) -> Response { 26 if let Some(extracted) = 27 extract_auth_token_from_header(headers.get("Authorization").and_then(|h| h.to_str().ok())) 28 { 29 let dpop_proof = headers.get("DPoP").and_then(|h| h.to_str().ok()); 30 if let Ok(user) = validate_token_with_dpop( 31 &state.db, 32 &extracted.token, 33 extracted.is_dpop, 34 dpop_proof, 35 "GET", 36 "/", 37 false, 38 false, 39 ) 40 .await 41 && user.is_oauth 42 { 43 return ApiError::Forbidden.into_response(); 44 } 45 } 46 Json(CheckSignupQueueOutput { 47 activated: true, 48 place_in_queue: None, 49 estimated_time_ms: None, 50 }) 51 .into_response() 52} 53 54#[derive(Deserialize)] 55#[serde(rename_all = "camelCase")] 56pub struct DereferenceScopeInput { 57 pub scope: String, 58} 59 60#[derive(Serialize)] 61#[serde(rename_all = "camelCase")] 62pub struct DereferenceScopeOutput { 63 pub scope: String, 64} 65 66pub async fn dereference_scope( 67 State(state): State<AppState>, 68 auth: BearerAuth, 69 Json(input): Json<DereferenceScopeInput>, 70) -> Response { 71 let _ = auth; 72 73 let scope_parts: Vec<&str> = input.scope.split_whitespace().collect(); 74 let mut resolved_scopes: Vec<String> = Vec::new(); 75 76 for part in scope_parts { 77 if let Some(cid_str) = part.strip_prefix("ref:") { 78 let cache_key = format!("scope_ref:{}", cid_str); 79 if let Some(cached) = state.cache.get(&cache_key).await { 80 for s in cached.split_whitespace() { 81 if !resolved_scopes.contains(&s.to_string()) { 82 resolved_scopes.push(s.to_string()); 83 } 84 } 85 continue; 86 } 87 88 let cid = match Cid::from_str(cid_str) { 89 Ok(c) => c, 90 Err(_) => { 91 tracing::warn!("Invalid CID in scope ref: {}", cid_str); 92 continue; 93 } 94 }; 95 96 let block_bytes = match state.block_store.get(&cid).await { 97 Ok(Some(b)) => b, 98 Ok(None) => { 99 tracing::warn!("Scope ref block not found: {}", cid_str); 100 continue; 101 } 102 Err(e) => { 103 tracing::warn!("Error fetching scope ref block {}: {:?}", cid_str, e); 104 continue; 105 } 106 }; 107 108 let scope_record: serde_json::Value = match serde_ipld_dagcbor::from_slice(&block_bytes) 109 { 110 Ok(v) => v, 111 Err(e) => { 112 tracing::warn!("Failed to decode scope ref block {}: {:?}", cid_str, e); 113 continue; 114 } 115 }; 116 117 if let Some(scope_value) = scope_record.get("scope").and_then(|v| v.as_str()) { 118 let _ = state 119 .cache 120 .set( 121 &cache_key, 122 scope_value, 123 std::time::Duration::from_secs(3600), 124 ) 125 .await; 126 for s in scope_value.split_whitespace() { 127 if !resolved_scopes.contains(&s.to_string()) { 128 resolved_scopes.push(s.to_string()); 129 } 130 } 131 } 132 } else if !resolved_scopes.contains(&part.to_string()) { 133 resolved_scopes.push(part.to_string()); 134 } 135 } 136 137 Json(DereferenceScopeOutput { 138 scope: resolved_scopes.join(" "), 139 }) 140 .into_response() 141}