feat: secrets CLI MVP — add/search/delete with PostgreSQL JSONB
Some checks failed
Secrets CLI - Build & Release / 检查版本 (push) Successful in 2s
Secrets CLI - Build & Release / Build (x86_64-unknown-linux-musl) (push) Failing after 41s
Secrets CLI - Build & Release / Build (aarch64-apple-darwin) (push) Failing after 55s
Secrets CLI - Build & Release / 发送通知 (push) Has been cancelled
Secrets CLI - Build & Release / Build (x86_64-pc-windows-msvc) (push) Has been cancelled

- Single `secrets` table with namespace/kind/name/tags/metadata/encrypted
- Auto-migrate on startup using uuidv7() primary keys and GIN indexes
- CLI commands: add (upsert, @file support), search (full-text + tags), delete
- Multi-platform Gitea Actions: debian (x86_64-musl), darwin-arm64, windows
  - continue-on-error + timeout-minutes=30 for offline runner tolerance
- VS Code tasks.json for local build/test/seed
- AGENTS.md for AI context

Made-with: Cursor
This commit is contained in:
voson
2026-03-18 14:10:45 +08:00
parent 3b5e26213c
commit 102e394914
16 changed files with 3656 additions and 544 deletions

44
src/db.rs Normal file
View File

@@ -0,0 +1,44 @@
use anyhow::Result;
use sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;
pub async fn create_pool(database_url: &str) -> Result<PgPool> {
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(database_url)
.await?;
Ok(pool)
}
pub async fn migrate(pool: &PgPool) -> Result<()> {
sqlx::raw_sql(
r#"
CREATE TABLE IF NOT EXISTS secrets (
id UUID PRIMARY KEY DEFAULT uuidv7(),
namespace VARCHAR(64) NOT NULL,
kind VARCHAR(64) NOT NULL,
name VARCHAR(256) NOT NULL,
tags TEXT[] NOT NULL DEFAULT '{}',
metadata JSONB NOT NULL DEFAULT '{}',
encrypted JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(namespace, kind, name)
);
-- idempotent column add for existing tables
DO $$ BEGIN
ALTER TABLE secrets ADD COLUMN IF NOT EXISTS metadata JSONB NOT NULL DEFAULT '{}';
EXCEPTION WHEN OTHERS THEN NULL;
END $$;
CREATE INDEX IF NOT EXISTS idx_secrets_namespace ON secrets(namespace);
CREATE INDEX IF NOT EXISTS idx_secrets_kind ON secrets(kind);
CREATE INDEX IF NOT EXISTS idx_secrets_tags ON secrets USING GIN(tags);
CREATE INDEX IF NOT EXISTS idx_secrets_metadata ON secrets USING GIN(metadata jsonb_path_ops);
"#,
)
.execute(pool)
.await?;
Ok(())
}