diff options
| author | MatthieuCoder <matthieu@matthieu-dev.xyz> | 2022-12-31 17:07:30 +0400 |
|---|---|---|
| committer | MatthieuCoder <matthieu@matthieu-dev.xyz> | 2022-12-31 17:07:30 +0400 |
| commit | 65652932f77ce194a10cbc8dd42f3064e2c1a132 (patch) | |
| tree | 4ca18a9317c4e561e917e9dd0cf39b695b43bc34 /exes/webhook/src/handler/handler.rs | |
| parent | a16bafdf5b0ec52fa0d73458597eee7c34ea5e7b (diff) | |
updates and bazel removal
Diffstat (limited to 'exes/webhook/src/handler/handler.rs')
| -rw-r--r-- | exes/webhook/src/handler/handler.rs | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/exes/webhook/src/handler/handler.rs b/exes/webhook/src/handler/handler.rs new file mode 100644 index 0000000..3294c22 --- /dev/null +++ b/exes/webhook/src/handler/handler.rs @@ -0,0 +1,176 @@ +use super::error::WebhookError; +use super::signature::validate_signature; +use crate::config::Config; +use ed25519_dalek::PublicKey; +use hyper::{ + body::{to_bytes, Bytes}, + service::Service, + Body, Method, Request, Response, StatusCode, +}; +use serde::{Deserialize, Serialize}; +use shared::nats_crate::Connection; +use shared::{ + log::{debug, error}, + payloads::{CachePayload, DispatchEventTagged, Tracing}, +}; +use std::{ + future::Future, + pin::Pin, + str::from_utf8, + sync::Arc, + task::{Context, Poll}, + time::Duration, +}; +use twilight_model::gateway::event::{DispatchEvent}; +use twilight_model::{ + application::interaction::{Interaction, InteractionType}, + gateway::payload::incoming::InteractionCreate, +}; + +/// Hyper service used to handle the discord webhooks +#[derive(Clone)] +pub struct HandlerService { + pub config: Arc<Config>, + pub nats: Arc<Connection>, + pub public_key: Arc<PublicKey>, +} + +impl HandlerService { + async fn check_request(&self, req: Request<Body>) -> Result<Bytes, WebhookError> { + if req.method() == Method::POST { + let signature = if let Some(sig) = req.headers().get("X-Signature-Ed25519") { + sig.to_owned() + } else { + return Err(WebhookError::new( + StatusCode::BAD_REQUEST, + "missing signature header", + )); + }; + + let timestamp = if let Some(timestamp) = req.headers().get("X-Signature-Timestamp") { + timestamp.to_owned() + } else { + return Err(WebhookError::new( + StatusCode::BAD_REQUEST, + "missing timestamp header", + )); + }; + let data = to_bytes(req.into_body()).await?; + + if validate_signature( + &self.public_key, + &[timestamp.as_bytes().to_vec(), data.to_vec()].concat(), + signature.to_str()?, + ) { + Ok(data) + } else { + Err(WebhookError::new( + StatusCode::UNAUTHORIZED, + "invalid signature", + )) + } + } else { + Err(WebhookError::new(StatusCode::NOT_FOUND, "not found")) + } + } + + async fn process_request( + &mut self, + req: Request<Body>, + ) -> Result<Response<Body>, WebhookError> { + match self.check_request(req).await { + Ok(data) => { + let utf8 = from_utf8(&data); + match utf8 { + Ok(data) => match serde_json::from_str::<Interaction>(data) { + Ok(value) => { + match value.kind { + InteractionType::Ping => Ok(Response::builder() + .header("Content-Type", "application/json") + .body(serde_json::to_string(&Ping { t: 1 }).unwrap().into()) + .unwrap()), + _ => { + debug!("calling nats"); + // this should hopefully not fail ? + + let data = CachePayload { + tracing: Tracing { + node_id: "".to_string(), + span: None, + }, + data: DispatchEventTagged { + data: DispatchEvent::InteractionCreate(Box::new( + InteractionCreate(value), + )), + }, + }; + + let payload = serde_json::to_string(&data).unwrap(); + + match self.nats.request_timeout( + "nova.cache.dispatch.INTERACTION_CREATE", + payload, + Duration::from_secs(2), + ) { + Ok(response) => Ok(Response::builder() + .header("Content-Type", "application/json") + .body(Body::from(response.data)) + .unwrap()), + + Err(error) => { + error!("failed to request nats: {}", error); + Err(WebhookError::new( + StatusCode::INTERNAL_SERVER_ERROR, + "failed to request nats", + )) + } + } + } + } + } + + Err(error) => { + error!("invalid json body: {}", error); + Err(WebhookError::new( + StatusCode::BAD_REQUEST, + "invalid json body", + )) + } + }, + + Err(_) => Err(WebhookError::new(StatusCode::BAD_REQUEST, "not utf-8 body")), + } + } + Err(error) => Err(error), + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Ping { + #[serde(rename = "type")] + t: i32, +} + +/// Implementation of the service +impl Service<Request<Body>> for HandlerService { + type Response = Response<Body>; + type Error = hyper::Error; + type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>; + + fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: Request<Body>) -> Self::Future { + let mut clone = self.clone(); + Box::pin(async move { + let response = clone.process_request(req).await; + + match response { + Ok(r) => Ok(r), + Err(e) => Ok(e.into()), + } + }) + } +} |
