this repo has no description
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}