From b2e6d3f5530cea8458374957e224a49d178e0ae9 Mon Sep 17 00:00:00 2001 From: BAaboe Date: Tue, 22 Jul 2025 21:58:14 +0200 Subject: [PATCH 1/6] feat: added ban table to the database --- migrations/2025-07-22-195121_add_ban/down.sql | 2 ++ migrations/2025-07-22-195121_add_ban/up.sql | 7 +++++++ 2 files changed, 9 insertions(+) create mode 100644 migrations/2025-07-22-195121_add_ban/down.sql create mode 100644 migrations/2025-07-22-195121_add_ban/up.sql diff --git a/migrations/2025-07-22-195121_add_ban/down.sql b/migrations/2025-07-22-195121_add_ban/down.sql new file mode 100644 index 0000000..62fe554 --- /dev/null +++ b/migrations/2025-07-22-195121_add_ban/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE guild_bans; diff --git a/migrations/2025-07-22-195121_add_ban/up.sql b/migrations/2025-07-22-195121_add_ban/up.sql new file mode 100644 index 0000000..3dd5f9b --- /dev/null +++ b/migrations/2025-07-22-195121_add_ban/up.sql @@ -0,0 +1,7 @@ +-- Your SQL goes here +CREATE TABLE guild_bans ( + guild_ban uuid NOT NULL REFERENCES guilds(uuid) ON DELETE CASCADE, + user_ban uuid NOT NULL REFERENCES users(uuid), + reason VARCHAR(200) DEFAULT NULL, + PRIMARY KEY (user_uuid, guild_uuid) +); From af7193750686de6d3ebfcb7cbe3f091c19008671 Mon Sep 17 00:00:00 2001 From: BAaboe Date: Tue, 22 Jul 2025 22:01:48 +0200 Subject: [PATCH 2/6] fix: fixed the ban table names --- migrations/2025-07-22-195121_add_ban/up.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migrations/2025-07-22-195121_add_ban/up.sql b/migrations/2025-07-22-195121_add_ban/up.sql index 3dd5f9b..020a1b0 100644 --- a/migrations/2025-07-22-195121_add_ban/up.sql +++ b/migrations/2025-07-22-195121_add_ban/up.sql @@ -1,7 +1,7 @@ -- Your SQL goes here CREATE TABLE guild_bans ( - guild_ban uuid NOT NULL REFERENCES guilds(uuid) ON DELETE CASCADE, - user_ban uuid NOT NULL REFERENCES users(uuid), + guild_uuid uuid NOT NULL REFERENCES guilds(uuid) ON DELETE CASCADE, + user_uuid uuid NOT NULL REFERENCES users(uuid), reason VARCHAR(200) DEFAULT NULL, PRIMARY KEY (user_uuid, guild_uuid) ); From ade45780fa72ebbcfa374833aea38b3136f924bc Mon Sep 17 00:00:00 2001 From: BAaboe Date: Tue, 22 Jul 2025 22:04:48 +0200 Subject: [PATCH 3/6] fix: forgot to add the change on schema --- src/schema.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/schema.rs b/src/schema.rs index 4095dcd..54284d6 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -47,6 +47,15 @@ diesel::table! { } } +diesel::table! { + guild_bans (user_uuid, guild_uuid) { + guild_uuid -> Uuid, + user_uuid -> Uuid, + #[max_length = 200] + reason -> Nullable, + } +} + diesel::table! { guild_members (uuid) { uuid -> Uuid, @@ -154,6 +163,8 @@ diesel::joinable!(access_tokens -> refresh_tokens (refresh_token)); diesel::joinable!(access_tokens -> users (uuid)); diesel::joinable!(channel_permissions -> channels (channel_uuid)); diesel::joinable!(channels -> guilds (guild_uuid)); +diesel::joinable!(guild_bans -> guilds (guild_uuid)); +diesel::joinable!(guild_bans -> users (user_uuid)); diesel::joinable!(guild_members -> guilds (guild_uuid)); diesel::joinable!(guild_members -> users (user_uuid)); diesel::joinable!(instance_permissions -> users (uuid)); @@ -171,6 +182,7 @@ diesel::allow_tables_to_appear_in_same_query!( channels, friend_requests, friends, + guild_bans, guild_members, guilds, instance_permissions, From f175c1932536be1063bff8561f73fad5be32c8ee Mon Sep 17 00:00:00 2001 From: BAaboe Date: Tue, 22 Jul 2025 23:18:21 +0200 Subject: [PATCH 4/6] fix: let you pass None to fetch_one_with_member for me --- src/api/v1/members/uuid/mod.rs | 6 ++++-- src/objects/member.rs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/api/v1/members/uuid/mod.rs b/src/api/v1/members/uuid/mod.rs index 244f5f8..7c39bff 100644 --- a/src/api/v1/members/uuid/mod.rs +++ b/src/api/v1/members/uuid/mod.rs @@ -1,5 +1,7 @@ //! `/api/v1/members/{uuid}` Member specific endpoints +pub mod ban; + use std::sync::Arc; use crate::{ @@ -29,7 +31,7 @@ pub async fn get( let me = Me::get(&mut conn, uuid).await?; - let member = Member::fetch_one_with_member(&app_state, &me, member_uuid).await?; + let member = Member::fetch_one_with_member(&app_state, Some(&me), member_uuid).await?; Member::check_membership(&mut conn, uuid, member.guild_uuid).await?; Ok((StatusCode::OK, Json(member))) @@ -46,7 +48,7 @@ pub async fn delete( let me = Me::get(&mut conn, uuid).await?; - let member = Member::fetch_one_with_member(&app_state, &me, member_uuid).await?; + let member = Member::fetch_one_with_member(&app_state, Some(&me), member_uuid).await?; let deleter = Member::check_membership(&mut conn, uuid, member.guild_uuid).await?; diff --git a/src/objects/member.rs b/src/objects/member.rs index 8678f4a..337c2e9 100644 --- a/src/objects/member.rs +++ b/src/objects/member.rs @@ -122,7 +122,7 @@ impl Member { pub async fn fetch_one_with_member( app_state: &AppState, - me: &Me, + me: Option<&Me>, uuid: Uuid, ) -> Result { let mut conn = app_state.pool.get().await?; @@ -134,7 +134,7 @@ impl Member { .get_result(&mut conn) .await?; - member.build(app_state, Some(me)).await + member.build(app_state, me).await } pub async fn fetch_all( From ad24215fef631b1dc1a81e8fc9e3277f6903cf3a Mon Sep 17 00:00:00 2001 From: BAaboe Date: Wed, 23 Jul 2025 00:50:50 +0200 Subject: [PATCH 5/6] feat: added endpoint for banning --- src/api/v1/members/mod.rs | 3 ++- src/api/v1/members/uuid/ban.rs | 47 ++++++++++++++++++++++++++++++++++ src/objects/member.rs | 37 ++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/api/v1/members/uuid/ban.rs diff --git a/src/api/v1/members/mod.rs b/src/api/v1/members/mod.rs index 34b6938..59ceac2 100644 --- a/src/api/v1/members/mod.rs +++ b/src/api/v1/members/mod.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use axum::{ Router, - routing::{delete, get}, + routing::{delete, get, post}, }; use crate::AppState; @@ -13,4 +13,5 @@ pub fn router() -> Router> { Router::new() .route("/{uuid}", get(uuid::get)) .route("/{uuid}", delete(uuid::delete)) + .route("/{uuid}/ban", post(uuid::ban::post)) } diff --git a/src/api/v1/members/uuid/ban.rs b/src/api/v1/members/uuid/ban.rs new file mode 100644 index 0000000..3fb1b58 --- /dev/null +++ b/src/api/v1/members/uuid/ban.rs @@ -0,0 +1,47 @@ +use std::sync::Arc; + +use axum::{ + Extension, + extract::{Path, State, Json}, + http::StatusCode, + response::IntoResponse, +}; +use diesel::{insert_into, RunQueryDsl}; +use serde::Deserialize; + +use crate::{ + api::v1::auth::CurrentUser, error::Error, objects::{Me, Member, Permissions}, schema::guild_bans::{self, dsl}, utils::global_checks, AppState +}; + +use uuid::Uuid; + +#[derive(Deserialize)] +pub struct RequstBody { + reason: String +} + + +pub async fn post( + State(app_state): State>, + Path(member_uuid): Path, + Extension(CurrentUser(uuid)): Extension>, + Json(payload): Json, +) -> Result{ + global_checks(&app_state, uuid).await?; + + let mut conn = app_state.pool.get().await?; + + let member = Member::fetch_one_with_member(&app_state, None, member_uuid).await?; + + if member.is_owner { + return Err(Error::Forbidden("Not allowed".to_string())); + } + + let baner = Member::check_membership(&mut conn, uuid, member.guild_uuid).await?; + baner.check_permission(&app_state, Permissions::ManageMember).await?; + + member.ban(&mut conn, &payload.reason).await?; + + + Ok(StatusCode::OK) +} diff --git a/src/objects/member.rs b/src/objects/member.rs index 337c2e9..40bcaee 100644 --- a/src/objects/member.rs +++ b/src/objects/member.rs @@ -1,3 +1,4 @@ +use axum::http::StatusCode; use diesel::{ ExpressionMethods, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper, delete, insert_into, @@ -11,6 +12,7 @@ use crate::{ error::Error, objects::{Me, Permissions, Role}, schema::guild_members, + schema::guild_bans, }; use super::{User, load_or_empty}; @@ -73,6 +75,13 @@ pub struct Member { user: User, } +#[derive(Serialize, Deserialize)] +pub struct GuildBan { + pub guild_uuid: Uuid, + pub user_uuid: Uuid, + pub reason: String, +} + impl Member { pub async fn count(conn: &mut Conn, guild_uuid: Uuid) -> Result { use guild_members::dsl; @@ -169,6 +178,18 @@ impl Member { ) -> Result { let mut conn = app_state.pool.get().await?; + use guild_bans::dsl; + let banned = dsl::guild_bans + .filter(guild_bans::guild_uuid.eq(guild_uuid)) + .filter(guild_bans::user_uuid.eq(user_uuid)) + .execute(&mut conn) + .await; + match banned { + Ok(_) => Err(Error::Forbidden("User banned".to_string())), + Err(diesel::result::Error::NotFound) => Ok(()), + Err(e) => Err(e.into()), + }?; + let member_uuid = Uuid::now_v7(); let member = MemberBuilder { @@ -195,4 +216,20 @@ impl Member { Ok(()) } + + pub async fn ban(self, conn: &mut Conn, reason: &String) -> Result<(), Error> { + 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(()) + } } From 8e31dc7acab5fd147e53d2d5d0c3782b53ee418a Mon Sep 17 00:00:00 2001 From: BAaboe Date: Wed, 23 Jul 2025 00:52:19 +0200 Subject: [PATCH 6/6] style: cargo clippy --fix && cargo fmt --- src/api/v1/members/uuid/ban.rs | 23 +++++++++++++---------- src/objects/member.rs | 3 +-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/api/v1/members/uuid/ban.rs b/src/api/v1/members/uuid/ban.rs index 3fb1b58..986b98d 100644 --- a/src/api/v1/members/uuid/ban.rs +++ b/src/api/v1/members/uuid/ban.rs @@ -2,46 +2,49 @@ use std::sync::Arc; use axum::{ Extension, - extract::{Path, State, Json}, + extract::{Json, Path, State}, http::StatusCode, response::IntoResponse, }; -use diesel::{insert_into, RunQueryDsl}; use serde::Deserialize; use crate::{ - api::v1::auth::CurrentUser, error::Error, objects::{Me, Member, Permissions}, schema::guild_bans::{self, dsl}, utils::global_checks, AppState + AppState, + api::v1::auth::CurrentUser, + error::Error, + objects::{Member, Permissions}, + utils::global_checks, }; use uuid::Uuid; #[derive(Deserialize)] pub struct RequstBody { - reason: String + reason: String, } - pub async fn post( State(app_state): State>, Path(member_uuid): Path, Extension(CurrentUser(uuid)): Extension>, Json(payload): Json, -) -> Result{ +) -> Result { global_checks(&app_state, uuid).await?; let mut conn = app_state.pool.get().await?; - + let member = Member::fetch_one_with_member(&app_state, None, member_uuid).await?; if member.is_owner { return Err(Error::Forbidden("Not allowed".to_string())); } - + let baner = Member::check_membership(&mut conn, uuid, member.guild_uuid).await?; - baner.check_permission(&app_state, Permissions::ManageMember).await?; + baner + .check_permission(&app_state, Permissions::ManageMember) + .await?; member.ban(&mut conn, &payload.reason).await?; - Ok(StatusCode::OK) } diff --git a/src/objects/member.rs b/src/objects/member.rs index 40bcaee..d07b701 100644 --- a/src/objects/member.rs +++ b/src/objects/member.rs @@ -1,4 +1,3 @@ -use axum::http::StatusCode; use diesel::{ ExpressionMethods, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper, delete, insert_into, @@ -11,8 +10,8 @@ use crate::{ AppState, Conn, error::Error, objects::{Me, Permissions, Role}, - schema::guild_members, schema::guild_bans, + schema::guild_members, }; use super::{User, load_or_empty};