Skip to content
Clone
b453d5d
232 lines 9.5 KB Rust
use models::space::space_members::{self, MemberStatus, MessageNotifications};
use models::space::spaces;
use sea_orm::sea_query::{Expr, ExprTrait};
use sea_orm::{ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, QueryFilter, Set};
use time::OffsetDateTime;
use uuid::Uuid;
use kenauth::proto;
fn timestamp(value: &Option<prost_types::Timestamp>) -> Option<OffsetDateTime> {
let value = value.as_ref()?;
OffsetDateTime::from_unix_timestamp(value.seconds)
.ok()?
.replace_nanosecond(value.nanos.clamp(0, 999_999_999) as u32)
.ok()
}
fn space_status(value: i32) -> proto::v1::OrganizationStatus {
proto::v1::OrganizationStatus::try_from(value).unwrap_or(proto::v1::OrganizationStatus::Active)
}
fn join_policy(value: i32) -> &'static str {
match proto::v1::OrganizationJoinPolicy::try_from(value)
.unwrap_or(proto::v1::OrganizationJoinPolicy::InviteOnly)
{
proto::v1::OrganizationJoinPolicy::Open => "open",
proto::v1::OrganizationJoinPolicy::Approval => "approval",
proto::v1::OrganizationJoinPolicy::Private => "private",
_ => "invite_only",
}
}
fn notifications(value: &str) -> MessageNotifications {
match value.trim() {
"mentions" => MessageNotifications::Mentions,
"nothing" => MessageNotifications::Nothing,
_ => MessageNotifications::All,
}
}
fn non_empty(value: &str) -> Option<String> {
let value = value.trim();
if value.is_empty() {
None
} else {
Some(value.to_string())
}
}
pub async fn upsert_space<C: ConnectionTrait>(
db: &C,
info: &proto::v1::OrganizationInfo,
) -> Result<spaces::Model, sea_orm::DbErr> {
let id = Uuid::parse_str(info.id.trim())
.map_err(|_| sea_orm::DbErr::Custom("organization id is not a uuid".into()))?;
let now = OffsetDateTime::now_utc();
let policy = join_policy(info.join_policy);
let active = space_status(info.status) == proto::v1::OrganizationStatus::Active;
let existing = spaces::Entity::find_by_id(id).one(db).await?;
let row = match existing {
Some(row) => {
let mut active_model: spaces::ActiveModel = row.into();
active_model.lower_name = Set(info.slug.clone());
active_model.name = Set(info.name.clone());
active_model.description = Set(non_empty(&info.description));
active_model.avatar_url = Set(non_empty(&info.avatar_url));
active_model.owner_id = Set(info.owner_user_id);
active_model.is_private = Set(policy == "private");
active_model.is_active = Set(active);
active_model.allow_join = Set(info.allow_join);
active_model.nums_members = Set(info.member_count as i64);
active_model.welcome_message = Set(non_empty(&info.welcome_message));
active_model.member_must_enable_2fa = Set(info.member_must_enable_2fa);
active_model.join_policy = Set(policy.to_string());
active_model.require_email_verified = Set(info.require_email_verified);
active_model.require_mfa = Set(info.require_mfa);
active_model.version = Set(info.version);
active_model.updated_at = Set(now);
active_model.update(db).await?
}
None => {
spaces::ActiveModel {
id: Set(id),
lower_name: Set(info.slug.clone()),
name: Set(info.name.clone()),
description: Set(non_empty(&info.description)),
owner_id: Set(info.owner_user_id),
avatar_url: Set(non_empty(&info.avatar_url)),
is_private: Set(policy == "private"),
is_active: Set(active),
allow_join: Set(info.allow_join),
allow_invite: Set(true),
invite_sort_url_hash: Set(None),
nums_members: Set(info.member_count as i64),
banner_url: Set(None),
banner_width: Set(None),
banner_height: Set(None),
invite_banner_url: Set(None),
invite_banner_width: Set(None),
invite_banner_height: Set(None),
embed_splash_url: Set(None),
embed_splash_width: Set(None),
embed_splash_height: Set(None),
welcome_message: Set(non_empty(&info.welcome_message)),
allow_join_level: Set(models::space::spaces::AllowJoinLevel::None),
member_must_enable_2fa: Set(info.member_must_enable_2fa),
welcome_object: Set(None),
join_policy: Set(policy.to_string()),
require_email_verified: Set(info.require_email_verified),
require_mfa: Set(info.require_mfa),
version: Set(info.version),
created_at: Set(timestamp(&info.created_at).unwrap_or(now)),
updated_at: Set(timestamp(&info.updated_at).unwrap_or(now)),
}
.insert(db)
.await?
}
};
Ok(row)
}
pub async fn remove_space<C: ConnectionTrait>(
db: &C,
organization_id: Uuid,
) -> Result<(), sea_orm::DbErr> {
spaces::Entity::delete_by_id(organization_id).exec(db).await?;
Ok(())
}
pub async fn upsert_member<C: ConnectionTrait>(
db: &C,
organization_id: Uuid,
member: &proto::v1::OrganizationMemberInfo,
) -> Result<space_members::Model, sea_orm::DbErr> {
let now = OffsetDateTime::now_utc();
let status = if member.banned_at.is_some() {
MemberStatus::Banned
} else if member.left_at.is_some() {
MemberStatus::Left
} else {
MemberStatus::Active
};
let invited_by = (member.invited_by > 0).then_some(member.invited_by);
let existing = space_members::Entity::find_by_id((organization_id, member.user_id))
.one(db)
.await?;
let row = match existing {
Some(row) => {
let mut active: space_members::ActiveModel = row.into();
active.status = Set(status);
active.alias_name = Set(non_empty(&member.alias_name));
active.alias_avatar_url = Set(non_empty(&member.alias_avatar_url));
active.message_notifications = Set(notifications(&member.message_notifications));
active.muted = Set(member.muted);
active.mute_until = Set(timestamp(&member.mute_until));
active.mobile_push = Set(member.mobile_push);
active.suppress_everyone = Set(member.suppress_everyone);
active.suppress_roles = Set(member.suppress_roles);
active.hide_muted_channels = Set(member.hide_muted_channels);
active.banned_at = Set(timestamp(&member.banned_at));
active.ban_reason = Set(non_empty(&member.ban_reason));
active.ban_expires_at = Set(timestamp(&member.ban_expires_at));
active.ban_allow_speak = Set(member.ban_allow_speak);
active.ban_allow_push_repo = Set(member.ban_allow_push_repo);
active.ban_allow_comment_any = Set(member.ban_allow_comment_any);
active.left_at = Set(timestamp(&member.left_at));
active.invited_by = Set(invited_by);
active.last_active_at = Set(timestamp(&member.last_active_at));
active.updated_at = Set(Some(now));
active.update(db).await?
}
None => {
space_members::ActiveModel {
space_id: Set(organization_id),
user_id: Set(member.user_id),
alias_name: Set(non_empty(&member.alias_name)),
alias_avatar_url: Set(non_empty(&member.alias_avatar_url)),
status: Set(status),
last_active_at: Set(timestamp(&member.last_active_at)),
left_at: Set(timestamp(&member.left_at)),
invited_at: Set(None),
invited_by: Set(invited_by),
joined_at: Set(timestamp(&member.joined_at).unwrap_or(now)),
created_at: Set(now),
updated_at: Set(Some(now)),
message_notifications: Set(notifications(&member.message_notifications)),
muted: Set(member.muted),
mute_until: Set(timestamp(&member.mute_until)),
mobile_push: Set(member.mobile_push),
suppress_everyone: Set(member.suppress_everyone),
suppress_roles: Set(member.suppress_roles),
hide_muted_channels: Set(member.hide_muted_channels),
banned_at: Set(timestamp(&member.banned_at)),
ban_reason: Set(non_empty(&member.ban_reason)),
ban_expires_at: Set(timestamp(&member.ban_expires_at)),
ban_allow_speak: Set(member.ban_allow_speak),
ban_allow_push_repo: Set(member.ban_allow_push_repo),
ban_allow_comment_any: Set(member.ban_allow_comment_any),
}
.insert(db)
.await?
}
};
Ok(row)
}
pub async fn remove_member<C: ConnectionTrait>(
db: &C,
organization_id: Uuid,
user_id: i64,
) -> Result<(), sea_orm::DbErr> {
space_members::Entity::delete_by_id((organization_id, user_id))
.exec(db)
.await?;
Ok(())
}
pub async fn bump_members<C: ConnectionTrait>(
db: &C,
organization_id: Uuid,
delta: i32,
) -> Result<(), sea_orm::DbErr> {
spaces::Entity::update_many()
.col_expr(
spaces::Column::NumsMembers,
Expr::col(spaces::Column::NumsMembers).add(delta),
)
.filter(spaces::Column::Id.eq(organization_id))
.exec(db)
.await?;
Ok(())
}