use axum::{
extract::{Path, Query},
http::StatusCode,
response::IntoResponse,
routing::{get, post},
Extension, Json, Router,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use super::{AccountError, AccountService, AccountStatus, CreateAccount, UpdateAccount};
pub fn account_routes() -> Router {
Router::new()
.route("/accounts", post(create_account).get(list_accounts))
.route(
"/accounts/{id}",
get(get_account)
.patch(update_account)
.delete(delete_account),
)
.route("/accounts/{id}/disable", post(disable_account))
.route("/accounts/{id}/enable", post(enable_account))
.route("/accounts/{id}/lock", post(lock_account))
.route("/accounts/{id}/unlock", post(unlock_account))
.route("/accounts/{id}/verify-email", post(verify_email))
.route("/accounts/{id}/change-password", post(change_password))
}
#[derive(Debug, Deserialize)]
pub struct ListAccountsQuery {
pub status: Option<String>,
pub limit: Option<usize>,
pub offset: Option<usize>,
}
#[derive(Debug, Deserialize)]
pub struct ReasonRequest {
pub reason: String,
}
#[derive(Debug, Deserialize)]
pub struct ChangePasswordRequest {
pub new_password: String,
}
#[derive(Debug, Serialize)]
pub struct AccountListResponse {
pub accounts: Vec<serde_json::Value>,
pub total: u64,
pub limit: usize,
pub offset: usize,
}
async fn create_account(
Extension(svc): Extension<Arc<AccountService>>,
Json(data): Json<CreateAccount>,
) -> Result<impl IntoResponse, crate::error::Error> {
let account = svc.create_account(data).await?;
Ok((StatusCode::CREATED, Json(account)))
}
async fn get_account(
Extension(svc): Extension<Arc<AccountService>>,
Path(id): Path<String>,
) -> Result<impl IntoResponse, crate::error::Error> {
let account = svc
.get_account(&id)
.await?
.ok_or(AccountError::NotFound(id))?;
Ok(Json(account))
}
async fn update_account(
Extension(svc): Extension<Arc<AccountService>>,
Path(id): Path<String>,
Json(data): Json<UpdateAccount>,
) -> Result<impl IntoResponse, crate::error::Error> {
let account = svc.update_account(&id, data).await?;
Ok(Json(account))
}
async fn delete_account(
Extension(svc): Extension<Arc<AccountService>>,
Path(id): Path<String>,
) -> Result<impl IntoResponse, crate::error::Error> {
svc.delete_account(&id).await?;
Ok(StatusCode::NO_CONTENT)
}
async fn list_accounts(
Extension(svc): Extension<Arc<AccountService>>,
Query(query): Query<ListAccountsQuery>,
) -> Result<impl IntoResponse, crate::error::Error> {
let limit = query.limit.unwrap_or(50).min(200);
let offset = query.offset.unwrap_or(0);
let status_filter = query
.status
.as_deref()
.and_then(|s| s.parse::<AccountStatus>().ok());
let accounts = svc.list_accounts(status_filter, limit, offset).await?;
let total = svc.count_accounts(status_filter).await?;
Ok(Json(AccountListResponse {
accounts: accounts
.into_iter()
.map(|a| serde_json::to_value(a).unwrap_or_default())
.collect(),
total,
limit,
offset,
}))
}
async fn disable_account(
Extension(svc): Extension<Arc<AccountService>>,
Path(id): Path<String>,
Json(body): Json<ReasonRequest>,
) -> Result<impl IntoResponse, crate::error::Error> {
svc.disable_account(&id, &body.reason).await?;
Ok(StatusCode::NO_CONTENT)
}
async fn enable_account(
Extension(svc): Extension<Arc<AccountService>>,
Path(id): Path<String>,
) -> Result<impl IntoResponse, crate::error::Error> {
svc.enable_account(&id).await?;
Ok(StatusCode::NO_CONTENT)
}
async fn lock_account(
Extension(svc): Extension<Arc<AccountService>>,
Path(id): Path<String>,
Json(body): Json<ReasonRequest>,
) -> Result<impl IntoResponse, crate::error::Error> {
svc.lock_account(&id, &body.reason).await?;
Ok(StatusCode::NO_CONTENT)
}
async fn unlock_account(
Extension(svc): Extension<Arc<AccountService>>,
Path(id): Path<String>,
) -> Result<impl IntoResponse, crate::error::Error> {
svc.unlock_account(&id).await?;
Ok(StatusCode::NO_CONTENT)
}
async fn verify_email(
Extension(svc): Extension<Arc<AccountService>>,
Path(id): Path<String>,
) -> Result<impl IntoResponse, crate::error::Error> {
svc.verify_email(&id).await?;
Ok(StatusCode::NO_CONTENT)
}
async fn change_password(
Extension(svc): Extension<Arc<AccountService>>,
Path(id): Path<String>,
Json(body): Json<ChangePasswordRequest>,
) -> Result<impl IntoResponse, crate::error::Error> {
svc.change_password(&id, &body.new_password).await?;
Ok(StatusCode::NO_CONTENT)
}