276 lines
8.0 KiB
Rust
276 lines
8.0 KiB
Rust
//! 管理员处理器
|
|
|
|
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<String>,
|
|
pub turn_port: u16,
|
|
pub turn_enabled: bool,
|
|
pub turn_server: Option<String>,
|
|
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<Arc<AppState>>,
|
|
_admin: AdminUser,
|
|
Query(params): Query<PaginationParams>,
|
|
) -> impl IntoResponse {
|
|
let repo = UserRepository::new(&state.db);
|
|
|
|
match repo.find_all(params.offset(), params.limit()).await {
|
|
Ok((users, total)) => {
|
|
let items: Vec<UserResponse> = 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<Arc<AppState>>,
|
|
_admin: AdminUser,
|
|
Path(user_id): Path<String>,
|
|
) -> 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<Arc<AppState>>,
|
|
_admin: AdminUser,
|
|
Query(params): Query<PaginationParams>,
|
|
) -> 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<DeviceResponse> = 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<Arc<AppState>>,
|
|
_admin: AdminUser,
|
|
Query(params): Query<PaginationParams>,
|
|
) -> impl IntoResponse {
|
|
let repo = SessionRepository::new(&state.db);
|
|
|
|
match repo.find_all(params.offset(), params.limit()).await {
|
|
Ok((sessions, total)) => {
|
|
let items: Vec<SessionResponse> = 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<Arc<AppState>>,
|
|
_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<Arc<AppState>>,
|
|
_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<EnvConfigRequest>,
|
|
) -> 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()
|
|
}
|