feat: add /guilds/{uuid}members

Also makes it return user object with the query
This commit is contained in:
Radical 2025-05-30 21:12:07 +02:00
parent 746285e0fb
commit c9a3e8c6c4
13 changed files with 141 additions and 42 deletions

View file

@ -64,7 +64,7 @@ pub async fn get(
let channel = Channel::fetch_one(&data, channel_uuid).await?; 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 let messages = channel
.fetch_messages(&data, message_request.amount, message_request.offset) .fetch_messages(&data, message_request.amount, message_request.offset)

View file

@ -27,7 +27,7 @@ pub async fn get(
let channel = Channel::fetch_one(&data, channel_uuid).await?; 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)) Ok(HttpResponse::Ok().json(channel))
} }
@ -52,7 +52,7 @@ pub async fn delete(
let channel = Channel::fetch_one(&data, channel_uuid).await?; 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?; channel.delete(&data).await?;

View file

@ -36,8 +36,7 @@ pub async fn ws(
let channel = Channel::fetch_one(&data, channel_uuid).await?; let channel = Channel::fetch_one(&data, channel_uuid).await?;
// Get server member from psql Member::check_membership(&mut conn, uuid, channel.guild_uuid).await?;
Member::fetch_one(&mut conn, uuid, channel.guild_uuid).await?;
let (mut res, mut session_1, stream) = actix_ws::handle(&req, stream)?; let (mut res, mut session_1, stream) = actix_ws::handle(&req, stream)?;

View file

@ -29,7 +29,7 @@ pub async fn get(
global_checks(&data, uuid).await?; 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 { if let Ok(cache_hit) = data.get_cache_key(format!("{}_channels", guild_uuid)).await {
return Ok(HttpResponse::Ok() return Ok(HttpResponse::Ok()
@ -70,7 +70,7 @@ pub async fn create(
global_checks(&data, uuid).await?; 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 // FIXME: Logic to check permissions, should probably be done in utils.rs

View file

@ -32,7 +32,7 @@ pub async fn upload(
global_checks(&data, uuid).await?; 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?; let mut guild = Guild::fetch_one(&mut conn, guild_uuid).await?;

View file

@ -29,7 +29,7 @@ pub async fn get(
global_checks(&data, uuid).await?; 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?; let guild = Guild::fetch_one(&mut conn, guild_uuid).await?;
@ -57,13 +57,13 @@ pub async fn create(
global_checks(&data, uuid).await?; 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 guild = Guild::fetch_one(&mut conn, guild_uuid).await?;
let custom_id = invite_request.as_ref().map(|ir| ir.custom_id.clone()); 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)) Ok(HttpResponse::Ok().json(invite))
} }

View file

@ -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<Data>,
) -> Result<HttpResponse, Error> {
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))
}

View file

@ -7,6 +7,7 @@ mod channels;
mod icon; mod icon;
mod invites; mod invites;
mod roles; mod roles;
mod members;
use crate::{ use crate::{
api::v1::auth::check_access_token, error::Error, structs::{Guild, Member}, utils::{get_auth_header, global_checks}, Data 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) .service(invites::create)
// Icon // Icon
.service(icon::upload) .service(icon::upload)
// Members
.service(members::get)
} }
/// `GET /api/v1/guilds/{uuid}` DESCRIPTION /// `GET /api/v1/guilds/{uuid}` DESCRIPTION
@ -81,7 +84,7 @@ pub async fn get(
global_checks(&data, uuid).await?; 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?; let guild = Guild::fetch_one(&mut conn, guild_uuid).await?;

View file

@ -29,7 +29,7 @@ pub async fn get(
let uuid = check_access_token(auth_header, &mut conn).await?; 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 { if let Ok(cache_hit) = data.get_cache_key(format!("{}_roles", guild_uuid)).await {
return Ok(HttpResponse::Ok() return Ok(HttpResponse::Ok()
@ -66,7 +66,7 @@ pub async fn create(
global_checks(&data, uuid).await?; 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 // FIXME: Logic to check permissions, should probably be done in utils.rs

View file

@ -22,7 +22,7 @@ pub async fn get(
global_checks(&data, uuid).await?; 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 { if let Ok(cache_hit) = data.get_cache_key(format!("{}", role_uuid)).await {
return Ok(HttpResponse::Ok() return Ok(HttpResponse::Ok()

View file

@ -42,7 +42,7 @@ pub async fn join(
let guild = Guild::fetch_one(&mut conn, invite.guild_uuid).await?; 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)) Ok(HttpResponse::Ok().json(guild))
} }

View file

@ -41,7 +41,7 @@ pub async fn get(req: HttpRequest, data: web::Data<Data>) -> Result<HttpResponse
let me = Me::get(&mut conn, uuid).await?; let me = Me::get(&mut conn, uuid).await?;
let memberships = me.fetch_memberships(&mut conn).await?; let memberships = me.fetch_memberships(&data).await?;
Ok(HttpResponse::Ok().json(memberships)) Ok(HttpResponse::Ok().json(memberships))
} }

View file

@ -473,7 +473,7 @@ impl Guild {
let member_uuid = Uuid::now_v7(); let member_uuid = Uuid::now_v7();
let member = Member { let member = MemberBuilder {
uuid: member_uuid, uuid: member_uuid,
nickname: None, nickname: None,
user_uuid: owner_uuid, user_uuid: owner_uuid,
@ -512,7 +512,7 @@ impl Guild {
pub async fn create_invite( pub async fn create_invite(
&self, &self,
conn: &mut Conn, conn: &mut Conn,
member: &Member, user_uuid: Uuid,
custom_id: Option<String>, custom_id: Option<String>,
) -> Result<Invite, Error> { ) -> Result<Invite, Error> {
let invite_id; let invite_id;
@ -530,7 +530,7 @@ impl Guild {
let invite = Invite { let invite = Invite {
id: invite_id, id: invite_id,
user_uuid: member.user_uuid, user_uuid,
guild_uuid: self.uuid, guild_uuid: self.uuid,
}; };
@ -666,11 +666,34 @@ impl Role {
#[derive(Serialize, Queryable, Selectable, Insertable)] #[derive(Serialize, Queryable, Selectable, Insertable)]
#[diesel(table_name = guild_members)] #[diesel(table_name = guild_members)]
#[diesel(check_for_backend(diesel::pg::Pg))] #[diesel(check_for_backend(diesel::pg::Pg))]
pub struct MemberBuilder {
pub uuid: Uuid,
pub nickname: Option<String>,
pub user_uuid: Uuid,
pub guild_uuid: Uuid,
}
impl MemberBuilder {
async fn build(&self, data: &Data) -> Result<Member, Error> {
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 struct Member {
pub uuid: Uuid, pub uuid: Uuid,
pub nickname: Option<String>, pub nickname: Option<String>,
pub user_uuid: Uuid, pub user_uuid: Uuid,
pub guild_uuid: Uuid, pub guild_uuid: Uuid,
user: User,
} }
impl Member { impl Member {
@ -685,26 +708,61 @@ impl Member {
Ok(count) Ok(count)
} }
pub async fn fetch_one( pub async fn check_membership(conn: &mut Conn, user_uuid: Uuid, guild_uuid: Uuid) -> Result<(), Error> {
conn: &mut Conn,
user_uuid: Uuid,
guild_uuid: Uuid,
) -> Result<Self, Error> {
use guild_members::dsl; use guild_members::dsl;
let member: Member = dsl::guild_members dsl::guild_members
.filter(dsl::user_uuid.eq(user_uuid)) .filter(dsl::user_uuid.eq(user_uuid))
.filter(dsl::guild_uuid.eq(guild_uuid)) .filter(dsl::guild_uuid.eq(guild_uuid))
.select(Member::as_select()) .select(MemberBuilder::as_select())
.get_result(conn) .get_result(conn)
.await?; .await?;
Ok(member) Ok(())
} }
pub async fn new(conn: &mut Conn, user_uuid: Uuid, guild_uuid: Uuid) -> Result<Self, Error> { pub async fn fetch_one(
data: &Data,
user_uuid: Uuid,
guild_uuid: Uuid,
) -> Result<Self, Error> {
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<Vec<Self>, Error> {
let mut conn = data.pool.get().await?;
use guild_members::dsl;
let member_builders: Vec<MemberBuilder> = 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<Self, Error> {
let mut conn = data.pool.get().await?;
let member_uuid = Uuid::now_v7(); let member_uuid = Uuid::now_v7();
let member = Member { let member = MemberBuilder {
uuid: member_uuid, uuid: member_uuid,
guild_uuid, guild_uuid,
user_uuid, user_uuid,
@ -712,16 +770,11 @@ impl Member {
}; };
insert_into(guild_members::table) insert_into(guild_members::table)
.values(member) .values(&member)
.execute(conn) .execute(&mut conn)
.await?; .await?;
Ok(Self { member.build(data).await
uuid: member_uuid,
nickname: None,
user_uuid,
guild_uuid,
})
} }
} }
@ -856,14 +909,28 @@ impl Me {
Ok(me) Ok(me)
} }
pub async fn fetch_memberships(&self, conn: &mut Conn) -> Result<Vec<Member>, Error> { pub async fn fetch_memberships(&self, data: &Data) -> Result<Vec<Member>, Error> {
let mut conn = data.pool.get().await?;
use guild_members::dsl; use guild_members::dsl;
let memberships: Vec<Member> = dsl::guild_members let member_builders: Vec<MemberBuilder> = dsl::guild_members
.filter(dsl::user_uuid.eq(self.uuid)) .filter(dsl::user_uuid.eq(self.uuid))
.select(Member::as_select()) .select(MemberBuilder::as_select())
.load(conn) .load(&mut conn)
.await?; .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) Ok(memberships)
} }