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() {