diff --git a/src/api/v1/members/mod.rs b/src/api/v1/members/mod.rs new file mode 100644 index 0000000..34b6938 --- /dev/null +++ b/src/api/v1/members/mod.rs @@ -0,0 +1,16 @@ +use std::sync::Arc; + +use axum::{ + Router, + routing::{delete, get}, +}; + +use crate::AppState; + +mod uuid; + +pub fn router() -> Router> { + Router::new() + .route("/{uuid}", get(uuid::get)) + .route("/{uuid}", delete(uuid::delete)) +} diff --git a/src/api/v1/members/uuid/mod.rs b/src/api/v1/members/uuid/mod.rs new file mode 100644 index 0000000..244f5f8 --- /dev/null +++ b/src/api/v1/members/uuid/mod.rs @@ -0,0 +1,60 @@ +//! `/api/v1/members/{uuid}` Member specific endpoints + +use std::sync::Arc; + +use crate::{ + AppState, + api::v1::auth::CurrentUser, + error::Error, + objects::{Me, Member, Permissions}, + utils::global_checks, +}; +use axum::{ + Extension, Json, + extract::{Path, State}, + http::StatusCode, + response::IntoResponse, +}; + +use uuid::Uuid; + +pub async fn get( + State(app_state): State>, + Path(member_uuid): Path, + Extension(CurrentUser(uuid)): Extension>, +) -> Result { + global_checks(&app_state, uuid).await?; + + let mut conn = app_state.pool.get().await?; + + let me = Me::get(&mut conn, uuid).await?; + + let member = Member::fetch_one_with_member(&app_state, &me, member_uuid).await?; + Member::check_membership(&mut conn, uuid, member.guild_uuid).await?; + + Ok((StatusCode::OK, Json(member))) +} + +pub async fn delete( + State(app_state): State>, + Path(member_uuid): Path, + Extension(CurrentUser(uuid)): Extension>, +) -> Result { + global_checks(&app_state, uuid).await?; + + let mut conn = app_state.pool.get().await?; + + let me = Me::get(&mut conn, uuid).await?; + + let member = Member::fetch_one_with_member(&app_state, &me, member_uuid).await?; + + let deleter = Member::check_membership(&mut conn, uuid, member.guild_uuid).await?; + + deleter + .check_permission(&app_state, Permissions::ManageMember) + .await?; + + member.delete(&mut conn).await?; + + Ok(StatusCode::OK) +} diff --git a/src/api/v1/mod.rs b/src/api/v1/mod.rs index 860944c..70271ef 100644 --- a/src/api/v1/mod.rs +++ b/src/api/v1/mod.rs @@ -11,6 +11,7 @@ mod channels; mod guilds; mod invites; mod me; +mod members; mod stats; mod users; @@ -19,6 +20,7 @@ pub fn router(app_state: Arc) -> Router> { .nest("/users", users::router()) .nest("/guilds", guilds::router()) .nest("/invites", invites::router()) + .nest("/members", members::router()) .nest("/me", me::router()) .layer(from_fn_with_state( app_state.clone(), diff --git a/src/objects/member.rs b/src/objects/member.rs index 50b76b0..8678f4a 100644 --- a/src/objects/member.rs +++ b/src/objects/member.rs @@ -1,5 +1,6 @@ use diesel::{ - ExpressionMethods, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper, insert_into, + ExpressionMethods, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper, delete, + insert_into, }; use diesel_async::RunQueryDsl; use serde::{Deserialize, Serialize}; @@ -119,6 +120,23 @@ impl Member { member.build(app_state, Some(me)).await } + pub async fn fetch_one_with_member( + app_state: &AppState, + me: &Me, + uuid: Uuid, + ) -> Result { + 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, Some(me)).await + } + pub async fn fetch_all( app_state: &AppState, me: &Me, @@ -168,4 +186,13 @@ impl Member { 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(()) + } }