mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-06 10:16:59 +00:00
refactor(api): centralize DID document building, update admin endpoints
This commit is contained in:
@@ -1,8 +1,4 @@
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use axum::{Json, extract::State};
|
||||
use serde::Deserialize;
|
||||
use tracing::warn;
|
||||
use tranquil_pds::api::EmptyResponse;
|
||||
@@ -20,7 +16,7 @@ pub async fn delete_account(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Json(input): Json<DeleteAccountInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<EmptyResponse>, ApiError> {
|
||||
let did = &input.did;
|
||||
let (user_id, handle) = state
|
||||
.user_repo
|
||||
@@ -52,5 +48,5 @@ pub async fn delete_account(
|
||||
.cache
|
||||
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
|
||||
.await;
|
||||
Ok(EmptyResponse::ok().into_response())
|
||||
Ok(Json(EmptyResponse {}))
|
||||
}
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use axum::{Json, extract::State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
@@ -30,7 +25,7 @@ pub async fn send_email(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Json(input): Json<SendEmailInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<SendEmailOutput>, ApiError> {
|
||||
let content = input.content.trim();
|
||||
if content.is_empty() {
|
||||
return Err(ApiError::InvalidRequest("content is required".into()));
|
||||
@@ -68,11 +63,11 @@ pub async fn send_email(
|
||||
handle,
|
||||
input.recipient_did
|
||||
);
|
||||
Ok((StatusCode::OK, Json(SendEmailOutput { sent: true })).into_response())
|
||||
Ok(Json(SendEmailOutput { sent: true }))
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to enqueue admin email: {:?}", e);
|
||||
Ok((StatusCode::OK, Json(SendEmailOutput { sent: false })).into_response())
|
||||
Ok(Json(SendEmailOutput { sent: false }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::common;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, RawQuery, State},
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@@ -68,7 +67,7 @@ pub async fn get_account_info(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Query(params): Query<GetAccountInfoParams>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<AccountInfo>, ApiError> {
|
||||
let account = state
|
||||
.infra_repo
|
||||
.get_admin_account_info_by_did(¶ms.did)
|
||||
@@ -79,26 +78,22 @@ pub async fn get_account_info(
|
||||
let invited_by = get_invited_by(&state, account.id).await;
|
||||
let invites = get_invites_for_user(&state, account.id).await;
|
||||
|
||||
Ok((
|
||||
StatusCode::OK,
|
||||
Json(AccountInfo {
|
||||
did: account.did,
|
||||
handle: account.handle,
|
||||
email: account.email,
|
||||
indexed_at: account.created_at.to_rfc3339(),
|
||||
invite_note: None,
|
||||
invites_disabled: account.invites_disabled,
|
||||
email_confirmed_at: if account.email_verified {
|
||||
Some(account.created_at.to_rfc3339())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
deactivated_at: account.deactivated_at.map(|dt| dt.to_rfc3339()),
|
||||
invited_by,
|
||||
invites,
|
||||
}),
|
||||
)
|
||||
.into_response())
|
||||
Ok(Json(AccountInfo {
|
||||
did: account.did,
|
||||
handle: account.handle,
|
||||
email: account.email,
|
||||
indexed_at: account.created_at.to_rfc3339(),
|
||||
invite_note: None,
|
||||
invites_disabled: account.invites_disabled,
|
||||
email_confirmed_at: if account.email_verified {
|
||||
Some(account.created_at.to_rfc3339())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
deactivated_at: account.deactivated_at.map(|dt| dt.to_rfc3339()),
|
||||
invited_by,
|
||||
invites,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn get_invited_by(state: &AppState, user_id: uuid::Uuid) -> Option<InviteCodeInfo> {
|
||||
@@ -133,16 +128,10 @@ async fn get_invites_for_user(
|
||||
.await
|
||||
.ok()?;
|
||||
|
||||
let uses_by_code: HashMap<String, Vec<InviteCodeUseInfo>> =
|
||||
uses.into_iter().fold(HashMap::new(), |mut acc, u| {
|
||||
acc.entry(u.code.clone())
|
||||
.or_default()
|
||||
.push(InviteCodeUseInfo {
|
||||
used_by: u.used_by_did,
|
||||
used_at: u.used_at.to_rfc3339(),
|
||||
});
|
||||
acc
|
||||
});
|
||||
let uses_by_code = common::group_invite_uses_by_code(uses, |u| InviteCodeUseInfo {
|
||||
used_by: u.used_by_did,
|
||||
used_at: u.used_at.to_rfc3339(),
|
||||
});
|
||||
|
||||
let invites: Vec<InviteCodeInfo> = invite_codes
|
||||
.into_iter()
|
||||
@@ -195,7 +184,7 @@ pub async fn get_account_infos(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
RawQuery(raw_query): RawQuery,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<GetAccountInfosOutput>, ApiError> {
|
||||
let dids: Vec<String> =
|
||||
tranquil_pds::util::parse_repeated_query_param(raw_query.as_deref(), "dids")
|
||||
.into_iter()
|
||||
@@ -244,18 +233,10 @@ pub async fn get_account_infos(
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let uses_by_code: HashMap<String, Vec<InviteCodeUseInfo>> =
|
||||
all_invite_uses
|
||||
.into_iter()
|
||||
.fold(HashMap::new(), |mut acc, u| {
|
||||
acc.entry(u.code.clone())
|
||||
.or_default()
|
||||
.push(InviteCodeUseInfo {
|
||||
used_by: u.used_by_did,
|
||||
used_at: u.used_at.to_rfc3339(),
|
||||
});
|
||||
acc
|
||||
});
|
||||
let uses_by_code = common::group_invite_uses_by_code(all_invite_uses, |u| InviteCodeUseInfo {
|
||||
used_by: u.used_by_did,
|
||||
used_at: u.used_at.to_rfc3339(),
|
||||
});
|
||||
|
||||
let (codes_by_user, code_info_map): (
|
||||
HashMap<uuid::Uuid, Vec<InviteCodeInfo>>,
|
||||
@@ -304,5 +285,5 @@ pub async fn get_account_infos(
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok((StatusCode::OK, Json(GetAccountInfosOutput { infos })).into_response())
|
||||
Ok(Json(GetAccountInfosOutput { infos }))
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
@@ -51,7 +49,7 @@ pub async fn search_accounts(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Query(params): Query<SearchAccountsParams>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<SearchAccountsOutput>, ApiError> {
|
||||
let limit = params.limit.clamp(1, 100);
|
||||
let email_filter = params.email.as_deref().map(|e| format!("%{}%", e));
|
||||
let handle_filter = params.handle.as_deref().map(|h| format!("%{}%", h));
|
||||
@@ -91,12 +89,8 @@ pub async fn search_accounts(
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok((
|
||||
StatusCode::OK,
|
||||
Json(SearchAccountsOutput {
|
||||
cursor: next_cursor,
|
||||
accounts,
|
||||
}),
|
||||
)
|
||||
.into_response())
|
||||
Ok(Json(SearchAccountsOutput {
|
||||
cursor: next_cursor,
|
||||
accounts,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use axum::{Json, extract::State};
|
||||
use serde::Deserialize;
|
||||
use tracing::{error, warn};
|
||||
use tranquil_pds::api::EmptyResponse;
|
||||
@@ -21,7 +17,7 @@ pub async fn update_account_email(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Json(input): Json<UpdateAccountEmailInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<EmptyResponse>, ApiError> {
|
||||
let account = input.account.trim();
|
||||
let email = input.email.trim();
|
||||
if account.is_empty() || email.is_empty() {
|
||||
@@ -39,7 +35,7 @@ pub async fn update_account_email(
|
||||
.await
|
||||
{
|
||||
Ok(0) => Err(ApiError::AccountNotFound),
|
||||
Ok(_) => Ok(EmptyResponse::ok().into_response()),
|
||||
Ok(_) => Ok(Json(EmptyResponse {})),
|
||||
Err(e) => {
|
||||
error!("DB error updating email: {:?}", e);
|
||||
Err(ApiError::InternalError(None))
|
||||
@@ -57,7 +53,7 @@ pub async fn update_account_handle(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Json(input): Json<UpdateAccountHandleInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<EmptyResponse>, ApiError> {
|
||||
let did = &input.did;
|
||||
let input_handle = input.handle.trim();
|
||||
if input_handle.is_empty() {
|
||||
@@ -125,7 +121,7 @@ pub async fn update_account_handle(
|
||||
{
|
||||
warn!("Failed to update PLC handle for admin handle update: {}", e);
|
||||
}
|
||||
Ok(EmptyResponse::ok().into_response())
|
||||
Ok(Json(EmptyResponse {}))
|
||||
}
|
||||
Err(e) => {
|
||||
error!("DB error updating handle: {:?}", e);
|
||||
@@ -144,7 +140,7 @@ pub async fn update_account_password(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Json(input): Json<UpdateAccountPasswordInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<EmptyResponse>, ApiError> {
|
||||
let did = &input.did;
|
||||
let password = input.password.trim();
|
||||
if password.is_empty() {
|
||||
@@ -161,7 +157,7 @@ pub async fn update_account_password(
|
||||
.await
|
||||
{
|
||||
Ok(0) => Err(ApiError::AccountNotFound),
|
||||
Ok(_) => Ok(EmptyResponse::ok().into_response()),
|
||||
Ok(_) => Ok(Json(EmptyResponse {})),
|
||||
Err(e) => {
|
||||
error!("DB error updating password: {:?}", e);
|
||||
Err(ApiError::InternalError(None))
|
||||
|
||||
@@ -8,7 +8,7 @@ use tranquil_types::CidLink;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ServerConfigResponse {
|
||||
pub struct ServerConfigOutput {
|
||||
pub server_name: String,
|
||||
pub primary_color: Option<String>,
|
||||
pub primary_color_dark: Option<String>,
|
||||
@@ -29,7 +29,7 @@ pub struct UpdateServerConfigRequest {
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct UpdateServerConfigResponse {
|
||||
pub struct UpdateServerConfigOutput {
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ fn is_valid_hex_color(s: &str) -> bool {
|
||||
|
||||
pub async fn get_server_config(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<Json<ServerConfigResponse>, ApiError> {
|
||||
) -> Result<Json<ServerConfigOutput>, ApiError> {
|
||||
let keys = &[
|
||||
"server_name",
|
||||
"primary_color",
|
||||
@@ -60,7 +60,7 @@ pub async fn get_server_config(
|
||||
|
||||
let config_map: std::collections::HashMap<String, String> = rows.into_iter().collect();
|
||||
|
||||
Ok(Json(ServerConfigResponse {
|
||||
Ok(Json(ServerConfigOutput {
|
||||
server_name: config_map
|
||||
.get("server_name")
|
||||
.cloned()
|
||||
@@ -77,7 +77,7 @@ pub async fn update_server_config(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Json(req): Json<UpdateServerConfigRequest>,
|
||||
) -> Result<Json<UpdateServerConfigResponse>, ApiError> {
|
||||
) -> Result<Json<UpdateServerConfigOutput>, ApiError> {
|
||||
if let Some(server_name) = req.server_name {
|
||||
let trimmed = server_name.trim();
|
||||
if trimmed.is_empty() || trimmed.len() > 100 {
|
||||
@@ -224,5 +224,5 @@ pub async fn update_server_config(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Json(UpdateServerConfigResponse { success: true }))
|
||||
Ok(Json(UpdateServerConfigOutput { success: true }))
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::common;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::error;
|
||||
@@ -23,7 +22,7 @@ pub async fn disable_invite_codes(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Json(input): Json<DisableInviteCodesInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<EmptyResponse>, ApiError> {
|
||||
if let Some(codes) = &input.codes
|
||||
&& let Err(e) = state.infra_repo.disable_invite_codes_by_code(codes).await
|
||||
{
|
||||
@@ -40,7 +39,7 @@ pub async fn disable_invite_codes(
|
||||
error!("DB error disabling invite codes by account: {:?}", e);
|
||||
}
|
||||
}
|
||||
Ok(EmptyResponse::ok().into_response())
|
||||
Ok(Json(EmptyResponse {}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -80,7 +79,7 @@ pub async fn get_invite_codes(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Query(params): Query<GetInviteCodesParams>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<GetInviteCodesOutput>, ApiError> {
|
||||
let limit = params.limit.unwrap_or(100).clamp(1, 500);
|
||||
let sort_order = match params.sort.as_deref() {
|
||||
Some("usage") => InviteCodeSortOrder::Usage,
|
||||
@@ -104,26 +103,21 @@ pub async fn get_invite_codes(
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let uses_by_code: std::collections::HashMap<String, Vec<InviteCodeUseInfo>> =
|
||||
if code_strings.is_empty() {
|
||||
std::collections::HashMap::new()
|
||||
} else {
|
||||
let uses_by_code = if code_strings.is_empty() {
|
||||
std::collections::HashMap::new()
|
||||
} else {
|
||||
common::group_invite_uses_by_code(
|
||||
state
|
||||
.infra_repo
|
||||
.get_invite_code_uses_batch(&code_strings)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.fold(std::collections::HashMap::new(), |mut acc, u| {
|
||||
acc.entry(u.code.clone())
|
||||
.or_default()
|
||||
.push(InviteCodeUseInfo {
|
||||
used_by: u.used_by_did.to_string(),
|
||||
used_at: u.used_at.to_rfc3339(),
|
||||
});
|
||||
acc
|
||||
})
|
||||
};
|
||||
.unwrap_or_default(),
|
||||
|u| InviteCodeUseInfo {
|
||||
used_by: u.used_by_did.to_string(),
|
||||
used_at: u.used_at.to_rfc3339(),
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
let codes: Vec<InviteCodeInfo> = codes_rows
|
||||
.iter()
|
||||
@@ -149,14 +143,10 @@ pub async fn get_invite_codes(
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok((
|
||||
StatusCode::OK,
|
||||
Json(GetInviteCodesOutput {
|
||||
cursor: next_cursor,
|
||||
codes,
|
||||
}),
|
||||
)
|
||||
.into_response())
|
||||
Ok(Json(GetInviteCodesOutput {
|
||||
cursor: next_cursor,
|
||||
codes,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -168,7 +158,7 @@ pub async fn disable_account_invites(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Json(input): Json<DisableAccountInvitesInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<EmptyResponse>, ApiError> {
|
||||
let account = input.account.trim();
|
||||
if account.is_empty() {
|
||||
return Err(ApiError::InvalidRequest("account is required".into()));
|
||||
@@ -182,7 +172,7 @@ pub async fn disable_account_invites(
|
||||
.set_invites_disabled(&account_did, true)
|
||||
.await
|
||||
{
|
||||
Ok(true) => Ok(EmptyResponse::ok().into_response()),
|
||||
Ok(true) => Ok(Json(EmptyResponse {})),
|
||||
Ok(false) => Err(ApiError::AccountNotFound),
|
||||
Err(e) => {
|
||||
error!("DB error disabling account invites: {:?}", e);
|
||||
@@ -200,7 +190,7 @@ pub async fn enable_account_invites(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Json(input): Json<EnableAccountInvitesInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<EmptyResponse>, ApiError> {
|
||||
let account = input.account.trim();
|
||||
if account.is_empty() {
|
||||
return Err(ApiError::InvalidRequest("account is required".into()));
|
||||
@@ -214,7 +204,7 @@ pub async fn enable_account_invites(
|
||||
.set_invites_disabled(&account_did, false)
|
||||
.await
|
||||
{
|
||||
Ok(true) => Ok(EmptyResponse::ok().into_response()),
|
||||
Ok(true) => Ok(Json(EmptyResponse {})),
|
||||
Ok(false) => Err(ApiError::AccountNotFound),
|
||||
Err(e) => {
|
||||
error!("DB error enabling account invites: {:?}", e);
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use axum::{Json, extract::State};
|
||||
use serde::Serialize;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::auth::{Admin, Auth};
|
||||
@@ -10,7 +6,7 @@ use tranquil_pds::state::AppState;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ServerStatsResponse {
|
||||
pub struct ServerStatsOutput {
|
||||
pub user_count: i64,
|
||||
pub repo_count: i64,
|
||||
pub record_count: i64,
|
||||
@@ -20,17 +16,16 @@ pub struct ServerStatsResponse {
|
||||
pub async fn get_server_stats(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<ServerStatsOutput>, ApiError> {
|
||||
let user_count = state.user_repo.count_users().await.unwrap_or(0);
|
||||
let repo_count = state.repo_repo.count_repos().await.unwrap_or(0);
|
||||
let record_count = state.repo_repo.count_all_records().await.unwrap_or(0);
|
||||
let blob_storage_bytes = state.blob_repo.sum_blob_storage().await.unwrap_or(0);
|
||||
|
||||
Ok(Json(ServerStatsResponse {
|
||||
Ok(Json(ServerStatsOutput {
|
||||
user_count,
|
||||
repo_count,
|
||||
record_count,
|
||||
blob_storage_bytes,
|
||||
})
|
||||
.into_response())
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use serde_json::{Value, json};
|
||||
use tracing::{error, warn};
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::auth::{Admin, Auth};
|
||||
@@ -37,7 +35,7 @@ pub async fn get_subject_status(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Query(params): Query<GetSubjectStatusParams>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<SubjectStatus>, ApiError> {
|
||||
if params.did.is_none() && params.uri.is_none() && params.blob.is_none() {
|
||||
return Err(ApiError::InvalidRequest(
|
||||
"Must provide did, uri, or blob".into(),
|
||||
@@ -57,18 +55,14 @@ pub async fn get_subject_status(
|
||||
applied: true,
|
||||
r#ref: Some(r.clone()),
|
||||
});
|
||||
return Ok((
|
||||
StatusCode::OK,
|
||||
Json(SubjectStatus {
|
||||
subject: json!({
|
||||
"$type": "com.atproto.admin.defs#repoRef",
|
||||
"did": did_str
|
||||
}),
|
||||
takedown,
|
||||
deactivated,
|
||||
return Ok(Json(SubjectStatus {
|
||||
subject: json!({
|
||||
"$type": "com.atproto.admin.defs#repoRef",
|
||||
"did": did_str
|
||||
}),
|
||||
)
|
||||
.into_response());
|
||||
takedown,
|
||||
deactivated,
|
||||
}));
|
||||
}
|
||||
Ok(None) => {
|
||||
return Err(ApiError::SubjectNotFound);
|
||||
@@ -89,19 +83,15 @@ pub async fn get_subject_status(
|
||||
applied: true,
|
||||
r#ref: Some(r.clone()),
|
||||
});
|
||||
return Ok((
|
||||
StatusCode::OK,
|
||||
Json(SubjectStatus {
|
||||
subject: json!({
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
"uri": uri_str,
|
||||
"cid": uri_str
|
||||
}),
|
||||
takedown,
|
||||
deactivated: None,
|
||||
return Ok(Json(SubjectStatus {
|
||||
subject: json!({
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
"uri": uri_str,
|
||||
"cid": uri_str
|
||||
}),
|
||||
)
|
||||
.into_response());
|
||||
takedown,
|
||||
deactivated: None,
|
||||
}));
|
||||
}
|
||||
Ok(None) => {
|
||||
return Err(ApiError::RecordNotFound);
|
||||
@@ -125,19 +115,15 @@ pub async fn get_subject_status(
|
||||
applied: true,
|
||||
r#ref: Some(r.clone()),
|
||||
});
|
||||
return Ok((
|
||||
StatusCode::OK,
|
||||
Json(SubjectStatus {
|
||||
subject: json!({
|
||||
"$type": "com.atproto.admin.defs#repoBlobRef",
|
||||
"did": did,
|
||||
"cid": blob.cid
|
||||
}),
|
||||
takedown,
|
||||
deactivated: None,
|
||||
return Ok(Json(SubjectStatus {
|
||||
subject: json!({
|
||||
"$type": "com.atproto.admin.defs#repoBlobRef",
|
||||
"did": did,
|
||||
"cid": blob.cid
|
||||
}),
|
||||
)
|
||||
.into_response());
|
||||
takedown,
|
||||
deactivated: None,
|
||||
}));
|
||||
}
|
||||
Ok(None) => {
|
||||
return Err(ApiError::BlobNotFound(None));
|
||||
@@ -169,11 +155,11 @@ pub async fn update_subject_status(
|
||||
State(state): State<AppState>,
|
||||
_auth: Auth<Admin>,
|
||||
Json(input): Json<UpdateSubjectStatusInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
let subject_type = input.subject.get("$type").and_then(|t| t.as_str());
|
||||
) -> Result<Json<serde_json::Value>, ApiError> {
|
||||
let subject_type = input.subject.get("$type").and_then(Value::as_str);
|
||||
match subject_type {
|
||||
Some("com.atproto.admin.defs#repoRef") => {
|
||||
let did_str = input.subject.get("did").and_then(|d| d.as_str());
|
||||
let did_str = input.subject.get("did").and_then(Value::as_str);
|
||||
if let Some(did_str) = did_str {
|
||||
let did: Did = match did_str.parse() {
|
||||
Ok(d) => d,
|
||||
@@ -238,24 +224,20 @@ pub async fn update_subject_status(
|
||||
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
|
||||
.await;
|
||||
}
|
||||
return Ok((
|
||||
StatusCode::OK,
|
||||
Json(json!({
|
||||
"subject": input.subject,
|
||||
"takedown": input.takedown.as_ref().map(|t| json!({
|
||||
"applied": t.applied,
|
||||
"ref": t.r#ref
|
||||
})),
|
||||
"deactivated": input.deactivated.as_ref().map(|d| json!({
|
||||
"applied": d.applied
|
||||
}))
|
||||
return Ok(Json(json!({
|
||||
"subject": input.subject,
|
||||
"takedown": input.takedown.as_ref().map(|t| json!({
|
||||
"applied": t.applied,
|
||||
"ref": t.r#ref
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
"deactivated": input.deactivated.as_ref().map(|d| json!({
|
||||
"applied": d.applied
|
||||
}))
|
||||
})));
|
||||
}
|
||||
}
|
||||
Some("com.atproto.repo.strongRef") => {
|
||||
let uri_str = input.subject.get("uri").and_then(|u| u.as_str());
|
||||
let uri_str = input.subject.get("uri").and_then(Value::as_str);
|
||||
if let Some(uri_str) = uri_str {
|
||||
let cid: CidLink = uri_str
|
||||
.parse()
|
||||
@@ -278,21 +260,17 @@ pub async fn update_subject_status(
|
||||
ApiError::InternalError(Some("Failed to update takedown status".into()))
|
||||
})?;
|
||||
}
|
||||
return Ok((
|
||||
StatusCode::OK,
|
||||
Json(json!({
|
||||
"subject": input.subject,
|
||||
"takedown": input.takedown.as_ref().map(|t| json!({
|
||||
"applied": t.applied,
|
||||
"ref": t.r#ref
|
||||
}))
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
return Ok(Json(json!({
|
||||
"subject": input.subject,
|
||||
"takedown": input.takedown.as_ref().map(|t| json!({
|
||||
"applied": t.applied,
|
||||
"ref": t.r#ref
|
||||
}))
|
||||
})));
|
||||
}
|
||||
}
|
||||
Some("com.atproto.admin.defs#repoBlobRef") => {
|
||||
let cid_str = input.subject.get("cid").and_then(|c| c.as_str());
|
||||
let cid_str = input.subject.get("cid").and_then(Value::as_str);
|
||||
if let Some(cid_str) = cid_str {
|
||||
let cid: CidLink = cid_str
|
||||
.parse()
|
||||
@@ -315,17 +293,13 @@ pub async fn update_subject_status(
|
||||
ApiError::InternalError(Some("Failed to update takedown status".into()))
|
||||
})?;
|
||||
}
|
||||
return Ok((
|
||||
StatusCode::OK,
|
||||
Json(json!({
|
||||
"subject": input.subject,
|
||||
"takedown": input.takedown.as_ref().map(|t| json!({
|
||||
"applied": t.applied,
|
||||
"ref": t.r#ref
|
||||
}))
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
return Ok(Json(json!({
|
||||
"subject": input.subject,
|
||||
"takedown": input.takedown.as_ref().map(|t| json!({
|
||||
"applied": t.applied,
|
||||
"ref": t.r#ref
|
||||
}))
|
||||
})));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::common;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Path, Query, State},
|
||||
@@ -10,6 +11,7 @@ use k256::elliptic_curve::sec1::ToEncodedPoint;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use tracing::{error, warn};
|
||||
use tranquil_pds::api::error::DbResultExt;
|
||||
use tranquil_pds::api::{ApiError, DidResponse, EmptyResponse};
|
||||
use tranquil_pds::auth::{Auth, NotTakendown};
|
||||
use tranquil_pds::plc::signing_key_to_did_key;
|
||||
@@ -188,91 +190,20 @@ async fn serve_handle_did_doc(state: &AppState, handle: &str, hostname: &str) ->
|
||||
|
||||
let service_endpoint = migrated_to_pds.unwrap_or_else(|| format!("https://{}", hostname));
|
||||
|
||||
if let Some((ovr, parsed)) = overrides.as_ref().and_then(|ovr| {
|
||||
serde_json::from_value::<Vec<DidWebVerificationMethod>>(ovr.verification_methods.clone())
|
||||
.ok()
|
||||
.filter(|p| !p.is_empty())
|
||||
.map(|p| (ovr, p))
|
||||
}) {
|
||||
let also_known_as = if !ovr.also_known_as.is_empty() {
|
||||
ovr.also_known_as.clone()
|
||||
} else {
|
||||
vec![format!("at://{}", current_handle)]
|
||||
};
|
||||
|
||||
return Json(json!({
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/multikey/v1",
|
||||
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||
],
|
||||
"id": did,
|
||||
"alsoKnownAs": also_known_as,
|
||||
"verificationMethod": parsed.iter().map(|m| json!({
|
||||
"id": format!("{}{}", did, if m.id.starts_with('#') { m.id.clone() } else { format!("#{}", m.id) }),
|
||||
"type": m.method_type,
|
||||
"controller": did,
|
||||
"publicKeyMultibase": m.public_key_multibase
|
||||
})).collect::<Vec<_>>(),
|
||||
"service": [{
|
||||
"id": "#atproto_pds",
|
||||
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
|
||||
"serviceEndpoint": service_endpoint
|
||||
}]
|
||||
}))
|
||||
.into_response();
|
||||
}
|
||||
|
||||
let key_info = match state.user_repo.get_user_key_by_id(user_id).await {
|
||||
Ok(Some(k)) => k,
|
||||
Ok(None) => return ApiError::InternalError(None).into_response(),
|
||||
Err(_) => return ApiError::InternalError(None).into_response(),
|
||||
};
|
||||
let key_bytes: Vec<u8> =
|
||||
match tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) {
|
||||
Ok(k) => k,
|
||||
Err(_) => {
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
};
|
||||
let public_key_multibase = match get_public_key_multibase(&key_bytes) {
|
||||
Ok(pk) => pk,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to generate public key multibase: {}", e);
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
let verification_methods =
|
||||
build_override_or_key_verification_methods(state, user_id, &did, overrides.as_ref()).await;
|
||||
let verification_methods = match verification_methods {
|
||||
Ok(vm) => vm,
|
||||
Err(resp) => return resp,
|
||||
};
|
||||
|
||||
let also_known_as = if let Some(ref ovr) = overrides {
|
||||
if !ovr.also_known_as.is_empty() {
|
||||
ovr.also_known_as.clone()
|
||||
} else {
|
||||
vec![format!("at://{}", current_handle)]
|
||||
}
|
||||
} else {
|
||||
vec![format!("at://{}", current_handle)]
|
||||
};
|
||||
|
||||
Json(json!({
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/multikey/v1",
|
||||
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||
],
|
||||
"id": did,
|
||||
"alsoKnownAs": also_known_as,
|
||||
"verificationMethod": [{
|
||||
"id": format!("{}#atproto", did),
|
||||
"type": "Multikey",
|
||||
"controller": did,
|
||||
"publicKeyMultibase": public_key_multibase
|
||||
}],
|
||||
"service": [{
|
||||
"id": "#atproto_pds",
|
||||
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
|
||||
"serviceEndpoint": service_endpoint
|
||||
}]
|
||||
}))
|
||||
let also_known_as = common::resolve_also_known_as(overrides.as_ref(), ¤t_handle);
|
||||
Json(common::build_did_document(
|
||||
&did,
|
||||
also_known_as,
|
||||
verification_methods,
|
||||
&service_endpoint,
|
||||
))
|
||||
.into_response()
|
||||
}
|
||||
|
||||
@@ -323,92 +254,65 @@ pub async fn user_did_doc(State(state): State<AppState>, Path(handle): Path<Stri
|
||||
|
||||
let service_endpoint = migrated_to_pds.unwrap_or_else(|| format!("https://{}", hostname));
|
||||
|
||||
if let Some((ovr, parsed)) = overrides.as_ref().and_then(|ovr| {
|
||||
let verification_methods =
|
||||
build_override_or_key_verification_methods(&state, user_id, &did, overrides.as_ref()).await;
|
||||
let verification_methods = match verification_methods {
|
||||
Ok(vm) => vm,
|
||||
Err(resp) => return resp,
|
||||
};
|
||||
|
||||
let also_known_as = common::resolve_also_known_as(overrides.as_ref(), ¤t_handle);
|
||||
Json(common::build_did_document(
|
||||
&did,
|
||||
also_known_as,
|
||||
verification_methods,
|
||||
&service_endpoint,
|
||||
))
|
||||
.into_response()
|
||||
}
|
||||
|
||||
async fn build_override_or_key_verification_methods(
|
||||
state: &AppState,
|
||||
user_id: uuid::Uuid,
|
||||
did: &str,
|
||||
overrides: Option<&tranquil_db_traits::DidWebOverrides>,
|
||||
) -> Result<Vec<serde_json::Value>, Response> {
|
||||
if let Some(parsed) = overrides.and_then(|ovr| {
|
||||
serde_json::from_value::<Vec<DidWebVerificationMethod>>(ovr.verification_methods.clone())
|
||||
.ok()
|
||||
.filter(|p| !p.is_empty())
|
||||
.map(|p| (ovr, p))
|
||||
}) {
|
||||
let also_known_as = if !ovr.also_known_as.is_empty() {
|
||||
ovr.also_known_as.clone()
|
||||
} else {
|
||||
vec![format!("at://{}", current_handle)]
|
||||
};
|
||||
|
||||
return Json(json!({
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/multikey/v1",
|
||||
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||
],
|
||||
"id": did,
|
||||
"alsoKnownAs": also_known_as,
|
||||
"verificationMethod": parsed.iter().map(|m| json!({
|
||||
"id": format!("{}{}", did, if m.id.starts_with('#') { m.id.clone() } else { format!("#{}", m.id) }),
|
||||
"type": m.method_type,
|
||||
"controller": did,
|
||||
"publicKeyMultibase": m.public_key_multibase
|
||||
})).collect::<Vec<_>>(),
|
||||
"service": [{
|
||||
"id": "#atproto_pds",
|
||||
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
|
||||
"serviceEndpoint": service_endpoint
|
||||
}]
|
||||
}))
|
||||
.into_response();
|
||||
return Ok(parsed
|
||||
.iter()
|
||||
.map(|m| {
|
||||
json!({
|
||||
"id": format!("{}{}", did, if m.id.starts_with('#') { m.id.clone() } else { format!("#{}", m.id) }),
|
||||
"type": m.method_type,
|
||||
"controller": did,
|
||||
"publicKeyMultibase": m.public_key_multibase
|
||||
})
|
||||
})
|
||||
.collect());
|
||||
}
|
||||
|
||||
let key_info = match state.user_repo.get_user_key_by_id(user_id).await {
|
||||
Ok(Some(k)) => k,
|
||||
Ok(None) => return ApiError::InternalError(None).into_response(),
|
||||
Err(_) => return ApiError::InternalError(None).into_response(),
|
||||
};
|
||||
let key_bytes: Vec<u8> =
|
||||
match tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) {
|
||||
Ok(k) => k,
|
||||
Err(_) => {
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
};
|
||||
let public_key_multibase = match get_public_key_multibase(&key_bytes) {
|
||||
Ok(pk) => pk,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to generate public key multibase: {}", e);
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
_ => return Err(ApiError::InternalError(None).into_response()),
|
||||
};
|
||||
let key_bytes =
|
||||
tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version)
|
||||
.map_err(|_| ApiError::InternalError(None).into_response())?;
|
||||
let public_key_multibase = get_public_key_multibase(&key_bytes).map_err(|e| {
|
||||
tracing::error!("Failed to generate public key multibase: {}", e);
|
||||
ApiError::InternalError(None).into_response()
|
||||
})?;
|
||||
|
||||
let also_known_as = if let Some(ref ovr) = overrides {
|
||||
if !ovr.also_known_as.is_empty() {
|
||||
ovr.also_known_as.clone()
|
||||
} else {
|
||||
vec![format!("at://{}", current_handle)]
|
||||
}
|
||||
} else {
|
||||
vec![format!("at://{}", current_handle)]
|
||||
};
|
||||
|
||||
Json(json!({
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/multikey/v1",
|
||||
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||
],
|
||||
"id": did,
|
||||
"alsoKnownAs": also_known_as,
|
||||
"verificationMethod": [{
|
||||
"id": format!("{}#atproto", did),
|
||||
"type": "Multikey",
|
||||
"controller": did,
|
||||
"publicKeyMultibase": public_key_multibase
|
||||
}],
|
||||
"service": [{
|
||||
"id": "#atproto_pds",
|
||||
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
|
||||
"serviceEndpoint": service_endpoint
|
||||
}]
|
||||
}))
|
||||
.into_response()
|
||||
Ok(vec![json!({
|
||||
"id": format!("{}#atproto", did),
|
||||
"type": "Multikey",
|
||||
"controller": did,
|
||||
"publicKeyMultibase": public_key_multibase
|
||||
})])
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -562,12 +466,12 @@ pub struct AtprotoPds {
|
||||
pub async fn get_recommended_did_credentials(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<NotTakendown>,
|
||||
) -> Result<Response, ApiError> {
|
||||
) -> Result<Json<GetRecommendedDidCredentialsOutput>, ApiError> {
|
||||
let handle = state
|
||||
.user_repo
|
||||
.get_handle_by_did(&auth.did)
|
||||
.await
|
||||
.map_err(|_| ApiError::InternalError(None))?
|
||||
.log_db_err("fetching handle for DID credentials")?
|
||||
.ok_or(ApiError::InternalError(None))?;
|
||||
|
||||
let key_bytes = auth.key_bytes.clone().ok_or_else(|| {
|
||||
@@ -593,21 +497,17 @@ pub async fn get_recommended_did_credentials(
|
||||
};
|
||||
vec![server_rotation_key]
|
||||
};
|
||||
Ok((
|
||||
StatusCode::OK,
|
||||
Json(GetRecommendedDidCredentialsOutput {
|
||||
rotation_keys,
|
||||
also_known_as: vec![format!("at://{}", handle)],
|
||||
verification_methods: VerificationMethods { atproto: did_key },
|
||||
services: Services {
|
||||
atproto_pds: AtprotoPds {
|
||||
service_type: tranquil_pds::plc::ServiceType::Pds.as_str().to_string(),
|
||||
endpoint: pds_endpoint,
|
||||
},
|
||||
Ok(Json(GetRecommendedDidCredentialsOutput {
|
||||
rotation_keys,
|
||||
also_known_as: vec![format!("at://{}", handle)],
|
||||
verification_methods: VerificationMethods { atproto: did_key },
|
||||
services: Services {
|
||||
atproto_pds: AtprotoPds {
|
||||
service_type: tranquil_pds::plc::ServiceType::Pds.as_str().to_string(),
|
||||
endpoint: pds_endpoint,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.into_response())
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -619,14 +519,12 @@ pub async fn update_handle(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<NotTakendown>,
|
||||
Json(input): Json<UpdateHandleInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope(
|
||||
) -> Result<Json<EmptyResponse>, ApiError> {
|
||||
tranquil_pds::auth::scope_check::check_identity_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
tranquil_pds::oauth::scopes::IdentityAttr::Handle,
|
||||
) {
|
||||
return Ok(e);
|
||||
}
|
||||
)?;
|
||||
let did = auth.did.clone();
|
||||
let _rate_limit = check_user_rate_limit_with_message::<HandleUpdateLimit>(
|
||||
&state,
|
||||
@@ -644,7 +542,7 @@ pub async fn update_handle(
|
||||
.user_repo
|
||||
.get_id_and_handle_by_did(&did)
|
||||
.await
|
||||
.map_err(|_| ApiError::InternalError(None))?
|
||||
.log_db_err("fetching user for handle update")?
|
||||
.ok_or(ApiError::InternalError(None))?;
|
||||
let user_id = user_row.id;
|
||||
let current_handle = user_row.handle;
|
||||
@@ -710,7 +608,7 @@ pub async fn update_handle(
|
||||
{
|
||||
warn!("Failed to sequence identity event for handle update: {}", e);
|
||||
}
|
||||
return Ok(EmptyResponse::ok().into_response());
|
||||
return Ok(Json(EmptyResponse {}));
|
||||
}
|
||||
if short_part.contains('.') {
|
||||
return Err(ApiError::InvalidHandle(Some(
|
||||
@@ -736,7 +634,7 @@ pub async fn update_handle(
|
||||
{
|
||||
warn!("Failed to sequence identity event for handle update: {}", e);
|
||||
}
|
||||
return Ok(EmptyResponse::ok().into_response());
|
||||
return Ok(Json(EmptyResponse {}));
|
||||
}
|
||||
match tranquil_pds::handle::verify_handle_ownership(&new_handle, &did).await {
|
||||
Ok(()) => {}
|
||||
@@ -766,7 +664,7 @@ pub async fn update_handle(
|
||||
.user_repo
|
||||
.check_handle_exists(&handle_typed, user_id)
|
||||
.await
|
||||
.map_err(|_| ApiError::InternalError(None))?;
|
||||
.log_db_err("checking handle existence")?;
|
||||
if handle_exists {
|
||||
return Err(ApiError::HandleTaken);
|
||||
}
|
||||
@@ -797,7 +695,7 @@ pub async fn update_handle(
|
||||
if let Err(e) = update_plc_handle(&state, &did, &handle_typed).await {
|
||||
warn!("Failed to update PLC handle: {}", e);
|
||||
}
|
||||
Ok(EmptyResponse::ok().into_response())
|
||||
Ok(Json(EmptyResponse {}))
|
||||
}
|
||||
|
||||
pub async fn update_plc_handle(
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use axum::response::{IntoResponse, Response};
|
||||
|
||||
use crate::api::error::ApiError;
|
||||
use crate::auth::AuthenticatedUser;
|
||||
use crate::state::AppState;
|
||||
@@ -29,21 +27,20 @@ async fn check_delegation_flag(
|
||||
did: &Did,
|
||||
check_is_delegated: bool,
|
||||
error_msg: &str,
|
||||
) -> Result<bool, Response> {
|
||||
) -> Result<bool, ApiError> {
|
||||
let result = if check_is_delegated {
|
||||
state.delegation_repo.is_delegated_account(did).await
|
||||
} else {
|
||||
state.delegation_repo.controls_any_accounts(did).await
|
||||
};
|
||||
match result {
|
||||
Ok(true) => Err(ApiError::InvalidDelegation(error_msg.into()).into_response()),
|
||||
Ok(true) => Err(ApiError::InvalidDelegation(error_msg.into())),
|
||||
Ok(false) => Ok(false),
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to check delegation status: {:?}", e);
|
||||
Err(
|
||||
ApiError::InternalError(Some("Failed to verify delegation status".into()))
|
||||
.into_response(),
|
||||
)
|
||||
Err(ApiError::InternalError(Some(
|
||||
"Failed to verify delegation status".into(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +48,7 @@ async fn check_delegation_flag(
|
||||
pub async fn verify_can_add_controllers<'a>(
|
||||
state: &AppState,
|
||||
user: &'a AuthenticatedUser,
|
||||
) -> Result<CanAddControllers<'a>, Response> {
|
||||
) -> Result<CanAddControllers<'a>, ApiError> {
|
||||
check_delegation_flag(
|
||||
state,
|
||||
&user.did,
|
||||
@@ -68,7 +65,7 @@ pub async fn verify_can_add_controllers<'a>(
|
||||
pub async fn verify_can_control_accounts<'a>(
|
||||
state: &AppState,
|
||||
user: &'a AuthenticatedUser,
|
||||
) -> Result<CanControlAccounts<'a>, Response> {
|
||||
) -> Result<CanControlAccounts<'a>, ApiError> {
|
||||
check_delegation_flag(
|
||||
state,
|
||||
&user.did,
|
||||
|
||||
Reference in New Issue
Block a user