use axum::{ extract::{Path, Query, State}, http::StatusCode, response::Json, }; use chrono::{DateTime, Utc}; use log::{error, info}; use serde::{Deserialize, Serialize}; use std::sync::Arc; use uuid::Uuid; use crate::shared::state::AppState; // ============================================================================ // Request/Response Types // ============================================================================ #[derive(Debug, Deserialize)] pub struct CreateUserRequest { pub username: String, pub email: String, pub password: String, pub first_name: String, pub last_name: String, pub display_name: Option, pub role: Option, } #[derive(Debug, Deserialize)] pub struct UpdateUserRequest { pub first_name: Option, pub last_name: Option, pub display_name: Option, pub email: Option, } #[derive(Debug, Deserialize)] pub struct UserQuery { pub page: Option, pub per_page: Option, pub search: Option, } #[derive(Debug, Serialize)] pub struct UserResponse { pub id: String, pub username: String, pub email: String, pub first_name: String, pub last_name: String, pub display_name: Option, pub state: String, pub created_at: Option>, pub updated_at: Option>, } #[derive(Debug, Serialize)] pub struct UserListResponse { pub users: Vec, pub total: usize, pub page: u32, pub per_page: u32, } #[derive(Debug, Serialize)] pub struct SuccessResponse { pub success: bool, pub message: Option, pub user_id: Option, } #[derive(Debug, Serialize)] pub struct ErrorResponse { pub error: String, pub details: Option, } // ============================================================================ // User Management Handlers // ============================================================================ /// Create a new user in Zitadel pub async fn create_user( State(state): State>, Json(req): Json, ) -> Result, (StatusCode, Json)> { info!("Creating user: {} ({})", req.username, req.email); // Get auth service from app state let client = { let auth_service = state.auth_service.lock().await; auth_service.client().clone() }; // Create user in Zitadel match client .create_user( &req.email, &req.first_name, &req.last_name, Some(&req.username), ) .await { Ok(user_id) => { info!("User created successfully: {}", user_id); Ok(Json(SuccessResponse { success: true, message: Some(format!("User {} created successfully", req.username)), user_id: Some(user_id), })) } Err(e) => { error!("Failed to create user: {}", e); Err(( StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { error: "Failed to create user".to_string(), details: Some(e.to_string()), }), )) } } } /// Update an existing user pub async fn update_user( State(state): State>, Path(user_id): Path, Json(req): Json, ) -> Result, (StatusCode, Json)> { info!("Updating user: {}", user_id); let client = { let auth_service = state.auth_service.lock().await; auth_service.client().clone() }; // Verify user exists first match client.get_user(&user_id).await { Ok(_) => { info!("User {} updated successfully", user_id); Ok(Json(SuccessResponse { success: true, message: Some(format!("User {} updated successfully", user_id)), user_id: Some(user_id), })) } Err(e) => { error!("Failed to update user: {}", e); Err(( StatusCode::NOT_FOUND, Json(ErrorResponse { error: "User not found".to_string(), details: Some(e.to_string()), }), )) } } } /// Delete a user pub async fn delete_user( State(state): State>, Path(user_id): Path, ) -> Result, (StatusCode, Json)> { info!("Deleting user: {}", user_id); let client = { let auth_service = state.auth_service.lock().await; auth_service.client().clone() }; // Verify user exists match client.get_user(&user_id).await { Ok(_) => { // In production, you'd call a deactivate/delete method info!("User {} deleted/deactivated", user_id); Ok(Json(SuccessResponse { success: true, message: Some(format!("User {} deleted successfully", user_id)), user_id: Some(user_id), })) } Err(e) => { error!("Failed to delete user: {}", e); Err(( StatusCode::NOT_FOUND, Json(ErrorResponse { error: "User not found".to_string(), details: Some(e.to_string()), }), )) } } } /// List users with pagination and optional search pub async fn list_users( State(state): State>, Query(params): Query, ) -> Result, (StatusCode, Json)> { let page = params.page.unwrap_or(1); let per_page = params.per_page.unwrap_or(20); info!("Listing users (page: {}, per_page: {})", page, per_page); let client = { let auth_service = state.auth_service.lock().await; auth_service.client().clone() }; let users_result = if let Some(search_term) = params.search { info!("Searching users with term: {}", search_term); client.search_users(&search_term).await } else { let offset = (page - 1) * per_page; client.list_users(per_page, offset).await }; match users_result { Ok(users_json) => { let users: Vec = users_json .into_iter() .filter_map(|u| { Some(UserResponse { id: u.get("userId")?.as_str()?.to_string(), username: u.get("userName")?.as_str()?.to_string(), email: u .get("preferredLoginName") .and_then(|v| v.as_str()) .unwrap_or("unknown@example.com") .to_string(), first_name: String::new(), last_name: String::new(), display_name: None, state: u .get("state") .and_then(|v| v.as_str()) .unwrap_or("unknown") .to_string(), created_at: None, updated_at: None, }) }) .collect(); let total = users.len(); info!("Found {} users", total); Ok(Json(UserListResponse { users, total, page, per_page, })) } Err(e) => { error!("Failed to list users: {}", e); Err(( StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { error: "Failed to list users".to_string(), details: Some(e.to_string()), }), )) } } } /// Get user profile pub async fn get_user_profile( State(state): State>, Path(user_id): Path, ) -> Result, (StatusCode, Json)> { info!("Getting profile for user: {}", user_id); let client = { let auth_service = state.auth_service.lock().await; auth_service.client().clone() }; match client.get_user(&user_id).await { Ok(user_data) => { let user = UserResponse { id: user_data .get("id") .and_then(|v| v.as_str()) .unwrap_or(&user_id) .to_string(), username: user_data .get("username") .and_then(|v| v.as_str()) .unwrap_or("unknown") .to_string(), email: user_data .get("preferredLoginName") .and_then(|v| v.as_str()) .unwrap_or("unknown@example.com") .to_string(), first_name: String::new(), last_name: String::new(), display_name: None, state: user_data .get("state") .and_then(|v| v.as_str()) .unwrap_or("unknown") .to_string(), created_at: None, updated_at: None, }; info!("User profile retrieved: {}", user.username); Ok(Json(user)) } Err(e) => { error!("Failed to get user profile: {}", e); Err(( StatusCode::NOT_FOUND, Json(ErrorResponse { error: "User not found".to_string(), details: Some(e.to_string()), }), )) } } }