this repo has no description
1use crate::state::AppState; 2use axum::{ 3 Json, 4 extract::State, 5 http::StatusCode, 6 response::{IntoResponse, Response}, 7}; 8use serde::Deserialize; 9use serde_json::json; 10use tracing::error; 11#[derive(Deserialize)] 12pub struct UpdateAccountEmailInput { 13 pub account: String, 14 pub email: String, 15} 16pub async fn update_account_email( 17 State(state): State<AppState>, 18 headers: axum::http::HeaderMap, 19 Json(input): Json<UpdateAccountEmailInput>, 20) -> Response { 21 let auth_header = headers.get("Authorization"); 22 if auth_header.is_none() { 23 return ( 24 StatusCode::UNAUTHORIZED, 25 Json(json!({"error": "AuthenticationRequired"})), 26 ) 27 .into_response(); 28 } 29 let account = input.account.trim(); 30 let email = input.email.trim(); 31 if account.is_empty() || email.is_empty() { 32 return ( 33 StatusCode::BAD_REQUEST, 34 Json(json!({"error": "InvalidRequest", "message": "account and email are required"})), 35 ) 36 .into_response(); 37 } 38 let result = sqlx::query!("UPDATE users SET email = $1 WHERE did = $2", email, account) 39 .execute(&state.db) 40 .await; 41 match result { 42 Ok(r) => { 43 if r.rows_affected() == 0 { 44 return ( 45 StatusCode::NOT_FOUND, 46 Json(json!({"error": "AccountNotFound", "message": "Account not found"})), 47 ) 48 .into_response(); 49 } 50 (StatusCode::OK, Json(json!({}))).into_response() 51 } 52 Err(e) => { 53 error!("DB error updating email: {:?}", e); 54 ( 55 StatusCode::INTERNAL_SERVER_ERROR, 56 Json(json!({"error": "InternalError"})), 57 ) 58 .into_response() 59 } 60 } 61} 62#[derive(Deserialize)] 63pub struct UpdateAccountHandleInput { 64 pub did: String, 65 pub handle: String, 66} 67pub async fn update_account_handle( 68 State(state): State<AppState>, 69 headers: axum::http::HeaderMap, 70 Json(input): Json<UpdateAccountHandleInput>, 71) -> Response { 72 let auth_header = headers.get("Authorization"); 73 if auth_header.is_none() { 74 return ( 75 StatusCode::UNAUTHORIZED, 76 Json(json!({"error": "AuthenticationRequired"})), 77 ) 78 .into_response(); 79 } 80 let did = input.did.trim(); 81 let handle = input.handle.trim(); 82 if did.is_empty() || handle.is_empty() { 83 return ( 84 StatusCode::BAD_REQUEST, 85 Json(json!({"error": "InvalidRequest", "message": "did and handle are required"})), 86 ) 87 .into_response(); 88 } 89 if !handle 90 .chars() 91 .all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_') 92 { 93 return ( 94 StatusCode::BAD_REQUEST, 95 Json(json!({"error": "InvalidHandle", "message": "Handle contains invalid characters"})), 96 ) 97 .into_response(); 98 } 99 let old_handle = sqlx::query_scalar!("SELECT handle FROM users WHERE did = $1", did) 100 .fetch_optional(&state.db) 101 .await 102 .ok() 103 .flatten(); 104 let existing = sqlx::query!("SELECT id FROM users WHERE handle = $1 AND did != $2", handle, did) 105 .fetch_optional(&state.db) 106 .await; 107 if let Ok(Some(_)) = existing { 108 return ( 109 StatusCode::BAD_REQUEST, 110 Json(json!({"error": "HandleTaken", "message": "Handle is already in use"})), 111 ) 112 .into_response(); 113 } 114 let result = sqlx::query!("UPDATE users SET handle = $1 WHERE did = $2", handle, did) 115 .execute(&state.db) 116 .await; 117 match result { 118 Ok(r) => { 119 if r.rows_affected() == 0 { 120 return ( 121 StatusCode::NOT_FOUND, 122 Json(json!({"error": "AccountNotFound", "message": "Account not found"})), 123 ) 124 .into_response(); 125 } 126 if let Some(old) = old_handle { 127 let _ = state.cache.delete(&format!("handle:{}", old)).await; 128 } 129 let _ = state.cache.delete(&format!("handle:{}", handle)).await; 130 (StatusCode::OK, Json(json!({}))).into_response() 131 } 132 Err(e) => { 133 error!("DB error updating handle: {:?}", e); 134 ( 135 StatusCode::INTERNAL_SERVER_ERROR, 136 Json(json!({"error": "InternalError"})), 137 ) 138 .into_response() 139 } 140 } 141} 142#[derive(Deserialize)] 143pub struct UpdateAccountPasswordInput { 144 pub did: String, 145 pub password: String, 146} 147pub async fn update_account_password( 148 State(state): State<AppState>, 149 headers: axum::http::HeaderMap, 150 Json(input): Json<UpdateAccountPasswordInput>, 151) -> Response { 152 let auth_header = headers.get("Authorization"); 153 if auth_header.is_none() { 154 return ( 155 StatusCode::UNAUTHORIZED, 156 Json(json!({"error": "AuthenticationRequired"})), 157 ) 158 .into_response(); 159 } 160 let did = input.did.trim(); 161 let password = input.password.trim(); 162 if did.is_empty() || password.is_empty() { 163 return ( 164 StatusCode::BAD_REQUEST, 165 Json(json!({"error": "InvalidRequest", "message": "did and password are required"})), 166 ) 167 .into_response(); 168 } 169 let password_hash = match bcrypt::hash(password, bcrypt::DEFAULT_COST) { 170 Ok(h) => h, 171 Err(e) => { 172 error!("Failed to hash password: {:?}", e); 173 return ( 174 StatusCode::INTERNAL_SERVER_ERROR, 175 Json(json!({"error": "InternalError"})), 176 ) 177 .into_response(); 178 } 179 }; 180 let result = sqlx::query!("UPDATE users SET password_hash = $1 WHERE did = $2", password_hash, did) 181 .execute(&state.db) 182 .await; 183 match result { 184 Ok(r) => { 185 if r.rows_affected() == 0 { 186 return ( 187 StatusCode::NOT_FOUND, 188 Json(json!({"error": "AccountNotFound", "message": "Account not found"})), 189 ) 190 .into_response(); 191 } 192 (StatusCode::OK, Json(json!({}))).into_response() 193 } 194 Err(e) => { 195 error!("DB error updating password: {:?}", e); 196 ( 197 StatusCode::INTERNAL_SERVER_ERROR, 198 Json(json!({"error": "InternalError"})), 199 ) 200 .into_response() 201 } 202 } 203}