Skip to content

Commit df0ab26

Browse files
authored
Dev (#5)
* chore(docker): Update .gitignore and remove unused docker-compose.yml - Add 'tmp/' directory to .gitignore to prevent temporary files from being tracked - Remove obsolete 'docker-compose.yml' file from 'tmp/' directory to clean up project structure * feat(cli-ui): Integrate Zustand and React Window for improved state management and performance - Added Zustand for global state management, replacing local state in App component. - Integrated React Window for efficient rendering of terminal logs, enhancing performance with large datasets. - Updated package.json and yarn.lock to include Zustand and React Window dependencies. - Refactored App and TerminalWindow components to utilize new state management and rendering techniques. - Improved working directory validation and process checking with enhanced logging. * chore(nuwax-cli): Bump version to 1.0.67 and update docker-compose command - Increment project version from 1.0.66 to 1.0.67 in Cargo.toml and Cargo.lock - Modify docker-compose command to include '--pull always' for updated image retrieval * update test env * chore(nuwax-cli): Update dependencies and refactor file handling - Added new dependencies: `infer`, `flate2`, and `tar` to Cargo.toml and Cargo.lock for enhanced file type detection and archive handling. - Refactored `get_version_download_file_path` to automatically find archive files and return appropriate error messages if not found. - Updated `extract_docker_service` to support both ZIP and TAR.GZ formats, improving the extraction process. - Implemented tests for archive file detection and extraction functionality to ensure reliability. * cargo fmt
1 parent 40b3039 commit df0ab26

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2097
-1419
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ history.db
1111
history.db.wal
1212
config.toml
1313
test_deploy
14+
tmp/
1415

1516
cli-ui/src-tauri/binaries
1617
data/duck_client.db

Cargo.lock

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ zip-extract = "0.4"
103103
flate2 = "1.1"
104104
tar = "0.4"
105105

106+
# 文件类型检测 (魔数检测)
107+
infer = "0.19"
108+
106109
# 加密和哈希
107110
sha2 = "0.10"
108111

cli-ui/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
"@tauri-apps/plugin-process": "^2.3.0",
1919
"@tauri-apps/plugin-shell": "^2.3.0",
2020
"@tauri-apps/plugin-updater": "^2.9.0",
21+
"zustand": "^4.5.4",
22+
"react-window": "^1.8.10",
2123
"react": "^18.3.1",
2224
"react-dom": "^18.3.1"
2325
},

cli-ui/src-tauri/src/commands/cli.rs

Lines changed: 125 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use serde::{Deserialize, Serialize};
22
use std::process::{Command as StdCommand, Stdio};
3+
use std::time::Instant;
34
use tauri::{AppHandle, Emitter, command};
45
use tauri_plugin_shell::{ShellExt, process::CommandEvent};
6+
use time::OffsetDateTime;
57

68
/// 调试环境变量和命令可用性
79
#[tauri::command]
@@ -158,6 +160,23 @@ pub struct ProcessCheckResult {
158160
pub message: String,
159161
}
160162

