//! 管理员处理器 use axum::{ extract::{Path, Query, State}, http::StatusCode, response::IntoResponse, Json, }; use serde::Serialize; use std::sync::Arc; use crate::models::{ ApiResponse, DeviceResponse, PaginatedResponse, PaginationParams, SessionResponse, StatsResponse, UserResponse, }; use crate::services::{ auth::UserRepository, device::DeviceRepository, session::SessionRepository, AppState, }; use super::{api_error, AdminUser}; /// 服务器配置响应 #[derive(Debug, Serialize)] pub struct ServerConfigResponse { pub version: String, pub http_port: u16, pub stun_port: u16, pub stun_enabled: bool, pub stun_servers: Vec, pub turn_port: u16, pub turn_enabled: bool, pub turn_server: Option, pub turn_username: String, pub turn_realm: String, pub jwt_expiry_hours: i64, pub database_type: String, } /// 获取所有用户 pub async fn list_users( State(state): State>, _admin: AdminUser, Query(params): Query, ) -> impl IntoResponse { let repo = UserRepository::new(&state.db); match repo.find_all(params.offset(), params.limit()).await { Ok((users, total)) => { let items: Vec = users.into_iter().map(Into::into).collect(); let total_pages = ((total as f64) / (params.limit() as f64)).ceil() as u32; let response = PaginatedResponse { items, total, page: params.page.unwrap_or(1), limit: params.limit(), total_pages, }; (StatusCode::OK, Json(ApiResponse::ok(response))).into_response() } Err(e) => api_error(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), } } /// 删除用户 pub async fn delete_user( State(state): State>, _admin: AdminUser, Path(user_id): Path, ) -> impl IntoResponse { let repo = UserRepository::new(&state.db); match repo.delete(&user_id).await { Ok(_) => (StatusCode::OK, Json(ApiResponse::ok("用户已删除"))).into_response(), Err(e) => api_error(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), } } /// 获取所有设备(包含绑定用户信息) pub async fn list_all_devices( State(state): State>, _admin: AdminUser, Query(params): Query, ) -> impl IntoResponse { let repo = DeviceRepository::new(&state.db); match repo.find_all_with_username(params.offset(), params.limit()).await { Ok((devices, total)) => { let items: Vec = devices.into_iter().map(Into::into).collect(); let total_pages = ((total as f64) / (params.limit() as f64)).ceil() as u32; let response = PaginatedResponse { items, total, page: params.page.unwrap_or(1), limit: params.limit(), total_pages, }; (StatusCode::OK, Json(ApiResponse::ok(response))).into_response() } Err(e) => api_error(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), } } /// 获取所有会话 pub async fn list_all_sessions( State(state): State>, _admin: AdminUser, Query(params): Query, ) -> impl IntoResponse { let repo = SessionRepository::new(&state.db); match repo.find_all(params.offset(), params.limit()).await { Ok((sessions, total)) => { let items: Vec = sessions.into_iter().map(Into::into).collect(); let total_pages = ((total as f64) / (params.limit() as f64)).ceil() as u32; let response = PaginatedResponse { items, total, page: params.page.unwrap_or(1), limit: params.limit(), total_pages, }; (StatusCode::OK, Json(ApiResponse::ok(response))).into_response() } Err(e) => api_error(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), } } /// 获取统计信息 pub async fn get_stats( State(state): State>, _admin: AdminUser, ) -> impl IntoResponse { let user_repo = UserRepository::new(&state.db); let device_repo = DeviceRepository::new(&state.db); let session_repo = SessionRepository::new(&state.db); let total_users = user_repo.find_all(0, 1).await.map(|(_, t)| t).unwrap_or(0); let (_, total_devices) = device_repo.find_all(0, 1).await.unwrap_or((vec![], 0)); let online_devices = device_repo.count_online().await.unwrap_or(0); let active_sessions = session_repo.count_active().await.unwrap_or(0); let (_, total_sessions) = session_repo.find_all(0, 1).await.unwrap_or((vec![], 0)); let response = StatsResponse { total_users, total_devices, online_devices, active_sessions, total_sessions, }; (StatusCode::OK, Json(ApiResponse::ok(response))).into_response() } /// 获取服务器配置 pub async fn get_server_config( State(state): State>, _admin: AdminUser, ) -> impl IntoResponse { let config = &state.config; // 获取 TURN 服务器地址 let turn_server = if config.enable_local_turn { let host = config.public_ip.as_deref().unwrap_or("localhost"); Some(format!("turn:{}:{}", host, config.turn_port)) } else { config.turn_server.clone() }; let response = ServerConfigResponse { version: env!("CARGO_PKG_VERSION").to_string(), http_port: config.port, stun_port: config.stun_port, stun_enabled: config.enable_local_stun, stun_servers: config.stun_servers.clone(), turn_port: config.turn_port, turn_enabled: config.enable_local_turn, turn_server, turn_username: config.turn_username.clone(), turn_realm: config.turn_realm.clone(), jwt_expiry_hours: config.jwt_expiry / 3600, database_type: "SQLite".to_string(), }; (StatusCode::OK, Json(ApiResponse::ok(response))).into_response() } /// 环境配置响应 #[derive(Debug, Serialize)] pub struct EnvConfigResponse { pub content: String, } /// 环境配置请求 #[derive(Debug, serde::Deserialize)] pub struct EnvConfigRequest { pub content: String, } /// 获取环境配置文件 pub async fn get_env_config( _admin: AdminUser, ) -> impl IntoResponse { let env_path = std::path::Path::new(".env"); let content = if env_path.exists() { std::fs::read_to_string(env_path).unwrap_or_default() } else { // 返回默认配置模板 generate_default_env_config() }; let response = EnvConfigResponse { content }; (StatusCode::OK, Json(ApiResponse::ok(response))).into_response() } /// 保存环境配置文件 pub async fn save_env_config( _admin: AdminUser, Json(req): Json, ) -> impl IntoResponse { let env_path = std::path::Path::new(".env"); match std::fs::write(env_path, &req.content) { Ok(_) => { tracing::info!("Environment config saved"); (StatusCode::OK, Json(ApiResponse::ok("配置已保存"))).into_response() } Err(e) => { tracing::error!("Failed to save env config: {}", e); api_error(StatusCode::INTERNAL_SERVER_ERROR, format!("保存失败: {}", e)) } } } /// Generate default environment config fn generate_default_env_config() -> String { r#"# EasyRemote Server Configuration # Server Settings HOST=0.0.0.0 PORT=8080 # STUN Server Settings ENABLE_LOCAL_STUN=true STUN_PORT=3478 # TURN Server Settings ENABLE_LOCAL_TURN=true TURN_PORT=3479 TURN_USERNAME=easyremote TURN_PASSWORD=easyremote123 TURN_REALM=easyremote # Public IP (optional, for generating correct STUN/TURN URLs) # PUBLIC_IP=your.public.ip # Database Settings DATABASE_URL=sqlite:easyremote.db?mode=rwc # JWT Settings # JWT_SECRET=your-secret-key-here JWT_EXPIRY=86400 # Log Level RUST_LOG=info,tower_http=debug "#.to_string() }