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