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}