feat: 支持 Linux 平台使用(CA 安装适配 + 静默启动),并修复 api_key 泄漏

CA 适配:
- 移除 CA 模块对 macOS/Windows 的硬性限制,Linux 走完整初始化流程
- 按发行版自动选择安装命令:Debian 系 update-ca-certificates,
  Fedora/RHEL/Arch 系 update-ca-trust extract;已安装检测比对信任锚文件
- 删除不可达的 CaState::Unsupported 分支及前端"请使用 macOS 或 Windows"提示

静默启动(参考 cc-switch 实现逻辑):
- 新增 DesktopSettings 持久化(service_settings 表)及 GET/PUT /api/settings/desktop
- 主窗口统一以 visible(false) 创建,启动时读取配置决定是否显示
- 应用设置页:开启"开机启动"后显示"静默启动"子开关

安全修复:
- ProviderEndpoint.api_key 增加 serde(skip_serializing),
  避免明文 API Key 随 JSON 响应泄漏(provider_console 测试恢复通过)

其他:
- Linux 补齐 open_terminal_with_command:按序探测 x-terminal-emulator /
  gnome-terminal / konsole 等并在新终端窗口执行命令
- "打开终端安装 CA"失败信息不再被前端静默吞掉
- 顺手修复上游遗留问题:desktop.rs 未使用导入、output.rs 测试模块位置、providers.rs 格式
This commit is contained in:
Linanwanttodo
2026-08-25 22:59:21 +08:00
parent 081e1f50e2
commit d95273c49b
18 changed files with 297 additions and 99 deletions
+40 -10
View File
@@ -15,7 +15,7 @@ use axum::{
};
use tauri::{
async_runtime::JoinHandle, webview::Color, AppHandle, Manager, RunEvent, WebviewUrl,
WebviewWindowBuilder,
WebviewWindow, WebviewWindowBuilder,
};
use tauri_plugin_opener::OpenerExt;
use tokio_util::sync::CancellationToken;
@@ -52,12 +52,30 @@ fn open_terminal_with_command(command: String) -> tauri::Result<()> {
.spawn()?;
Ok(())
}
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
#[cfg(target_os = "linux")]
{
let _ = command;
const TERMINALS: &[(&str, &[&str])] = &[
("x-terminal-emulator", &["-e"]),
("gnome-terminal", &["--"]),
("konsole", &["-e"]),
("xfce4-terminal", &["--execute"]),
("alacritty", &["-e"]),
("kitty", &[]),
];
let script = format!("{command}; exec bash");
for (terminal, separator) in TERMINALS {
let mut process = Command::new(terminal);
process.args(*separator);
process.arg("bash").arg("-c").arg(&script);
match process.spawn() {
Ok(_) => return Ok(()),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => continue,
Err(error) => return Err(error.into()),
}
}
Err(tauri::Error::from(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"terminal guidance is unsupported on this platform",
std::io::ErrorKind::NotFound,
"no supported terminal emulator found",
)))
}
}
@@ -96,7 +114,10 @@ fn desktop_api_router(app: AppHandle) -> Router {
.layer(Extension(app))
}
fn create_main_window(app: &AppHandle, address: std::net::SocketAddr) -> tauri::Result<()> {
fn create_main_window(
app: &AppHandle,
address: std::net::SocketAddr,
) -> tauri::Result<WebviewWindow> {
let url = format!("http://{address}/__byok-api__/")
.parse()
.expect("local frontend URL");
@@ -108,15 +129,15 @@ fn create_main_window(app: &AppHandle, address: std::net::SocketAddr) -> tauri::
.background_color(Color(20, 20, 20, 255))
.decorations(cfg!(target_os = "macos"))
.shadow(true)
.resizable(true);
.resizable(true)
.visible(false);
#[cfg(target_os = "macos")]
let builder = builder
.title_bar_style(tauri::TitleBarStyle::Overlay)
.hidden_title(true);
builder.build()?;
Ok(())
builder.build()
}
pub fn run() {
@@ -160,6 +181,9 @@ pub fn run() {
let listener = tauri::async_runtime::block_on(server.bind())?;
let address = listener.local_addr()?;
tauri::async_runtime::block_on(server.harness().cleanup_stale_settings())?;
let silent_start = tauri::async_runtime::block_on(server.store().desktop_settings())
.map(|settings| settings.silent_start)
.unwrap_or(false);
let shutdown = CancellationToken::new();
let server_shutdown = shutdown.clone();
let app_handle = app.handle().clone();
@@ -176,7 +200,13 @@ pub fn run() {
server: Mutex::new(Some(task)),
exiting: AtomicBool::new(false),
});
create_main_window(app.handle(), address)?;
let window = create_main_window(app.handle(), address)?;
if silent_start {
tracing::info!("silent start enabled; keeping the main window hidden");
} else {
window.show()?;
window.set_focus()?;
}
tray::create(app)?;
Ok(())
})