backend/src/objects/member.rs
2025-07-25 00:21:20 +02:00

224 lines
6 KiB
Rust

use diesel::{
ExpressionMethods, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper, insert_into, delete,
};
use diesel_async::RunQueryDsl;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
AppState, Conn,
error::Error,
objects::{GuildBan, Me, Permissions, Role},
schema::{guild_bans, guild_members},
};
use super::{User, load_or_empty};
#[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<String>,
pub user_uuid: Uuid,
pub guild_uuid: Uuid,
pub is_owner: bool,
}
impl MemberBuilder {
pub async fn build(&self, app_state: &AppState, me: Option<&Me>) -> Result<Member, Error> {
let user;
if let Some(me) = me {
user = User::fetch_one_with_friendship(app_state, me, self.user_uuid).await?;
} else {
user = User::fetch_one(app_state, self.user_uuid).await?;
}
Ok(Member {
uuid: self.uuid,
nickname: self.nickname.clone(),
user_uuid: self.user_uuid,
guild_uuid: self.guild_uuid,
is_owner: self.is_owner,
user,
})
}
pub async fn check_permission(
&self,
app_state: &AppState,
permission: Permissions,
) -> Result<(), Error> {
if !self.is_owner {
let roles = Role::fetch_from_member(app_state, self.uuid).await?;
let allowed = roles.iter().any(|r| r.permissions & permission as i64 != 0);
if !allowed {
return Err(Error::Forbidden("Not allowed".to_string()));
}
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
pub struct Member {
pub uuid: Uuid,
pub nickname: Option<String>,
pub user_uuid: Uuid,
pub guild_uuid: Uuid,
pub is_owner: bool,
user: User,
}
impl Member {
pub async fn count(conn: &mut Conn, guild_uuid: Uuid) -> Result<i64, Error> {
use guild_members::dsl;
let count: i64 = dsl::guild_members
.filter(dsl::guild_uuid.eq(guild_uuid))
.count()
.get_result(conn)
.await?;
Ok(count)
}
pub async fn check_membership(
conn: &mut Conn,
user_uuid: Uuid,
guild_uuid: Uuid,
) -> Result<MemberBuilder, Error> {
use guild_members::dsl;
let member_builder = dsl::guild_members
.filter(dsl::user_uuid.eq(user_uuid))
.filter(dsl::guild_uuid.eq(guild_uuid))
.select(MemberBuilder::as_select())
.get_result(conn)
.await?;
Ok(member_builder)
}
pub async fn fetch_one(
app_state: &AppState,
me: &Me,
user_uuid: Uuid,
guild_uuid: Uuid,
) -> Result<Self, Error> {
let mut conn = app_state.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(app_state, Some(me)).await
}
pub async fn fetch_one_with_member(
app_state: &AppState,
me: Option<&Me>,
uuid: Uuid,
) -> Result<Self, Error> {
let mut conn = app_state.pool.get().await?;
use guild_members::dsl;
let member: MemberBuilder = dsl::guild_members
.filter(dsl::uuid.eq(uuid))
.select(MemberBuilder::as_select())
.get_result(&mut conn)
.await?;
member.build(app_state, me).await
}
pub async fn fetch_all(
app_state: &AppState,
me: &Me,
guild_uuid: Uuid,
) -> Result<Vec<Self>, Error> {
let mut conn = app_state.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 mut members = vec![];
for builder in member_builders {
members.push(builder.build(app_state, Some(me)).await?);
}
Ok(members)
}
pub async fn new(
app_state: &AppState,
user_uuid: Uuid,
guild_uuid: Uuid,
) -> Result<Self, Error> {
let mut conn = app_state.pool.get().await?;
let banned = GuildBan::fetch_one(&mut conn, guild_uuid, user_uuid).await;
match banned {
Ok(_) => Err(Error::Forbidden("User banned".to_string())),
Err(Error::SqlError(diesel::result::Error::NotFound)) => Ok(()),
Err(e) => Err(e),
}?;
let member_uuid = Uuid::now_v7();
let member = MemberBuilder {
uuid: member_uuid,
guild_uuid,
user_uuid,
nickname: None,
is_owner: false,
};
insert_into(guild_members::table)
.values(&member)
.execute(&mut conn)
.await?;
member.build(app_state, None).await
}
pub async fn delete(self, conn: &mut Conn) -> Result<(), Error> {
delete(guild_members::table)
.filter(guild_members::uuid.eq(self.uuid))
.execute(conn)
.await?;
Ok(())
}
pub async fn ban(self, conn: &mut Conn, reason: &String) -> Result<(), Error> {
if self.is_owner {
return Err(Error::Forbidden("Can not ban owner".to_string()));
}
use guild_bans::dsl;
insert_into(guild_bans::table)
.values((
dsl::guild_uuid.eq(self.guild_uuid),
dsl::user_uuid.eq(self.user_uuid),
dsl::reason.eq(reason),
))
.execute(conn)
.await?;
self.delete(conn).await?;
Ok(())
}
}