fix: 深度 review 修复 10 个 bug/问题

Critical & High:
- daemon 日志:启动时将 stdout/stderr 重定向到 ~/.wx-cli/daemon.log
  而非 /dev/null,使 wx daemon logs 真正可用
- q_history 找不到聊天时改为 bail! 而非 ok:true+error 字段,
  避免 CLI 静默返回空输出
- init 写 config.json 默认路径改为 ~/.wx-cli/config.json,
  避免写入系统 bin 目录(/usr/local/bin/config.json)
- LIKE 通配符:搜索关键词中的 %/_/\ 现在正确转义
- WAL 路径:改用 OsString.push 拼接 "-wal" 后缀,
  避免 display() 在非 UTF-8 路径上失效
- cmd_stop:检查 kill() 返回值,ESRCH 时给出明确提示

Performance & Code quality:
- full_decrypt:改为流式逐页读写,峰值内存从 2×文件大小降为 O(1)
- Regex:msg_table_re() 用 OnceLock 静态编译,避免热路径重复编译
- mtime_nanos:消除 daemon/mod.rs 与 cache.rs 的重复定义
- use super::super::cli::transport → use super::transport
- 删除未使用的 save_config、Request::to_json_line 死代码
This commit is contained in:
jackwener
2026-04-16 17:07:15 +08:00
parent dfd020a2b9
commit 7f869e7c3b
15 changed files with 100 additions and 70 deletions
+17 -16
View File
@@ -4,6 +4,7 @@ use anyhow::{bail, Result};
use aes::Aes256;
use cbc::Decryptor;
use cbc::cipher::{BlockDecryptMut, KeyIvInit};
use std::io::{Read, Write};
use std::path::Path;
pub const PAGE_SZ: usize = 4096;
@@ -73,34 +74,34 @@ fn aes_cbc_decrypt(key: &[u8; 32], iv: &[u8; 16], data: &[u8]) -> Result<Vec<u8>
Ok(buf)
}
/// 完整解密一个 SQLCipher 数据库文件
/// 完整解密一个 SQLCipher 数据库文件(流式,逐页读写避免全量载入内存)
///
/// 读取 `db_path`,按 PAGE_SZ 分页解密,写入 `out_path`
pub fn full_decrypt(db_path: &Path, out_path: &Path, enc_key: &[u8; 32]) -> Result<()> {
let data = std::fs::read(db_path)?;
if data.is_empty() {
bail!("数据库文件为空: {}", db_path.display());
}
if let Some(parent) = out_path.parent() {
std::fs::create_dir_all(parent)?;
}
let total_pages = (data.len() + PAGE_SZ - 1) / PAGE_SZ;
let mut out = Vec::with_capacity(data.len());
let mut input = std::fs::File::open(db_path)?;
let file_size = input.metadata()?.len() as usize;
if file_size == 0 {
bail!("数据库文件为空: {}", db_path.display());
}
let mut output = std::fs::File::create(out_path)?;
let total_pages = (file_size + PAGE_SZ - 1) / PAGE_SZ;
let mut page_buf = vec![0u8; PAGE_SZ];
for pgno in 1..=total_pages {
let offset = (pgno - 1) * PAGE_SZ;
let end = std::cmp::min(offset + PAGE_SZ, data.len());
let mut page = data[offset..end].to_vec();
let n = input.read(&mut page_buf)?;
if n == 0 { break; }
// 不足一页则补零
if page.len() < PAGE_SZ {
page.resize(PAGE_SZ, 0);
if n < PAGE_SZ {
page_buf[n..].fill(0);
}
let dec = decrypt_page(enc_key, &page, pgno as u32)?;
out.extend_from_slice(&dec);
let dec = decrypt_page(enc_key, &page_buf, pgno as u32)?;
output.write_all(&dec)?;
}
std::fs::write(out_path, &out)?;
Ok(())
}