Compare commits
2 commits
3816af56e3
...
8d91ec78a6
Author | SHA1 | Date | |
---|---|---|---|
8d91ec78a6 | |||
e9cc2a3f0e |
6 changed files with 197 additions and 37 deletions
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||
use ::uuid::Uuid;
|
||||
use axum::{
|
||||
Extension, Json,
|
||||
extract::{Path, State},
|
||||
extract::{Path, Query, State},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
};
|
||||
|
@ -12,13 +12,14 @@ use crate::{
|
|||
AppState,
|
||||
api::v1::auth::CurrentUser,
|
||||
error::Error,
|
||||
objects::{Me, Member},
|
||||
objects::{Me, Member, PaginationRequest},
|
||||
utils::global_checks,
|
||||
};
|
||||
|
||||
pub async fn get(
|
||||
State(app_state): State<Arc<AppState>>,
|
||||
Path(guild_uuid): Path<Uuid>,
|
||||
Query(pagination): Query<PaginationRequest>,
|
||||
Extension(CurrentUser(uuid)): Extension<CurrentUser<Uuid>>,
|
||||
) -> Result<impl IntoResponse, Error> {
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
@ -29,7 +30,14 @@ pub async fn get(
|
|||
|
||||
let me = Me::get(&mut conn, uuid).await?;
|
||||
|
||||
let members = Member::fetch_all(&mut conn, &app_state.cache_pool, &me, guild_uuid).await?;
|
||||
let members = Member::fetch_page(
|
||||
&mut conn,
|
||||
&app_state.cache_pool,
|
||||
&me,
|
||||
guild_uuid,
|
||||
pagination,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok((StatusCode::OK, Json(members)))
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ pub async fn post(
|
|||
global_checks(&mut conn, &app_state.config, uuid).await?;
|
||||
|
||||
let member =
|
||||
Member::fetch_one_with_member(&mut conn, &app_state.cache_pool, None, member_uuid).await?;
|
||||
Member::fetch_one_with_uuid(&mut conn, &app_state.cache_pool, None, member_uuid).await?;
|
||||
|
||||
let caller = Member::check_membership(&mut conn, uuid, member.guild_uuid).await?;
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ pub async fn get(
|
|||
let me = Me::get(&mut conn, uuid).await?;
|
||||
|
||||
let member =
|
||||
Member::fetch_one_with_member(&mut conn, &app_state.cache_pool, Some(&me), member_uuid)
|
||||
Member::fetch_one_with_uuid(&mut conn, &app_state.cache_pool, Some(&me), member_uuid)
|
||||
.await?;
|
||||
Member::check_membership(&mut conn, uuid, member.guild_uuid).await?;
|
||||
|
||||
|
@ -51,7 +51,7 @@ pub async fn delete(
|
|||
let me = Me::get(&mut conn, uuid).await?;
|
||||
|
||||
let member =
|
||||
Member::fetch_one_with_member(&mut conn, &app_state.cache_pool, Some(&me), member_uuid)
|
||||
Member::fetch_one_with_uuid(&mut conn, &app_state.cache_pool, Some(&me), member_uuid)
|
||||
.await?;
|
||||
|
||||
let deleter = Member::check_membership(&mut conn, uuid, member.guild_uuid).await?;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use diesel::{
|
||||
ExpressionMethods, Identifiable, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper,
|
||||
delete, insert_into,
|
||||
Associations, BoolExpressionMethods, ExpressionMethods, Identifiable, Insertable, JoinOnDsl,
|
||||
QueryDsl, Queryable, Selectable, SelectableHelper, define_sql_function, delete, insert_into,
|
||||
sql_types::{Nullable, VarChar},
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -9,14 +10,21 @@ use uuid::Uuid;
|
|||
use crate::{
|
||||
Conn,
|
||||
error::Error,
|
||||
objects::{GuildBan, Me, Permissions, Role},
|
||||
schema::{guild_bans, guild_members},
|
||||
objects::PaginationRequest,
|
||||
schema::{friends, guild_bans, guild_members, users},
|
||||
};
|
||||
|
||||
use super::{User, load_or_empty};
|
||||
use super::{
|
||||
Friend, Guild, GuildBan, Me, Pagination, Permissions, Role, User, load_or_empty,
|
||||
user::UserBuilder,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Queryable, Identifiable, Selectable, Insertable)]
|
||||
define_sql_function! { fn coalesce(x: Nullable<VarChar>, y: Nullable<VarChar>, z: VarChar) -> Text; }
|
||||
|
||||
#[derive(Serialize, Queryable, Identifiable, Selectable, Insertable, Associations)]
|
||||
#[diesel(table_name = guild_members)]
|
||||
#[diesel(belongs_to(UserBuilder, foreign_key = user_uuid))]
|
||||
#[diesel(belongs_to(Guild, foreign_key = guild_uuid))]
|
||||
#[diesel(primary_key(uuid))]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct MemberBuilder {
|
||||
|
@ -55,6 +63,32 @@ impl MemberBuilder {
|
|||
})
|
||||
}
|
||||
|
||||
async fn build_with_parts(
|
||||
&self,
|
||||
conn: &mut Conn,
|
||||
cache_pool: &redis::Client,
|
||||
user_builder: UserBuilder,
|
||||
friend: Option<Friend>,
|
||||
) -> Result<Member, Error> {
|
||||
let mut user = user_builder.build();
|
||||
|
||||
if let Some(friend) = friend {
|
||||
user.friends_since = Some(friend.accepted_at);
|
||||
}
|
||||
|
||||
let roles = Role::fetch_from_member(conn, cache_pool, self).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,
|
||||
roles,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn check_permission(
|
||||
&self,
|
||||
conn: &mut Conn,
|
||||
|
@ -120,52 +154,156 @@ impl Member {
|
|||
user_uuid: Uuid,
|
||||
guild_uuid: Uuid,
|
||||
) -> Result<Self, Error> {
|
||||
use friends::dsl as fdsl;
|
||||
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(conn)
|
||||
.await?;
|
||||
let (member, user, friend): (MemberBuilder, UserBuilder, Option<Friend>) =
|
||||
dsl::guild_members
|
||||
.filter(dsl::guild_uuid.eq(guild_uuid))
|
||||
.filter(dsl::user_uuid.eq(user_uuid))
|
||||
.inner_join(users::table)
|
||||
.left_join(
|
||||
fdsl::friends.on(fdsl::uuid1
|
||||
.eq(me.uuid)
|
||||
.and(fdsl::uuid2.eq(users::uuid))
|
||||
.or(fdsl::uuid2.eq(me.uuid).and(fdsl::uuid1.eq(users::uuid)))),
|
||||
)
|
||||
.order_by(coalesce(
|
||||
dsl::nickname,
|
||||
users::display_name,
|
||||
users::username,
|
||||
))
|
||||
.select((
|
||||
MemberBuilder::as_select(),
|
||||
UserBuilder::as_select(),
|
||||
Option::<Friend>::as_select(),
|
||||
))
|
||||
.get_result(conn)
|
||||
.await?;
|
||||
|
||||
member.build(conn, cache_pool, Some(me)).await
|
||||
member
|
||||
.build_with_parts(conn, cache_pool, user, friend)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn fetch_one_with_member(
|
||||
pub async fn fetch_one_with_uuid(
|
||||
conn: &mut Conn,
|
||||
cache_pool: &redis::Client,
|
||||
me: Option<&Me>,
|
||||
uuid: Uuid,
|
||||
) -> Result<Self, Error> {
|
||||
let member: MemberBuilder;
|
||||
let user: UserBuilder;
|
||||
let friend: Option<Friend>;
|
||||
use friends::dsl as fdsl;
|
||||
use guild_members::dsl;
|
||||
let member: MemberBuilder = dsl::guild_members
|
||||
.filter(dsl::uuid.eq(uuid))
|
||||
.select(MemberBuilder::as_select())
|
||||
.get_result(conn)
|
||||
.await?;
|
||||
if let Some(me) = me {
|
||||
(member, user, friend) = dsl::guild_members
|
||||
.filter(dsl::uuid.eq(uuid))
|
||||
.inner_join(users::table)
|
||||
.left_join(
|
||||
fdsl::friends.on(fdsl::uuid1
|
||||
.eq(me.uuid)
|
||||
.and(fdsl::uuid2.eq(users::uuid))
|
||||
.or(fdsl::uuid2.eq(me.uuid).and(fdsl::uuid1.eq(users::uuid)))),
|
||||
)
|
||||
.order_by(coalesce(
|
||||
dsl::nickname,
|
||||
users::display_name,
|
||||
users::username,
|
||||
))
|
||||
.select((
|
||||
MemberBuilder::as_select(),
|
||||
UserBuilder::as_select(),
|
||||
Option::<Friend>::as_select(),
|
||||
))
|
||||
.get_result(conn)
|
||||
.await?;
|
||||
} else {
|
||||
(member, user) = dsl::guild_members
|
||||
.filter(dsl::uuid.eq(uuid))
|
||||
.inner_join(users::table)
|
||||
.order_by(coalesce(
|
||||
dsl::nickname,
|
||||
users::display_name,
|
||||
users::username,
|
||||
))
|
||||
.select((MemberBuilder::as_select(), UserBuilder::as_select()))
|
||||
.get_result(conn)
|
||||
.await?;
|
||||
|
||||
member.build(conn, cache_pool, me).await
|
||||
friend = None;
|
||||
}
|
||||
|
||||
member
|
||||
.build_with_parts(conn, cache_pool, user, friend)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn fetch_all(
|
||||
pub async fn fetch_page(
|
||||
conn: &mut Conn,
|
||||
cache_pool: &redis::Client,
|
||||
me: &Me,
|
||||
guild_uuid: Uuid,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
pagination: PaginationRequest,
|
||||
) -> Result<Pagination<Self>, Error> {
|
||||
let per_page = pagination.per_page.unwrap_or(50);
|
||||
let page_multiplier: i64 = ((pagination.page - 1) * per_page).into();
|
||||
|
||||
if !(10..=100).contains(&per_page) {
|
||||
return Err(Error::BadRequest(
|
||||
"Invalid amount per page requested".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
use friends::dsl as fdsl;
|
||||
use guild_members::dsl;
|
||||
let member_builders: Vec<MemberBuilder> = load_or_empty(
|
||||
let member_builders: Vec<(MemberBuilder, UserBuilder, Option<Friend>)> = load_or_empty(
|
||||
dsl::guild_members
|
||||
.filter(dsl::guild_uuid.eq(guild_uuid))
|
||||
.select(MemberBuilder::as_select())
|
||||
.inner_join(users::table)
|
||||
.left_join(
|
||||
fdsl::friends.on(fdsl::uuid1
|
||||
.eq(me.uuid)
|
||||
.and(fdsl::uuid2.eq(users::uuid))
|
||||
.or(fdsl::uuid2.eq(me.uuid).and(fdsl::uuid1.eq(users::uuid)))),
|
||||
)
|
||||
.limit(per_page.into())
|
||||
.offset(page_multiplier)
|
||||
.order_by(coalesce(
|
||||
dsl::nickname,
|
||||
users::display_name,
|
||||
users::username,
|
||||
))
|
||||
.select((
|
||||
MemberBuilder::as_select(),
|
||||
UserBuilder::as_select(),
|
||||
Option::<Friend>::as_select(),
|
||||
))
|
||||
.load(conn)
|
||||
.await,
|
||||
)?;
|
||||
|
||||
let mut members = vec![];
|
||||
let member_count: i64 = dsl::guild_members
|
||||
.filter(dsl::guild_uuid.eq(guild_uuid))
|
||||
.count()
|
||||
.get_result(conn)
|
||||
.await?;
|
||||
|
||||
for builder in member_builders {
|
||||
members.push(builder.build(conn, cache_pool, Some(me)).await?);
|
||||
let pages = member_count as f32 / per_page as f32;
|
||||
|
||||
let mut members = Pagination::<Member> {
|
||||
objects: Vec::with_capacity(member_builders.len()),
|
||||
amount: member_builders.len() as i32,
|
||||
pages: pages.ceil() as i32,
|
||||
page: pagination.page,
|
||||
};
|
||||
|
||||
for (member, user, friend) in member_builders {
|
||||
members.objects.push(
|
||||
member
|
||||
.build_with_parts(conn, cache_pool, user, friend)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(members)
|
||||
|
|
|
@ -4,7 +4,7 @@ use lettre::{
|
|||
transport::smtp::authentication::Credentials,
|
||||
};
|
||||
use log::debug;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
mod bans;
|
||||
|
@ -76,6 +76,20 @@ impl Cookies for Request<Body> {
|
|||
}
|
||||
*/
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Pagination<T> {
|
||||
objects: Vec<T>,
|
||||
amount: i32,
|
||||
pages: i32,
|
||||
page: i32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PaginationRequest {
|
||||
pub page: i32,
|
||||
pub per_page: Option<i32>,
|
||||
}
|
||||
|
||||
fn load_or_empty<T>(
|
||||
query_result: Result<Vec<T>, diesel::result::Error>,
|
||||
) -> Result<Vec<T>, diesel::result::Error> {
|
||||
|
|
|
@ -21,7 +21,7 @@ pub struct UserBuilder {
|
|||
}
|
||||
|
||||
impl UserBuilder {
|
||||
fn build(self) -> User {
|
||||
pub fn build(self) -> User {
|
||||
User {
|
||||
uuid: self.uuid,
|
||||
username: self.username,
|
||||
|
@ -36,13 +36,13 @@ impl UserBuilder {
|
|||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct User {
|
||||
uuid: Uuid,
|
||||
pub uuid: Uuid,
|
||||
username: String,
|
||||
display_name: Option<String>,
|
||||
avatar: Option<String>,
|
||||
pronouns: Option<String>,
|
||||
about: Option<String>,
|
||||
friends_since: Option<DateTime<Utc>>,
|
||||
pub friends_since: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl User {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue