1use crate::api::{
6 ApiResponse, ApiState, extract_bearer_token, responses::Pagination, validate_api_token,
7};
8use axum::{
9 Json,
10 extract::{Path, Query, State},
11 http::HeaderMap,
12};
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Serialize)]
17pub struct UserListItem {
18 pub id: String,
19 pub username: String,
20 pub email: String,
21 pub roles: Vec<String>,
22 pub active: bool,
23 pub created_at: String,
24 pub last_login: Option<String>,
25}
26
27#[derive(Debug, Serialize)]
29pub struct UserListResponse {
30 pub users: Vec<UserListItem>,
31 pub pagination: Pagination,
32}
33
34#[derive(Debug, Deserialize)]
36pub struct UserListQuery {
37 #[serde(default = "default_page")]
38 pub page: u32,
39 #[serde(default = "default_limit")]
40 pub limit: u32,
41 #[serde(default)]
42 pub search: Option<String>,
43 #[serde(default)]
44 pub role: Option<String>,
45 #[serde(default)]
46 pub active: Option<bool>,
47}
48
49fn default_page() -> u32 {
50 1
51}
52fn default_limit() -> u32 {
53 20
54}
55
56#[derive(Debug, Deserialize)]
58pub struct CreateUserRequest {
59 pub username: String,
60 pub password: String,
61 pub email: String,
62 #[serde(default)]
63 pub first_name: Option<String>,
64 #[serde(default)]
65 pub last_name: Option<String>,
66 #[serde(default)]
67 pub roles: Vec<String>,
68 #[serde(default = "default_active")]
69 pub active: bool,
70}
71
72fn default_active() -> bool {
73 true
74}
75
76#[derive(Debug, Deserialize)]
78pub struct UpdateUserRolesRequest {
79 pub roles: Vec<String>,
80}
81
82#[derive(Debug, Serialize)]
84pub struct SystemStats {
85 pub total_users: u64,
86 pub active_sessions: u64,
87 pub total_tokens: u64,
88 pub failed_logins_24h: u64,
89 pub system_uptime: String,
90 pub memory_usage: String,
91 pub cpu_usage: String,
92}
93
94pub async fn list_users(
97 State(state): State<ApiState>,
98 headers: HeaderMap,
99 Query(query): Query<UserListQuery>,
100) -> ApiResponse<UserListResponse> {
101 match extract_bearer_token(&headers) {
102 Some(token) => {
103 match validate_api_token(&state.auth_framework, &token).await {
104 Ok(auth_token) => {
105 if !auth_token.roles.contains(&"admin".to_string()) {
107 return ApiResponse::<UserListResponse>::forbidden_typed();
108 }
109
110 let total_users = 150u64; let _offset = (query.page - 1) * query.limit;
113
114 let page = if query.page == 0 { 1 } else { query.page };
116 let limit = if query.limit == 0 {
117 20
118 } else {
119 query.limit.min(100)
120 }; let total_pages = ((total_users as f64) / (limit as f64)).ceil() as u32;
124 let total_pages = if total_pages == 0 { 1 } else { total_pages };
125
126 let users = vec![
127 UserListItem {
128 id: "user_1".to_string(),
129 username: "admin@example.com".to_string(),
130 email: "admin@example.com".to_string(),
131 roles: vec!["admin".to_string()],
132 active: true,
133 created_at: "2024-01-01T00:00:00Z".to_string(),
134 last_login: Some("2024-08-17T10:30:00Z".to_string()),
135 },
136 UserListItem {
137 id: "user_2".to_string(),
138 username: "user@example.com".to_string(),
139 email: "user@example.com".to_string(),
140 roles: vec!["user".to_string()],
141 active: true,
142 created_at: "2024-01-02T00:00:00Z".to_string(),
143 last_login: Some("2024-08-16T15:45:00Z".to_string()),
144 },
145 ];
146
147 let pagination = Pagination {
148 page,
149 limit,
150 total: total_users,
151 pages: total_pages,
152 };
153
154 let response = UserListResponse { users, pagination };
155
156 ApiResponse::success(response)
157 }
158 Err(e) => {
159 let error_response = ApiResponse::<()>::from(e);
161 ApiResponse::<UserListResponse> {
162 success: error_response.success,
163 data: None,
164 error: error_response.error,
165 message: error_response.message,
166 }
167 }
168 }
169 }
170 None => ApiResponse::<UserListResponse>::unauthorized_typed(),
171 }
172}
173
174pub async fn create_user(
177 State(state): State<ApiState>,
178 headers: HeaderMap,
179 Json(req): Json<CreateUserRequest>,
180) -> ApiResponse<UserListItem> {
181 if req.username.is_empty() || req.password.is_empty() || req.email.is_empty() {
183 return ApiResponse::<UserListItem>::validation_error_typed(
184 "Username, password, and email are required",
185 );
186 }
187
188 if req.password.len() < 8 {
189 return ApiResponse::<UserListItem>::validation_error_typed(
190 "Password must be at least 8 characters long",
191 );
192 }
193
194 match extract_bearer_token(&headers) {
195 Some(token) => {
196 match validate_api_token(&state.auth_framework, &token).await {
197 Ok(auth_token) => {
198 if !auth_token.roles.contains(&"admin".to_string()) {
200 return ApiResponse::forbidden_typed();
201 }
202
203 let new_user = UserListItem {
210 id: format!("user_{}", chrono::Utc::now().timestamp()),
211 username: req.username,
212 email: req.email,
213 roles: req.roles,
214 active: req.active,
215 created_at: chrono::Utc::now().to_rfc3339(),
216 last_login: None,
217 };
218
219 tracing::info!("New user created: {}", new_user.id);
220 ApiResponse::success(new_user)
221 }
222 Err(e) => {
223 let error_response = ApiResponse::<()>::from(e);
225 ApiResponse::<UserListItem> {
226 success: error_response.success,
227 data: None,
228 error: error_response.error,
229 message: error_response.message,
230 }
231 }
232 }
233 }
234 None => ApiResponse::<UserListItem>::unauthorized_typed(),
235 }
236}
237
238pub async fn update_user_roles(
241 State(state): State<ApiState>,
242 headers: HeaderMap,
243 Path(user_id): Path<String>,
244 Json(req): Json<UpdateUserRolesRequest>,
245) -> ApiResponse<()> {
246 match extract_bearer_token(&headers) {
247 Some(token) => {
248 match validate_api_token(&state.auth_framework, &token).await {
249 Ok(auth_token) => {
250 if !auth_token.roles.contains(&"admin".to_string()) {
252 return ApiResponse::forbidden();
253 }
254
255 tracing::info!("Updated roles for user {}: {:?}", user_id, req.roles);
262 ApiResponse::<()>::ok_with_message("User roles updated successfully")
263 }
264 Err(e) => e.into(),
265 }
266 }
267 None => ApiResponse::unauthorized(),
268 }
269}
270
271pub async fn delete_user(
274 State(state): State<ApiState>,
275 headers: HeaderMap,
276 Path(user_id): Path<String>,
277) -> ApiResponse<()> {
278 match extract_bearer_token(&headers) {
279 Some(token) => {
280 match validate_api_token(&state.auth_framework, &token).await {
281 Ok(auth_token) => {
282 if !auth_token.roles.contains(&"admin".to_string()) {
284 return ApiResponse::forbidden();
285 }
286
287 if auth_token.user_id == user_id {
289 return ApiResponse::validation_error("Cannot delete your own account");
290 }
291
292 tracing::info!("User deleted: {}", user_id);
299 ApiResponse::<()>::ok_with_message("User deleted successfully")
300 }
301 Err(e) => e.into(),
302 }
303 }
304 None => ApiResponse::unauthorized(),
305 }
306}
307
308#[derive(Debug, Deserialize)]
311pub struct ActivateUserRequest {
312 pub active: bool,
313}
314
315pub async fn activate_user(
316 State(state): State<ApiState>,
317 headers: HeaderMap,
318 Path(user_id): Path<String>,
319 Json(req): Json<ActivateUserRequest>,
320) -> ApiResponse<()> {
321 match extract_bearer_token(&headers) {
322 Some(token) => {
323 match validate_api_token(&state.auth_framework, &token).await {
324 Ok(auth_token) => {
325 if !auth_token.roles.contains(&"admin".to_string()) {
327 return ApiResponse::forbidden();
328 }
329
330 let action = if req.active {
336 "activated"
337 } else {
338 "deactivated"
339 };
340 tracing::info!("User {} {}", user_id, action);
341 ApiResponse::<()>::ok_with_message(format!("User {} successfully", action))
342 }
343 Err(e) => e.into(),
344 }
345 }
346 None => ApiResponse::unauthorized(),
347 }
348}
349
350pub async fn get_system_stats(
353 State(state): State<ApiState>,
354 headers: HeaderMap,
355) -> ApiResponse<SystemStats> {
356 match extract_bearer_token(&headers) {
357 Some(token) => {
358 match validate_api_token(&state.auth_framework, &token).await {
359 Ok(auth_token) => {
360 if !auth_token.roles.contains(&"admin".to_string()) {
362 return ApiResponse::forbidden_typed();
363 }
364
365 let stats = SystemStats {
367 total_users: 1250,
368 active_sessions: 45,
369 total_tokens: 892,
370 failed_logins_24h: 12,
371 system_uptime: "15 days, 4 hours".to_string(),
372 memory_usage: "256 MB / 1 GB".to_string(),
373 cpu_usage: "12%".to_string(),
374 };
375
376 ApiResponse::success(stats)
377 }
378 Err(_e) => ApiResponse::error_typed("AUTH_ERROR", "Token validation failed"),
379 }
380 }
381 None => ApiResponse::unauthorized_typed(),
382 }
383}
384
385#[derive(Debug, Serialize)]
388pub struct AuditLogEntry {
389 pub id: String,
390 pub timestamp: String,
391 pub user_id: String,
392 pub action: String,
393 pub resource: String,
394 pub ip_address: String,
395 pub user_agent: String,
396 pub result: String,
397}
398
399#[derive(Debug, Serialize)]
400pub struct AuditLogResponse {
401 pub logs: Vec<AuditLogEntry>,
402 pub pagination: Pagination,
403}
404
405#[derive(Debug, Deserialize)]
406pub struct AuditLogQuery {
407 #[serde(default = "default_page")]
408 pub page: u32,
409 #[serde(default = "default_limit")]
410 pub limit: u32,
411 #[serde(default)]
412 pub user_id: Option<String>,
413 #[serde(default)]
414 pub action: Option<String>,
415 #[serde(default)]
416 pub start_date: Option<String>,
417 #[serde(default)]
418 pub end_date: Option<String>,
419}
420
421pub async fn get_audit_logs(
422 State(state): State<ApiState>,
423 headers: HeaderMap,
424 Query(query): Query<AuditLogQuery>,
425) -> ApiResponse<AuditLogResponse> {
426 match extract_bearer_token(&headers) {
427 Some(token) => {
428 match validate_api_token(&state.auth_framework, &token).await {
429 Ok(auth_token) => {
430 if !auth_token.roles.contains(&"admin".to_string()) {
432 return ApiResponse::forbidden_typed();
433 }
434
435 let total_logs = 1500u64; let _offset = (query.page - 1) * query.limit;
438
439 let page = if query.page == 0 { 1 } else { query.page };
441 let limit = if query.limit == 0 {
442 20
443 } else {
444 query.limit.min(100)
445 }; let total_pages = ((total_logs as f64) / (limit as f64)).ceil() as u32;
449 let total_pages = if total_pages == 0 { 1 } else { total_pages };
450
451 let logs = vec![
452 AuditLogEntry {
453 id: "audit_1".to_string(),
454 timestamp: "2024-08-17T10:30:00Z".to_string(),
455 user_id: "user_123".to_string(),
456 action: "login".to_string(),
457 resource: "/auth/login".to_string(),
458 ip_address: "192.168.1.100".to_string(),
459 user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)".to_string(),
460 result: "success".to_string(),
461 },
462 AuditLogEntry {
463 id: "audit_2".to_string(),
464 timestamp: "2024-08-17T10:25:00Z".to_string(),
465 user_id: "user_456".to_string(),
466 action: "password_change".to_string(),
467 resource: "/users/change-password".to_string(),
468 ip_address: "192.168.1.101".to_string(),
469 user_agent: "Mozilla/5.0 (macOS; Intel Mac OS X 10_15_7)".to_string(),
470 result: "success".to_string(),
471 },
472 ];
473
474 let pagination = Pagination {
475 page,
476 limit,
477 total: total_logs,
478 pages: total_pages,
479 };
480
481 let response = AuditLogResponse { logs, pagination };
482
483 ApiResponse::success(response)
484 }
485 Err(_e) => ApiResponse::error_typed("AUTH_ERROR", "Token validation failed"),
486 }
487 }
488 None => ApiResponse::unauthorized_typed(),
489 }
490}
491
492