diff --git a/src/api/v1/guilds/uuid/auditlogs.rs b/src/api/v1/guilds/uuid/auditlogs.rs new file mode 100644 index 0000000..c8033e3 --- /dev/null +++ b/src/api/v1/guilds/uuid/auditlogs.rs @@ -0,0 +1,37 @@ +use std::sync::Arc; + +use ::uuid::Uuid; +use axum::{ + Extension, Json, + extract::{Path, Query, State}, + http::StatusCode, + response::IntoResponse, +}; + +use crate::{ + api::v1::auth::CurrentUser, error::Error, objects::{AuditLog, Member, PaginationRequest, Permissions}, utils::global_checks, AppState +}; + +pub async fn get( + State(app_state): State>, + Path(guild_uuid): Path, + Query(pagination): Query, + Extension(CurrentUser(uuid)): Extension>, +) -> Result { + let mut conn = app_state.pool.get().await?; + + global_checks(&mut conn, &app_state.config, uuid).await?; + + let caller = Member::check_membership(&mut conn, uuid, guild_uuid).await?; + caller.check_permission(&mut conn, &app_state.cache_pool, Permissions::ManageGuild).await?; + + + let logs = AuditLog::fetch_page( + &mut conn, + guild_uuid, + pagination, + ) + .await?; + + Ok((StatusCode::OK, Json(logs))) +} diff --git a/src/api/v1/guilds/uuid/mod.rs b/src/api/v1/guilds/uuid/mod.rs index 53f469b..81d749a 100644 --- a/src/api/v1/guilds/uuid/mod.rs +++ b/src/api/v1/guilds/uuid/mod.rs @@ -17,6 +17,7 @@ mod channels; mod invites; mod members; mod roles; +mod auditlogs; use crate::{ AppState, @@ -46,6 +47,8 @@ pub fn router() -> Router> { // Bans .route("/bans", get(bans::get)) .route("/bans/{uuid}", delete(bans::unban)) + // Audit Logs + .route("/auditlogs", get(auditlogs::get)) } /// `GET /api/v1/guilds/{uuid}` DESCRIPTION diff --git a/src/objects/auditlog.rs b/src/objects/auditlog.rs index d55fe06..3b5ff6e 100644 --- a/src/objects/auditlog.rs +++ b/src/objects/auditlog.rs @@ -1,7 +1,7 @@ use uuid::Uuid; -use diesel::{insert_into, Insertable, Queryable, Selectable, SelectableHelper}; +use diesel::{insert_into, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper, ExpressionMethods}; use serde::{Deserialize, Serialize}; -use crate::{error::Error, schema::audit_logs, Conn}; +use crate::{error::Error, objects::{load_or_empty, Pagination, PaginationRequest}, schema::audit_logs, Conn}; use diesel_async::RunQueryDsl; @@ -25,6 +25,55 @@ pub struct AuditLog { impl AuditLog { + pub async fn count(conn: &mut Conn, guild_uuid: Uuid) -> Result { + use audit_logs::dsl; + let count: i64 = dsl::audit_logs + .filter(dsl::guild_uuid.eq(guild_uuid)) + .count() + .get_result(conn) + .await?; + + Ok(count) + } + pub async fn fetch_page( + conn: &mut Conn, + guild_uuid: Uuid, + pagination: PaginationRequest, + ) -> Result, Error> { + + // TODO: Maybe add cache, but I do not know how + let per_page = pagination.per_page.unwrap_or(20); + let offset = (pagination.page - 1) * per_page; + + if !(10..=100).contains(&per_page) { + return Err(Error::BadRequest( + "Invalid amount per page requested".to_string(), + )); + } + + use audit_logs::dsl; + let logs: Vec = load_or_empty( + dsl::audit_logs + .filter(dsl::guild_uuid.eq(guild_uuid)) + .limit(per_page.into()) + .offset(offset as i64) + .select(AuditLog::as_select()) + .load(conn) + .await + )?; + + let pages = (AuditLog::count(conn, guild_uuid).await? as f32 / per_page as f32).ceil(); + + let paginated_logs = Pagination:: { + objects: logs.clone(), + amount: logs.len() as i32, + pages: pages as i32, + page: pagination.page, + }; + + Ok(paginated_logs) + } + #[allow(clippy::new_ret_no_self)] pub async fn new( conn: &mut Conn,