gitdataai/lib/channel/http/handler/invite.rs

144 lines
4.9 KiB
Rust

use chrono::Utc;
use uuid::Uuid;
use crate::event::{RoomInfo, UserInfo, WorkspaceInfo, invite};
use crate::{ChannelBus, ChannelError, ChannelResult};
use super::WsHandler;
use super::WsOutEvent;
impl WsHandler {
pub(super) async fn invite_create(
bus: &ChannelBus,
user_id: Uuid,
workspace: Uuid,
_room: Option<Uuid>,
_max_uses: Option<i32>,
_expires_at: Option<chrono::DateTime<chrono::Utc>>,
) -> ChannelResult<Option<WsOutEvent>> {
Self::ensure_workspace_member(bus, user_id, workspace).await?;
let invite_id = Uuid::now_v7();
let code = Uuid::now_v7().to_string();
let id_key = format!("invite:id:{}", invite_id);
let code_key = format!("invite:code:{}", code);
let meta = serde_json::json!({
"workspace": workspace,
"created_by": user_id,
"room": _room,
"max_uses": _max_uses,
"expires_at": _expires_at,
});
bus.inner.cache.set(&id_key, &meta.to_string()).await?;
bus.inner
.cache
.set(&code_key, &invite_id.to_string())
.await?;
let inv_room = match _room {
Some(r) => Some(
bus.lookup_room(r)
.await
.unwrap_or_else(|_| RoomInfo::unknown(r)),
),
None => None,
};
let data = invite::InviteCreatedService {
id: invite_id,
workspace: bus
.lookup_workspace(workspace)
.await
.unwrap_or_else(|_| WorkspaceInfo::unknown(workspace)),
room: inv_room,
inviter: bus
.lookup_user(user_id)
.await
.unwrap_or_else(|_| UserInfo::unknown(user_id)),
invitee: None,
code,
max_uses: _max_uses,
expires_at: _expires_at,
created_at: Utc::now(),
};
Ok(Some(WsOutEvent::InviteCreated { data }))
}
pub(super) async fn invite_accept(
bus: &ChannelBus,
user_id: Uuid,
code: String,
) -> ChannelResult<Option<WsOutEvent>> {
let code_key = format!("invite:code:{}", code);
let invite_id_str: Option<String> =
bus.inner.cache.get(&code_key).await?;
let invite_id = invite_id_str
.as_deref()
.and_then(|s| Uuid::parse_str(s).ok())
.ok_or(ChannelError::RoomNotFound)?;
let id_key = format!("invite:id:{}", invite_id);
let stored: Option<String> = bus.inner.cache.get(&id_key).await?;
let meta: serde_json::Value = stored
.as_deref()
.and_then(|s| serde_json::from_str(s).ok())
.ok_or(ChannelError::RoomNotFound)?;
let wk = meta["workspace"]
.as_str()
.and_then(|s| Uuid::parse_str(s).ok())
.ok_or(ChannelError::RoomNotFound)?;
db::sqlx::query(
"INSERT INTO wk_member (wk, \"user\", owner, admin, join_at) \
VALUES ($1, $2, false, false, now()) \
ON CONFLICT DO NOTHING",
)
.bind(wk)
.bind(user_id)
.execute(bus.inner.db.writer())
.await?;
db::sqlx::query(
"INSERT INTO wk_apply_join (wk, \"user\", status, created_at, updated_at) \
VALUES ($1, $2, 'accepted', now(), now())",
)
.bind(wk)
.bind(user_id)
.execute(bus.inner.db.writer())
.await?;
bus.inner.cache.remove(&code_key).await?;
bus.inner.cache.remove(&id_key).await?;
let data = invite::InviteAcceptedService {
id: Uuid::now_v7(),
workspace: bus
.lookup_workspace(wk)
.await
.unwrap_or_else(|_| WorkspaceInfo::unknown(wk)),
room: None,
user: bus
.lookup_user(user_id)
.await
.unwrap_or_else(|_| UserInfo::unknown(user_id)),
accepted_at: Utc::now(),
};
bus.workspace_changed(wk).await?;
Ok(Some(WsOutEvent::InviteAccepted { data }))
}
pub(super) async fn invite_revoke(
bus: &ChannelBus,
_user_id: Uuid,
id: Uuid,
) -> ChannelResult<Option<WsOutEvent>> {
let id_key = format!("invite:id:{}", id);
let stored: Option<String> = bus.inner.cache.get(&id_key).await?;
let meta: serde_json::Value = stored
.as_deref()
.and_then(|s| serde_json::from_str(s).ok())
.ok_or(ChannelError::RoomNotFound)?;
let created_by = meta["created_by"]
.as_str()
.and_then(|s| Uuid::parse_str(s).ok())
.ok_or(ChannelError::RoomNotFound)?;
if created_by != _user_id {
return Err(ChannelError::AccessDenied);
}
bus.inner.cache.remove(&id_key).await?;
Ok(None)
}
}