Implement User Management UI (Phase 11 P0 #1)
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled

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:
Warren
2026-06-24 05:10:27 +08:00
parent 72503f7db9
commit e07d17aee7
9 changed files with 615 additions and 2 deletions

View File

@@ -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::*;

View 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(())
}

View File

@@ -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");