diff --git a/src/api/v1/channels/uuid/messages.rs b/src/api/v1/channels/uuid/messages.rs index 90055c8..ea5377e 100644 --- a/src/api/v1/channels/uuid/messages.rs +++ b/src/api/v1/channels/uuid/messages.rs @@ -64,7 +64,7 @@ pub async fn get( let channel = Channel::fetch_one(&data, channel_uuid).await?; - Member::fetch_one(&mut conn, uuid, channel.guild_uuid).await?; + Member::check_membership(&mut conn, uuid, channel.guild_uuid).await?; let messages = channel .fetch_messages(&data, message_request.amount, message_request.offset) diff --git a/src/api/v1/channels/uuid/mod.rs b/src/api/v1/channels/uuid/mod.rs index f6c93fe..4e81142 100644 --- a/src/api/v1/channels/uuid/mod.rs +++ b/src/api/v1/channels/uuid/mod.rs @@ -27,7 +27,7 @@ pub async fn get( let channel = Channel::fetch_one(&data, channel_uuid).await?; - Member::fetch_one(&mut conn, uuid, channel.guild_uuid).await?; + Member::check_membership(&mut conn, uuid, channel.guild_uuid).await?; Ok(HttpResponse::Ok().json(channel)) } @@ -52,7 +52,7 @@ pub async fn delete( let channel = Channel::fetch_one(&data, channel_uuid).await?; - Member::fetch_one(&mut conn, uuid, channel.guild_uuid).await?; + Member::check_membership(&mut conn, uuid, channel.guild_uuid).await?; channel.delete(&data).await?; diff --git a/src/api/v1/channels/uuid/socket.rs b/src/api/v1/channels/uuid/socket.rs index c7ca1e8..34e0fdc 100644 --- a/src/api/v1/channels/uuid/socket.rs +++ b/src/api/v1/channels/uuid/socket.rs @@ -36,8 +36,7 @@ pub async fn ws( let channel = Channel::fetch_one(&data, channel_uuid).await?; - // Get server member from psql - Member::fetch_one(&mut conn, uuid, channel.guild_uuid).await?; + Member::check_membership(&mut conn, uuid, channel.guild_uuid).await?; let (mut res, mut session_1, stream) = actix_ws::handle(&req, stream)?; diff --git a/src/api/v1/guilds/uuid/channels.rs b/src/api/v1/guilds/uuid/channels.rs index 813de13..2d01e2b 100644 --- a/src/api/v1/guilds/uuid/channels.rs +++ b/src/api/v1/guilds/uuid/channels.rs @@ -29,7 +29,7 @@ pub async fn get( global_checks(&data, uuid).await?; - Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + Member::check_membership(&mut conn, uuid, guild_uuid).await?; if let Ok(cache_hit) = data.get_cache_key(format!("{}_channels", guild_uuid)).await { return Ok(HttpResponse::Ok() @@ -70,7 +70,7 @@ pub async fn create( global_checks(&data, uuid).await?; - Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + Member::check_membership(&mut conn, uuid, guild_uuid).await?; // FIXME: Logic to check permissions, should probably be done in utils.rs diff --git a/src/api/v1/guilds/uuid/icon.rs b/src/api/v1/guilds/uuid/icon.rs index 0ac4470..ae71321 100644 --- a/src/api/v1/guilds/uuid/icon.rs +++ b/src/api/v1/guilds/uuid/icon.rs @@ -32,7 +32,7 @@ pub async fn upload( global_checks(&data, uuid).await?; - Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + Member::check_membership(&mut conn, uuid, guild_uuid).await?; let mut guild = Guild::fetch_one(&mut conn, guild_uuid).await?; diff --git a/src/api/v1/guilds/uuid/invites/mod.rs b/src/api/v1/guilds/uuid/invites/mod.rs index 83ea08d..e985625 100644 --- a/src/api/v1/guilds/uuid/invites/mod.rs +++ b/src/api/v1/guilds/uuid/invites/mod.rs @@ -29,7 +29,7 @@ pub async fn get( global_checks(&data, uuid).await?; - Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + Member::check_membership(&mut conn, uuid, guild_uuid).await?; let guild = Guild::fetch_one(&mut conn, guild_uuid).await?; @@ -57,13 +57,13 @@ pub async fn create( global_checks(&data, uuid).await?; - let member = Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + Member::check_membership(&mut conn, uuid, guild_uuid).await?; let guild = Guild::fetch_one(&mut conn, guild_uuid).await?; let custom_id = invite_request.as_ref().map(|ir| ir.custom_id.clone()); - let invite = guild.create_invite(&mut conn, &member, custom_id).await?; + let invite = guild.create_invite(&mut conn, uuid, custom_id).await?; Ok(HttpResponse::Ok().json(invite)) } diff --git a/src/api/v1/guilds/uuid/members.rs b/src/api/v1/guilds/uuid/members.rs new file mode 100644 index 0000000..2cc416a --- /dev/null +++ b/src/api/v1/guilds/uuid/members.rs @@ -0,0 +1,30 @@ +use crate::{ + api::v1::auth::check_access_token, error::Error, structs::Member, utils::{get_auth_header, global_checks}, Data +}; +use ::uuid::Uuid; +use actix_web::{HttpRequest, HttpResponse, get, web}; + +#[get("{uuid}/members")] +pub async fn get( + req: HttpRequest, + path: web::Path<(Uuid,)>, + data: web::Data, +) -> Result { + let headers = req.headers(); + + let auth_header = get_auth_header(headers)?; + + let guild_uuid = path.into_inner().0; + + let mut conn = data.pool.get().await?; + + let uuid = check_access_token(auth_header, &mut conn).await?; + + global_checks(&data, uuid).await?; + + Member::check_membership(&mut conn, uuid, guild_uuid).await?; + + let members = Member::fetch_all(&data, guild_uuid).await?; + + Ok(HttpResponse::Ok().json(members)) +} diff --git a/src/api/v1/guilds/uuid/mod.rs b/src/api/v1/guilds/uuid/mod.rs index 7ab719d..69c3f31 100644 --- a/src/api/v1/guilds/uuid/mod.rs +++ b/src/api/v1/guilds/uuid/mod.rs @@ -7,6 +7,7 @@ mod channels; mod icon; mod invites; mod roles; +mod members; use crate::{ api::v1::auth::check_access_token, error::Error, structs::{Guild, Member}, utils::{get_auth_header, global_checks}, Data @@ -28,6 +29,8 @@ pub fn web() -> Scope { .service(invites::create) // Icon .service(icon::upload) + // Members + .service(members::get) } /// `GET /api/v1/guilds/{uuid}` DESCRIPTION @@ -81,7 +84,7 @@ pub async fn get( global_checks(&data, uuid).await?; - Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + Member::check_membership(&mut conn, uuid, guild_uuid).await?; let guild = Guild::fetch_one(&mut conn, guild_uuid).await?; diff --git a/src/api/v1/guilds/uuid/roles/mod.rs b/src/api/v1/guilds/uuid/roles/mod.rs index 3ae9c5b..9eb6349 100644 --- a/src/api/v1/guilds/uuid/roles/mod.rs +++ b/src/api/v1/guilds/uuid/roles/mod.rs @@ -29,7 +29,7 @@ pub async fn get( let uuid = check_access_token(auth_header, &mut conn).await?; - Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + Member::check_membership(&mut conn, uuid, guild_uuid).await?; if let Ok(cache_hit) = data.get_cache_key(format!("{}_roles", guild_uuid)).await { return Ok(HttpResponse::Ok() @@ -66,7 +66,7 @@ pub async fn create( global_checks(&data, uuid).await?; - Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + Member::check_membership(&mut conn, uuid, guild_uuid).await?; // FIXME: Logic to check permissions, should probably be done in utils.rs diff --git a/src/api/v1/guilds/uuid/roles/uuid.rs b/src/api/v1/guilds/uuid/roles/uuid.rs index 21ab748..9e7853f 100644 --- a/src/api/v1/guilds/uuid/roles/uuid.rs +++ b/src/api/v1/guilds/uuid/roles/uuid.rs @@ -22,7 +22,7 @@ pub async fn get( global_checks(&data, uuid).await?; - Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + Member::check_membership(&mut conn, uuid, guild_uuid).await?; if let Ok(cache_hit) = data.get_cache_key(format!("{}", role_uuid)).await { return Ok(HttpResponse::Ok() diff --git a/src/api/v1/invites/id.rs b/src/api/v1/invites/id.rs index f7e4a08..6ddc53f 100644 --- a/src/api/v1/invites/id.rs +++ b/src/api/v1/invites/id.rs @@ -42,7 +42,7 @@ pub async fn join( let guild = Guild::fetch_one(&mut conn, invite.guild_uuid).await?; - Member::new(&mut conn, uuid, guild.uuid).await?; + Member::new(&data, uuid, guild.uuid).await?; Ok(HttpResponse::Ok().json(guild)) } diff --git a/src/api/v1/me/guilds.rs b/src/api/v1/me/guilds.rs index 1e92a34..516fc35 100644 --- a/src/api/v1/me/guilds.rs +++ b/src/api/v1/me/guilds.rs @@ -41,7 +41,7 @@ pub async fn get(req: HttpRequest, data: web::Data) -> Result, ) -> Result { let invite_id; @@ -530,7 +530,7 @@ impl Guild { let invite = Invite { id: invite_id, - user_uuid: member.user_uuid, + user_uuid, guild_uuid: self.uuid, }; @@ -666,11 +666,34 @@ impl Role { #[derive(Serialize, Queryable, Selectable, Insertable)] #[diesel(table_name = guild_members)] #[diesel(check_for_backend(diesel::pg::Pg))] +pub struct MemberBuilder { + pub uuid: Uuid, + pub nickname: Option, + pub user_uuid: Uuid, + pub guild_uuid: Uuid, +} + +impl MemberBuilder { + async fn build(&self, data: &Data) -> Result { + let user = User::fetch_one(data, self.user_uuid).await?; + + Ok(Member { + uuid: self.uuid, + nickname: self.nickname.clone(), + user_uuid: self.user_uuid, + guild_uuid: self.guild_uuid, + user, + }) + } +} + +#[derive(Serialize, Deserialize)] pub struct Member { pub uuid: Uuid, pub nickname: Option, pub user_uuid: Uuid, pub guild_uuid: Uuid, + user: User, } impl Member { @@ -685,26 +708,61 @@ impl Member { Ok(count) } - pub async fn fetch_one( - conn: &mut Conn, - user_uuid: Uuid, - guild_uuid: Uuid, - ) -> Result { + pub async fn check_membership(conn: &mut Conn, user_uuid: Uuid, guild_uuid: Uuid) -> Result<(), Error> { use guild_members::dsl; - let member: Member = dsl::guild_members + dsl::guild_members .filter(dsl::user_uuid.eq(user_uuid)) .filter(dsl::guild_uuid.eq(guild_uuid)) - .select(Member::as_select()) + .select(MemberBuilder::as_select()) .get_result(conn) .await?; - Ok(member) + Ok(()) } - pub async fn new(conn: &mut Conn, user_uuid: Uuid, guild_uuid: Uuid) -> Result { + pub async fn fetch_one( + data: &Data, + user_uuid: Uuid, + guild_uuid: Uuid, + ) -> Result { + let mut conn = data.pool.get().await?; + + use guild_members::dsl; + let member: MemberBuilder = dsl::guild_members + .filter(dsl::user_uuid.eq(user_uuid)) + .filter(dsl::guild_uuid.eq(guild_uuid)) + .select(MemberBuilder::as_select()) + .get_result(&mut conn) + .await?; + + member.build(data).await + } + + pub async fn fetch_all(data: &Data, guild_uuid: Uuid) -> Result, Error> { + let mut conn = data.pool.get().await?; + + use guild_members::dsl; + let member_builders: Vec = load_or_empty( + dsl::guild_members + .filter(dsl::guild_uuid.eq(guild_uuid)) + .select(MemberBuilder::as_select()) + .load(&mut conn) + .await + )?; + + let member_futures = member_builders.iter().map(async move |m| { + m.build(data).await + }); + + futures::future::try_join_all(member_futures).await + } + + pub async fn new(data: &Data, user_uuid: Uuid, guild_uuid: Uuid) -> Result { + let mut conn = data.pool.get().await?; + let member_uuid = Uuid::now_v7(); - let member = Member { + let member = MemberBuilder { uuid: member_uuid, guild_uuid, user_uuid, @@ -712,16 +770,11 @@ impl Member { }; insert_into(guild_members::table) - .values(member) - .execute(conn) + .values(&member) + .execute(&mut conn) .await?; - Ok(Self { - uuid: member_uuid, - nickname: None, - user_uuid, - guild_uuid, - }) + member.build(data).await } } @@ -856,14 +909,28 @@ impl Me { Ok(me) } - pub async fn fetch_memberships(&self, conn: &mut Conn) -> Result, Error> { + pub async fn fetch_memberships(&self, data: &Data) -> Result, Error> { + let mut conn = data.pool.get().await?; + use guild_members::dsl; - let memberships: Vec = dsl::guild_members + let member_builders: Vec = dsl::guild_members .filter(dsl::user_uuid.eq(self.uuid)) - .select(Member::as_select()) - .load(conn) + .select(MemberBuilder::as_select()) + .load(&mut conn) .await?; + let user = User::fetch_one(data, self.uuid).await?; + + let memberships = member_builders.iter().map(|m| { + Member { + uuid: m.uuid, + nickname: m.nickname.clone(), + user_uuid: m.user_uuid, + guild_uuid: m.guild_uuid, + user: user.clone(), + } + }).collect(); + Ok(memberships) }