Files
tranquil-pds/src/api/admin/account/update.rs
T
2025-12-16 21:23:11 +02:00

220 lines
6.5 KiB
Rust

use crate::state::AppState;
use axum::{
Json,
extract::State,
http::StatusCode,
response::{IntoResponse, Response},
};
use serde::Deserialize;
use serde_json::json;
use tracing::error;
#[derive(Deserialize)]
pub struct UpdateAccountEmailInput {
pub account: String,
pub email: String,
}
pub async fn update_account_email(
State(state): State<AppState>,
headers: axum::http::HeaderMap,
Json(input): Json<UpdateAccountEmailInput>,
) -> Response {
let auth_header = headers.get("Authorization");
if auth_header.is_none() {
return (
StatusCode::UNAUTHORIZED,
Json(json!({"error": "AuthenticationRequired"})),
)
.into_response();
}
let account = input.account.trim();
let email = input.email.trim();
if account.is_empty() || email.is_empty() {
return (
StatusCode::BAD_REQUEST,
Json(json!({"error": "InvalidRequest", "message": "account and email are required"})),
)
.into_response();
}
let result = sqlx::query!("UPDATE users SET email = $1 WHERE did = $2", email, account)
.execute(&state.db)
.await;
match result {
Ok(r) => {
if r.rows_affected() == 0 {
return (
StatusCode::NOT_FOUND,
Json(json!({"error": "AccountNotFound", "message": "Account not found"})),
)
.into_response();
}
(StatusCode::OK, Json(json!({}))).into_response()
}
Err(e) => {
error!("DB error updating email: {:?}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": "InternalError"})),
)
.into_response()
}
}
}
#[derive(Deserialize)]
pub struct UpdateAccountHandleInput {
pub did: String,
pub handle: String,
}
pub async fn update_account_handle(
State(state): State<AppState>,
headers: axum::http::HeaderMap,
Json(input): Json<UpdateAccountHandleInput>,
) -> Response {
let auth_header = headers.get("Authorization");
if auth_header.is_none() {
return (
StatusCode::UNAUTHORIZED,
Json(json!({"error": "AuthenticationRequired"})),
)
.into_response();
}
let did = input.did.trim();
let handle = input.handle.trim();
if did.is_empty() || handle.is_empty() {
return (
StatusCode::BAD_REQUEST,
Json(json!({"error": "InvalidRequest", "message": "did and handle are required"})),
)
.into_response();
}
if !handle
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_')
{
return (
StatusCode::BAD_REQUEST,
Json(
json!({"error": "InvalidHandle", "message": "Handle contains invalid characters"}),
),
)
.into_response();
}
let old_handle = sqlx::query_scalar!("SELECT handle FROM users WHERE did = $1", did)
.fetch_optional(&state.db)
.await
.ok()
.flatten();
let existing = sqlx::query!(
"SELECT id FROM users WHERE handle = $1 AND did != $2",
handle,
did
)
.fetch_optional(&state.db)
.await;
if let Ok(Some(_)) = existing {
return (
StatusCode::BAD_REQUEST,
Json(json!({"error": "HandleTaken", "message": "Handle is already in use"})),
)
.into_response();
}
let result = sqlx::query!("UPDATE users SET handle = $1 WHERE did = $2", handle, did)
.execute(&state.db)
.await;
match result {
Ok(r) => {
if r.rows_affected() == 0 {
return (
StatusCode::NOT_FOUND,
Json(json!({"error": "AccountNotFound", "message": "Account not found"})),
)
.into_response();
}
if let Some(old) = old_handle {
let _ = state.cache.delete(&format!("handle:{}", old)).await;
}
let _ = state.cache.delete(&format!("handle:{}", handle)).await;
(StatusCode::OK, Json(json!({}))).into_response()
}
Err(e) => {
error!("DB error updating handle: {:?}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": "InternalError"})),
)
.into_response()
}
}
}
#[derive(Deserialize)]
pub struct UpdateAccountPasswordInput {
pub did: String,
pub password: String,
}
pub async fn update_account_password(
State(state): State<AppState>,
headers: axum::http::HeaderMap,
Json(input): Json<UpdateAccountPasswordInput>,
) -> Response {
let auth_header = headers.get("Authorization");
if auth_header.is_none() {
return (
StatusCode::UNAUTHORIZED,
Json(json!({"error": "AuthenticationRequired"})),
)
.into_response();
}
let did = input.did.trim();
let password = input.password.trim();
if did.is_empty() || password.is_empty() {
return (
StatusCode::BAD_REQUEST,
Json(json!({"error": "InvalidRequest", "message": "did and password are required"})),
)
.into_response();
}
let password_hash = match bcrypt::hash(password, bcrypt::DEFAULT_COST) {
Ok(h) => h,
Err(e) => {
error!("Failed to hash password: {:?}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": "InternalError"})),
)
.into_response();
}
};
let result = sqlx::query!(
"UPDATE users SET password_hash = $1 WHERE did = $2",
password_hash,
did
)
.execute(&state.db)
.await;
match result {
Ok(r) => {
if r.rows_affected() == 0 {
return (
StatusCode::NOT_FOUND,
Json(json!({"error": "AccountNotFound", "message": "Account not found"})),
)
.into_response();
}
(StatusCode::OK, Json(json!({}))).into_response()
}
Err(e) => {
error!("DB error updating password: {:?}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": "InternalError"})),
)
.into_response()
}
}
}