feat: Audit Logs (#466)

* fix: squash audit log branch changes

Signed-off-by: Zomatree <me@zomatree.live>

* feat: emoji edit + pronoun

Signed-off-by: Zomatree <me@zomatree.live>

* fix: use tokio instead of async-std

Signed-off-by: Zomatree <me@zomatree.live>

* feat: add slowmode to audit log

Signed-off-by: Zomatree <me@zomatree.live>

* chore: update comments on permissions

Signed-off-by: Zomatree <me@zomatree.live>

---------

Signed-off-by: Zomatree <me@zomatree.live>
This commit is contained in:
Zomatree
2026-07-13 21:39:28 +01:00
committed by GitHub
parent f60c8340f4
commit 502203d37c
57 changed files with 2014 additions and 150 deletions

View File

@@ -0,0 +1,62 @@
use revolt_result::{create_error, Error};
use revolt_rocket_okapi::{OpenApiError, gen::OpenApiGenerator, request::{OpenApiFromRequest, RequestHeaderInput}, revolt_okapi::openapi3::{Parameter, ParameterValue}};
use rocket::{
http::Status,
request::{FromRequest, Outcome, Request},
};
use schemars::schema::{InstanceType, SchemaObject, SingleOrVec};
/// Newtype for an audit log reason.
///
/// Extracts the reason from the `X-Audit-Log-Reason` header if provided.
pub struct AuditLogReason(pub Option<String>);
#[async_trait]
impl<'r> FromRequest<'r> for AuditLogReason {
type Error = Error;
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let reason = req.headers().get_one("x-audit-log-reason");
if reason.is_some_and(|str| str.len() > 512) {
return Outcome::Error((Status::BadRequest, create_error!(HeaderTooLarge)));
};
Outcome::Success(Self(reason.map(|str| str.to_string())))
}
}
impl OpenApiFromRequest<'_> for AuditLogReason {
fn from_request_input(
_gen: &mut OpenApiGenerator,
_name: String,
_required: bool,
) -> Result<RequestHeaderInput, OpenApiError> {
Ok(RequestHeaderInput::Parameter(Parameter {
name: "X-Audit-Log-Reason".to_string(),
description: Some("Reason for action which is stored in the audit log.".to_string()),
allow_empty_value: false,
required: false,
deprecated: false,
extensions: schemars::Map::new(),
location: "header".to_string(),
value: ParameterValue::Schema {
allow_reserved: false,
example: None,
examples: None,
explode: None,
style: None,
schema: SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
..Default::default()
},
},
}))
}
}
impl From<AuditLogReason> for Option<String> {
fn from(value: AuditLogReason) -> Self {
value.0
}
}

View File

@@ -1,3 +1,4 @@
pub mod audit_log_reason;
pub mod ratelimits;
#[cfg(test)]