Check if user is a member before inserting a membership (#62)

It would typically fail on insertion due to uniqueness constraints: user id - room id.

Reviewed-on: lavina/lavina#62
Co-authored-by: Mikhail <mikhail@liamets.dev>
Co-committed-by: Mikhail <mikhail@liamets.dev>
This commit is contained in:
Mikhail 2024-05-08 22:10:32 +00:00 committed by Nikita Vilunov
parent 7f2c6a1013
commit 5512a74999
2 changed files with 28 additions and 1 deletions

View File

@ -1,8 +1,29 @@
use crate::player::PlayerConnection;
use anyhow::Result;
use crate::repo::Storage;
impl Storage {
#[tracing::instrument(skip(self), name = "Storage::is_room_member")]
pub async fn is_room_member(&self, room_id: u32, player_id: u32) -> Result<bool> {
let mut executor = self.conn.lock().await;
let res: (u32,) = sqlx::query_as(
"
select
count(*)
from
memberships
where
user_id = ? and room_id = ?;
",
)
.bind(player_id)
.bind(room_id)
.fetch_one(&mut *executor)
.await?;
Ok(res.0 > 0)
}
#[tracing::instrument(skip(self), name = "Storage::add_room_member")]
pub async fn add_room_member(&self, room_id: u32, player_id: u32) -> Result<()> {
let mut executor = self.conn.lock().await;

View File

@ -14,6 +14,7 @@ use crate::repo::Storage;
/// Opaque room id
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct RoomId(Str);
impl RoomId {
pub fn from(str: impl Into<Str>) -> Result<RoomId> {
let bytes = str.into();
@ -36,6 +37,7 @@ impl RoomId {
/// Shared data structure for storing metadata about rooms.
#[derive(Clone)]
pub struct RoomRegistry(Arc<AsyncRwLock<RoomRegistryInner>>);
impl RoomRegistry {
pub fn new(metrics: &mut MetricRegistry, storage: Storage) -> Result<RoomRegistry> {
let metric_active_rooms = IntGauge::new("chat_rooms_active", "Number of alive room actors")?;
@ -141,6 +143,7 @@ impl RoomRegistryInner {
#[derive(Clone)]
pub struct RoomHandle(Arc<AsyncRwLock<Room>>);
impl RoomHandle {
#[tracing::instrument(skip(self, player_handle), name = "RoomHandle::subscribe")]
pub async fn subscribe(&self, player_id: &PlayerId, player_handle: PlayerHandle) {
@ -154,7 +157,9 @@ impl RoomHandle {
let mut lock = self.0.write().await;
tracing::info!("Adding a new member to a room");
let room_storage_id = lock.storage_id;
lock.storage.add_room_member(room_storage_id, player_storage_id).await.unwrap();
if !lock.storage.is_room_member(room_storage_id, player_storage_id).await.unwrap() {
lock.storage.add_room_member(room_storage_id, player_storage_id).await.unwrap();
}
lock.members.insert(player_id.clone());
let update = Updates::RoomJoined {
room_id: lock.room_id.clone(),
@ -230,6 +235,7 @@ struct Room {
topic: Str,
storage: Storage,
}
impl Room {
#[tracing::instrument(skip(self, body, created_at), name = "Room::send_message")]
async fn send_message(&mut self, author_id: &PlayerId, body: Str, created_at: DateTime<Utc>) -> Result<()> {