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 existing = sqlx::query!("SELECT id FROM users WHERE handle = $1 AND did != $2", handle, did) 112 .fetch_optional(&state.db) 113 .await; 114 115 if let Ok(Some(_)) = existing { 116 return ( 117 StatusCode::BAD_REQUEST, 118 Json(json!({"error": "HandleTaken", "message": "Handle is already in use"})), 119 ) 120 .into_response(); 121 } 122 123 let result = sqlx::query!("UPDATE users SET handle = $1 WHERE did = $2", handle, did) 124 .execute(&state.db) 125 .await; 126 127 match result { 128 Ok(r) => { 129 if r.rows_affected() == 0 { 130 return ( 131 StatusCode::NOT_FOUND, 132 Json(json!({"error": "AccountNotFound", "message": "Account not found"})), 133 ) 134 .into_response(); 135 } 136 (StatusCode::OK, Json(json!({}))).into_response() 137 } 138 Err(e) => { 139 error!("DB error updating handle: {:?}", e); 140 ( 141 StatusCode::INTERNAL_SERVER_ERROR, 142 Json(json!({"error": "InternalError"})), 143 ) 144 .into_response() 145 } 146 } 147} 148 149#[derive(Deserialize)] 150pub struct UpdateAccountPasswordInput { 151 pub did: String, 152 pub password: String, 153} 154 155pub async fn update_account_password( 156 State(state): State<AppState>, 157 headers: axum::http::HeaderMap, 158 Json(input): Json<UpdateAccountPasswordInput>, 159) -> Response { 160 let auth_header = headers.get("Authorization"); 161 if auth_header.is_none() { 162 return ( 163 StatusCode::UNAUTHORIZED, 164 Json(json!({"error": "AuthenticationRequired"})), 165 ) 166 .into_response(); 167 } 168 169 let did = input.did.trim(); 170 let password = input.password.trim(); 171 172 if did.is_empty() || password.is_empty() { 173 return ( 174 StatusCode::BAD_REQUEST, 175 Json(json!({"error": "InvalidRequest", "message": "did and password are required"})), 176 ) 177 .into_response(); 178 } 179 180 let password_hash = match bcrypt::hash(password, bcrypt::DEFAULT_COST) { 181 Ok(h) => h, 182 Err(e) => { 183 error!("Failed to hash password: {:?}", e); 184 return ( 185 StatusCode::INTERNAL_SERVER_ERROR, 186 Json(json!({"error": "InternalError"})), 187 ) 188 .into_response(); 189 } 190 }; 191 192 let result = sqlx::query!("UPDATE users SET password_hash = $1 WHERE did = $2", password_hash, did) 193 .execute(&state.db) 194 .await; 195 196 match result { 197 Ok(r) => { 198 if r.rows_affected() == 0 { 199 return ( 200 StatusCode::NOT_FOUND, 201 Json(json!({"error": "AccountNotFound", "message": "Account not found"})), 202 ) 203 .into_response(); 204 } 205 (StatusCode::OK, Json(json!({}))).into_response() 206 } 207 Err(e) => { 208 error!("DB error updating password: {:?}", e); 209 ( 210 StatusCode::INTERNAL_SERVER_ERROR, 211 Json(json!({"error": "InternalError"})), 212 ) 213 .into_response() 214 } 215 } 216}