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