163+
#[derive(Debug, Serialize, Clone)]
164+
pub struct CliStreamEvent {
165+
pub command_id: Option<String>,
166+
pub stream: String,
167+
pub chunk: String,
168+
pub seq: u64,
169+
pub timestamp: String,
170+
}
171+
172+
#[derive(Debug, Serialize, Clone)]
173+
pub struct CliCompleteEvent {
174+
pub command_id: Option<String>,
175+
pub exit_code: i32,
176+
pub duration_ms: u128,
177+
pub timestamp: String,
178+
}
179+
161180
/// 获取用户的完整环境变量(包括shell配置)
162181
fn get_user_environment() -> std::collections::HashMap<String, String> {
163182
let mut env = std::env::vars().collect::<std::collections::HashMap<String, String>>();
@@ -260,6 +279,7 @@ pub async fn execute_duck_cli_sidecar(
260279
app: AppHandle,
261280
args: Vec<String>,
262281
working_dir: Option<String>,
282+
command_id: Option<String>,
263283
) -> Result<CommandResult, String> {
264284
let shell = app.shell();
265285

@@ -286,24 +306,52 @@ pub async fn execute_duck_cli_sidecar(
286306
let mut stdout = String::new();
287307
let mut stderr = String::new();
288308
let mut exit_code = 0;
309+
let mut seq: u64 = 0;
310+
let start = Instant::now();
289311

290312
while let Some(event) = rx.recv().await {
291313
match event {
292314
CommandEvent::Stdout(data) => {
293315
let output = String::from_utf8_lossy(&data);
294316
stdout.push_str(&output);
295-
// 实时发送输出到前端
296-
let _ = app.emit("cli-output", &output);
317+
let payload = CliStreamEvent {
318+
command_id: command_id.clone(),
319+
stream: "stdout".to_string(),
320+
chunk: output.to_string(),
321+
seq,
322+
timestamp: OffsetDateTime::now_utc()
323+
.format(&time::format_description::well_known::Rfc3339)
324+
.unwrap_or_default(),
325+
};
326+
seq += 1;
327+
let _ = app.emit("cli-output", payload);
297328
}
298329
CommandEvent::Stderr(data) => {
299330
let output = String::from_utf8_lossy(&data);
300331
stderr.push_str(&output);
301-
// 实时发送错误到前端
302-
let _ = app.emit("cli-error", &output);
332+
let payload = CliStreamEvent {
333+
command_id: command_id.clone(),
334+
stream: "stderr".to_string(),
335+
chunk: output.to_string(),
336+
seq,
337+
timestamp: OffsetDateTime::now_utc()
338+
.format(&time::format_description::well_known::Rfc3339)
339+
.unwrap_or_default(),
340+
};
341+
seq += 1;
342+
let _ = app.emit("cli-error", payload);
303343
}
304344
CommandEvent::Terminated(payload) => {
305345
exit_code = payload.code.unwrap_or(-1);
306-
let _ = app.emit("cli-complete", exit_code);
346+
let done = CliCompleteEvent {
347+
command_id: command_id.clone(),
348+
exit_code,
349+
duration_ms: start.elapsed().as_millis(),
350+
timestamp: OffsetDateTime::now_utc()
351+
.format(&time::format_description::well_known::Rfc3339)
352+
.unwrap_or_default(),
353+
};
354+
let _ = app.emit("cli-complete", done);
307355
break;
308356
}
309357
_ => {}
@@ -324,6 +372,7 @@ pub async fn execute_duck_cli_system(
324372
app: AppHandle,
325373
args: Vec<String>,
326374
working_dir: Option<String>,
375+
command_id: Option<String>,
327376
) -> Result<CommandResult, String> {
328377
let shell = app.shell();
329378

@@ -343,6 +392,7 @@ pub async fn execute_duck_cli_system(
343392
cmd = cmd.env(key, value);
344393
}
345394

395+
let start = Instant::now();
346396
let output = cmd
347397
.output()
348398
.await
@@ -352,14 +402,39 @@ pub async fn execute_duck_cli_system(
352402
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
353403
let exit_code = output.status.code().unwrap_or(-1);
354404

355-
// 发送输出到前端
356405
if !stdout.is_empty() {
357-
let _ = app.emit("cli-output", &stdout);
406+
let payload = CliStreamEvent {
407+
command_id: command_id.clone(),
408+
stream: "stdout".to_string(),
409+
chunk: stdout.clone(),
410+
seq: 0,
411+
timestamp: OffsetDateTime::now_utc()
412+
.format(&time::format_description::well_known::Rfc3339)
413+
.unwrap_or_default(),
414+
};
415+
let _ = app.emit("cli-output", payload);
358416
}
359417
if !stderr.is_empty() {
360-
let _ = app.emit("cli-error", &stderr);
418+
let payload = CliStreamEvent {
419+
command_id: command_id.clone(),
420+
stream: "stderr".to_string(),
421+
chunk: stderr.clone(),
422+
seq: 1,
423+
timestamp: OffsetDateTime::now_utc()
424+
.format(&time::format_description::well_known::Rfc3339)
425+
.unwrap_or_default(),
426+
};
427+
let _ = app.emit("cli-error", payload);
361428
}
362-
let _ = app.emit("cli-complete", exit_code);
429+
let done = CliCompleteEvent {
430+
command_id: command_id.clone(),
431+
exit_code,
432+
duration_ms: start.elapsed().as_millis(),
433+
timestamp: OffsetDateTime::now_utc()
434+
.format(&time::format_description::well_known::Rfc3339)
435+
.unwrap_or_default(),
436+
};
437+
let _ = app.emit("cli-complete", done);
363438

364439
Ok(CommandResult {
365440
success: exit_code == 0,
@@ -375,27 +450,62 @@ pub async fn execute_duck_cli_smart(
375450
app: AppHandle,
376451
args: Vec<String>,
377452
working_dir: Option<String>,
453+
command_id: Option<String>,
378454
) -> Result<CommandResult, String> {
379455
// 优先使用Sidecar方式
380-
match execute_duck_cli_sidecar(app.clone(), args.clone(), working_dir.clone()).await {
456+
match execute_duck_cli_sidecar(
457+
app.clone(),
458+
args.clone(),
459+
working_dir.clone(),
460+
command_id.clone(),
461+
)
462+
.await
463+
{
381464
Ok(result) => {
382465
// Sidecar成功,直接返回结果(已发送事件)
383466
Ok(result)
384467
}
385468
Err(sidecar_error) => {
386469
// 发送降级通知
387-
let _ = app.emit("cli-output", "⚠️ Sidecar方式失败,使用系统命令...");
470+
let warn_payload = CliStreamEvent {
471+
command_id: command_id.clone(),
472+
stream: "stdout".to_string(),
473+
chunk: "⚠️ Sidecar方式失败,使用系统命令...".to_string(),
474+
seq: 0,
475+
timestamp: OffsetDateTime::now_utc()
476+
.format(&time::format_description::well_known::Rfc3339)
477+
.unwrap_or_default(),
478+
};
479+
let _ = app.emit("cli-output", warn_payload);
388480

389481
// 降级到系统命令
390-
match execute_duck_cli_system(app.clone(), args, working_dir).await {
482+
match execute_duck_cli_system(app.clone(), args, working_dir, command_id.clone()).await
483+
{
391484
Ok(result) => {
392485
// System成功,返回结果(已发送事件)
393486
Ok(result)
394487
}
395488
Err(system_error) => {
396489
// 发送失败通知
397-
let _ = app.emit("cli-error", "❌ 所有CLI执行方式都失败");
398-
let _ = app.emit("cli-complete", -1);
490+
let err_payload = CliStreamEvent {
491+
command_id: command_id.clone(),
492+
stream: "stderr".to_string(),
493+
chunk: "❌ 所有CLI执行方式都失败".to_string(),
494+
seq: 1,
495+
timestamp: OffsetDateTime::now_utc()
496+
.format(&time::format_description::well_known::Rfc3339)
497+
.unwrap_or_default(),
498+
};
499+
let _ = app.emit("cli-error", err_payload);
500+
let done = CliCompleteEvent {
501+
command_id: command_id.clone(),
502+
exit_code: -1,
503+
duration_ms: 0,
504+
timestamp: OffsetDateTime::now_utc()
505+
.format(&time::format_description::well_known::Rfc3339)
506+
.unwrap_or_default(),
507+
};
508+
let _ = app.emit("cli-complete", done);
399509

400510
Err(format!(
401511
"所有CLI执行方式都失败 - Sidecar: {sidecar_error} | System: {system_error}"
@@ -409,7 +519,7 @@ pub async fn execute_duck_cli_smart(
409519
/// 检查CLI工具版本
410520
#[command]
411521
pub async fn get_cli_version(app: AppHandle) -> Result<CliVersion, String> {
412-
match execute_duck_cli_smart(app, vec!["--version".to_string()], None).await {
522+
match execute_duck_cli_smart(app, vec!["--version".to_string()], None, None).await {
413523
Ok(result) => {
414524
if result.success {
415525
// 从输出中提取版本号

0 commit comments

Comments
 (0)