feat: user-scoped history/delete/rollback, dashboard & login UI, ignore *.pem

- Filter history/rollback/delete by user_id in secrets-core
- MCP tools/web pass user context; dashboard refresh; favicon static
- .gitignore *.pem; vscode tasks tweaks
- clippy: collapse else-if in rollback latest-history branch

Made-with: Cursor
This commit is contained in:
voson
2026-03-20 20:11:19 +08:00
parent 49fb7430a8
commit 5df4141935
14 changed files with 536 additions and 165 deletions

View File

@@ -99,6 +99,11 @@ pub async fn migrate(pool: &PgPool) -> Result<()> {
CREATE INDEX IF NOT EXISTS idx_entries_history_ns_kind_name
ON entries_history(namespace, kind, name, version DESC);
-- Backfill: add user_id to entries_history for multi-tenant isolation
ALTER TABLE entries_history ADD COLUMN IF NOT EXISTS user_id UUID;
CREATE INDEX IF NOT EXISTS idx_entries_history_user_id
ON entries_history(user_id) WHERE user_id IS NOT NULL;
-- ── secrets_history: field-level snapshot ────────────────────────────────
CREATE TABLE IF NOT EXISTS secrets_history (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
@@ -159,6 +164,7 @@ pub async fn migrate(pool: &PgPool) -> Result<()> {
pub struct EntrySnapshotParams<'a> {
pub entry_id: uuid::Uuid,
pub user_id: Option<uuid::Uuid>,
pub namespace: &'a str,
pub kind: &'a str,
pub name: &'a str,
@@ -175,8 +181,8 @@ pub async fn snapshot_entry_history(
let actor = current_actor();
sqlx::query(
"INSERT INTO entries_history \
(entry_id, namespace, kind, name, version, action, tags, metadata, actor) \
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
(entry_id, namespace, kind, name, version, action, tags, metadata, actor, user_id) \
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
)
.bind(p.entry_id)
.bind(p.namespace)
@@ -187,6 +193,7 @@ pub async fn snapshot_entry_history(
.bind(p.tags)
.bind(p.metadata)
.bind(&actor)
.bind(p.user_id)
.execute(&mut **tx)
.await?;
Ok(())