Implement User Management UI (Phase 11 P0 #1)
User Management Features:
- Users.vue: Complete user CRUD interface
- Tauri commands: 5 auth user endpoints
- REST API: DataProvider trait extensions
UI Components:
- User list table (username, home_dir, status)
- Create user dialog (username, password, home_dir, status)
- Edit user dialog (password optional, home_dir, status)
- Delete user confirmation
- Reset password prompt
Tauri Commands (renamed to avoid conflict):
- list_auth_users: List all users from auth database
- create_auth_user: Create user with bcrypt password
- update_auth_user: Update user (optional password)
- delete_auth_user: Delete user
- reset_auth_password: Reset password
DataProvider Trait Extensions:
- list_users(): List all users
- create_user(): Create user with password
- update_user(): Update user (optional password)
- delete_user(): Delete user
- reset_password(): Reset password
Implementations:
- SqliteProvider: Full implementation (sftpgo_users table)
- PgProvider: Full implementation (users table)
Router:
- Added /users route
Home.vue:
- Added User Management card
Build: ✅ Tauri + markbase-core
Tests: 495 markbase-core + 201 smb-server
This commit is contained in:
@@ -6,6 +6,7 @@ pub mod management;
|
||||
pub mod health;
|
||||
pub mod monitor;
|
||||
pub mod backup;
|
||||
pub mod user_management;
|
||||
|
||||
pub use file_ops::*;
|
||||
pub use install::*;
|
||||
@@ -14,4 +15,5 @@ pub use diagnostic::*;
|
||||
pub use management::*;
|
||||
pub use health::*;
|
||||
pub use monitor::*;
|
||||
pub use backup::*;
|
||||
pub use backup::*;
|
||||
pub use user_management::*;
|
||||
100
markbase-tauri/src-tauri/src/commands/user_management.rs
Normal file
100
markbase-tauri/src-tauri/src/commands/user_management.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use markbase_core::provider::{DataProvider, User, ProviderError, sqlite::SqliteProvider};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, LazyLock, Mutex};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UserInfo {
|
||||
pub username: String,
|
||||
pub home_dir: String,
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref DATA_PROVIDER: LazyLock<Arc<Mutex<Box<dyn DataProvider>>>> =
|
||||
LazyLock::new(|| {
|
||||
Arc::new(Mutex::new(Box::new(
|
||||
SqliteProvider::new(&PathBuf::from("data/auth.sqlite").to_string_lossy().to_string())
|
||||
.expect("Failed to create SqliteProvider")
|
||||
) as Box<dyn DataProvider>))
|
||||
});
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_auth_users() -> Result<Vec<UserInfo>, String> {
|
||||
let provider = DATA_PROVIDER.lock().unwrap();
|
||||
|
||||
let users = provider.list_users().map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(users.into_iter().map(|u| UserInfo {
|
||||
username: u.username,
|
||||
home_dir: u.home_dir.to_string_lossy().to_string(),
|
||||
status: if u.status == 1 { "active".to_string() } else { "disabled".to_string() },
|
||||
}).collect())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn create_auth_user(
|
||||
username: String,
|
||||
password: String,
|
||||
home_dir: String,
|
||||
status: String,
|
||||
) -> Result<(), String> {
|
||||
let provider = DATA_PROVIDER.lock().unwrap();
|
||||
|
||||
let user = User {
|
||||
username: username.clone(),
|
||||
password_hash: String::new(),
|
||||
home_dir: PathBuf::from(home_dir),
|
||||
uid: 1000,
|
||||
gid: 1000,
|
||||
permissions: "*".to_string(),
|
||||
status: if status == "active" { 1 } else { 0 },
|
||||
};
|
||||
|
||||
provider.create_user(&user, &password).map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn update_auth_user(
|
||||
username: String,
|
||||
password: Option<String>,
|
||||
home_dir: String,
|
||||
status: String,
|
||||
) -> Result<(), String> {
|
||||
let provider = DATA_PROVIDER.lock().unwrap();
|
||||
|
||||
let user = User {
|
||||
username: username.clone(),
|
||||
password_hash: String::new(),
|
||||
home_dir: PathBuf::from(home_dir),
|
||||
uid: 1000,
|
||||
gid: 1000,
|
||||
permissions: "*".to_string(),
|
||||
status: if status == "active" { 1 } else { 0 },
|
||||
};
|
||||
|
||||
provider.update_user(&user, password.as_deref()).map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn delete_auth_user(username: String) -> Result<(), String> {
|
||||
let provider = DATA_PROVIDER.lock().unwrap();
|
||||
|
||||
provider.delete_user(&username).map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn reset_auth_password(username: String, new_password: String) -> Result<(), String> {
|
||||
let provider = DATA_PROVIDER.lock().unwrap();
|
||||
|
||||
provider.reset_password(&username, &new_password).map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -42,6 +42,11 @@ fn main() {
|
||||
get_backup_config,
|
||||
set_backup_config,
|
||||
run_backup,
|
||||
list_auth_users,
|
||||
create_auth_user,
|
||||
update_auth_user,
|
||||
delete_auth_user,
|
||||
reset_auth_password,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
Reference in New Issue
Block a user