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}