refactor: 代码审阅优化
Some checks failed
Secrets CLI - Build & Release / 版本 & Release (push) Successful in 2s
Secrets CLI - Build & Release / 质量检查 (fmt / clippy / test) (push) Successful in 1m42s
Secrets CLI - Build & Release / Build (x86_64-unknown-linux-musl) (push) Successful in 1m18s
Secrets CLI - Build & Release / 发布草稿 Release (push) Successful in 2s
Secrets CLI - Build & Release / Build (aarch64-apple-darwin) (push) Failing after 7m40s
Secrets CLI - Build & Release / Build (x86_64-pc-windows-msvc) (push) Has been cancelled
Some checks failed
Secrets CLI - Build & Release / 版本 & Release (push) Successful in 2s
Secrets CLI - Build & Release / 质量检查 (fmt / clippy / test) (push) Successful in 1m42s
Secrets CLI - Build & Release / Build (x86_64-unknown-linux-musl) (push) Successful in 1m18s
Secrets CLI - Build & Release / 发布草稿 Release (push) Successful in 2s
Secrets CLI - Build & Release / Build (aarch64-apple-darwin) (push) Failing after 7m40s
Secrets CLI - Build & Release / Build (x86_64-pc-windows-msvc) (push) Has been cancelled
P0: - fix(config): config_dir 使用 home_dir 回退,避免 ~ 不展开 - fix(search): 模糊查询转义 LIKE 通配符 % 和 _ P1: - chore(db): 连接池添加 acquire_timeout 10s - refactor(update): 消除 meta_keys/secret_keys 重复计算 P2: - refactor(config): 合并 ConfigAction 枚举 - chore(deps): 移除 clap/env、uuid/v4 无用 features - perf(main): delete 命令跳过 master_key 加载 - i18n(config): 统一错误消息为英文 - perf(search): show_secrets=false 时不再解密获取 key_count - feat(delete,update): 支持 -o json/json-compact 输出 P3: - feat(search): --tag 支持多值交叉过滤 docs: 将 seed-data.sh 替换为 setup-gitea-actions.sh Made-with: Cursor
This commit is contained in:
@@ -10,7 +10,7 @@ pub struct SearchArgs<'a> {
|
||||
pub namespace: Option<&'a str>,
|
||||
pub kind: Option<&'a str>,
|
||||
pub name: Option<&'a str>,
|
||||
pub tag: Option<&'a str>,
|
||||
pub tags: &'a [String],
|
||||
pub query: Option<&'a str>,
|
||||
pub show_secrets: bool,
|
||||
pub fields: &'a [String],
|
||||
@@ -37,13 +37,22 @@ pub async fn run(pool: &PgPool, args: SearchArgs<'_>, master_key: Option<&[u8; 3
|
||||
conditions.push(format!("name = ${}", idx));
|
||||
idx += 1;
|
||||
}
|
||||
if args.tag.is_some() {
|
||||
conditions.push(format!("tags @> ARRAY[${}]", idx));
|
||||
idx += 1;
|
||||
if !args.tags.is_empty() {
|
||||
// Use PostgreSQL array containment: tags @> ARRAY[$n, $m, ...] means all specified tags must be present
|
||||
let placeholders: Vec<String> = args
|
||||
.tags
|
||||
.iter()
|
||||
.map(|_| {
|
||||
let p = format!("${}", idx);
|
||||
idx += 1;
|
||||
p
|
||||
})
|
||||
.collect();
|
||||
conditions.push(format!("tags @> ARRAY[{}]", placeholders.join(", ")));
|
||||
}
|
||||
if args.query.is_some() {
|
||||
conditions.push(format!(
|
||||
"(name ILIKE ${i} OR namespace ILIKE ${i} OR kind ILIKE ${i} OR metadata::text ILIKE ${i} OR EXISTS (SELECT 1 FROM unnest(tags) t WHERE t ILIKE ${i}))",
|
||||
"(name ILIKE ${i} ESCAPE '\\' OR namespace ILIKE ${i} ESCAPE '\\' OR kind ILIKE ${i} ESCAPE '\\' OR metadata::text ILIKE ${i} ESCAPE '\\' OR EXISTS (SELECT 1 FROM unnest(tags) t WHERE t ILIKE ${i} ESCAPE '\\'))",
|
||||
i = idx
|
||||
));
|
||||
idx += 1;
|
||||
@@ -81,11 +90,16 @@ pub async fn run(pool: &PgPool, args: SearchArgs<'_>, master_key: Option<&[u8; 3
|
||||
if let Some(v) = args.name {
|
||||
q = q.bind(v);
|
||||
}
|
||||
if let Some(v) = args.tag {
|
||||
q = q.bind(v);
|
||||
for v in args.tags {
|
||||
q = q.bind(v.as_str());
|
||||
}
|
||||
if let Some(v) = args.query {
|
||||
q = q.bind(format!("%{}%", v));
|
||||
q = q.bind(format!(
|
||||
"%{}%",
|
||||
v.replace('\\', "\\\\")
|
||||
.replace('%', "\\%")
|
||||
.replace('_', "\\_")
|
||||
));
|
||||
}
|
||||
q = q.bind(args.limit as i64).bind(args.offset as i64);
|
||||
|
||||
@@ -186,7 +200,7 @@ fn to_json(
|
||||
Err(e) => json!({"_error": e.to_string()}),
|
||||
}
|
||||
} else {
|
||||
json!({"_encrypted": true, "_key_count": encrypted_key_count(row, master_key)})
|
||||
json!({"_encrypted": true})
|
||||
};
|
||||
|
||||
json!({
|
||||
@@ -201,19 +215,6 @@ fn to_json(
|
||||
"updated_at": row.updated_at.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the number of keys in the encrypted JSON (decrypts to count; 0 on failure).
|
||||
fn encrypted_key_count(row: &Secret, master_key: Option<&[u8; 32]>) -> usize {
|
||||
if row.encrypted.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
let Some(key) = master_key else { return 0 };
|
||||
match crypto::decrypt_json(key, &row.encrypted) {
|
||||
Ok(Value::Object(m)) => m.len(),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn print_text(
|
||||
row: &Secret,
|
||||
show_secrets: bool,
|
||||
|
||||
Reference in New Issue
Block a user