registry: add sqlx compile-time checks for auth and secrets

This commit is contained in:
a.tolmachev
2026-04-12 12:24:27 +03:00
parent dec311b143
commit 5f187a6817
13 changed files with 749 additions and 87 deletions
@@ -0,0 +1,25 @@
{
"db_name": "PostgreSQL",
"query": "insert into users (\n id,\n email,\n display_name,\n password_hash,\n status,\n created_at\n ) values (\n $1, $2, $3, $4, 'active', now()\n )\n on conflict (email) do update\n set display_name = excluded.display_name,\n password_hash = excluded.password_hash,\n status = 'active'\n returning id",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text",
"Text",
"Text",
"Text"
]
},
"nullable": [
false
]
},
"hash": "3df72b8a0ac4a76b15909fc78268a1c87d1cd184973bf1cecc0c725691432c08"
}
@@ -0,0 +1,70 @@
{
"db_name": "PostgreSQL",
"query": "select\n id,\n workspace_id,\n name,\n kind,\n status,\n current_version,\n to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\",\n to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"updated_at!\",\n to_char(last_used_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as last_used_at\n from secrets\n where workspace_id = $1\n order by name asc",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "workspace_id",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "name",
"type_info": "Text"
},
{
"ordinal": 3,
"name": "kind",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "status",
"type_info": "Text"
},
{
"ordinal": 5,
"name": "current_version",
"type_info": "Int4"
},
{
"ordinal": 6,
"name": "created_at!",
"type_info": "Text"
},
{
"ordinal": 7,
"name": "updated_at!",
"type_info": "Text"
},
{
"ordinal": 8,
"name": "last_used_at",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
null,
null,
null
]
},
"hash": "4b6bd8192eea43df8d50504e86e9517becbccf7462aa3008c356f7ef93cd73b4"
}
@@ -0,0 +1,71 @@
{
"db_name": "PostgreSQL",
"query": "select\n id,\n workspace_id,\n name,\n kind,\n status,\n current_version,\n to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\",\n to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"updated_at!\",\n to_char(last_used_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as last_used_at\n from secrets\n where workspace_id = $1 and id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "workspace_id",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "name",
"type_info": "Text"
},
{
"ordinal": 3,
"name": "kind",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "status",
"type_info": "Text"
},
{
"ordinal": 5,
"name": "current_version",
"type_info": "Int4"
},
{
"ordinal": 6,
"name": "created_at!",
"type_info": "Text"
},
{
"ordinal": 7,
"name": "updated_at!",
"type_info": "Text"
},
{
"ordinal": 8,
"name": "last_used_at",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
null,
null,
null
]
},
"hash": "4fcd20a68487da32614c2d7ad6152e35c0251e45ed744b3c4883e7ab8dd64747"
}
@@ -0,0 +1,52 @@
{
"db_name": "PostgreSQL",
"query": "select\n id,\n email,\n display_name,\n password_hash as \"password_hash!\",\n status,\n to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\"\n from users\n where id = $1\n limit 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "email",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "display_name",
"type_info": "Text"
},
{
"ordinal": 3,
"name": "password_hash!",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "status",
"type_info": "Text"
},
{
"ordinal": 5,
"name": "created_at!",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false,
true,
false,
null
]
},
"hash": "5c767e8840c289a2afdde4b13d8c6bd13f51b118a1f5395513ad4783b3b2e1fd"
}
@@ -0,0 +1,59 @@
{
"db_name": "PostgreSQL",
"query": "select\n s.id,\n s.user_id,\n s.current_workspace_id,\n u.email,\n u.display_name,\n u.status,\n to_char(u.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\"\n from user_sessions s\n join users u on u.id = s.user_id\n where s.id = $1\n and s.secret_hash = $2\n and s.status = 'active'\n and s.expires_at > now()\n limit 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "user_id",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "current_workspace_id",
"type_info": "Text"
},
{
"ordinal": 3,
"name": "email",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "display_name",
"type_info": "Text"
},
{
"ordinal": 5,
"name": "status",
"type_info": "Text"
},
{
"ordinal": 6,
"name": "created_at!",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
false,
true,
false,
false,
false,
null
]
},
"hash": "88116bca616419bee79adc6aa5d864379a0236889b0a66e7b9b45197565b3787"
}
@@ -0,0 +1,53 @@
{
"db_name": "PostgreSQL",
"query": "select\n sv.secret_id,\n sv.version,\n sv.ciphertext,\n sv.key_version,\n to_char(sv.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\",\n sv.created_by\n from secrets s\n join secret_versions sv\n on sv.secret_id = s.id and sv.version = s.current_version\n where s.workspace_id = $1 and s.id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "secret_id",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "version",
"type_info": "Int4"
},
{
"ordinal": 2,
"name": "ciphertext",
"type_info": "Text"
},
{
"ordinal": 3,
"name": "key_version",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "created_at!",
"type_info": "Text"
},
{
"ordinal": 5,
"name": "created_by",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
false,
false,
false,
null,
true
]
},
"hash": "d50ada14acca676613ea3312e92c73a85fd24606e88c9b07fc33535690b540d1"
}
@@ -0,0 +1,52 @@
{
"db_name": "PostgreSQL",
"query": "select\n id,\n email,\n display_name,\n password_hash as \"password_hash!\",\n status,\n to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\"\n from users\n where email = $1\n limit 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "email",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "display_name",
"type_info": "Text"
},
{
"ordinal": 3,
"name": "password_hash!",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "status",
"type_info": "Text"
},
{
"ordinal": 5,
"name": "created_at!",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false,
true,
false,
null
]
},
"hash": "da6cb061da338d4ed4f0c0da995251f5dd1af2b7667e3fa01994e568c962ea77"
}
Generated
+249
View File
@@ -226,6 +226,9 @@ name = "bitflags"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
dependencies = [
"serde_core",
]
[[package]]
name = "blake2"
@@ -304,6 +307,12 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "const-oid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "cookie"
version = "0.18.1"
@@ -561,6 +570,17 @@ version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
[[package]]
name = "der"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"const-oid",
"pem-rfc7468",
"zeroize",
]
[[package]]
name = "deranged"
version = "0.5.8"
@@ -577,6 +597,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"const-oid",
"crypto-common",
"subtle",
]
@@ -672,6 +693,17 @@ version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "flume"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
dependencies = [
"futures-core",
"futures-sink",
"spin",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -1216,6 +1248,9 @@ name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [
"spin",
]
[[package]]
name = "leb128fmt"
@@ -1229,6 +1264,12 @@ version = "0.2.183"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
[[package]]
name = "libm"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
[[package]]
name = "libredox"
version = "0.1.15"
@@ -1241,6 +1282,16 @@ dependencies = [
"redox_syscall 0.7.3",
]
[[package]]
name = "libsqlite3-sys"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [
"pkg-config",
"vcpkg",
]
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
@@ -1368,12 +1419,48 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "num-bigint-dig"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
dependencies = [
"lazy_static",
"libm",
"num-integer",
"num-iter",
"num-traits",
"rand 0.8.5",
"smallvec",
"zeroize",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -1381,6 +1468,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
"libm",
]
[[package]]
@@ -1444,6 +1532,15 @@ dependencies = [
"subtle",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
[[package]]
name = "percent-encoding"
version = "2.3.2"
@@ -1493,6 +1590,33 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs1"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
"der",
"pkcs8",
"spki",
]
[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
"der",
"spki",
]
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "plain"
version = "0.2.3"
@@ -1965,6 +2089,26 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]]
name = "rsa"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
dependencies = [
"const-oid",
"digest",
"num-bigint-dig",
"num-integer",
"num-traits",
"pkcs1",
"pkcs8",
"rand_core 0.6.4",
"signature",
"spki",
"subtle",
"zeroize",
]
[[package]]
name = "rustc-hash"
version = "2.1.1"
@@ -2210,6 +2354,16 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest",
"rand_core 0.6.4",
]
[[package]]
name = "slab"
version = "0.4.12"
@@ -2235,6 +2389,25 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "spki"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
]
[[package]]
name = "sqlx"
version = "0.8.6"
@@ -2243,7 +2416,9 @@ checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc"
dependencies = [
"sqlx-core",
"sqlx-macros",
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
]
[[package]]
@@ -2312,12 +2487,56 @@ dependencies = [
"serde_json",
"sha2",
"sqlx-core",
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
"syn",
"tokio",
"url",
]
[[package]]
name = "sqlx-mysql"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
dependencies = [
"atoi",
"base64",
"bitflags",
"byteorder",
"bytes",
"crc",
"digest",
"dotenvy",
"either",
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"generic-array",
"hex",
"hkdf",
"hmac",
"itoa",
"log",
"md-5",
"memchr",
"once_cell",
"percent-encoding",
"rand 0.8.5",
"rsa",
"serde",
"sha1",
"sha2",
"smallvec",
"sqlx-core",
"stringprep",
"thiserror",
"tracing",
"whoami",
]
[[package]]
name = "sqlx-postgres"
version = "0.8.6"
@@ -2355,6 +2574,30 @@ dependencies = [
"whoami",
]
[[package]]
name = "sqlx-sqlite"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
dependencies = [
"atoi",
"flume",
"futures-channel",
"futures-core",
"futures-executor",
"futures-intrusive",
"futures-util",
"libsqlite3-sys",
"log",
"percent-encoding",
"serde",
"serde_urlencoded",
"sqlx-core",
"thiserror",
"tracing",
"url",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
@@ -2896,6 +3139,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.5"
+1 -1
View File
@@ -40,7 +40,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
sha2 = "0.10"
sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio-rustls", "postgres"] }
sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio-rustls", "postgres", "macros", "json"] }
thiserror = "2"
time = { version = "0.3", features = ["formatting", "parsing"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
+52 -29
View File
@@ -7,7 +7,8 @@ impl PostgresRegistry {
display_name: &str,
password_hash: &str,
) -> Result<UserId, RegistryError> {
let row = sqlx::query(
let user_id = format!("user_{}", uuid::Uuid::now_v7().simple());
let row = sqlx::query!(
"insert into users (
id,
email,
@@ -23,15 +24,15 @@ impl PostgresRegistry {
password_hash = excluded.password_hash,
status = 'active'
returning id",
user_id,
email,
display_name,
password_hash,
)
.bind(format!("user_{}", uuid::Uuid::now_v7().simple()))
.bind(email)
.bind(display_name)
.bind(password_hash)
.fetch_one(&self.pool)
.await?;
Ok(UserId::new(row.try_get::<String, _>("id")?))
Ok(UserId::new(row.id))
}
pub async fn ensure_membership(
@@ -65,46 +66,70 @@ impl PostgresRegistry {
&self,
email: &str,
) -> Result<Option<AuthUserRecord>, RegistryError> {
let row = sqlx::query(
let row = sqlx::query!(
"select
id,
email,
display_name,
password_hash,
password_hash as \"password_hash!\",
status,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\"
from users
where email = $1
limit 1",
email,
)
.bind(email)
.fetch_optional(&self.pool)
.await?;
row.as_ref().map(map_auth_user_record).transpose()
row.map(|row| {
Ok(AuthUserRecord {
user: User {
id: UserId::new(row.id),
email: row.email,
display_name: row.display_name,
status: deserialize_enum_text(&row.status, "status")?,
created_at: row.created_at,
},
password_hash: row.password_hash,
})
})
.transpose()
}
pub async fn get_auth_user_by_id(
&self,
user_id: &UserId,
) -> Result<Option<AuthUserRecord>, RegistryError> {
let row = sqlx::query(
let row = sqlx::query!(
"select
id,
email,
display_name,
password_hash,
password_hash as \"password_hash!\",
status,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\"
from users
where id = $1
limit 1",
user_id.as_str(),
)
.bind(user_id.as_str())
.fetch_optional(&self.pool)
.await?;
row.as_ref().map(map_auth_user_record).transpose()
row.map(|row| {
Ok(AuthUserRecord {
user: User {
id: UserId::new(row.id),
email: row.email,
display_name: row.display_name,
status: deserialize_enum_text(&row.status, "status")?,
created_at: row.created_at,
},
password_hash: row.password_hash,
})
})
.transpose()
}
pub async fn update_user_profile(
@@ -204,7 +229,7 @@ impl PostgresRegistry {
session_id: &UserSessionId,
secret_hash: &str,
) -> Result<Option<SessionRecord>, RegistryError> {
let row = sqlx::query(
let row = sqlx::query!(
"select
s.id,
s.user_id,
@@ -212,7 +237,7 @@ impl PostgresRegistry {
u.email,
u.display_name,
u.status,
to_char(u.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at
to_char(u.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\"
from user_sessions s
join users u on u.id = s.user_id
where s.id = $1
@@ -220,9 +245,9 @@ impl PostgresRegistry {
and s.status = 'active'
and s.expires_at > now()
limit 1",
session_id.as_str(),
secret_hash,
)
.bind(session_id.as_str())
.bind(secret_hash)
.fetch_optional(&self.pool)
.await?;
@@ -230,18 +255,16 @@ impl PostgresRegistry {
return Ok(None);
};
let user_id = UserId::new(row.try_get::<String, _>("user_id")?);
let user_id = UserId::new(row.user_id);
let user = User {
id: user_id.clone(),
email: row.try_get("email")?,
display_name: row.try_get("display_name")?,
status: deserialize_enum_text(&row.try_get::<String, _>("status")?, "status")?,
created_at: row.try_get("created_at")?,
email: row.email,
display_name: row.display_name,
status: deserialize_enum_text(&row.status, "status")?,
created_at: row.created_at,
};
let memberships = self.list_workspaces_for_user(&user_id).await?;
let stored_workspace_id = row
.try_get::<Option<String>, _>("current_workspace_id")?
.map(WorkspaceId::new);
let stored_workspace_id = row.current_workspace_id.map(WorkspaceId::new);
let current_workspace_id = stored_workspace_id
.filter(|workspace_id| {
memberships
@@ -255,7 +278,7 @@ impl PostgresRegistry {
});
Ok(Some(SessionRecord {
session_id: UserSessionId::new(row.try_get::<String, _>("id")?),
session_id: UserSessionId::new(row.id),
user,
memberships,
current_workspace_id,
-41
View File
@@ -530,13 +530,6 @@ fn map_membership_record(row: &PgRow) -> Result<MembershipRecord, RegistryError>
})
}
fn map_auth_user_record(row: &PgRow) -> Result<AuthUserRecord, RegistryError> {
Ok(AuthUserRecord {
user: map_user(row)?,
password_hash: row.try_get("password_hash")?,
})
}
fn map_user(row: &PgRow) -> Result<User, RegistryError> {
Ok(User {
id: UserId::new(row.try_get::<String, _>("id")?),
@@ -593,40 +586,6 @@ fn map_platform_api_key_record(row: &PgRow) -> Result<PlatformApiKeyRecord, Regi
})
}
fn map_secret_record(row: &PgRow) -> Result<SecretRecord, RegistryError> {
Ok(SecretRecord {
secret: Secret {
id: SecretId::new(row.try_get::<String, _>("id")?),
workspace_id: WorkspaceId::new(row.try_get::<String, _>("workspace_id")?),
name: row.try_get("name")?,
kind: deserialize_enum_text(&row.try_get::<String, _>("kind")?, "kind")?,
status: deserialize_enum_text(&row.try_get::<String, _>("status")?, "status")?,
current_version: from_db_version(
row.try_get::<i32, _>("current_version")?,
"current_version",
)?,
created_at: row.try_get("created_at")?,
updated_at: row.try_get("updated_at")?,
last_used_at: row.try_get("last_used_at")?,
},
})
}
fn map_secret_version_record(row: &PgRow) -> Result<SecretVersionRecord, RegistryError> {
Ok(SecretVersionRecord {
secret_version: SecretVersion {
secret_id: SecretId::new(row.try_get::<String, _>("secret_id")?),
version: from_db_version(row.try_get::<i32, _>("version")?, "version")?,
ciphertext: row.try_get("ciphertext")?,
key_version: row.try_get("key_version")?,
created_at: row.try_get("created_at")?,
created_by: row
.try_get::<Option<String>, _>("created_by")?
.map(UserId::new),
},
})
}
fn map_stream_session(row: &PgRow) -> Result<StreamSession, RegistryError> {
Ok(StreamSession {
id: StreamSessionId::new(row.try_get::<String, _>("id")?),
+59 -16
View File
@@ -5,7 +5,7 @@ impl PostgresRegistry {
&self,
workspace_id: &WorkspaceId,
) -> Result<Vec<SecretRecord>, RegistryError> {
let rows = sqlx::query(
let rows = sqlx::query!(
"select
id,
workspace_id,
@@ -13,18 +13,34 @@ impl PostgresRegistry {
kind,
status,
current_version,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as updated_at,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\",
to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"updated_at!\",
to_char(last_used_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as last_used_at
from secrets
where workspace_id = $1
order by name asc",
workspace_id.as_str(),
)
.bind(workspace_id.as_str())
.fetch_all(&self.pool)
.await?;
rows.iter().map(map_secret_record).collect()
rows.into_iter()
.map(|row| {
Ok(SecretRecord {
secret: Secret {
id: SecretId::new(row.id),
workspace_id: WorkspaceId::new(row.workspace_id),
name: row.name,
kind: deserialize_enum_text(&row.kind, "kind")?,
status: deserialize_enum_text(&row.status, "status")?,
current_version: from_db_version(row.current_version, "current_version")?,
created_at: row.created_at,
updated_at: row.updated_at,
last_used_at: row.last_used_at,
},
})
})
.collect()
}
pub async fn get_secret(
@@ -32,7 +48,7 @@ impl PostgresRegistry {
workspace_id: &WorkspaceId,
secret_id: &SecretId,
) -> Result<Option<SecretRecord>, RegistryError> {
let row = sqlx::query(
let row = sqlx::query!(
"select
id,
workspace_id,
@@ -40,18 +56,33 @@ impl PostgresRegistry {
kind,
status,
current_version,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as updated_at,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\",
to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"updated_at!\",
to_char(last_used_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as last_used_at
from secrets
where workspace_id = $1 and id = $2",
workspace_id.as_str(),
secret_id.as_str(),
)
.bind(workspace_id.as_str())
.bind(secret_id.as_str())
.fetch_optional(&self.pool)
.await?;
row.as_ref().map(map_secret_record).transpose()
row.map(|row| {
Ok(SecretRecord {
secret: Secret {
id: SecretId::new(row.id),
workspace_id: WorkspaceId::new(row.workspace_id),
name: row.name,
kind: deserialize_enum_text(&row.kind, "kind")?,
status: deserialize_enum_text(&row.status, "status")?,
current_version: from_db_version(row.current_version, "current_version")?,
created_at: row.created_at,
updated_at: row.updated_at,
last_used_at: row.last_used_at,
},
})
})
.transpose()
}
pub async fn get_current_secret_version(
@@ -59,25 +90,37 @@ impl PostgresRegistry {
workspace_id: &WorkspaceId,
secret_id: &SecretId,
) -> Result<Option<SecretVersionRecord>, RegistryError> {
let row = sqlx::query(
let row = sqlx::query!(
"select
sv.secret_id,
sv.version,
sv.ciphertext,
sv.key_version,
to_char(sv.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
to_char(sv.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\",
sv.created_by
from secrets s
join secret_versions sv
on sv.secret_id = s.id and sv.version = s.current_version
where s.workspace_id = $1 and s.id = $2",
workspace_id.as_str(),
secret_id.as_str(),
)
.bind(workspace_id.as_str())
.bind(secret_id.as_str())
.fetch_optional(&self.pool)
.await?;
row.as_ref().map(map_secret_version_record).transpose()
row.map(|row| {
Ok(SecretVersionRecord {
secret_version: SecretVersion {
secret_id: SecretId::new(row.secret_id),
version: from_db_version(row.version, "version")?,
ciphertext: row.ciphertext,
key_version: row.key_version,
created_at: row.created_at,
created_by: row.created_by.map(UserId::new),
},
})
})
.transpose()
}
pub async fn create_secret(
+6
View File
@@ -13,6 +13,12 @@ clippy:
test:
cargo test --workspace --all-targets
sqlx-prepare database_url:
DATABASE_URL={{database_url}} cargo sqlx prepare --workspace -- --all-targets
sqlx-check:
SQLX_OFFLINE=true cargo check --workspace
verify:
just fmt-check
just clippy