Files
crank/crates/crank-registry/src/postgres/api_key.rs
T
2026-04-12 12:03:24 +03:00

181 lines
5.5 KiB
Rust

use super::*;
impl PostgresRegistry {
pub async fn list_platform_api_keys(
&self,
workspace_id: &WorkspaceId,
) -> Result<Vec<PlatformApiKeyRecord>, RegistryError> {
let rows = sqlx::query(
"select
id,
workspace_id,
name,
prefix,
scopes_json,
status,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
to_char(last_used_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as last_used_at
from platform_api_keys
where workspace_id = $1
order by created_at desc",
)
.bind(workspace_id.as_str())
.fetch_all(&self.pool)
.await?;
rows.iter().map(map_platform_api_key_record).collect()
}
pub async fn get_platform_api_key_by_secret_for_workspace_slug(
&self,
workspace_slug: &str,
secret_hash: &str,
) -> Result<Option<PlatformApiKeyRecord>, RegistryError> {
let row = sqlx::query(
"select
k.id,
k.workspace_id,
k.name,
k.prefix,
k.scopes_json,
k.status,
to_char(k.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
to_char(k.last_used_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as last_used_at
from platform_api_keys k
join workspaces w on w.id = k.workspace_id
where w.slug = $1
and k.secret_hash = $2
and k.status = 'active'
limit 1",
)
.bind(workspace_slug)
.bind(secret_hash)
.fetch_optional(&self.pool)
.await?;
row.as_ref().map(map_platform_api_key_record).transpose()
}
pub async fn create_platform_api_key(
&self,
request: CreatePlatformApiKeyRequest<'_>,
) -> Result<(), RegistryError> {
let result = sqlx::query(
"insert into platform_api_keys (
id,
workspace_id,
name,
prefix,
secret_hash,
scopes_json,
status,
created_at,
last_used_at,
revoked_at
) values (
$1, $2, $3, $4, $5, $6, $7, $8::timestamptz, $9::timestamptz, $10::timestamptz
)",
)
.bind(request.api_key.id.as_str())
.bind(request.api_key.workspace_id.as_str())
.bind(&request.api_key.name)
.bind(&request.api_key.prefix)
.bind(request.secret_hash)
.bind(Json(serialize_json_value(&request.api_key.scopes)?))
.bind(serialize_enum_text(&request.api_key.status, "status")?)
.bind(&request.api_key.created_at)
.bind(request.api_key.last_used_at.as_deref())
.bind(Option::<&str>::None)
.execute(&self.pool)
.await;
match result {
Ok(_) => Ok(()),
Err(sqlx::Error::Database(error))
if error.constraint() == Some("platform_api_keys_workspace_name_idx") =>
{
Err(RegistryError::Storage(sqlx::Error::Database(error)))
}
Err(error) => Err(RegistryError::Storage(error)),
}
}
pub async fn revoke_platform_api_key(
&self,
workspace_id: &WorkspaceId,
key_id: &PlatformApiKeyId,
revoked_at: &str,
) -> Result<(), RegistryError> {
let result = sqlx::query(
"update platform_api_keys
set status = $1,
revoked_at = $2::timestamptz
where workspace_id = $3 and id = $4",
)
.bind(serialize_enum_text(
&PlatformApiKeyStatus::Revoked,
"status",
)?)
.bind(revoked_at)
.bind(workspace_id.as_str())
.bind(key_id.as_str())
.execute(&self.pool)
.await?;
if result.rows_affected() == 0 {
return Err(RegistryError::PlatformApiKeyNotFound {
key_id: key_id.as_str().to_owned(),
});
}
Ok(())
}
pub async fn delete_platform_api_key(
&self,
workspace_id: &WorkspaceId,
key_id: &PlatformApiKeyId,
) -> Result<(), RegistryError> {
let result =
sqlx::query("delete from platform_api_keys where workspace_id = $1 and id = $2")
.bind(workspace_id.as_str())
.bind(key_id.as_str())
.execute(&self.pool)
.await?;
if result.rows_affected() == 0 {
return Err(RegistryError::PlatformApiKeyNotFound {
key_id: key_id.as_str().to_owned(),
});
}
Ok(())
}
pub async fn touch_platform_api_key(
&self,
workspace_id: &WorkspaceId,
key_id: &PlatformApiKeyId,
used_at: &str,
) -> Result<(), RegistryError> {
let result = sqlx::query(
"update platform_api_keys
set last_used_at = $3::timestamptz
where workspace_id = $1 and id = $2",
)
.bind(workspace_id.as_str())
.bind(key_id.as_str())
.bind(used_at)
.execute(&self.pool)
.await?;
if result.rows_affected() == 0 {
return Err(RegistryError::PlatformApiKeyNotFound {
key_id: key_id.as_str().to_owned(),
});
}
Ok(())
}
}