feat(v3): migrate workspace to API, Tauri desktop, and v3 crates; remove legacy MCP stack
Some checks failed
Secrets v3 CI / 检查 (push) Has been cancelled
Some checks failed
Secrets v3 CI / 检查 (push) Has been cancelled
- Add apps/api, desktop Tauri shell, domain/application/crypto/device-auth/infrastructure-db - Replace desktop-daemon vault integration; drop secrets-core and secrets-mcp* - Ignore apps/desktop/dist and generated Tauri icons; document icon/dist steps in AGENTS.md - Apply rustfmt; fix clippy (collapsible_if, HTTP method as str)
This commit is contained in:
15
crates/infrastructure-db/Cargo.toml
Normal file
15
crates/infrastructure-db/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "secrets-infrastructure-db"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "secrets_infrastructure_db"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
dotenvy.workspace = true
|
||||
sqlx.workspace = true
|
||||
tracing.workspace = true
|
||||
uuid.workspace = true
|
||||
29
crates/infrastructure-db/src/lib.rs
Normal file
29
crates/infrastructure-db/src/lib.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
mod migrate;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use sqlx::PgPool;
|
||||
use sqlx::postgres::{PgConnectOptions, PgPoolOptions};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub use migrate::migrate_current_schema;
|
||||
|
||||
pub fn load_database_url() -> Result<String> {
|
||||
std::env::var("SECRETS_DATABASE_URL")
|
||||
.context("SECRETS_DATABASE_URL is required for current services")
|
||||
}
|
||||
|
||||
pub async fn create_pool(database_url: &str) -> Result<PgPool> {
|
||||
let options =
|
||||
PgConnectOptions::from_str(database_url).context("failed to parse SECRETS_DATABASE_URL")?;
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(
|
||||
std::env::var("SECRETS_DATABASE_POOL_SIZE")
|
||||
.ok()
|
||||
.and_then(|v| v.parse::<u32>().ok())
|
||||
.unwrap_or(10),
|
||||
)
|
||||
.connect_with(options)
|
||||
.await
|
||||
.context("failed to connect to PostgreSQL")?;
|
||||
Ok(pool)
|
||||
}
|
||||
108
crates/infrastructure-db/src/migrate.rs
Normal file
108
crates/infrastructure-db/src/migrate.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use anyhow::Result;
|
||||
use sqlx::PgPool;
|
||||
|
||||
pub async fn migrate_current_schema(pool: &PgPool) -> Result<()> {
|
||||
sqlx::raw_sql(
|
||||
r#"
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||
email VARCHAR(256),
|
||||
name VARCHAR(256) NOT NULL DEFAULT '',
|
||||
avatar_url TEXT,
|
||||
key_salt BYTEA,
|
||||
key_check BYTEA,
|
||||
key_params JSONB,
|
||||
key_version BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS oauth_accounts (
|
||||
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
provider VARCHAR(32) NOT NULL,
|
||||
provider_id VARCHAR(256) NOT NULL,
|
||||
email VARCHAR(256),
|
||||
name VARCHAR(256),
|
||||
avatar_url TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(provider, provider_id),
|
||||
UNIQUE(user_id, provider)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS devices (
|
||||
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
display_name VARCHAR(256) NOT NULL,
|
||||
platform VARCHAR(64) NOT NULL,
|
||||
client_version VARCHAR(64) NOT NULL,
|
||||
device_fingerprint TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_devices_user_id ON devices(user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS device_login_tokens (
|
||||
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||
device_id UUID NOT NULL REFERENCES devices(id) ON DELETE CASCADE,
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_device_login_tokens_device_id ON device_login_tokens(device_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS auth_events (
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
device_id UUID NOT NULL REFERENCES devices(id) ON DELETE CASCADE,
|
||||
device_name VARCHAR(256) NOT NULL,
|
||||
platform VARCHAR(64) NOT NULL,
|
||||
client_version VARCHAR(64) NOT NULL,
|
||||
ip_addr TEXT,
|
||||
forwarded_ip TEXT,
|
||||
login_method VARCHAR(32) NOT NULL,
|
||||
login_result VARCHAR(32) NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_auth_events_user_id_created_at
|
||||
ON auth_events(user_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_auth_events_device_id_created_at
|
||||
ON auth_events(device_id, created_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vault_objects (
|
||||
object_id UUID PRIMARY KEY,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
object_kind VARCHAR(32) NOT NULL,
|
||||
revision BIGINT NOT NULL,
|
||||
cipher_version INTEGER NOT NULL DEFAULT 1,
|
||||
ciphertext BYTEA NOT NULL DEFAULT '\x',
|
||||
content_hash TEXT NOT NULL DEFAULT '',
|
||||
deleted_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by_device UUID REFERENCES devices(id) ON DELETE SET NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_vault_objects_user_revision
|
||||
ON vault_objects(user_id, revision ASC);
|
||||
CREATE INDEX IF NOT EXISTS idx_vault_objects_user_deleted
|
||||
ON vault_objects(user_id, deleted_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vault_object_revisions (
|
||||
object_id UUID NOT NULL,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
revision BIGINT NOT NULL,
|
||||
cipher_version INTEGER NOT NULL DEFAULT 1,
|
||||
ciphertext BYTEA NOT NULL DEFAULT '\x',
|
||||
content_hash TEXT NOT NULL DEFAULT '',
|
||||
deleted_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (object_id, revision)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_vault_object_revisions_user_revision
|
||||
ON vault_object_revisions(user_id, revision ASC);
|
||||
"#,
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user