diff --git a/migrations/2025-07-22-195121_add_ban/up.sql b/migrations/2025-07-22-195121_add_ban/up.sql index 020a1b0..d8d7fab 100644 --- a/migrations/2025-07-22-195121_add_ban/up.sql +++ b/migrations/2025-07-22-195121_add_ban/up.sql @@ -3,5 +3,6 @@ CREATE TABLE guild_bans ( guild_uuid uuid NOT NULL REFERENCES guilds(uuid) ON DELETE CASCADE, user_uuid uuid NOT NULL REFERENCES users(uuid), reason VARCHAR(200) DEFAULT NULL, + ban_time TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (user_uuid, guild_uuid) ); diff --git a/src/api/v1/guilds/uuid/bans.rs b/src/api/v1/guilds/uuid/bans.rs new file mode 100644 index 0000000..44ed48d --- /dev/null +++ b/src/api/v1/guilds/uuid/bans.rs @@ -0,0 +1,36 @@ +use std::sync::Arc; + +use axum::{ + Extension, Json, + extract::{Path, State}, + http::StatusCode, + response::IntoResponse, +}; +use uuid::Uuid; + +use crate::{ + AppState, + api::v1::auth::CurrentUser, + error::Error, + objects::{GuildBan, Member, Permissions}, + utils::global_checks, +}; + +pub async fn get( + State(app_state): State>, + Path(guild_uuid): Path, + Extension(CurrentUser(uuid)): Extension>, +) -> Result { + global_checks(&app_state, uuid).await?; + + let mut conn = app_state.pool.get().await?; + + let member = Member::check_membership(&mut conn, uuid, guild_uuid).await?; + member + .check_permission(&app_state, Permissions::BanMember) + .await?; + + let all_guild_bans = GuildBan::fetch_all(&mut conn, guild_uuid).await?; + + Ok((StatusCode::OK, Json(all_guild_bans))) +} diff --git a/src/api/v1/guilds/uuid/mod.rs b/src/api/v1/guilds/uuid/mod.rs index 52f0b64..2174932 100644 --- a/src/api/v1/guilds/uuid/mod.rs +++ b/src/api/v1/guilds/uuid/mod.rs @@ -12,6 +12,7 @@ use axum::{ use bytes::Bytes; use uuid::Uuid; +mod bans; mod channels; mod invites; mod members; @@ -42,6 +43,8 @@ pub fn router() -> Router> { .route("/invites", post(invites::create)) // Members .route("/members", get(members::get)) + // Bans + .route("/bans", get(bans::get)) } /// `GET /api/v1/guilds/{uuid}` DESCRIPTION diff --git a/src/objects/bans.rs b/src/objects/bans.rs new file mode 100644 index 0000000..9eb58f5 --- /dev/null +++ b/src/objects/bans.rs @@ -0,0 +1,57 @@ +use diesel::{ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use diesel_async::RunQueryDsl; + +use crate::{Conn, error::Error, objects::load_or_empty, schema::guild_bans}; + +#[derive(Selectable, Queryable, Serialize, Deserialize)] +#[diesel(table_name = guild_bans)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct GuildBan { + pub guild_uuid: Uuid, + pub user_uuid: Uuid, + pub reason: Option, + pub ban_time: chrono::DateTime, +} + +impl GuildBan { + pub async fn fetch_one( + conn: &mut Conn, + guild_uuid: Uuid, + user_uuid: Uuid, + ) -> Result { + use guild_bans::dsl; + let guild_ban = dsl::guild_bans + .filter(dsl::guild_uuid.eq(guild_uuid)) + .filter(dsl::user_uuid.eq(user_uuid)) + .select(GuildBan::as_select()) + .get_result(conn) + .await?; + + Ok(guild_ban) + } + + pub async fn fetch_all(conn: &mut Conn, guild_uuid: Uuid) -> Result, Error> { + use guild_bans::dsl; + let all_guild_bans = load_or_empty( + dsl::guild_bans + .filter(dsl::guild_uuid.eq(guild_uuid)) + .load(conn) + .await, + )?; + + Ok(all_guild_bans) + } + + pub async fn unban(self, conn: &mut Conn) -> Result<(), Error> { + use guild_bans::dsl; + diesel::delete(guild_bans::table) + .filter(dsl::guild_uuid.eq(self.guild_uuid)) + .filter(dsl::user_uuid.eq(self.user_uuid)) + .execute(conn) + .await?; + Ok(()) + } +} diff --git a/src/objects/member.rs b/src/objects/member.rs index 50a0a24..fcd0b6b 100644 --- a/src/objects/member.rs +++ b/src/objects/member.rs @@ -1,6 +1,5 @@ use diesel::{ - ExpressionMethods, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper, delete, - insert_into, + ExpressionMethods, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper, insert_into, }; use diesel_async::RunQueryDsl; use serde::{Deserialize, Serialize}; @@ -74,13 +73,6 @@ 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; @@ -208,7 +200,7 @@ impl Member { } pub async fn delete(self, conn: &mut Conn) -> Result<(), Error> { - delete(guild_members::table) + diesel::delete(guild_members::table) .filter(guild_members::uuid.eq(self.uuid)) .execute(conn) .await?; diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 4af16d8..3bcce9c 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -7,6 +7,7 @@ use log::debug; use serde::Deserialize; use uuid::Uuid; +mod bans; mod channel; mod email_token; mod friends; @@ -19,6 +20,7 @@ mod password_reset_token; mod role; mod user; +pub use bans::GuildBan; pub use channel::Channel; pub use email_token::EmailToken; pub use friends::Friend; diff --git a/src/schema.rs b/src/schema.rs index 54284d6..e4d6730 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -53,6 +53,7 @@ diesel::table! { user_uuid -> Uuid, #[max_length = 200] reason -> Nullable, + ban_time -> Timestamptz, } }