From 261deb1303d47acb68851fc1f778ee49990986ae Mon Sep 17 00:00:00 2001 From: mofeng-git Date: Tue, 10 Feb 2026 22:30:52 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=94=B6=E6=95=9B=E5=8D=95?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=A8=A1=E5=9E=8B=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=8F=AF=E8=AE=BF=E9=97=AE=E6=80=A7=E4=B8=8E=E5=93=8D=E5=BA=94?= =?UTF-8?q?=E5=BC=8F=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端移除 is_admin 权限字段与相关逻辑,统一为单用户系统模型 - 修复会话过期清理的时间比较方式(改为 RFC3339 参数比较) - /api/config 聚合配置增加敏感字段脱敏,避免暴露 TURN/RustDesk 密钥与密码 - 配置更新日志改为摘要,避免打印完整配置内容 - 前端修复可点击卡片语义与键盘可达,补齐图标按钮可访问名称 - 调整弹窗与抽屉的响应式尺寸,优化多端显示与交互 --- src/auth/session.rs | 4 +++- src/auth/user.rs | 20 ++++++++------------ src/config/store.rs | 1 - src/web/handlers/config/mod.rs | 18 +++++++++++++++++- src/web/handlers/config/rustdesk.rs | 2 +- src/web/handlers/mod.rs | 16 ++++------------ web/src/components/AppLayout.vue | 10 +++++----- web/src/components/StatsSheet.vue | 4 ++-- web/src/components/StatusCard.vue | 16 ++++++++++------ web/src/views/ConsoleView.vue | 9 +++++---- web/src/views/LoginView.vue | 3 ++- web/src/views/SettingsView.vue | 27 +++++++++++++++++---------- web/src/views/SetupView.vue | 11 +++++++---- 13 files changed, 81 insertions(+), 60 deletions(-) diff --git a/src/auth/session.rs b/src/auth/session.rs index 9902acc8..6106dc16 100644 --- a/src/auth/session.rs +++ b/src/auth/session.rs @@ -110,7 +110,9 @@ impl SessionStore { /// Delete all expired sessions pub async fn cleanup_expired(&self) -> Result { - let result = sqlx::query("DELETE FROM sessions WHERE expires_at < datetime('now')") + let now = Utc::now().to_rfc3339(); + let result = sqlx::query("DELETE FROM sessions WHERE expires_at < ?1") + .bind(now) .execute(&self.pool) .await?; Ok(result.rows_affected()) diff --git a/src/auth/user.rs b/src/auth/user.rs index 0854a5ab..986fc41b 100644 --- a/src/auth/user.rs +++ b/src/auth/user.rs @@ -7,7 +7,7 @@ use super::password::{hash_password, verify_password}; use crate::error::{AppError, Result}; /// User row type from database -type UserRow = (String, String, String, i32, String, String); +type UserRow = (String, String, String, String, String); /// User data #[derive(Debug, Clone, Serialize, Deserialize)] @@ -16,7 +16,6 @@ pub struct User { pub username: String, #[serde(skip_serializing)] pub password_hash: String, - pub is_admin: bool, pub created_at: DateTime, pub updated_at: DateTime, } @@ -24,12 +23,11 @@ pub struct User { impl User { /// Convert from database row to User fn from_row(row: UserRow) -> Self { - let (id, username, password_hash, is_admin, created_at, updated_at) = row; + let (id, username, password_hash, created_at, updated_at) = row; Self { id, username, password_hash, - is_admin: is_admin != 0, created_at: DateTime::parse_from_rfc3339(&created_at) .map(|dt| dt.with_timezone(&Utc)) .unwrap_or_else(|_| Utc::now()), @@ -53,7 +51,7 @@ impl UserStore { } /// Create a new user - pub async fn create(&self, username: &str, password: &str, is_admin: bool) -> Result { + pub async fn create(&self, username: &str, password: &str) -> Result { // Check if username already exists if self.get_by_username(username).await?.is_some() { return Err(AppError::BadRequest(format!( @@ -68,21 +66,19 @@ impl UserStore { id: Uuid::new_v4().to_string(), username: username.to_string(), password_hash, - is_admin, created_at: now, updated_at: now, }; sqlx::query( r#" - INSERT INTO users (id, username, password_hash, is_admin, created_at, updated_at) - VALUES (?1, ?2, ?3, ?4, ?5, ?6) + INSERT INTO users (id, username, password_hash, created_at, updated_at) + VALUES (?1, ?2, ?3, ?4, ?5) "#, ) .bind(&user.id) .bind(&user.username) .bind(&user.password_hash) - .bind(user.is_admin as i32) .bind(user.created_at.to_rfc3339()) .bind(user.updated_at.to_rfc3339()) .execute(&self.pool) @@ -94,7 +90,7 @@ impl UserStore { /// Get user by ID pub async fn get(&self, user_id: &str) -> Result> { let row: Option = sqlx::query_as( - "SELECT id, username, password_hash, is_admin, created_at, updated_at FROM users WHERE id = ?1", + "SELECT id, username, password_hash, created_at, updated_at FROM users WHERE id = ?1", ) .bind(user_id) .fetch_optional(&self.pool) @@ -106,7 +102,7 @@ impl UserStore { /// Get user by username pub async fn get_by_username(&self, username: &str) -> Result> { let row: Option = sqlx::query_as( - "SELECT id, username, password_hash, is_admin, created_at, updated_at FROM users WHERE username = ?1", + "SELECT id, username, password_hash, created_at, updated_at FROM users WHERE username = ?1", ) .bind(username) .fetch_optional(&self.pool) @@ -178,7 +174,7 @@ impl UserStore { /// List all users pub async fn list(&self) -> Result> { let rows: Vec = sqlx::query_as( - "SELECT id, username, password_hash, is_admin, created_at, updated_at FROM users ORDER BY created_at", + "SELECT id, username, password_hash, created_at, updated_at FROM users ORDER BY created_at", ) .fetch_all(&self.pool) .await?; diff --git a/src/config/store.rs b/src/config/store.rs index 0e48be8d..ce7a1b5b 100644 --- a/src/config/store.rs +++ b/src/config/store.rs @@ -82,7 +82,6 @@ impl ConfigStore { id TEXT PRIMARY KEY, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, - is_admin INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ) diff --git a/src/web/handlers/config/mod.rs b/src/web/handlers/config/mod.rs index 6748ac20..3a28bc10 100644 --- a/src/web/handlers/config/mod.rs +++ b/src/web/handlers/config/mod.rs @@ -50,10 +50,26 @@ use std::sync::Arc; use crate::config::AppConfig; use crate::state::AppState; +fn sanitize_config_for_api(config: &mut AppConfig) { + // Auth secrets + config.auth.totp_secret = None; + + // Stream secrets + config.stream.turn_password = None; + + // RustDesk secrets + config.rustdesk.device_password.clear(); + config.rustdesk.relay_key = None; + config.rustdesk.public_key = None; + config.rustdesk.private_key = None; + config.rustdesk.signing_public_key = None; + config.rustdesk.signing_private_key = None; +} + /// 获取完整配置 pub async fn get_all_config(State(state): State>) -> Json { let mut config = (*state.config.get()).clone(); // 不暴露敏感信息 - config.auth.totp_secret = None; + sanitize_config_for_api(&mut config); Json(config) } diff --git a/src/web/handlers/config/rustdesk.rs b/src/web/handlers/config/rustdesk.rs index 29dbf3c9..ae1a9648 100644 --- a/src/web/handlers/config/rustdesk.rs +++ b/src/web/handlers/config/rustdesk.rs @@ -139,7 +139,7 @@ pub async fn regenerate_device_password( Ok(Json(RustDeskConfigResponse::from(&new_config))) } -/// 获取设备密码(管理员专用) +/// 获取设备密码(已认证用户) pub async fn get_device_password(State(state): State>) -> Json { let config = state.config.get().rustdesk.clone(); Json(serde_json::json!({ diff --git a/src/web/handlers/mod.rs b/src/web/handlers/mod.rs index 5a7a16af..6c3d9f6c 100644 --- a/src/web/handlers/mod.rs +++ b/src/web/handlers/mod.rs @@ -589,11 +589,8 @@ pub async fn setup_init( )); } - // Create admin user - state - .users - .create(&req.username, &req.password, true) - .await?; + // Create single system user + state.users.create(&req.username, &req.password).await?; // Update config state @@ -771,10 +768,7 @@ pub async fn setup_init( } } - tracing::info!( - "System initialized successfully with admin user: {}", - req.username - ); + tracing::info!("System initialized successfully"); Ok(Json(LoginResponse { success: true, @@ -799,7 +793,7 @@ pub async fn update_config( // Keep old config for rollback let old_config = state.config.get(); - tracing::info!("Received config update: {:?}", req.updates); + tracing::info!("Received config update request"); // Validate and merge config first (outside the update closure) let config_json = serde_json::to_value(&old_config) @@ -808,8 +802,6 @@ pub async fn update_config( let merged = merge_json(config_json, req.updates.clone()) .map_err(|_| AppError::Internal("Failed to merge config".to_string()))?; - tracing::debug!("Merged config: {:?}", merged); - let new_config: AppConfig = serde_json::from_value(merged) .map_err(|e| AppError::BadRequest(format!("Invalid config format: {}", e)))?; diff --git a/web/src/components/AppLayout.vue b/web/src/components/AppLayout.vue index ec22a9a7..879c5123 100644 --- a/web/src/components/AppLayout.vue +++ b/web/src/components/AppLayout.vue @@ -52,7 +52,7 @@ async function handleLogout() {