From d29bd2e85dbb30f66967995fb8eb969a7b5adf12 Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:49:52 +0100 Subject: [PATCH] `/guilds` endpoint --- src/database/redis.rs | 6 ++++++ src/server/authorize.rs | 9 +++------ src/server/error.rs | 30 ++++++++++++++++++++---------- src/server/routes/guilds/_id.rs | 0 src/server/routes/guilds/mod.rs | 29 +++++++++++++++++++++++++++++ src/server/routes/mod.rs | 4 +++- 6 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 src/server/routes/guilds/_id.rs create mode 100644 src/server/routes/guilds/mod.rs diff --git a/src/database/redis.rs b/src/database/redis.rs index 830e05a..2d07d3f 100644 --- a/src/database/redis.rs +++ b/src/database/redis.rs @@ -83,4 +83,10 @@ impl RedisConnection { connection.zincr(path, user_id.to_string(), count).await } + #[cfg(feature = "api")] + pub async fn check_guild(&self, id: Id) -> Result { + let count: u8 = self.client.get_async_connection() + .await?.exists(format!("guilds.{id}")).await?; + Ok(count == 1) + } } \ No newline at end of file diff --git a/src/server/authorize.rs b/src/server/authorize.rs index 0d35d4b..8d9e759 100644 --- a/src/server/authorize.rs +++ b/src/server/authorize.rs @@ -1,14 +1,10 @@ -use std::convert::Infallible; use std::fmt::Display; use ed25519_dalek::Verifier; use ed25519_dalek::{PublicKey, Signature}; use std::str::FromStr; -use futures_util::{StreamExt, TryFutureExt, TryStreamExt}; use warp::Filter; use warp::hyper::body::Bytes; -use warp::path::Exact; -use warp::reject::Reject; -use crate::server::error::{MapErrorIntoInternalRejection, Rejection}; +use crate::server::error::Rejection; use crate::{err, reject, with_value}; pub fn verify_signature( @@ -44,7 +40,8 @@ pub fn filter(public_key: PublicKey) } async fn f(public_key: PublicKey, timestamp: String, signature: String, body: Bytes) -> Result<(), warp::Rejection> { - let body = String::from_utf8(body.to_vec()).map_rejection()?; + let body = String::from_utf8(body.to_vec()) + .map_err(|_| reject!(Rejection::BodyNotConvertableToString))?; if !verify_signature( public_key, diff --git a/src/server/error.rs b/src/server/error.rs index 361418a..d3f8eef 100644 --- a/src/server/error.rs +++ b/src/server/error.rs @@ -1,24 +1,36 @@ use std::convert::Infallible; -use std::fmt::{Debug, Display, Formatter, Pointer, Write}; +use std::fmt::{Debug, Display, Formatter, Write}; use reqwest::StatusCode; use warp::reject::Reject; use warp::Reply; #[derive(Debug)] pub enum Rejection { + #[cfg(feature = "http-interactions")] BodyNotConvertableToString, + #[cfg(feature = "http-interactions")] InvalidSignature, + #[cfg(feature = "api")] InvalidCode, + #[cfg(feature = "api")] + Unauthorized, + #[cfg(feature = "api")] Internal(anyhow::Error) } impl Display for Rejection { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { + #[cfg(feature = "http-interactions")] Rejection::BodyNotConvertableToString => f.write_str("Cannot convert bytes from body into utf8 encoded string"), + #[cfg(feature = "http-interactions")] Rejection::InvalidSignature => f.write_str("Couldn't verify signature"), + #[cfg(feature = "api")] Rejection::InvalidCode => f.write_str("Invalid `code` was provided"), - Rejection::Internal(err) => std::fmt::Display::fmt(&err, f) + #[cfg(feature = "api")] + Rejection::Unauthorized => f.write_str("Invalid authorization data provided"), + #[cfg(feature = "api")] + Rejection::Internal(err) => std::fmt::Display::fmt(&err, f), }?; Ok(()) } @@ -41,11 +53,10 @@ macro_rules! reject { } pub trait MapErrorIntoInternalRejection { - fn map_rejection(self) -> Result; - } +#[cfg(feature = "api")] impl> MapErrorIntoInternalRejection for Result where Self: Sized { fn map_rejection(self) -> Result { self.map_err(|err| reject!(Rejection::Internal(err.into()))) @@ -54,18 +65,17 @@ impl> MapErrorIntoInternalRejection for Result Response { -// handle_rejection(self) -// } -// } - pub async fn handle_rejection(rejection: warp::Rejection) -> Result { Ok(if let Some(rejection) = rejection.find::() { warp::reply::with_status(rejection.to_string(), match rejection { + #[cfg(feature = "http-interactions")] Rejection::BodyNotConvertableToString => StatusCode::BAD_REQUEST, + #[cfg(feature = "http-interactions")] Rejection::InvalidSignature => StatusCode::BAD_REQUEST, + #[cfg(feature = "api")] Rejection::InvalidCode => StatusCode::BAD_REQUEST, + #[cfg(feature = "api")] + Rejection::Unauthorized => StatusCode::UNAUTHORIZED, _ => StatusCode::INTERNAL_SERVER_ERROR }) } else { diff --git a/src/server/routes/guilds/_id.rs b/src/server/routes/guilds/_id.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/server/routes/guilds/mod.rs b/src/server/routes/guilds/mod.rs new file mode 100644 index 0000000..c38ac75 --- /dev/null +++ b/src/server/routes/guilds/mod.rs @@ -0,0 +1,29 @@ +use std::sync::Arc; +use futures_util::future::join_all; +use warp::{Filter, Reply}; +use crate::context::Context; +use crate::{response_type, with_value}; +use crate::server::error::MapErrorIntoInternalRejection; +use crate::server::session::{Authenticator, AuthorizationInformation, authorize_user, Sessions}; + +mod _id; + +pub fn list(context: Arc, authenticator: Arc, sessions: Arc) -> response_type!() { + let with_context = with_value!(context); + + warp::get() + .and(warp::path("guilds")) + .and(authorize_user(authenticator, sessions)) + .and(with_context) + .and_then(run) +} + +async fn run( + info: Arc, + context: Arc +) -> Result { + let guilds = info.http.current_user_guilds() + .await.map_rejection()?.model().await.map_rejection()?; + // join_all(guilds.map(|guild| context.redis.) + Ok(warp::reply::json(&guilds)) +} \ No newline at end of file diff --git a/src/server/routes/mod.rs b/src/server/routes/mod.rs index bd6896d..df4cfb2 100644 --- a/src/server/routes/mod.rs +++ b/src/server/routes/mod.rs @@ -9,6 +9,7 @@ use crate::response_type; mod interactions; #[cfg(feature = "api")] mod login; +mod guilds; #[cfg(feature = "api")] mod users { @@ -26,7 +27,7 @@ pub fn get_all_routes( #[cfg(feature = "http-interactions")] let filter = filter.or(interactions::filter( - discord_http, context, public_key + discord_http, context.to_owned(), public_key )); #[cfg(feature = "api")] @@ -41,6 +42,7 @@ pub fn get_all_routes( let filter = filter .or(login::login(authenticator.to_owned(), sessions.to_owned())) .or(users::me::run(authenticator.to_owned(), sessions.to_owned())) + .or(guilds::list(context, authenticator, sessions)); filter } \ No newline at end of file