144 lines
4.9 KiB
Rust
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)
|
|
}
|
|
}
|