mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
feat: release v0.83.2 with full pending changes
This commit is contained in:
@@ -11,10 +11,11 @@ use proxycast_agent::tools::heartbeat_tool::{
|
||||
HeartbeatCycleResult, HeartbeatExecutionRecord, HeartbeatService, HeartbeatStatus,
|
||||
HeartbeatTaskPreview, HeartbeatToolError,
|
||||
};
|
||||
use proxycast_core::app_paths;
|
||||
use proxycast_core::database::dao::heartbeat::HeartbeatExecution;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri::AppHandle;
|
||||
|
||||
/// Heartbeat Service 适配器
|
||||
///
|
||||
@@ -35,9 +36,7 @@ impl HeartbeatServiceAdapter {
|
||||
|
||||
/// 获取应用数据目录
|
||||
fn app_data_dir(&self) -> Result<PathBuf, HeartbeatToolError> {
|
||||
self.app_handle
|
||||
.path()
|
||||
.app_data_dir()
|
||||
app_paths::preferred_data_dir()
|
||||
.map_err(|e| HeartbeatToolError::IoError(format!("获取应用数据目录失败: {}", e)))
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use std::fs;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::UNIX_EPOCH;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri::AppHandle;
|
||||
use zip::write::FileOptions;
|
||||
use zip::{CompressionMethod, ZipWriter};
|
||||
|
||||
@@ -522,9 +522,7 @@ pub async fn export_support_bundle(
|
||||
get_log_storage_diagnostics_from_path(log_file_path.clone(), in_memory_log_count);
|
||||
let persisted_log_tail = read_persisted_logs_tail_from_path(log_file_path, 200)?;
|
||||
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
let app_data_dir = proxycast_core::app_paths::preferred_data_dir()
|
||||
.ok()
|
||||
.or_else(guess_proxycast_app_data_dir);
|
||||
let config_path = guess_proxycast_config_path();
|
||||
@@ -564,7 +562,7 @@ pub async fn export_support_bundle(
|
||||
}
|
||||
|
||||
fn guess_proxycast_app_data_dir() -> Option<PathBuf> {
|
||||
dirs::data_dir().map(|dir| dir.join("proxycast"))
|
||||
proxycast_core::app_paths::preferred_data_dir().ok()
|
||||
}
|
||||
|
||||
fn guess_proxycast_config_path() -> Option<PathBuf> {
|
||||
|
||||
@@ -757,7 +757,7 @@ pub fn run() {
|
||||
service.set_app_handle(app_handle.clone());
|
||||
|
||||
if service.get_config().enabled {
|
||||
let app_data_dir = match app_handle.path().app_data_dir() {
|
||||
let app_data_dir = match proxycast_core::app_paths::preferred_data_dir() {
|
||||
Ok(dir) => dir,
|
||||
Err(e) => {
|
||||
tracing::error!("[启动] 无法获取应用数据目录: {}", e);
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::models::app_type::AppType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri::AppHandle;
|
||||
use tauri_plugin_autostart::ManagerExt;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
@@ -1119,9 +1119,8 @@ fn get_download_directory(app_handle: &AppHandle) -> Result<PathBuf, String> {
|
||||
}
|
||||
|
||||
// 回退到应用数据目录
|
||||
let app_data_dir = app_handle
|
||||
.path()
|
||||
.app_data_dir()
|
||||
let _ = app_handle;
|
||||
let app_data_dir = proxycast_core::app_paths::preferred_data_dir()
|
||||
.map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
|
||||
let download_dir = app_data_dir.join("downloads");
|
||||
|
||||
@@ -12,12 +12,12 @@ use crate::services::heartbeat_service::{
|
||||
CycleResult, HeartbeatServiceState, HeartbeatStatus, HeartbeatTaskPreview,
|
||||
};
|
||||
use crate::AppState;
|
||||
use proxycast_core::app_paths;
|
||||
use proxycast_core::config::{DeliveryConfig, HeartbeatSecurityConfig, TaskSchedule};
|
||||
use proxycast_core::database::dao::heartbeat::HeartbeatExecution;
|
||||
use proxycast_websocket::handlers::{RpcHandler, RpcHandlerState};
|
||||
use proxycast_websocket::protocol::{CronHealthResult, GatewayRpcRequest, RpcMethod};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::Manager;
|
||||
use uuid::Uuid;
|
||||
|
||||
// ========== 配置响应类型 ==========
|
||||
@@ -135,9 +135,7 @@ pub async fn update_heartbeat_config(
|
||||
// 处理启停逻辑
|
||||
if config.enabled && !was_enabled {
|
||||
service.set_app_handle(app.clone());
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
let app_data_dir = app_paths::preferred_data_dir()
|
||||
.map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let self_ref = hb_state.0.clone();
|
||||
service.start(app_data_dir, self_ref).await?;
|
||||
@@ -162,12 +160,10 @@ pub async fn get_heartbeat_status(
|
||||
#[tauri::command]
|
||||
pub async fn get_heartbeat_tasks(
|
||||
hb_state: tauri::State<'_, HeartbeatServiceState>,
|
||||
app: tauri::AppHandle,
|
||||
_app: tauri::AppHandle,
|
||||
) -> Result<Vec<HeartbeatTaskPreview>, String> {
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let app_data_dir =
|
||||
app_paths::preferred_data_dir().map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let service = hb_state.0.read().await;
|
||||
service.preview_tasks(&app_data_dir)
|
||||
}
|
||||
@@ -303,11 +299,12 @@ pub async fn get_task_templates() -> Result<Vec<TaskTemplate>, String> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn apply_task_template(template_id: String, app: tauri::AppHandle) -> Result<(), String> {
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
pub async fn apply_task_template(
|
||||
template_id: String,
|
||||
_app: tauri::AppHandle,
|
||||
) -> Result<(), String> {
|
||||
let app_data_dir =
|
||||
app_paths::preferred_data_dir().map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
|
||||
let template = TaskTemplateRegistry::get_template_by_id(&template_id)
|
||||
.ok_or_else(|| format!("模板不存在: {}", template_id))?;
|
||||
@@ -320,17 +317,15 @@ pub async fn apply_task_template(template_id: String, app: tauri::AppHandle) ->
|
||||
#[tauri::command]
|
||||
pub async fn add_heartbeat_task(
|
||||
hb_state: tauri::State<'_, HeartbeatServiceState>,
|
||||
app: tauri::AppHandle,
|
||||
_app: tauri::AppHandle,
|
||||
description: String,
|
||||
priority: Option<u8>,
|
||||
timeout_secs: Option<u64>,
|
||||
once: Option<bool>,
|
||||
model: Option<String>,
|
||||
) -> Result<(), String> {
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let app_data_dir =
|
||||
app_paths::preferred_data_dir().map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let service = hb_state.0.read().await;
|
||||
service.add_task(
|
||||
&app_data_dir,
|
||||
@@ -345,13 +340,11 @@ pub async fn add_heartbeat_task(
|
||||
#[tauri::command]
|
||||
pub async fn delete_heartbeat_task(
|
||||
hb_state: tauri::State<'_, HeartbeatServiceState>,
|
||||
app: tauri::AppHandle,
|
||||
_app: tauri::AppHandle,
|
||||
index: usize,
|
||||
) -> Result<(), String> {
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let app_data_dir =
|
||||
app_paths::preferred_data_dir().map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let service = hb_state.0.read().await;
|
||||
service.delete_task(&app_data_dir, index)
|
||||
}
|
||||
@@ -359,7 +352,7 @@ pub async fn delete_heartbeat_task(
|
||||
#[tauri::command]
|
||||
pub async fn update_heartbeat_task(
|
||||
hb_state: tauri::State<'_, HeartbeatServiceState>,
|
||||
app: tauri::AppHandle,
|
||||
_app: tauri::AppHandle,
|
||||
index: usize,
|
||||
description: String,
|
||||
priority: Option<u8>,
|
||||
@@ -367,10 +360,8 @@ pub async fn update_heartbeat_task(
|
||||
once: Option<bool>,
|
||||
model: Option<String>,
|
||||
) -> Result<(), String> {
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let app_data_dir =
|
||||
app_paths::preferred_data_dir().map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let service = hb_state.0.read().await;
|
||||
service.update_task(
|
||||
&app_data_dir,
|
||||
@@ -388,7 +379,7 @@ pub async fn update_heartbeat_task(
|
||||
#[tauri::command]
|
||||
pub async fn generate_content_creator_tasks(
|
||||
state: tauri::State<'_, AppState>,
|
||||
app: tauri::AppHandle,
|
||||
_app: tauri::AppHandle,
|
||||
) -> Result<usize, String> {
|
||||
use crate::services::heartbeat_service::templates::ContentCreatorTaskGenerator;
|
||||
|
||||
@@ -401,10 +392,8 @@ pub async fn generate_content_creator_tasks(
|
||||
let count = tasks.len();
|
||||
|
||||
if count > 0 {
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let app_data_dir =
|
||||
app_paths::preferred_data_dir().map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
ContentCreatorTaskGenerator::append_to_heartbeat(tasks, &app_data_dir)?;
|
||||
}
|
||||
|
||||
@@ -418,10 +407,8 @@ pub async fn trigger_heartbeat_now(
|
||||
hb_state: tauri::State<'_, HeartbeatServiceState>,
|
||||
app: tauri::AppHandle,
|
||||
) -> Result<CycleResult, String> {
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let app_data_dir =
|
||||
app_paths::preferred_data_dir().map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
|
||||
let result = {
|
||||
let service = hb_state.0.read().await;
|
||||
|
||||
@@ -1698,17 +1698,15 @@ fn resolve_profile_data_dir_from_base(base_dir: &Path, profile_key: &str) -> Pat
|
||||
}
|
||||
|
||||
fn resolve_profile_data_dir(app: &AppHandle, profile_key: &str) -> Result<PathBuf, String> {
|
||||
let base_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
let _ = app;
|
||||
let base_dir = proxycast_core::app_paths::preferred_data_dir()
|
||||
.map_err(|e| format!("获取应用数据目录失败: {e}"))?;
|
||||
Ok(resolve_profile_data_dir_from_base(&base_dir, profile_key))
|
||||
}
|
||||
|
||||
fn resolve_chrome_profile_data_dir(app: &AppHandle, profile_key: &str) -> Result<PathBuf, String> {
|
||||
let base_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
let _ = app;
|
||||
let base_dir = proxycast_core::app_paths::preferred_data_dir()
|
||||
.map_err(|e| format!("获取应用数据目录失败: {e}"))?;
|
||||
Ok(base_dir
|
||||
.join("chrome_profiles")
|
||||
|
||||
@@ -10,6 +10,8 @@ use std::path::PathBuf;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::io::Write;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::os::windows::process::CommandExt;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::path::Path;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::process::Command;
|
||||
@@ -18,6 +20,9 @@ use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind};
|
||||
#[cfg(target_os = "windows")]
|
||||
use winreg::{enums::*, RegKey};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct WindowsStartupCheck {
|
||||
pub key: String,
|
||||
@@ -129,7 +134,7 @@ pub fn collect_windows_startup_diagnostics(app: &AppHandle) -> WindowsStartupDia
|
||||
let mut errors = Vec::new();
|
||||
let mut warnings = Vec::new();
|
||||
|
||||
let app_data_dir: Option<PathBuf> = app.path().app_data_dir().ok();
|
||||
let app_data_dir: Option<PathBuf> = proxycast_core::app_paths::preferred_data_dir().ok();
|
||||
let home_dir = dirs::home_dir();
|
||||
let legacy_proxycast_dir = home_dir.clone().map(|home| home.join(".proxycast"));
|
||||
let db_path = crate::database::get_db_path().ok();
|
||||
@@ -171,23 +176,27 @@ pub fn collect_windows_startup_diagnostics(app: &AppHandle) -> WindowsStartupDia
|
||||
}
|
||||
|
||||
match &legacy_proxycast_dir {
|
||||
Some(path) => match ensure_dir_writable(path) {
|
||||
Some(path) if path.exists() => match ensure_existing_dir_writable(path) {
|
||||
Ok(()) => checks.push(ok_check(
|
||||
"legacy_proxycast_dir",
|
||||
format!("用户目录数据根可写: {}", path.display()),
|
||||
format!("检测到旧版数据目录且可访问: {}", path.display()),
|
||||
)),
|
||||
Err(error) => {
|
||||
errors.push(format!("用户目录数据根不可写: {}", path.display()));
|
||||
checks.push(error_check(
|
||||
warnings.push(format!("旧版数据目录不可访问: {}", path.display()));
|
||||
checks.push(warn_check(
|
||||
"legacy_proxycast_dir",
|
||||
format!("用户目录数据根不可写: {}", path.display()),
|
||||
format!("旧版数据目录不可访问: {}", path.display()),
|
||||
Some(error),
|
||||
));
|
||||
}
|
||||
},
|
||||
Some(path) => checks.push(ok_check(
|
||||
"legacy_proxycast_dir",
|
||||
format!("未检测到旧版数据目录: {}", path.display()),
|
||||
)),
|
||||
None => {
|
||||
errors.push("无法解析用户 Home 目录".to_string());
|
||||
checks.push(error_check(
|
||||
warnings.push("无法解析用户 Home 目录".to_string());
|
||||
checks.push(warn_check(
|
||||
"legacy_proxycast_dir",
|
||||
"无法解析用户 Home 目录".to_string(),
|
||||
None,
|
||||
@@ -439,6 +448,25 @@ fn ensure_dir_writable(path: &Path) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn ensure_existing_dir_writable(path: &Path) -> Result<(), String> {
|
||||
if !path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let probe = path.join("proxycast-write-test.tmp");
|
||||
let mut file = std::fs::File::create(&probe)
|
||||
.map_err(|e| format!("创建测试文件失败 {}: {e}", probe.display()))?;
|
||||
file.write_all(b"proxycast")
|
||||
.map_err(|e| format!("写入测试文件失败 {}: {e}", probe.display()))?;
|
||||
file.sync_all()
|
||||
.map_err(|e| format!("刷新测试文件失败 {}: {e}", probe.display()))?;
|
||||
std::fs::remove_file(&probe)
|
||||
.map_err(|e| format!("删除测试文件失败 {}: {e}", probe.display()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn check_database_file(path: &Path) -> Result<(), String> {
|
||||
if let Some(parent) = path.parent() {
|
||||
@@ -447,11 +475,16 @@ fn check_database_file(path: &Path) -> Result<(), String> {
|
||||
|
||||
let conn = rusqlite::Connection::open(path)
|
||||
.map_err(|e| format!("打开数据库失败 {}: {e}", path.display()))?;
|
||||
conn.execute("PRAGMA user_version", [])
|
||||
conn.query_row("PRAGMA user_version", [], |_| Ok(()))
|
||||
.map_err(|e| format!("执行数据库探测失败 {}: {e}", path.display()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn apply_windows_no_window(command: &mut Command) {
|
||||
command.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn detect_webview2_runtime_version() -> Option<String> {
|
||||
const VALUE_NAME: &str = "pv";
|
||||
@@ -579,7 +612,9 @@ fn detect_shell_availability() -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
let pwsh_check = Command::new("pwsh").args(["-v"]).output();
|
||||
let mut pwsh_check = Command::new("pwsh");
|
||||
apply_windows_no_window(&mut pwsh_check);
|
||||
let pwsh_check = pwsh_check.args(["-v"]).output();
|
||||
if pwsh_check
|
||||
.map(|output| output.status.success())
|
||||
.unwrap_or(false)
|
||||
|
||||
@@ -7,6 +7,8 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Map, Value};
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
use std::ffi::OsString;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Stdio;
|
||||
use std::sync::OnceLock;
|
||||
@@ -28,6 +30,8 @@ const NODE_MIN_VERSION: (u64, u64, u64) = (22, 0, 0);
|
||||
const OPENCLAW_PROGRESS_LOG_LIMIT: usize = 400;
|
||||
const OPENCLAW_INSTALLER_USER_AGENT: &str = "ProxyCast-OpenClaw";
|
||||
const OPENCLAW_TEMP_CARGO_CHECK_DIR: &str = "/tmp/proxycast-cargo-check";
|
||||
#[cfg(target_os = "windows")]
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -1518,9 +1522,8 @@ fn openclaw_proxycast_config_path() -> PathBuf {
|
||||
}
|
||||
|
||||
fn openclaw_installer_download_dir(app: &AppHandle) -> Result<PathBuf, String> {
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
let _ = app;
|
||||
let app_data_dir = proxycast_core::app_paths::preferred_data_dir()
|
||||
.map_err(|e| format!("无法获取应用数据目录: {e}"))?;
|
||||
let dir = app_data_dir.join("downloads").join("openclaw-installers");
|
||||
std::fs::create_dir_all(&dir).map_err(|e| format!("创建 OpenClaw 下载目录失败: {e}"))?;
|
||||
@@ -2200,6 +2203,8 @@ fn prepend_path(dir: &Path) -> Option<OsString> {
|
||||
}
|
||||
|
||||
fn apply_binary_runtime_path(command: &mut Command, binary_path: &str) {
|
||||
apply_windows_no_window(command);
|
||||
|
||||
let Some(bin_dir) = Path::new(binary_path).parent() else {
|
||||
return;
|
||||
};
|
||||
@@ -2208,9 +2213,18 @@ fn apply_binary_runtime_path(command: &mut Command, binary_path: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_windows_no_window(_command: &mut Command) {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
_command.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
}
|
||||
|
||||
async fn find_command_in_shell(command_name: &str) -> Result<Option<String>, String> {
|
||||
if cfg!(target_os = "windows") {
|
||||
let output = Command::new("cmd")
|
||||
let mut command = Command::new("cmd");
|
||||
apply_windows_no_window(&mut command);
|
||||
let output = command
|
||||
.arg("/C")
|
||||
.arg("where")
|
||||
.arg(command_name)
|
||||
@@ -2384,7 +2398,9 @@ fn sibling_node_path(command_path: &Path) -> Option<PathBuf> {
|
||||
}
|
||||
|
||||
async fn read_binary_semver(path: &Path) -> Option<(u64, u64, u64)> {
|
||||
let output = Command::new(path)
|
||||
let mut command = Command::new(path);
|
||||
apply_windows_no_window(&mut command);
|
||||
let output = command
|
||||
.arg("--version")
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
@@ -2494,6 +2510,8 @@ fn spawn_shell_command(command_line: &str) -> Result<Child, String> {
|
||||
cmd
|
||||
};
|
||||
|
||||
apply_windows_no_window(&mut command);
|
||||
|
||||
command
|
||||
.env("NO_COLOR", "1")
|
||||
.env("CLICOLOR", "0")
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use tauri::{Emitter, Manager};
|
||||
use tauri::Emitter;
|
||||
|
||||
use proxycast_terminal::emitter::TerminalEventEmit;
|
||||
|
||||
@@ -23,9 +23,7 @@ impl TerminalEventEmit for TauriEmitter {
|
||||
}
|
||||
|
||||
fn app_data_dir(&self) -> Result<PathBuf, String> {
|
||||
self.0
|
||||
.path()
|
||||
.app_data_dir()
|
||||
proxycast_core::app_paths::preferred_data_dir()
|
||||
.map_err(|e| format!("获取应用数据目录失败: {e}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,13 +236,13 @@ fn handle_open_log_dir<R: Runtime>(app: &AppHandle<R>) {
|
||||
info!("[托盘] 用户请求打开日志目录");
|
||||
|
||||
// 获取日志目录路径
|
||||
let log_dir = if let Ok(data_dir) = app.path().app_data_dir() {
|
||||
data_dir.join("logs")
|
||||
} else if let Some(home) = dirs::home_dir() {
|
||||
home.join(".proxycast").join("logs")
|
||||
} else {
|
||||
error!("[托盘] 无法确定日志目录路径");
|
||||
return;
|
||||
let _ = app;
|
||||
let log_dir = match proxycast_core::app_paths::resolve_logs_dir() {
|
||||
Ok(dir) => dir,
|
||||
Err(error) => {
|
||||
error!("[托盘] 无法确定日志目录路径: {}", error);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// 确保目录存在
|
||||
|
||||
Reference in New Issue
Block a user