Something went wrong. Try again.
A downstream fork of Tranquil PDS for use in margin.cafe
Something went wrong. Try again.
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200use axum::{Json, extract::State};use serde::Deserialize;use tracing::{error, info, warn};use tranquil_pds::api::EmptyResponse;use tranquil_pds::api::error::ApiError;use tranquil_pds::auth::{Admin, Auth};use tranquil_pds::state::AppState;use tranquil_pds::types::{Did, Handle, PlainPassword};
#[derive(Deserialize)]pub struct UpdateAccountEmailInput { pub account: String, pub email: String,}
pub async fn update_account_email( State(state): State<AppState>, _auth: Auth<Admin>, Json(input): Json<UpdateAccountEmailInput>,) -> Result<Json<EmptyResponse>, ApiError> { let account = input.account.trim(); let email = input.email.trim(); if account.is_empty() || email.is_empty() { return Err(ApiError::InvalidRequest( "account and email are required".into(), )); } let account_did: Did = account .parse() .map_err(|_| ApiError::InvalidDid("Invalid DID format".into()))?;
match state .repos .user .admin_update_email(&account_did, email) .await { Ok(0) => Err(ApiError::AccountNotFound), Ok(_) => Ok(Json(EmptyResponse {})), Err(e) => { error!("DB error updating email: {:?}", e); Err(ApiError::InternalError(None)) } }}
#[derive(Deserialize)]pub struct UpdateAccountHandleInput { pub did: Did, pub handle: String,}
pub async fn update_account_handle( State(state): State<AppState>, _auth: Auth<Admin>, Json(input): Json<UpdateAccountHandleInput>,) -> Result<Json<EmptyResponse>, ApiError> { let did = &input.did; let input_handle = input.handle.trim(); if input_handle.is_empty() { return Err(ApiError::InvalidRequest("handle is required".into())); } if !input_handle .chars() .all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_') { return Err(ApiError::InvalidHandle(None)); } let available_domains = tranquil_config::get().server.user_handle_domain_list(); let handle = if !input_handle.contains('.') { format!("{}.{}", input_handle, &available_domains[0]) } else { input_handle.to_string() }; let old_handle = state.repos.user.get_handle_by_did(did).await.ok().flatten(); let user_id = state .repos .user .get_id_by_did(did) .await .ok() .flatten() .ok_or(ApiError::AccountNotFound)?; let handle_for_check: Handle = handle.parse().map_err(|_| ApiError::InvalidHandle(None))?; if let Ok(true) = state .repos .user .check_handle_exists(&handle_for_check, user_id) .await { return Err(ApiError::HandleTaken); } match state .repos .user .admin_update_handle(did, &handle_for_check) .await { Ok(0) => Err(ApiError::AccountNotFound), Ok(_) => { if let Some(old) = old_handle { let _ = state .cache .delete(&tranquil_pds::cache_keys::handle_key(&old)) .await; } let _ = state .cache .delete(&tranquil_pds::cache_keys::handle_key(&handle_for_check)) .await; if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event( &state, did, Some(&handle_for_check), ) .await { warn!( "Failed to sequence identity event for admin handle update: {}", e ); } if let Err(e) = crate::identity::did::update_plc_handle(&state, did, &handle_for_check).await { warn!("Failed to update PLC handle for admin handle update: {}", e); } Ok(Json(EmptyResponse {})) } Err(e) => { error!("DB error updating handle: {:?}", e); Err(ApiError::InternalError(None)) } }}
#[derive(Deserialize)]pub struct UpdateAccountPasswordInput { pub did: Did, pub password: PlainPassword,}
pub async fn update_account_password( State(state): State<AppState>, _auth: Auth<Admin>, Json(input): Json<UpdateAccountPasswordInput>,) -> Result<Json<EmptyResponse>, ApiError> { let did = &input.did; let password = input.password.trim(); if password.is_empty() { return Err(ApiError::InvalidRequest("password is required".into())); } let password_hash = crate::common::hash_or_internal_error(password)?;
match state .repos .user .admin_update_password(did, &password_hash) .await { Ok(0) => Err(ApiError::AccountNotFound), Ok(_) => Ok(Json(EmptyResponse {})), Err(e) => { error!("DB error updating password: {:?}", e); Err(ApiError::InternalError(None)) } }}
#[derive(Deserialize)]pub struct SetAdminStatusInput { pub did: Did, pub admin: bool,}
pub async fn set_admin_status( State(state): State<AppState>, auth: Auth<Admin>, Json(input): Json<SetAdminStatusInput>,) -> Result<Json<EmptyResponse>, ApiError> { info!( actor = %auth.did, target = %input.did, admin = input.admin, "admin status change" );
state .repos .user .set_admin_status(&input.did, input.admin) .await .map_err(|e| { error!("DB error setting admin status: {:?}", e); ApiError::InternalError(None) })?;
Ok(Json(EmptyResponse {}))}