mirror of
https://github.com/OpenListTeam/OpenList-Desktop.git
synced 2025-11-25 03:14:56 +08:00
feat: optimize admin password management and add reset
This commit is contained in:
@@ -1,16 +1,103 @@
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use tauri::State;
|
||||
|
||||
use crate::object::structs::AppState;
|
||||
|
||||
static ADMIN_PWD_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(r"Successfully created the admin user and the initial password is: (\w+)")
|
||||
.expect("Invalid regex pattern")
|
||||
});
|
||||
fn generate_random_password() -> String {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
|
||||
if let Ok(duration) = SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||
duration.as_nanos().hash(&mut hasher);
|
||||
}
|
||||
|
||||
std::process::id().hash(&mut hasher);
|
||||
|
||||
let dummy = [1, 2, 3];
|
||||
(dummy.as_ptr() as usize).hash(&mut hasher);
|
||||
|
||||
let hash = hasher.finish();
|
||||
|
||||
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
let mut password = String::new();
|
||||
let mut current_hash = hash;
|
||||
|
||||
for _ in 0..16 {
|
||||
let index = (current_hash % chars.len() as u64) as usize;
|
||||
password.push(chars.chars().nth(index).unwrap());
|
||||
current_hash = current_hash.wrapping_mul(1103515245).wrapping_add(12345);
|
||||
}
|
||||
|
||||
password
|
||||
}
|
||||
|
||||
async fn execute_openlist_admin_set(
|
||||
password: &str,
|
||||
state: &State<'_, AppState>,
|
||||
) -> Result<(), String> {
|
||||
let exe_path =
|
||||
env::current_exe().map_err(|e| format!("Failed to determine executable path: {e}"))?;
|
||||
let app_dir = exe_path
|
||||
.parent()
|
||||
.ok_or("Executable has no parent directory")?;
|
||||
|
||||
let possible_names = ["openlist", "openlist.exe"];
|
||||
|
||||
let mut openlist_exe = None;
|
||||
for name in &possible_names {
|
||||
let exe_path = app_dir.join(name);
|
||||
if exe_path.exists() {
|
||||
openlist_exe = Some(exe_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let openlist_exe = openlist_exe.ok_or_else(|| {
|
||||
format!(
|
||||
"OpenList executable not found. Searched for: {:?} in {}",
|
||||
possible_names,
|
||||
app_dir.display()
|
||||
)
|
||||
})?;
|
||||
|
||||
log::info!(
|
||||
"Setting new admin password using: {}",
|
||||
openlist_exe.display()
|
||||
);
|
||||
|
||||
let mut cmd = Command::new(&openlist_exe);
|
||||
cmd.args(["admin", "set", password]);
|
||||
|
||||
if let Some(settings) = state.get_settings()
|
||||
&& !settings.openlist.data_dir.is_empty()
|
||||
{
|
||||
cmd.arg("--data");
|
||||
cmd.arg(&settings.openlist.data_dir);
|
||||
log::info!("Using data directory: {}", settings.openlist.data_dir);
|
||||
}
|
||||
log::info!("Executing command: {cmd:?}");
|
||||
let output = cmd
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to execute openlist command: {e}"))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
log::error!("OpenList admin set command failed. stdout: {stdout}, stderr: {stderr}");
|
||||
return Err(format!("OpenList admin set command failed: {stderr}"));
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
log::info!("Successfully set admin password. Output: {stdout}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_log_paths(source: Option<&str>, data_dir: Option<&str>) -> Result<Vec<PathBuf>, String> {
|
||||
let exe_path =
|
||||
@@ -45,20 +132,78 @@ fn resolve_log_paths(source: Option<&str>, data_dir: Option<&str>) -> Result<Vec
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_admin_password(state: State<'_, AppState>) -> Result<String, String> {
|
||||
let data_dir = state
|
||||
.get_settings()
|
||||
.map(|s| s.openlist.data_dir)
|
||||
.filter(|d| !d.is_empty());
|
||||
if let Some(settings) = state.get_settings()
|
||||
&& let Some(ref stored_password) = settings.app.admin_password
|
||||
&& !stored_password.is_empty()
|
||||
{
|
||||
log::info!("Found admin password in local settings");
|
||||
return Ok(stored_password.clone());
|
||||
}
|
||||
|
||||
let paths = resolve_log_paths(Some("openlist_core"), data_dir.as_deref())?;
|
||||
let content =
|
||||
std::fs::read_to_string(&paths[0]).map_err(|e| format!("Failed to read log file: {e}"))?;
|
||||
let new_password = generate_random_password();
|
||||
|
||||
ADMIN_PWD_REGEX
|
||||
.captures_iter(&content)
|
||||
.filter_map(|cap| cap.get(1).map(|m| m.as_str().to_string()))
|
||||
.last()
|
||||
.ok_or_else(|| "No admin password found in logs".into())
|
||||
if let Err(e) = execute_openlist_admin_set(&new_password, &state).await {
|
||||
return Err(format!("Failed to set new admin password: {e}"));
|
||||
}
|
||||
|
||||
log::info!("Successfully generated and set new admin password");
|
||||
|
||||
if let Some(mut settings) = state.get_settings() {
|
||||
settings.app.admin_password = Some(new_password.clone());
|
||||
state.update_settings(settings.clone());
|
||||
|
||||
if let Err(e) = settings.save() {
|
||||
log::warn!("Failed to save new admin password to settings: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(new_password)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn reset_admin_password(state: State<'_, AppState>) -> Result<String, String> {
|
||||
log::info!("Forcing admin password reset");
|
||||
let new_password = generate_random_password();
|
||||
if let Err(e) = execute_openlist_admin_set(&new_password, &state).await {
|
||||
return Err(format!("Failed to set new admin password: {e}"));
|
||||
}
|
||||
log::info!("Successfully generated and set new admin password via force reset");
|
||||
|
||||
if let Some(mut settings) = state.get_settings() {
|
||||
settings.app.admin_password = Some(new_password.clone());
|
||||
state.update_settings(settings.clone());
|
||||
|
||||
if let Err(e) = settings.save() {
|
||||
log::warn!("Failed to save new admin password to settings: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(new_password)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn set_admin_password(
|
||||
password: String,
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<String, String> {
|
||||
log::info!("Setting custom admin password");
|
||||
|
||||
if let Err(e) = execute_openlist_admin_set(&password, &state).await {
|
||||
return Err(format!("Failed to set admin password: {e}"));
|
||||
}
|
||||
|
||||
log::info!("Successfully set custom admin password");
|
||||
|
||||
if let Some(mut settings) = state.get_settings() {
|
||||
settings.app.admin_password = Some(password.clone());
|
||||
state.update_settings(settings.clone());
|
||||
|
||||
if let Err(e) = settings.save() {
|
||||
log::warn!("Failed to save admin password to settings: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(password)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -7,6 +7,7 @@ pub struct AppConfig {
|
||||
pub gh_proxy: Option<String>,
|
||||
pub gh_proxy_api: Option<bool>,
|
||||
pub open_links_in_browser: Option<bool>,
|
||||
pub admin_password: Option<String>,
|
||||
}
|
||||
|
||||
impl AppConfig {
|
||||
@@ -17,6 +18,7 @@ impl AppConfig {
|
||||
gh_proxy: None,
|
||||
gh_proxy_api: Some(false),
|
||||
open_links_in_browser: Some(false),
|
||||
admin_password: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@ use cmd::firewall::{add_firewall_rule, check_firewall_rule, remove_firewall_rule
|
||||
use cmd::http_api::{
|
||||
delete_process, get_process_list, restart_process, start_process, stop_process, update_process,
|
||||
};
|
||||
use cmd::logs::{clear_logs, get_admin_password, get_logs};
|
||||
use cmd::logs::{
|
||||
clear_logs, get_admin_password, get_logs, reset_admin_password, set_admin_password,
|
||||
};
|
||||
use cmd::openlist_core::{create_openlist_core_process, get_openlist_core_status};
|
||||
use cmd::os_operate::{
|
||||
get_available_versions, list_files, open_file, open_folder, open_url, open_url_in_browser,
|
||||
@@ -148,6 +150,8 @@ pub fn run() {
|
||||
get_logs,
|
||||
clear_logs,
|
||||
get_admin_password,
|
||||
reset_admin_password,
|
||||
set_admin_password,
|
||||
get_binary_version,
|
||||
select_directory,
|
||||
get_available_versions,
|
||||
|
||||
Reference in New Issue
Block a user