diff --git a/crates/delta/src/routes/webhooks/mod.rs b/crates/delta/src/routes/webhooks/mod.rs index 6fdc732f..eb2d5d0e 100644 --- a/crates/delta/src/routes/webhooks/mod.rs +++ b/crates/delta/src/routes/webhooks/mod.rs @@ -2,16 +2,22 @@ use rocket::Route; use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; mod webhook_delete; +mod webhook_delete_token; mod webhook_edit; +mod webhook_edit_token; mod webhook_execute; +mod webhook_fetch_token; mod webhook_fetch; mod webhook_execute_github; pub fn routes() -> (Vec, OpenApi) { openapi_get_routes_spec![ + webhook_delete_token::req, webhook_delete::req, + webhook_edit_token::req, webhook_edit::req, webhook_execute::req, + webhook_fetch_token::req, webhook_fetch::req, webhook_execute_github::req ] diff --git a/crates/delta/src/routes/webhooks/webhook_delete.rs b/crates/delta/src/routes/webhooks/webhook_delete.rs index 84e401d1..2d3991c6 100644 --- a/crates/delta/src/routes/webhooks/webhook_delete.rs +++ b/crates/delta/src/routes/webhooks/webhook_delete.rs @@ -1,16 +1,19 @@ -use revolt_quark::{Db, Ref, Result, EmptyResponse, Error}; +use revolt_quark::{Db, Ref, Result, EmptyResponse, models::User, perms, Permission}; -/// # Deletes a webhook +/// # Deletes a webhook with a token /// -/// deletes a webhook +/// deletes a webhook with a token #[openapi(tag = "Webhooks")] -#[delete("//")] -pub async fn req(db: &Db, target: Ref, token: String) -> Result { +#[delete("/")] +pub async fn req(db: &Db, user: User, target: Ref) -> Result { let webhook = target.as_webhook(db).await?; - (webhook.token == token) - .then_some(()) - .ok_or(Error::InvalidCredentials)?; + let channel = Ref::from_unchecked(webhook.channel.clone()).as_channel(db).await?; + + perms(&user) + .channel(&channel) + .throw_permission(db, Permission::ManageWebhooks) + .await?; webhook.delete(db).await?; diff --git a/crates/delta/src/routes/webhooks/webhook_delete_token.rs b/crates/delta/src/routes/webhooks/webhook_delete_token.rs new file mode 100644 index 00000000..7f9bca11 --- /dev/null +++ b/crates/delta/src/routes/webhooks/webhook_delete_token.rs @@ -0,0 +1,18 @@ +use revolt_quark::{Db, Ref, Result, EmptyResponse, Error}; + +/// # Deletes a webhook with a token +/// +/// deletes a webhook with a token +#[openapi(tag = "Webhooks")] +#[delete("//")] +pub async fn req(db: &Db, target: Ref, token: String) -> Result { + let webhook = target.as_webhook(db).await?; + + (webhook.token == token) + .then_some(()) + .ok_or(Error::InvalidCredentials)?; + + webhook.delete(db).await?; + + Ok(EmptyResponse) +} diff --git a/crates/delta/src/routes/webhooks/webhook_edit.rs b/crates/delta/src/routes/webhooks/webhook_edit.rs index 0d5e1d81..0271eb66 100644 --- a/crates/delta/src/routes/webhooks/webhook_edit.rs +++ b/crates/delta/src/routes/webhooks/webhook_edit.rs @@ -1,4 +1,4 @@ -use revolt_quark::{Db, Ref, Result, Error, models::{webhook::{FieldsWebhook, Webhook, PartialWebhook}, File}}; +use revolt_quark::{Db, Ref, Result, Error, models::{webhook::{FieldsWebhook, Webhook, PartialWebhook}, File, User}, Permission, perms}; use serde::{Serialize, Deserialize}; use validator::Validate; use rocket::serde::json::Json; @@ -19,17 +19,20 @@ pub struct WebhookEditBody { /// /// edits a webhook #[openapi(tag = "Webhooks")] -#[patch("//", data="")] -pub async fn req(db: &Db, target: Ref, token: String, data: Json) -> Result> { +#[patch("/", data="")] +pub async fn req(db: &Db, target: Ref, user: User, data: Json) -> Result> { let data = data.into_inner(); data.validate() .map_err(|error| Error::FailedValidation { error })?; let mut webhook = target.as_webhook(db).await?; - (webhook.token == token) - .then_some(()) - .ok_or(Error::InvalidCredentials)?; + let channel = Ref::from_unchecked(webhook.channel.clone()).as_channel(db).await?; + + perms(&user) + .channel(&channel) + .throw_permission(db, Permission::ManageWebhooks) + .await?; if data.name.is_none() && data.avatar.is_none() diff --git a/crates/delta/src/routes/webhooks/webhook_edit_token.rs b/crates/delta/src/routes/webhooks/webhook_edit_token.rs new file mode 100644 index 00000000..efb18e4a --- /dev/null +++ b/crates/delta/src/routes/webhooks/webhook_edit_token.rs @@ -0,0 +1,57 @@ +use revolt_quark::{Db, Ref, Result, Error, models::{webhook::{FieldsWebhook, Webhook, PartialWebhook}, File}}; +use serde::{Serialize, Deserialize}; +use validator::Validate; +use rocket::serde::json::Json; + +#[derive(Serialize, Deserialize, Validate, JsonSchema)] +pub struct WebhookEditBody { + #[validate(length(min = 1, max = 32))] + name: Option, + + #[validate(length(min = 1, max = 128))] + avatar: Option, + + #[serde(default)] + remove: Vec +} + +/// # Edits a webhook with a token +/// +/// edits a webhook with a token +#[openapi(tag = "Webhooks")] +#[patch("//", data="")] +pub async fn req(db: &Db, target: Ref, token: String, data: Json) -> Result> { + let data = data.into_inner(); + data.validate() + .map_err(|error| Error::FailedValidation { error })?; + + let mut webhook = target.as_webhook(db).await?; + + (webhook.token == token) + .then_some(()) + .ok_or(Error::InvalidCredentials)?; + + if data.name.is_none() + && data.avatar.is_none() + && data.remove.is_empty() + { + return Ok(Json(webhook)) + }; + + let mut partial = PartialWebhook::default(); + + let WebhookEditBody { name, avatar, remove } = data; + + if let Some(name) = name { + partial.name = Some(name) + } + + if let Some(avatar) = avatar { + let file = File::use_avatar(db, &avatar, &webhook.id).await?; + partial.avatar = Some(file) + } + + webhook.update(db, partial, remove).await?; + + Ok(Json(webhook)) +} diff --git a/crates/delta/src/routes/webhooks/webhook_fetch.rs b/crates/delta/src/routes/webhooks/webhook_fetch.rs index 6472ba0e..d1b919f8 100644 --- a/crates/delta/src/routes/webhooks/webhook_fetch.rs +++ b/crates/delta/src/routes/webhooks/webhook_fetch.rs @@ -1,17 +1,28 @@ -use revolt_quark::{Db, Ref, Result, Error, models::Webhook}; +use revolt_quark::{Db, Ref, Result, models::{Webhook, File}}; use rocket::serde::json::Json; +use serde::{Serialize, Deserialize}; + + +// This route is used to get the info about the webhook by clients to get the name and avatar, +// so this function cant return the token or require any permissions. + +#[derive(Serialize, Deserialize, Debug, JsonSchema)] +pub struct WebhookData { + #[serde(rename = "_id")] + pub id: String, + pub name: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub avatar: Option, + pub channel: String, +} /// # gets a webhook /// /// gets a webhook #[openapi(tag = "Webhooks")] -#[get("//")] -pub async fn req(db: &Db, target: Ref, token: String) -> Result> { - let webhook = target.as_webhook(db).await?; +#[get("/")] +pub async fn req(db: &Db, target: Ref) -> Result> { + let Webhook { id, name, avatar, channel, .. } = target.as_webhook(db).await?; - (webhook.token == token) - .then_some(()) - .ok_or(Error::InvalidCredentials)?; - - Ok(Json(webhook)) + Ok(Json(WebhookData { id, name, avatar, channel })) } diff --git a/crates/delta/src/routes/webhooks/webhook_fetch_token.rs b/crates/delta/src/routes/webhooks/webhook_fetch_token.rs new file mode 100644 index 00000000..6472ba0e --- /dev/null +++ b/crates/delta/src/routes/webhooks/webhook_fetch_token.rs @@ -0,0 +1,17 @@ +use revolt_quark::{Db, Ref, Result, Error, models::Webhook}; +use rocket::serde::json::Json; + +/// # gets a webhook +/// +/// gets a webhook +#[openapi(tag = "Webhooks")] +#[get("//")] +pub async fn req(db: &Db, target: Ref, token: String) -> Result> { + let webhook = target.as_webhook(db).await?; + + (webhook.token == token) + .then_some(()) + .ok_or(Error::InvalidCredentials)?; + + Ok(Json(webhook)) +}