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:
95
src/main.rs
95
src/main.rs
@@ -18,7 +18,10 @@ use output::resolve_output_mode;
|
||||
version,
|
||||
about = "Secrets & config manager backed by PostgreSQL — optimised for AI agents",
|
||||
after_help = "QUICK START:
|
||||
# First time setup (run once per device)
|
||||
# 1. Configure database (once per device)
|
||||
secrets config set-db \"postgres://postgres:<password>@<host>:<port>/secrets\"
|
||||
|
||||
# 2. Initialize master key (once per device)
|
||||
secrets init
|
||||
|
||||
# Discover what namespaces / kinds exist
|
||||
@@ -52,7 +55,12 @@ enum Commands {
|
||||
///
|
||||
/// Prompts for a master password, derives a key with Argon2id, and stores
|
||||
/// it in the OS Keychain. Use the same password on every device.
|
||||
#[command(after_help = "EXAMPLES:
|
||||
///
|
||||
/// NOTE: Run `secrets config set-db <URL>` first if database is not configured.
|
||||
#[command(after_help = "PREREQUISITE:
|
||||
Database must be configured first. Run: secrets config set-db <DATABASE_URL>
|
||||
|
||||
EXAMPLES:
|
||||
# First device: generates a new Argon2id salt and stores master key
|
||||
secrets init
|
||||
|
||||
@@ -155,9 +163,9 @@ enum Commands {
|
||||
/// Exact name filter, e.g. gitea, i-uf63f2uookgs5uxmrdyc
|
||||
#[arg(long)]
|
||||
name: Option<String>,
|
||||
/// Filter by tag, e.g. --tag aliyun
|
||||
/// Filter by tag, e.g. --tag aliyun (repeatable for AND intersection)
|
||||
#[arg(long)]
|
||||
tag: Option<String>,
|
||||
tag: Vec<String>,
|
||||
/// Fuzzy keyword (matches name, namespace, kind, tags, metadata text)
|
||||
#[arg(short, long)]
|
||||
query: Option<String>,
|
||||
@@ -201,6 +209,9 @@ enum Commands {
|
||||
/// Exact name of the record to delete
|
||||
#[arg(long)]
|
||||
name: String,
|
||||
/// Output format: text (default on TTY), json, json-compact
|
||||
#[arg(short, long = "output")]
|
||||
output: Option<String>,
|
||||
},
|
||||
|
||||
/// Incrementally update an existing record (merge semantics; record must exist).
|
||||
@@ -252,6 +263,9 @@ enum Commands {
|
||||
/// Delete a secret field by key (repeatable)
|
||||
#[arg(long = "remove-secret")]
|
||||
remove_secrets: Vec<String>,
|
||||
/// Output format: text (default on TTY), json, json-compact
|
||||
#[arg(short, long = "output")]
|
||||
output: Option<String>,
|
||||
},
|
||||
|
||||
/// Manage CLI configuration (database connection, etc.)
|
||||
@@ -298,15 +312,8 @@ async fn main() -> Result<()> {
|
||||
.init();
|
||||
|
||||
// config subcommand needs no database or master key
|
||||
if let Commands::Config { action } = &cli.command {
|
||||
let cmd_action = match action {
|
||||
ConfigAction::SetDb { url } => {
|
||||
commands::config::ConfigAction::SetDb { url: url.clone() }
|
||||
}
|
||||
ConfigAction::Show => commands::config::ConfigAction::Show,
|
||||
ConfigAction::Path => commands::config::ConfigAction::Path,
|
||||
};
|
||||
return commands::config::run(cmd_action).await;
|
||||
if let Commands::Config { action } = cli.command {
|
||||
return commands::config::run(action).await;
|
||||
}
|
||||
|
||||
let db_url = config::resolve_db_url(&cli.db_url)?;
|
||||
@@ -314,14 +321,14 @@ async fn main() -> Result<()> {
|
||||
db::migrate(&pool).await?;
|
||||
|
||||
// init needs a pool but sets up the master key — handle before loading it
|
||||
if let Commands::Init = &cli.command {
|
||||
if let Commands::Init = cli.command {
|
||||
return commands::init::run(&pool).await;
|
||||
}
|
||||
|
||||
// All remaining commands require the master key from the OS Keychain
|
||||
let master_key = crypto::load_master_key()?;
|
||||
// All remaining commands require the master key from the OS Keychain,
|
||||
// except delete which operates on plaintext metadata only.
|
||||
|
||||
match &cli.command {
|
||||
match cli.command {
|
||||
Commands::Init | Commands::Config { .. } => unreachable!(),
|
||||
|
||||
Commands::Add {
|
||||
@@ -333,18 +340,19 @@ async fn main() -> Result<()> {
|
||||
secrets,
|
||||
output,
|
||||
} => {
|
||||
let master_key = crypto::load_master_key()?;
|
||||
let _span =
|
||||
tracing::info_span!("cmd", command = "add", %namespace, %kind, %name).entered();
|
||||
let out = resolve_output_mode(output.as_deref())?;
|
||||
commands::add::run(
|
||||
&pool,
|
||||
commands::add::AddArgs {
|
||||
namespace,
|
||||
kind,
|
||||
name,
|
||||
tags,
|
||||
meta_entries: meta,
|
||||
secret_entries: secrets,
|
||||
namespace: &namespace,
|
||||
kind: &kind,
|
||||
name: &name,
|
||||
tags: &tags,
|
||||
meta_entries: &meta,
|
||||
secret_entries: &secrets,
|
||||
output: out,
|
||||
},
|
||||
&master_key,
|
||||
@@ -366,8 +374,9 @@ async fn main() -> Result<()> {
|
||||
sort,
|
||||
output,
|
||||
} => {
|
||||
let master_key = crypto::load_master_key()?;
|
||||
let _span = tracing::info_span!("cmd", command = "search").entered();
|
||||
let show = *show_secrets || fields.iter().any(|f| f.starts_with("secret"));
|
||||
let show = show_secrets || fields.iter().any(|f| f.starts_with("secret"));
|
||||
let out = resolve_output_mode(output.as_deref())?;
|
||||
commands::search::run(
|
||||
&pool,
|
||||
@@ -375,14 +384,14 @@ async fn main() -> Result<()> {
|
||||
namespace: namespace.as_deref(),
|
||||
kind: kind.as_deref(),
|
||||
name: name.as_deref(),
|
||||
tag: tag.as_deref(),
|
||||
tags: &tag,
|
||||
query: query.as_deref(),
|
||||
show_secrets: show,
|
||||
fields,
|
||||
summary: *summary,
|
||||
limit: *limit,
|
||||
offset: *offset,
|
||||
sort,
|
||||
fields: &fields,
|
||||
summary,
|
||||
limit,
|
||||
offset,
|
||||
sort: &sort,
|
||||
output: out,
|
||||
},
|
||||
Some(&master_key),
|
||||
@@ -394,10 +403,12 @@ async fn main() -> Result<()> {
|
||||
namespace,
|
||||
kind,
|
||||
name,
|
||||
output,
|
||||
} => {
|
||||
let _span =
|
||||
tracing::info_span!("cmd", command = "delete", %namespace, %kind, %name).entered();
|
||||
commands::delete::run(&pool, namespace, kind, name).await?;
|
||||
let out = resolve_output_mode(output.as_deref())?;
|
||||
commands::delete::run(&pool, &namespace, &kind, &name, out).await?;
|
||||
}
|
||||
|
||||
Commands::Update {
|
||||
@@ -410,21 +421,25 @@ async fn main() -> Result<()> {
|
||||
remove_meta,
|
||||
secrets,
|
||||
remove_secrets,
|
||||
output,
|
||||
} => {
|
||||
let master_key = crypto::load_master_key()?;
|
||||
let _span =
|
||||
tracing::info_span!("cmd", command = "update", %namespace, %kind, %name).entered();
|
||||
let out = resolve_output_mode(output.as_deref())?;
|
||||
commands::update::run(
|
||||
&pool,
|
||||
commands::update::UpdateArgs {
|
||||
namespace,
|
||||
kind,
|
||||
name,
|
||||
add_tags,
|
||||
remove_tags,
|
||||
meta_entries: meta,
|
||||
remove_meta,
|
||||
secret_entries: secrets,
|
||||
remove_secrets,
|
||||
namespace: &namespace,
|
||||
kind: &kind,
|
||||
name: &name,
|
||||
add_tags: &add_tags,
|
||||
remove_tags: &remove_tags,
|
||||
meta_entries: &meta,
|
||||
remove_meta: &remove_meta,
|
||||
secret_entries: &secrets,
|
||||
remove_secrets: &remove_secrets,
|
||||
output: out,
|
||||
},
|
||||
&master_key,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user