registry: extract storage domain modules

This commit is contained in:
a.tolmachev
2026-04-12 11:54:18 +03:00
parent 825d3cb138
commit 77c449f455
5 changed files with 1500 additions and 1483 deletions
@@ -0,0 +1,319 @@
use super::*;
impl PostgresRegistry {
pub async fn list_workspaces(&self) -> Result<Vec<WorkspaceRecord>, RegistryError> {
let rows = sqlx::query(
"select
id,
slug,
display_name,
status,
settings_json,
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
from workspaces
order by slug asc",
)
.fetch_all(&self.pool)
.await?;
rows.iter().map(map_workspace_record).collect()
}
pub async fn list_workspaces_for_user(
&self,
user_id: &UserId,
) -> Result<Vec<WorkspaceMembershipRecord>, RegistryError> {
let rows = sqlx::query(
"select
w.id,
w.slug,
w.display_name,
w.status,
w.settings_json,
to_char(w.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
to_char(w.updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as updated_at,
m.role
from memberships m
join workspaces w on w.id = m.workspace_id
where m.user_id = $1
order by w.slug asc",
)
.bind(user_id.as_str())
.fetch_all(&self.pool)
.await?;
rows.iter().map(map_workspace_membership_record).collect()
}
pub async fn list_memberships(
&self,
workspace_id: &WorkspaceId,
) -> Result<Vec<MembershipRecord>, RegistryError> {
let rows = sqlx::query(
"select
m.workspace_id,
m.user_id,
m.role,
to_char(m.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
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 user_created_at
from memberships m
join users u on u.id = m.user_id
where m.workspace_id = $1
order by u.email asc",
)
.bind(workspace_id.as_str())
.fetch_all(&self.pool)
.await?;
rows.iter().map(map_membership_record).collect()
}
pub async fn update_membership_role(
&self,
workspace_id: &WorkspaceId,
user_id: &UserId,
role: MembershipRole,
) -> Result<(), RegistryError> {
let result = sqlx::query(
"update memberships
set role = $3
where workspace_id = $1 and user_id = $2",
)
.bind(workspace_id.as_str())
.bind(user_id.as_str())
.bind(serialize_enum_text(&role, "role")?)
.execute(&self.pool)
.await?;
if result.rows_affected() == 0 {
return Err(RegistryError::MembershipNotFound {
workspace_id: workspace_id.as_str().to_owned(),
user_id: user_id.as_str().to_owned(),
});
}
Ok(())
}
pub async fn delete_membership(
&self,
workspace_id: &WorkspaceId,
user_id: &UserId,
) -> Result<(), RegistryError> {
let result = sqlx::query(
"delete from memberships
where workspace_id = $1 and user_id = $2",
)
.bind(workspace_id.as_str())
.bind(user_id.as_str())
.execute(&self.pool)
.await?;
if result.rows_affected() == 0 {
return Err(RegistryError::MembershipNotFound {
workspace_id: workspace_id.as_str().to_owned(),
user_id: user_id.as_str().to_owned(),
});
}
Ok(())
}
pub async fn list_invitations(
&self,
workspace_id: &WorkspaceId,
) -> Result<Vec<InvitationRecord>, RegistryError> {
let rows = sqlx::query(
"select
id,
workspace_id,
email,
role,
status,
token_hash,
to_char(expires_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as expires_at,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at
from invitation_tokens
where workspace_id = $1
order by created_at desc",
)
.bind(workspace_id.as_str())
.fetch_all(&self.pool)
.await?;
rows.iter().map(map_invitation_record).collect()
}
pub async fn create_invitation(
&self,
request: CreateInvitationRequest<'_>,
) -> Result<(), RegistryError> {
sqlx::query(
"insert into invitation_tokens (
id,
workspace_id,
email,
role,
status,
token_hash,
expires_at,
created_at
) values (
$1, $2, $3, $4, $5, $6, $7::timestamptz, $8::timestamptz
)",
)
.bind(request.invitation.id.as_str())
.bind(request.invitation.workspace_id.as_str())
.bind(&request.invitation.email)
.bind(serialize_enum_text(&request.invitation.role, "role")?)
.bind(serialize_enum_text(&request.invitation.status, "status")?)
.bind(&request.invitation.token_hash)
.bind(&request.invitation.expires_at)
.bind(&request.invitation.created_at)
.execute(&self.pool)
.await?;
Ok(())
}
pub async fn delete_invitation(
&self,
workspace_id: &WorkspaceId,
invitation_id: &InvitationId,
) -> Result<(), RegistryError> {
let result =
sqlx::query("delete from invitation_tokens where workspace_id = $1 and id = $2")
.bind(workspace_id.as_str())
.bind(invitation_id.as_str())
.execute(&self.pool)
.await?;
if result.rows_affected() == 0 {
return Err(RegistryError::InvitationNotFound {
invitation_id: invitation_id.as_str().to_owned(),
});
}
Ok(())
}
pub async fn delete_workspace(&self, workspace_id: &WorkspaceId) -> Result<(), RegistryError> {
let result = sqlx::query("delete from workspaces where id = $1")
.bind(workspace_id.as_str())
.execute(&self.pool)
.await?;
if result.rows_affected() == 0 {
return Err(RegistryError::WorkspaceNotFound {
workspace_id: workspace_id.as_str().to_owned(),
});
}
Ok(())
}
pub async fn get_workspace(
&self,
workspace_id: &WorkspaceId,
) -> Result<Option<WorkspaceRecord>, RegistryError> {
let row = sqlx::query(
"select
id,
slug,
display_name,
status,
settings_json,
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
from workspaces
where id = $1",
)
.bind(workspace_id.as_str())
.fetch_optional(&self.pool)
.await?;
row.as_ref().map(map_workspace_record).transpose()
}
pub async fn create_workspace(
&self,
request: CreateWorkspaceRequest<'_>,
) -> Result<(), RegistryError> {
let result = sqlx::query(
"insert into workspaces (
id,
slug,
display_name,
status,
settings_json,
created_at,
updated_at
) values (
$1, $2, $3, $4, $5, $6::timestamptz, $7::timestamptz
)",
)
.bind(request.workspace.id.as_str())
.bind(&request.workspace.slug)
.bind(&request.workspace.display_name)
.bind(serialize_enum_text(&request.workspace.status, "status")?)
.bind(Json(request.workspace.settings.clone()))
.bind(&request.workspace.created_at)
.bind(&request.workspace.updated_at)
.execute(&self.pool)
.await;
match result {
Ok(_) => Ok(()),
Err(sqlx::Error::Database(error))
if error.constraint() == Some("workspaces_slug_key") =>
{
Err(RegistryError::WorkspaceSlugAlreadyExists {
slug: request.workspace.slug.clone(),
})
}
Err(error) => Err(RegistryError::Storage(error)),
}
}
pub async fn update_workspace(
&self,
request: UpdateWorkspaceRequest<'_>,
) -> Result<(), RegistryError> {
let result = sqlx::query(
"update workspaces
set slug = $1,
display_name = $2,
status = $3,
settings_json = $4,
updated_at = $5::timestamptz
where id = $6",
)
.bind(&request.workspace.slug)
.bind(&request.workspace.display_name)
.bind(serialize_enum_text(&request.workspace.status, "status")?)
.bind(Json(request.workspace.settings.clone()))
.bind(&request.workspace.updated_at)
.bind(request.workspace.id.as_str())
.execute(&self.pool)
.await;
match result {
Ok(result) if result.rows_affected() == 0 => Err(RegistryError::WorkspaceNotFound {
workspace_id: request.workspace.id.as_str().to_owned(),
}),
Ok(_) => Ok(()),
Err(sqlx::Error::Database(error))
if error.constraint() == Some("workspaces_slug_key") =>
{
Err(RegistryError::WorkspaceSlugAlreadyExists {
slug: request.workspace.slug.clone(),
})
}
Err(error) => Err(RegistryError::Storage(error)),
}
}
}