this repo has no description
1use crate::api::error::ApiError; 2use crate::auth::{extract_bearer_token_from_header, validate_bearer_token}; 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(token) = 27 extract_bearer_token_from_header(headers.get("Authorization").and_then(|h| h.to_str().ok())) 28 && let Ok(user) = validate_bearer_token(&state.db, &token).await 29 && user.is_oauth 30 { 31 return ApiError::Forbidden.into_response(); 32 } 33 Json(CheckSignupQueueOutput { 34 activated: true, 35 place_in_queue: None, 36 estimated_time_ms: None, 37 }) 38 .into_response() 39} 40 41#[derive(Deserialize)] 42#[serde(rename_all = "camelCase")] 43pub struct DereferenceScopeInput { 44 pub scope: String, 45} 46 47#[derive(Serialize)] 48#[serde(rename_all = "camelCase")] 49pub struct DereferenceScopeOutput { 50 pub scope: String, 51} 52 53pub async fn dereference_scope( 54 State(state): State<AppState>, 55 headers: HeaderMap, 56 Json(input): Json<DereferenceScopeInput>, 57) -> Response { 58 let Some(token) = extract_bearer_token_from_header( 59 headers.get("Authorization").and_then(|h| h.to_str().ok()), 60 ) else { 61 return ApiError::AuthenticationRequired.into_response(); 62 }; 63 64 if validate_bearer_token(&state.db, &token).await.is_err() { 65 return ApiError::AuthenticationFailed(None).into_response(); 66 } 67 68 let scope_parts: Vec<&str> = input.scope.split_whitespace().collect(); 69 let mut resolved_scopes: Vec<String> = Vec::new(); 70 71 for part in scope_parts { 72 if let Some(cid_str) = part.strip_prefix("ref:") { 73 let cache_key = format!("scope_ref:{}", cid_str); 74 if let Some(cached) = state.cache.get(&cache_key).await { 75 for s in cached.split_whitespace() { 76 if !resolved_scopes.contains(&s.to_string()) { 77 resolved_scopes.push(s.to_string()); 78 } 79 } 80 continue; 81 } 82 83 let cid = match Cid::from_str(cid_str) { 84 Ok(c) => c, 85 Err(_) => { 86 tracing::warn!("Invalid CID in scope ref: {}", cid_str); 87 continue; 88 } 89 }; 90 91 let block_bytes = match state.block_store.get(&cid).await { 92 Ok(Some(b)) => b, 93 Ok(None) => { 94 tracing::warn!("Scope ref block not found: {}", cid_str); 95 continue; 96 } 97 Err(e) => { 98 tracing::warn!("Error fetching scope ref block {}: {:?}", cid_str, e); 99 continue; 100 } 101 }; 102 103 let scope_record: serde_json::Value = match serde_ipld_dagcbor::from_slice(&block_bytes) 104 { 105 Ok(v) => v, 106 Err(e) => { 107 tracing::warn!("Failed to decode scope ref block {}: {:?}", cid_str, e); 108 continue; 109 } 110 }; 111 112 if let Some(scope_value) = scope_record.get("scope").and_then(|v| v.as_str()) { 113 let _ = state 114 .cache 115 .set( 116 &cache_key, 117 scope_value, 118 std::time::Duration::from_secs(3600), 119 ) 120 .await; 121 for s in scope_value.split_whitespace() { 122 if !resolved_scopes.contains(&s.to_string()) { 123 resolved_scopes.push(s.to_string()); 124 } 125 } 126 } 127 } else if !resolved_scopes.contains(&part.to_string()) { 128 resolved_scopes.push(part.to_string()); 129 } 130 } 131 132 Json(DereferenceScopeOutput { 133 scope: resolved_scopes.join(" "), 134 }) 135 .into_response() 136}