diff --git a/src-tauri/icons/1024x1024.png b/src-tauri/icons/1024x1024.png new file mode 100644 index 0000000..312c394 Binary files /dev/null and b/src-tauri/icons/1024x1024.png differ diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png index 3ebb6c0..8bab508 100644 Binary files a/src-tauri/icons/128x128.png and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png index a12e82b..054a9b0 100644 Binary files a/src-tauri/icons/128x128@2x.png and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/16x16.png b/src-tauri/icons/16x16.png new file mode 100644 index 0000000..cf51a7f Binary files /dev/null and b/src-tauri/icons/16x16.png differ diff --git a/src-tauri/icons/16x16@2x.png b/src-tauri/icons/16x16@2x.png new file mode 100644 index 0000000..1bbe063 Binary files /dev/null and b/src-tauri/icons/16x16@2x.png differ diff --git a/src-tauri/icons/256x256.png b/src-tauri/icons/256x256.png new file mode 100644 index 0000000..054a9b0 Binary files /dev/null and b/src-tauri/icons/256x256.png differ diff --git a/src-tauri/icons/256x256@2x.png b/src-tauri/icons/256x256@2x.png new file mode 100644 index 0000000..287d956 Binary files /dev/null and b/src-tauri/icons/256x256@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png index aa859f0..1bbe063 100644 Binary files a/src-tauri/icons/32x32.png and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/32x32@2x.png b/src-tauri/icons/32x32@2x.png new file mode 100644 index 0000000..ea7bb57 Binary files /dev/null and b/src-tauri/icons/32x32@2x.png differ diff --git a/src-tauri/icons/512x512.png b/src-tauri/icons/512x512.png new file mode 100644 index 0000000..287d956 Binary files /dev/null and b/src-tauri/icons/512x512.png differ diff --git a/src-tauri/icons/64x64.png b/src-tauri/icons/64x64.png new file mode 100644 index 0000000..ea7bb57 Binary files /dev/null and b/src-tauri/icons/64x64.png differ diff --git a/src-tauri/icons/64x64@2x.png b/src-tauri/icons/64x64@2x.png new file mode 100644 index 0000000..8bab508 Binary files /dev/null and b/src-tauri/icons/64x64@2x.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns index 5f0f92e..4c601f5 100644 Binary files a/src-tauri/icons/icon.icns and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico index 10843a3..16f2b53 100644 Binary files a/src-tauri/icons/icon.ico and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.iconset/icon_128x128.png b/src-tauri/icons/icon.iconset/icon_128x128.png index 3ebb6c0..8bab508 100644 Binary files a/src-tauri/icons/icon.iconset/icon_128x128.png and b/src-tauri/icons/icon.iconset/icon_128x128.png differ diff --git a/src-tauri/icons/icon.iconset/icon_128x128@2x.png b/src-tauri/icons/icon.iconset/icon_128x128@2x.png index a12e82b..054a9b0 100644 Binary files a/src-tauri/icons/icon.iconset/icon_128x128@2x.png and b/src-tauri/icons/icon.iconset/icon_128x128@2x.png differ diff --git a/src-tauri/icons/icon.iconset/icon_16x16.png b/src-tauri/icons/icon.iconset/icon_16x16.png index a5350eb..cf51a7f 100644 Binary files a/src-tauri/icons/icon.iconset/icon_16x16.png and b/src-tauri/icons/icon.iconset/icon_16x16.png differ diff --git a/src-tauri/icons/icon.iconset/icon_16x16@2x.png b/src-tauri/icons/icon.iconset/icon_16x16@2x.png index 5de0597..1bbe063 100644 Binary files a/src-tauri/icons/icon.iconset/icon_16x16@2x.png and b/src-tauri/icons/icon.iconset/icon_16x16@2x.png differ diff --git a/src-tauri/icons/icon.iconset/icon_256x256.png b/src-tauri/icons/icon.iconset/icon_256x256.png index a12e82b..054a9b0 100644 Binary files a/src-tauri/icons/icon.iconset/icon_256x256.png and b/src-tauri/icons/icon.iconset/icon_256x256.png differ diff --git a/src-tauri/icons/icon.iconset/icon_256x256@2x.png b/src-tauri/icons/icon.iconset/icon_256x256@2x.png index 3a81290..287d956 100644 Binary files a/src-tauri/icons/icon.iconset/icon_256x256@2x.png and b/src-tauri/icons/icon.iconset/icon_256x256@2x.png differ diff --git a/src-tauri/icons/icon.iconset/icon_32x32.png b/src-tauri/icons/icon.iconset/icon_32x32.png index 5de0597..1bbe063 100644 Binary files a/src-tauri/icons/icon.iconset/icon_32x32.png and b/src-tauri/icons/icon.iconset/icon_32x32.png differ diff --git a/src-tauri/icons/icon.iconset/icon_32x32@2x.png b/src-tauri/icons/icon.iconset/icon_32x32@2x.png index 016f5a1..ea7bb57 100644 Binary files a/src-tauri/icons/icon.iconset/icon_32x32@2x.png and b/src-tauri/icons/icon.iconset/icon_32x32@2x.png differ diff --git a/src-tauri/icons/icon.iconset/icon_512x512.png b/src-tauri/icons/icon.iconset/icon_512x512.png new file mode 100644 index 0000000..287d956 Binary files /dev/null and b/src-tauri/icons/icon.iconset/icon_512x512.png differ diff --git a/src-tauri/icons/icon.iconset/icon_512x512@2x.png b/src-tauri/icons/icon.iconset/icon_512x512@2x.png new file mode 100644 index 0000000..312c394 Binary files /dev/null and b/src-tauri/icons/icon.iconset/icon_512x512@2x.png differ diff --git a/src-tauri/icons/icon.svg b/src-tauri/icons/icon.svg new file mode 100644 index 0000000..556f4ea --- /dev/null +++ b/src-tauri/icons/icon.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src-tauri/src/commands/extensions.rs b/src-tauri/src/commands/extensions.rs index 013c380..219b317 100644 --- a/src-tauri/src/commands/extensions.rs +++ b/src-tauri/src/commands/extensions.rs @@ -194,3 +194,77 @@ pub fn get_clawapp_status() -> Result { result.insert("url".into(), Value::String("http://localhost:3210".into())); Ok(Value::Object(result)) } + +/// 一键安装 cftunnel +#[tauri::command] +pub async fn install_cftunnel(app: tauri::AppHandle) -> Result { + use std::process::Stdio; + use std::io::{BufRead, BufReader}; + use tauri::Emitter; + + let _ = app.emit("install-log", "开始安装 cftunnel..."); + let _ = app.emit("install-progress", 10); + + // 下载并安装脚本 + let install_script = r#" +#!/bin/bash +set -e +cd /tmp +echo "下载 cftunnel..." +curl -fsSL https://raw.githubusercontent.com/qingchencloud/cftunnel/main/install.sh -o cftunnel-install.sh +chmod +x cftunnel-install.sh +echo "执行安装..." +./cftunnel-install.sh +echo "安装完成" +"#; + + let _ = app.emit("install-log", "下载安装脚本..."); + let _ = app.emit("install-progress", 30); + + let mut child = Command::new("bash") + .arg("-c") + .arg(install_script) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .map_err(|e| format!("启动安装进程失败: {e}"))?; + + let stderr = child.stderr.take(); + let stdout = child.stdout.take(); + + // 读取 stderr + let app2 = app.clone(); + let handle = std::thread::spawn(move || { + if let Some(pipe) = stderr { + for line in BufReader::new(pipe).lines().map_while(Result::ok) { + let _ = app2.emit("install-log", &line); + } + } + }); + + // 读取 stdout + let mut progress = 40; + if let Some(pipe) = stdout { + for line in BufReader::new(pipe).lines().map_while(Result::ok) { + let _ = app.emit("install-log", &line); + if progress < 90 { + progress += 5; + let _ = app.emit("install-progress", progress); + } + } + } + + let _ = handle.join(); + let _ = app.emit("install-progress", 95); + + let status = child.wait().map_err(|e| format!("等待安装进程失败: {e}"))?; + let _ = app.emit("install-progress", 100); + + if !status.success() { + let _ = app.emit("install-log", "❌ 安装失败"); + return Err("安装失败,请查看日志".into()); + } + + let _ = app.emit("install-log", "✅ cftunnel 安装成功"); + Ok("安装成功".into()) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 3022685..11652d7 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -46,6 +46,7 @@ pub fn run() { extensions::cftunnel_action, extensions::get_cftunnel_logs, extensions::get_clawapp_status, + extensions::install_cftunnel, ]) .run(tauri::generate_context!()) .expect("启动 ClawPanel 失败"); diff --git a/src/lib/tauri-api.js b/src/lib/tauri-api.js index 1f12670..4879048 100644 --- a/src/lib/tauri-api.js +++ b/src/lib/tauri-api.js @@ -178,4 +178,5 @@ export const api = { cftunnelAction: (action) => invoke('cftunnel_action', { action }), getCftunnelLogs: (lines = 20) => invoke('get_cftunnel_logs', { lines }), getClawappStatus: () => invoke('get_clawapp_status'), + installCftunnel: () => invoke('install_cftunnel'), } diff --git a/src/pages/extensions.js b/src/pages/extensions.js index 5dc276f..af0b7a0 100644 --- a/src/pages/extensions.js +++ b/src/pages/extensions.js @@ -63,8 +63,12 @@ async function loadCftunnel(page) { function renderCftunnel(el, s) { if (!s.installed) { el.innerHTML = ` -
cftunnel 未安装
- 前往安装 +
cftunnel 未安装
+
+ + 查看文档 +
+
` return } @@ -199,6 +203,9 @@ function bindEvents(page) { case 'clawapp-refresh': await loadClawapp(page) break + case 'install-cftunnel': + await handleInstallCftunnel(page) + break } }) } @@ -238,3 +245,58 @@ async function handleCftunnelLogs(page) { area.innerHTML = `
读取日志失败: ${e}
` } } + +async function handleInstallCftunnel(page) { + const area = page.querySelector('#install-progress-area') + if (!area) return + + // 显示进度条 + area.innerHTML = ` +
+
+
+
+
+
准备安装...
+
+
+
+ ` + + const progressFill = area.querySelector('#install-progress-fill') + const progressText = area.querySelector('#install-progress-text') + const logBox = area.querySelector('#install-log-box') + + let unlistenLog, unlistenProgress + try { + const { listen } = await import('@tauri-apps/api/event') + + unlistenLog = await listen('install-log', (e) => { + logBox.textContent += e.payload + '\n' + logBox.scrollTop = logBox.scrollHeight + }) + + unlistenProgress = await listen('install-progress', (e) => { + const progress = e.payload + progressFill.style.width = progress + '%' + progressText.textContent = `安装中... ${progress}%` + }) + + await api.installCftunnel() + + progressFill.classList.add('done') + progressText.textContent = '✅ 安装完成' + toast('cftunnel 安装成功', 'success') + + // 3 秒后刷新状态 + setTimeout(() => loadCftunnel(page), 3000) + } catch (e) { + progressFill.classList.add('error') + progressText.textContent = '❌ 安装失败' + logBox.textContent += '\n错误: ' + e + toast('安装失败: ' + e, 'error') + } finally { + unlistenLog?.() + unlistenProgress?.() + } +}