diff --git a/Cargo.lock b/Cargo.lock index 8d55ada1..7bdf672b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2867,7 +2867,10 @@ dependencies = [ "mongodb", "nanoid", "once_cell", + "redis-kiss", + "revolt-models", "revolt-permissions", + "revolt-presence", "revolt-result", "revolt_optional_struct", "rocket", @@ -2902,6 +2905,7 @@ dependencies = [ "revolt-database", "revolt-models", "revolt-quark", + "revolt-result", "revolt_rocket_okapi", "rocket", "rocket_authifier", @@ -2911,7 +2915,7 @@ dependencies = [ "serde_json", "ulid 0.4.1", "url", - "validator 0.14.0", + "validator 0.16.0", "vergen", ] @@ -2919,10 +2923,10 @@ dependencies = [ name = "revolt-models" version = "0.0.2" dependencies = [ - "revolt-database", - "revolt-presence", + "revolt_optional_struct", "schemars", "serde", + "validator 0.16.0", ] [[package]] @@ -2981,6 +2985,7 @@ dependencies = [ "redis-kiss", "regex", "reqwest", + "revolt-models", "revolt-presence", "revolt-result", "revolt_okapi", @@ -2995,7 +3000,7 @@ dependencies = [ "serde", "serde_json", "ulid 0.5.0", - "validator 0.14.0", + "validator 0.16.0", "web-push", ] @@ -3003,8 +3008,12 @@ dependencies = [ name = "revolt-result" version = "0.0.2" dependencies = [ + "revolt_okapi", + "revolt_rocket_okapi", + "rocket", "schemars", "serde", + "serde_json", ] [[package]] @@ -4378,23 +4387,6 @@ dependencies = [ "serde", ] -[[package]] -name = "validator" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0f08911ab0fee2c5009580f04615fa868898ee57de10692a45da0c3bcc3e5e" -dependencies = [ - "idna", - "lazy_static", - "regex", - "serde", - "serde_derive", - "serde_json", - "url", - "validator_derive", - "validator_types", -] - [[package]] name = "validator" version = "0.15.0" @@ -4411,10 +4403,26 @@ dependencies = [ ] [[package]] -name = "validator_derive" -version = "0.14.0" +name = "validator" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85135714dba11a1bd0b3eb1744169266f1a38977bf4e3ff5e2e1acb8c2b7eee" +checksum = "32ad5bf234c7d3ad1042e5252b7eddb2c4669ee23f32c7dd0e9b7705f07ef591" +dependencies = [ + "idna", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", +] + +[[package]] +name = "validator_derive" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af" dependencies = [ "if_chain", "lazy_static", @@ -4428,9 +4436,9 @@ dependencies = [ [[package]] name = "validator_types" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded9d97e1d42327632f5f3bae6403c04886e2de3036261ef42deebd931a6a291" +checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3" dependencies = [ "proc-macro2", "syn 1.0.107", diff --git a/crates/delta/Cargo.toml b/crates/delta/Cargo.toml index 1445cb60..99b1d7c4 100644 --- a/crates/delta/Cargo.toml +++ b/crates/delta/Cargo.toml @@ -31,7 +31,7 @@ nanoid = "0.4.0" # serde serde_json = "1.0.57" serde = { version = "1.0.115", features = ["derive"] } -validator = { version = "0.14", features = ["derive"] } +validator = { version = "0.16", features = ["derive"] } # async futures = "0.3.8" @@ -56,8 +56,9 @@ revolt_rocket_okapi = { version = "0.9.1", features = [ "swagger" ] } revolt-quark = { path = "../quark" } # core -revolt-database = { path = "../core/database", features = [ "rocket-impl" ] } -revolt-models = { path = "../core/models", features = [ "schemas", "redis-is-patched" ] } +revolt-database = { path = "../core/database", features = [ "rocket-impl", "redis-is-patched" ] } +revolt-models = { path = "../core/models", features = [ "schemas", "validator" ] } +revolt-result = { path = "../core/result", features = [ "rocket", "okapi" ] } [build-dependencies] vergen = "7.5.0" diff --git a/crates/delta/src/routes/bots/fetch.rs b/crates/delta/src/routes/bots/fetch.rs index 7ccac797..48aaaf5b 100644 --- a/crates/delta/src/routes/bots/fetch.rs +++ b/crates/delta/src/routes/bots/fetch.rs @@ -23,11 +23,12 @@ pub async fn fetch_bot( } Ok(Json(FetchBotResponse { - user: revolt_models::v0::User::from( - db.fetch_user(&bot.id).await.map_err(Error::from_core)?, - None, - ) - .await, + user: db + .fetch_user(&bot.id) + .await + .map_err(Error::from_core)? + .into(None) + .await, bot: bot.into(), })) } diff --git a/crates/delta/src/routes/bots/fetch_public.rs b/crates/delta/src/routes/bots/fetch_public.rs index 796dc43e..258a9cd3 100644 --- a/crates/delta/src/routes/bots/fetch_public.rs +++ b/crates/delta/src/routes/bots/fetch_public.rs @@ -21,5 +21,5 @@ pub async fn fetch_public_bot( } let user = db.fetch_user(&bot.id).await.map_err(Error::from_core)?; - Ok(Json(PublicBot::from(bot, user))) + Ok(Json(bot.into_public_bot(user))) } diff --git a/crates/delta/src/routes/channels/webhook_create.rs b/crates/delta/src/routes/channels/webhook_create.rs index 7d2bafbb..eb8b71bb 100644 --- a/crates/delta/src/routes/channels/webhook_create.rs +++ b/crates/delta/src/routes/channels/webhook_create.rs @@ -1,5 +1,9 @@ -use revolt_quark::{models::{User, Webhook, File, Channel}, perms, Db, Error, Permission, Ref, Result}; -use rocket::serde::json::Json; +use revolt_database::{Database, Webhook}; +use revolt_quark::{ + models::{Channel, User}, + perms, Db, Error, Permission, Ref, Result, +}; +use rocket::{serde::json::Json, State}; use serde::{Deserialize, Serialize}; use ulid::Ulid; use validator::Validate; @@ -10,46 +14,56 @@ pub struct CreateWebhookBody { name: String, #[validate(length(min = 1, max = 128))] - avatar: Option + avatar: Option, } /// # Creates a webhook /// -/// creates a webhook which 3rd party platforms can use to send messages +/// Creates a webhook which 3rd party platforms can use to send messages #[openapi(tag = "Webhooks")] #[post("//webhooks", data = "")] -pub async fn req(db: &Db, user: User, target: Ref, data: Json) -> Result> { +pub async fn req( + db: &State, + legacy_db: &Db, + user: User, + target: Ref, + data: Json, +) -> Result> { let data = data.into_inner(); data.validate() .map_err(|error| Error::FailedValidation { error })?; - let channel = target.as_channel(db).await?; + let channel = target.as_channel(legacy_db).await?; if !matches!(channel, Channel::TextChannel { .. } | Channel::Group { .. }) { - return Err(Error::InvalidOperation) + return Err(Error::InvalidOperation); } let mut permissions = perms(&user).channel(&channel); permissions - .has_permission(db, Permission::ManageWebhooks) + .has_permission(legacy_db, Permission::ManageWebhooks) .await?; let webhook_id = Ulid::new().to_string(); let avatar = match &data.avatar { - Some(id) => Some(File::use_avatar(db, id, &webhook_id).await?), - None => None + Some(id) => Some( + db.find_and_use_attachment(id, "avatars", "user", &webhook_id) + .await + .map_err(Error::from_core)?, + ), + None => None, }; let webhook = Webhook { id: webhook_id, name: data.name, avatar, - channel: channel.id().to_string(), - token: Some(nanoid::nanoid!(64)) + channel_id: channel.id().to_string(), + token: Some(nanoid::nanoid!(64)), }; - webhook.create(db).await?; + webhook.create(db).await.map_err(Error::from_core)?; - Ok(Json(webhook)) + Ok(Json(webhook.into())) } diff --git a/crates/delta/src/routes/channels/webhook_fetch_all.rs b/crates/delta/src/routes/channels/webhook_fetch_all.rs index 8f058187..4c03b128 100644 --- a/crates/delta/src/routes/channels/webhook_fetch_all.rs +++ b/crates/delta/src/routes/channels/webhook_fetch_all.rs @@ -1,19 +1,31 @@ -use revolt_quark::{models::{User, Webhook}, perms, Db, Permission, Ref, Result}; -use rocket::serde::json::Json; +use revolt_database::Database; +use revolt_models::v0::Webhook; +use revolt_quark::{models::User, perms, Db, Error, Permission, Ref, Result}; +use rocket::{serde::json::Json, State}; /// # Gets all webhooks /// -/// gets all webhooks inside the channel +/// Gets all webhooks inside the channel #[openapi(tag = "Webhooks")] -#[get("//webhooks")] -pub async fn req(db: &Db, user: User, target: Ref) -> Result>> { - let channel = target.as_channel(db).await?; +#[get("//webhooks")] +pub async fn req( + db: &State, + legacy_db: &Db, + user: User, + channel_id: Ref, +) -> Result>> { + let channel = channel_id.as_channel(legacy_db).await?; let mut permissions = perms(&user).channel(&channel); permissions - .has_permission(db, Permission::ManageWebhooks) + .has_permission(legacy_db, Permission::ManageWebhooks) .await?; - let webhooks = db.fetch_webhooks_for_channel(channel.id()).await?; - - Ok(Json(webhooks)) + Ok(Json( + db.fetch_webhooks_for_channel(channel.id()) + .await + .map_err(Error::from_core)? + .into_iter() + .map(|v| v.into()) + .collect::>(), + )) } diff --git a/crates/delta/src/routes/webhooks/webhook_delete.rs b/crates/delta/src/routes/webhooks/webhook_delete.rs index 7a4dfc9d..03c79f63 100644 --- a/crates/delta/src/routes/webhooks/webhook_delete.rs +++ b/crates/delta/src/routes/webhooks/webhook_delete.rs @@ -1,21 +1,34 @@ -use revolt_quark::{Db, Ref, Result, EmptyResponse, models::User, perms, Permission}; +use revolt_database::Database; +use revolt_quark::{models::User, perms, Db, Error, Permission, Result}; +use rocket::State; +use rocket_empty::EmptyResponse; /// # Deletes a webhook /// -/// deletes a webhook +/// Deletes a webhook #[openapi(tag = "Webhooks")] -#[delete("/")] -pub async fn webhook_delete(db: &Db, user: User, target: Ref) -> Result { - let webhook = target.as_webhook(db).await?; +#[delete("/")] +pub async fn webhook_delete( + db: &State, + legacy_db: &Db, + user: User, + webhook_id: String, +) -> Result { + let webhook = db + .fetch_webhook(&webhook_id) + .await + .map_err(Error::from_core)?; - let channel = Ref::from_unchecked(webhook.channel.clone()).as_channel(db).await?; + let channel = legacy_db.fetch_channel(&webhook.channel_id).await?; perms(&user) .channel(&channel) - .throw_permission(db, Permission::ManageWebhooks) + .throw_permission(legacy_db, Permission::ManageWebhooks) .await?; - webhook.delete(db).await?; - - Ok(EmptyResponse) + webhook + .delete(db) + .await + .map(|_| EmptyResponse) + .map_err(Error::from_core) } diff --git a/crates/delta/src/routes/webhooks/webhook_delete_token.rs b/crates/delta/src/routes/webhooks/webhook_delete_token.rs index dffcdd83..2b2ed0cd 100644 --- a/crates/delta/src/routes/webhooks/webhook_delete_token.rs +++ b/crates/delta/src/routes/webhooks/webhook_delete_token.rs @@ -1,18 +1,19 @@ -use revolt_quark::{Db, Ref, Result, EmptyResponse, Error}; +use revolt_database::Database; +use revolt_result::Result; +use rocket::State; +use rocket_empty::EmptyResponse; /// # Deletes a webhook /// -/// deletes a webhook with a token +/// Deletes a webhook with a token #[openapi(tag = "Webhooks")] -#[delete("//")] -pub async fn webhook_delete_token(db: &Db, target: Ref, token: String) -> Result { - let webhook = target.as_webhook(db).await?; - - (webhook.token.as_deref() == Some(&token)) - .then_some(()) - .ok_or(Error::InvalidCredentials)?; - - webhook.delete(db).await?; - - Ok(EmptyResponse) +#[delete("//")] +pub async fn webhook_delete_token( + db: &State, + webhook_id: String, + token: String, +) -> Result { + let webhook = db.fetch_webhook(&webhook_id).await?; + webhook.assert_token(&token)?; + webhook.delete(db).await.map(|_| EmptyResponse) } diff --git a/crates/delta/src/routes/webhooks/webhook_edit.rs b/crates/delta/src/routes/webhooks/webhook_edit.rs index 8644aa2f..3c8a1301 100644 --- a/crates/delta/src/routes/webhooks/webhook_edit.rs +++ b/crates/delta/src/routes/webhooks/webhook_edit.rs @@ -1,60 +1,65 @@ -use revolt_quark::{Db, Ref, Result, Error, models::{webhook::{FieldsWebhook, Webhook, PartialWebhook}, File, User}, Permission, perms}; -use serde::{Serialize, Deserialize}; +use revolt_database::{Database, PartialWebhook}; +use revolt_models::v0::{DataEditWebhook, Webhook}; +use revolt_quark::{models::User, perms, Db, Error, Permission, Result}; +use rocket::{serde::json::Json, State}; 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 /// -/// edits a webhook +/// Edits a webhook #[openapi(tag = "Webhooks")] -#[patch("/", data="")] -pub async fn webhook_edit(db: &Db, target: Ref, user: User, data: Json) -> Result> { +#[patch("/", data = "")] +pub async fn webhook_edit( + db: &State, + legacy_db: &Db, + webhook_id: String, + 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?; + let mut webhook = db + .fetch_webhook(&webhook_id) + .await + .map_err(Error::from_core)?; - let channel = Ref::from_unchecked(webhook.channel.clone()).as_channel(db).await?; + let channel = legacy_db.fetch_channel(&webhook.channel_id).await?; perms(&user) .channel(&channel) - .throw_permission(db, Permission::ManageWebhooks) + .throw_permission(legacy_db, Permission::ManageWebhooks) .await?; - if data.name.is_none() - && data.avatar.is_none() - && data.remove.is_empty() - { - return Ok(Json(webhook)) + if data.name.is_none() && data.avatar.is_none() && data.remove.is_empty() { + return Ok(Json(webhook.into())); }; - let mut partial = PartialWebhook::default(); + let DataEditWebhook { + name, + avatar, + remove, + } = data; - let WebhookEditBody { name, avatar, remove } = data; - - if let Some(name) = name { - partial.name = Some(name) - } + let mut partial = PartialWebhook { + name, + ..Default::default() + }; if let Some(avatar) = avatar { - let file = File::use_avatar(db, &avatar, &webhook.id).await?; + let file = db + .find_and_use_attachment(&avatar, "avatars", "user", &webhook.id) + .await + .map_err(Error::from_core)?; + partial.avatar = Some(file) } - webhook.update(db, partial, remove).await?; + webhook + .update(db, partial, remove.into_iter().map(|v| v.into()).collect()) + .await + .map_err(Error::from_core)?; - Ok(Json(webhook)) + Ok(Json(webhook.into())) } diff --git a/crates/delta/src/routes/webhooks/webhook_edit_token.rs b/crates/delta/src/routes/webhooks/webhook_edit_token.rs index f8082f74..2bf0d2f7 100644 --- a/crates/delta/src/routes/webhooks/webhook_edit_token.rs +++ b/crates/delta/src/routes/webhooks/webhook_edit_token.rs @@ -1,57 +1,56 @@ -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 -} +use revolt_database::{Database, PartialWebhook}; +use revolt_models::v0::{DataEditWebhook, Webhook}; +use revolt_models::validator::Validate; +use revolt_result::{create_error, Result}; +use rocket::{serde::json::Json, State}; /// # Edits a webhook /// -/// edits a webhook with a token +/// Edits a webhook with a token #[openapi(tag = "Webhooks")] -#[patch("//", data="")] -pub async fn webhook_edit_token(db: &Db, target: Ref, token: String, data: Json) -> Result> { +#[patch("//", data = "")] +pub async fn webhook_edit_token( + db: &State, + webhook_id: String, + token: String, + data: Json, +) -> Result> { let data = data.into_inner(); - data.validate() - .map_err(|error| Error::FailedValidation { error })?; + data.validate().map_err(|error| { + create_error!(FailedValidation { + error: error.to_string() + }) + })?; - let mut webhook = target.as_webhook(db).await?; + let mut webhook = db.fetch_webhook(&webhook_id).await?; + webhook.assert_token(&token)?; - (webhook.token.as_deref() == Some(&token)) - .then_some(()) - .ok_or(Error::InvalidCredentials)?; - - if data.name.is_none() - && data.avatar.is_none() - && data.remove.is_empty() - { - return Ok(Json(webhook)) + if data.name.is_none() && data.avatar.is_none() && data.remove.is_empty() { + return Ok(Json(webhook.into())); }; - let mut partial = PartialWebhook::default(); + let DataEditWebhook { + name, + avatar, + remove, + } = data; - let WebhookEditBody { name, avatar, remove } = data; - - if let Some(name) = name { - partial.name = Some(name) - } + let mut partial = PartialWebhook { + name, + ..Default::default() + }; if let Some(avatar) = avatar { - let file = File::use_avatar(db, &avatar, &webhook.id).await?; + let file = db + .find_and_use_attachment(&avatar, "avatars", "user", &webhook.id) + .await?; + partial.avatar = Some(file) } - webhook.update(db, partial, remove).await?; + webhook + .update(db, partial, remove.into_iter().map(|v| v.into()).collect()) + .await?; - Ok(Json(webhook)) + Ok(Json(webhook.into())) } diff --git a/crates/delta/src/routes/webhooks/webhook_execute.rs b/crates/delta/src/routes/webhooks/webhook_execute.rs index 40a9fdaf..94772d6a 100644 --- a/crates/delta/src/routes/webhooks/webhook_execute.rs +++ b/crates/delta/src/routes/webhooks/webhook_execute.rs @@ -1,26 +1,47 @@ -use revolt_quark::{Db, Ref, Result, Error, models::message::{Message, DataMessageSend}, web::idempotency::IdempotencyKey, types::push::MessageAuthor}; -use rocket::serde::json::Json; +use revolt_database::Database; +use revolt_quark::{ + models::message::{DataMessageSend, Message}, + types::push::MessageAuthor, + web::idempotency::IdempotencyKey, + Db, Error, Result, +}; +use rocket::{serde::json::Json, State}; use validator::Validate; /// # Executes a webhook /// -/// executes a webhook and sends a message +/// Executes a webhook and sends a message #[openapi(tag = "Webhooks")] -#[post("//", data="")] -pub async fn webhook_execute(db: &Db, target: Ref, token: String, data: Json, idempotency: IdempotencyKey) -> Result> { +#[post("//", data = "")] +pub async fn webhook_execute( + db: &State, + legacy_db: &Db, + webhook_id: String, + token: String, + data: Json, + idempotency: IdempotencyKey, +) -> Result> { let data = data.into_inner(); data.validate() .map_err(|error| Error::FailedValidation { error })?; - let webhook = target.as_webhook(db).await?; + let webhook = db + .fetch_webhook(&webhook_id) + .await + .map_err(Error::from_core)?; - (webhook.token.as_deref() == Some(&token)) - .then_some(()) - .ok_or(Error::InvalidCredentials)?; + webhook.assert_token(&token).map_err(Error::from_core)?; - let channel = Ref::from_unchecked(webhook.channel.clone()).as_channel(db).await?; - let message = channel.send_message(db, data, MessageAuthor::Webhook(&webhook), idempotency).await?; + let channel = legacy_db.fetch_channel(&webhook.channel_id).await?; + let message = channel + .send_message( + legacy_db, + data, + MessageAuthor::Webhook(&webhook.into()), + idempotency, + ) + .await?; Ok(Json(message)) } diff --git a/crates/delta/src/routes/webhooks/webhook_execute_github.rs b/crates/delta/src/routes/webhooks/webhook_execute_github.rs index 1e094505..409b602c 100644 --- a/crates/delta/src/routes/webhooks/webhook_execute_github.rs +++ b/crates/delta/src/routes/webhooks/webhook_execute_github.rs @@ -1,6 +1,16 @@ -use revolt_quark::{Db, Ref, Result, Error, models::{Message, message::SendableEmbed}, types::push::MessageAuthor}; -use rocket::{Request, request::FromRequest, http::Status}; -use revolt_rocket_okapi::{request::{OpenApiFromRequest, RequestHeaderInput}, revolt_okapi::openapi3::{Parameter, ParameterValue, MediaType}, gen::OpenApiGenerator}; +use revolt_database::Database; +use revolt_models::v0::Webhook; +use revolt_quark::{ + models::{message::SendableEmbed, Message}, + types::push::MessageAuthor, + Db, Error, Result, +}; +use revolt_rocket_okapi::{ + gen::OpenApiGenerator, + request::{OpenApiFromRequest, RequestHeaderInput}, + revolt_okapi::openapi3::{MediaType, Parameter, ParameterValue}, +}; +use rocket::{http::Status, request::FromRequest, Request, State}; use schemars::schema::SchemaObject; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -30,14 +40,14 @@ pub struct GithubUser { #[derive(Serialize, Deserialize, Debug, JsonSchema)] pub struct GithubRepositorySecurityAndAnalysisStatus { - status: String + status: String, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] pub struct GithubRepositorySecurityAndAnalysis { advanced_security: GithubRepositorySecurityAndAnalysisStatus, secret_scanning: GithubRepositorySecurityAndAnalysisStatus, - secret_scanning_push_protection: GithubRepositorySecurityAndAnalysisStatus + secret_scanning_push_protection: GithubRepositorySecurityAndAnalysisStatus, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -55,7 +65,7 @@ pub struct GithubRepositoryCodeOfConduct { name: String, url: String, body: Option, - html_url: Option + html_url: Option, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -64,7 +74,7 @@ pub struct GithubRepositoryPermissions { maintain: Option, push: Option, triage: Option, - pull: Option + pull: Option, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -155,16 +165,15 @@ pub struct GithubRepository { watchers: Option, allow_forking: Option, web_commit_signoff_required: Option, - security_and_analysis: Option + security_and_analysis: Option, } - #[derive(Serialize, Deserialize, Debug, JsonSchema)] pub struct CommitAuthor { date: Option, email: Option, name: String, - username: Option + username: Option, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -179,13 +188,13 @@ pub struct GithubCommit { removed: Option>, timestamp: String, tree_id: String, - url: String + url: String, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] pub struct GithubReactions { - #[serde(rename="+1")] + #[serde(rename = "+1")] plus_one: u32, - #[serde(rename="-1")] + #[serde(rename = "-1")] minus_one: u32, confused: u32, eyes: u32, @@ -194,7 +203,7 @@ pub struct GithubReactions { laugh: u32, rocket: u32, total_count: u32, - url: String + url: String, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -212,7 +221,7 @@ pub struct GithubComment { reactions: Option, updated_at: Value, url: String, - user: GithubUser + user: GithubUser, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -221,7 +230,7 @@ pub struct GithubDiscussionComment { comment: GithubComment, child_comment_count: u32, parent_id: Option, - repository_url: String + repository_url: String, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -235,7 +244,7 @@ pub struct GithubDiscussionCategory { created_at: Value, updated_at: Value, slug: String, - is_answerable: bool + is_answerable: bool, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -243,8 +252,8 @@ pub struct GithubDiscussion { repository_url: String, category: GithubDiscussionCategory, answer_html_url: Option, - answer_chosen_at: Value, // ?? - answer_chosen_by: Value, // ?? + answer_chosen_at: Value, // ?? + answer_chosen_by: Value, // ?? html_url: String, id: u32, node_id: String, @@ -260,7 +269,7 @@ pub struct GithubDiscussion { active_lock_reason: Option, body: String, reactions: GithubReactions, - timeline_url: String + timeline_url: String, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -268,12 +277,12 @@ pub struct GithubDiscussion { #[allow(clippy::large_enum_variant)] pub enum GithubDiscussionEvent { Created { - discussion: GithubDiscussion + discussion: GithubDiscussion, }, Answered { discussion: GithubDiscussion, - answer: GithubDiscussionComment - } + answer: GithubDiscussionComment, + }, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -282,10 +291,10 @@ pub enum GithubDiscussionEvent { pub enum DiscussionCommentEvent { Created { comment: GithubDiscussionComment, - discussion: GithubDiscussion + discussion: GithubDiscussion, }, Deleted {}, - Edited {} + Edited {}, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -305,7 +314,7 @@ pub struct GithubMilestone { created_at: Value, updated_at: Value, closed_at: Option, - due_on: Option + due_on: Option, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -322,10 +331,9 @@ pub struct GithubAppPermissions { checks: Option, metadata: Option, contents: Option, - deployments: Option + deployments: Option, } - #[derive(Serialize, Deserialize, Debug, JsonSchema)] pub struct GithubApp { id: u32, @@ -344,7 +352,7 @@ pub struct GithubApp { client_id: Option, client_secret: Option, webhook_secret: Option, - pem: Option + pem: Option, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -380,7 +388,7 @@ pub struct GithubIssue { performed_via_github_app: Option, author_association: String, reactions: Option, - title: String + title: String, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -389,10 +397,10 @@ pub struct GithubIssue { pub enum IssueCommentEvent { Created { comment: GithubComment, - issue: GithubIssue + issue: GithubIssue, }, Deleted {}, - Edited {} + Edited {}, } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -400,32 +408,26 @@ pub enum IssueCommentEvent { #[allow(clippy::large_enum_variant)] pub enum IssuesEvent { Assigned {}, - Closed { - issue: GithubIssue - }, + Closed { issue: GithubIssue }, Deleted {}, Demilestoned {}, Edited {}, Labeled {}, Locked {}, Milestoned {}, - Opened { - issue: GithubIssue - }, + Opened { issue: GithubIssue }, Pinned {}, - Reopened { - issue: GithubIssue - }, + Reopened { issue: GithubIssue }, Transferred {}, Unassigned {}, Unlabeled {}, Unlocked {}, - Unpinned {} + Unpinned {}, } #[derive(Serialize, Deserialize, Debug)] pub struct StarEvent { - starred_at: Option + starred_at: Option, } #[derive(Serialize, Deserialize, Debug)] @@ -440,12 +442,12 @@ pub struct PushEvent { forced: bool, head_commit: Option, pusher: CommitAuthor, - r#ref: String + r#ref: String, } #[derive(Serialize, Deserialize, Debug)] pub struct CommitCommentEvent { - comment: GithubComment + comment: GithubComment, } #[derive(Serialize, Deserialize, Debug)] @@ -453,19 +455,19 @@ pub struct CreateEvent { master_branch: String, pusher_type: String, r#ref: String, - ref_type: String + ref_type: String, } #[derive(Serialize, Deserialize, Debug)] pub struct DeleteEvent { pusher_type: String, r#ref: String, - ref_type: String + ref_type: String, } #[derive(Serialize, Deserialize, Debug)] pub struct ForkEvent { - forkee: GithubRepository + forkee: GithubRepository, } #[derive(Serialize, Deserialize, Debug)] @@ -481,7 +483,7 @@ pub struct GithubTeam { permission: String, members_url: String, repositories_url: String, - parent: Option> + parent: Option>, } #[derive(Serialize, Deserialize, Debug)] @@ -490,17 +492,17 @@ pub struct GithubHead { r#ref: String, repo: GithubRepository, sha: String, - user: Option + user: Option, } #[derive(Serialize, Deserialize, Debug)] pub struct GithubHref { - href: String + href: String, } #[derive(Serialize, Deserialize, Debug)] pub struct GithubLinks { - #[serde(rename="self")] + #[serde(rename = "self")] _self: GithubHref, html: GithubHref, comments: GithubHref, @@ -508,7 +510,7 @@ pub struct GithubLinks { statuses: GithubHref, issue: GithubHref, review_comments: GithubHref, - review_comment: GithubHref + review_comment: GithubHref, } #[derive(Serialize, Deserialize, Debug)] @@ -516,7 +518,7 @@ pub struct GithubAutoMerge { enabled_by: GithubUser, merge_method: String, commit_title: String, - commit_message: String + commit_message: String, } #[derive(Serialize, Deserialize, Debug)] @@ -548,7 +550,7 @@ pub struct GithubPullRequest { _links: GithubLinks, author_association: String, auto_merge: Option, - draft: bool + draft: bool, } #[derive(Serialize, Deserialize, Debug)] @@ -559,7 +561,7 @@ pub enum PullRequestEvent { AutoMergeEnabled {}, Closed { number: u32, - pull_request: GithubPullRequest + pull_request: GithubPullRequest, }, ConvertedToDraft {}, Demilestoned {}, @@ -571,20 +573,19 @@ pub enum PullRequestEvent { Milestoned {}, Opened { number: u32, - pull_request: GithubPullRequest + pull_request: GithubPullRequest, }, ReadyForReview {}, Reopened { number: u32, - pull_request: GithubPullRequest - + pull_request: GithubPullRequest, }, ReviewRequestRemoved {}, ReviewRequest {}, Synchronized {}, Unassigned {}, Unlabeled {}, - Unlocked {} + Unlocked {}, } #[derive(Debug)] @@ -601,14 +602,14 @@ pub enum BaseEvent { Fork(ForkEvent), IssueComment(IssueCommentEvent), Issues(IssuesEvent), - PullRequest(PullRequestEvent) + PullRequest(PullRequestEvent), } #[derive(Serialize, Deserialize, Debug)] pub struct _Event { action: Option, sender: GithubUser, - repository: GithubRepository + repository: GithubRepository, } #[derive(Debug)] @@ -616,7 +617,7 @@ pub struct Event { event: BaseEvent, action: Option, sender: GithubUser, - repository: GithubRepository + repository: GithubRepository, } #[derive(Debug, JsonSchema)] @@ -634,7 +635,7 @@ impl<'r> std::ops::Deref for EventHeader<'r> { impl<'r> FromRequest<'r> for EventHeader<'r> { type Error = Error; - async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome { + async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome { let headers = request.headers(); let Some(event) = headers.get_one("X-GitHub-Event") else { return rocket::request::Outcome::Failure((Status::BadRequest, Error::InvalidOperation)) @@ -651,29 +652,30 @@ impl<'r> OpenApiFromRequest<'r> for EventHeader<'r> { _required: bool, ) -> revolt_rocket_okapi::Result { let mut content = schemars::Map::new(); - content.insert("X-Github-Event".to_string(), MediaType { - schema: Some(SchemaObject { - string: Some(Box::default()), - ..Default::default() - }), - example: None, - examples: None, - encoding: schemars::Map::new(), - extensions: schemars::Map::new(), - }); + content.insert( + "X-Github-Event".to_string(), + MediaType { + schema: Some(SchemaObject { + string: Some(Box::default()), + ..Default::default() + }), + example: None, + examples: None, + encoding: schemars::Map::new(), + extensions: schemars::Map::new(), + }, + ); - Ok(RequestHeaderInput::Parameter( - Parameter { - name: "X-Github-Event".to_string(), - location: "header".to_string(), - required: true, - description: Some("The name of the github event".to_string()), - deprecated: false, - allow_empty_value: false, - value: ParameterValue::Content { content }, - extensions: schemars::Map::new() - } - )) + Ok(RequestHeaderInput::Parameter(Parameter { + name: "X-Github-Event".to_string(), + location: "header".to_string(), + required: true, + description: Some("The name of the github event".to_string()), + deprecated: false, + allow_empty_value: false, + value: ParameterValue::Content { content }, + extensions: schemars::Map::new(), + })) } } @@ -724,53 +726,82 @@ fn convert_event(data: &str, event_name: &str) -> Result { "issue_comment" => BaseEvent::IssueComment(safe_from_str(data)?), "issues" => BaseEvent::Issues(safe_from_str(data)?), "pull_request" => BaseEvent::PullRequest(safe_from_str(data)?), - _ => return Err(Error::InvalidOperation) + _ => return Err(Error::InvalidOperation), }; - let _Event { action, sender, repository } = event; + let _Event { + action, + sender, + repository, + } = event; Ok(Event { action, sender, repository, - event: base_event + event: base_event, }) } /// # Executes a webhook specific to github /// -/// executes a webhook specific to github and sends a message containg the relavent info about the event +/// Executes a webhook specific to github and sends a message containing the relevant info about the event #[openapi(tag = "Webhooks")] -#[post("///github", data="")] -pub async fn webhook_execute_github(db: &Db, target: Ref, token: String, event: EventHeader<'_>, data: String) -> Result<()> { - let webhook = target.as_webhook(db).await?; +#[post("///github", data = "")] +pub async fn webhook_execute_github( + db: &State, + legacy_db: &Db, + webhook_id: String, + token: String, + event: EventHeader<'_>, + data: String, +) -> Result<()> { + let webhook = db + .fetch_webhook(&webhook_id) + .await + .map_err(Error::from_core)?; - (webhook.token.as_deref() == Some(&token)) - .then_some(()) - .ok_or(Error::InvalidCredentials)?; - - let channel = db.fetch_channel(&webhook.channel).await?; + webhook.assert_token(&token).map_err(Error::from_core)?; + let channel = legacy_db.fetch_channel(&webhook.channel_id).await?; let event = convert_event(&data, &event)?; let sendable_embed = match event.event { BaseEvent::Star(_) => { - if event.action.as_deref() != Some("created") { return Ok(()) }; + if event.action.as_deref() != Some("created") { + return Ok(()); + }; SendableEmbed { title: Some(event.sender.login), - description: Some(format!("#### [[{}] New star added]({})", event.repository.full_name, event.repository.html_url)), + description: Some(format!( + "#### [[{}] New star added]({})", + event.repository.full_name, event.repository.html_url + )), colour: Some(GREY.to_string()), icon_url: Some(event.sender.avatar_url), url: Some(event.sender.html_url), ..Default::default() } - }, - BaseEvent::Push( PushEvent { after, commits, compare, forced, r#ref, .. }) => { + } + BaseEvent::Push(PushEvent { + after, + commits, + compare, + forced, + r#ref, + .. + }) => { let Some(branch) = r#ref.split('/').nth(2) else { return Ok(()) }; if forced { - let description = format!("#### [{}] Branch {} was force-pushed to {}\n[compare changes]({})", event.repository.full_name, branch, &after[0..=7], compare); + let description = format!( + "#### [{}] Branch {} was force-pushed to {}\n[compare changes]({})", + event.repository.full_name, + branch, + &after[0..=7], + compare + ); SendableEmbed { icon_url: Some(event.sender.avatar_url), @@ -781,10 +812,24 @@ pub async fn webhook_execute_github(db: &Db, target: Ref, token: String, event: ..Default::default() } } else { - let title = format!("[[{}:{}] {} new commit]({})", event.repository.full_name, branch, commits.len(), compare); + let title = format!( + "[[{}:{}] {} new commit]({})", + event.repository.full_name, + branch, + commits.len(), + compare + ); let commit_description = commits .into_iter() - .map(|commit| format!("[`{}`]({}) {} - {}", &commit.id[0..=7], commit.url, shorten_text(&commit.message, 50), commit.author.name)) + .map(|commit| { + format!( + "[`{}`]({}) {} - {}", + &commit.id[0..=7], + commit.url, + shorten_text(&commit.message, 50), + commit.author.name + ) + }) .collect::>() .join("\n"); @@ -797,193 +842,245 @@ pub async fn webhook_execute_github(db: &Db, target: Ref, token: String, event: ..Default::default() } } - }, + } BaseEvent::CommitComment(CommitCommentEvent { comment }) => { let commit_id = match comment.commit_id { Some(id) => id[0..=7].to_string(), - None => "".to_string() + None => "".to_string(), }; SendableEmbed { icon_url: Some(event.sender.avatar_url), url: Some(event.sender.html_url), title: Some(event.sender.login), - description: Some(format!("#### [[{}] New comment on commit `{}`]({})\n{}", event.repository.full_name, commit_id, comment.html_url, shorten_text(&comment.body, 450))), + description: Some(format!( + "#### [[{}] New comment on commit `{}`]({})\n{}", + event.repository.full_name, + commit_id, + comment.html_url, + shorten_text(&comment.body, 450) + )), colour: Some(GREY.to_string()), ..Default::default() } - }, - BaseEvent::Ping => { - return Ok(()) - }, - BaseEvent::Create(CreateEvent { r#ref, ref_type, .. }) => { - SendableEmbed { - icon_url: Some(event.sender.avatar_url), - url: Some(event.sender.html_url), - title: Some(event.sender.login), - description: Some(format!("#### [{}] New {} created: {}", event.repository.full_name, ref_type, r#ref)), - colour: Some(GREY.to_string()), - ..Default::default() - } - }, - BaseEvent::Delete( DeleteEvent { r#ref, ref_type, .. }) => { - SendableEmbed { - icon_url: Some(event.sender.avatar_url), - url: Some(event.sender.html_url), - title: Some(event.sender.login), - description: Some(format!("#### [{}] {} deleted: {}", event.repository.full_name, ref_type, r#ref)), - colour: Some(GREY.to_string()), - ..Default::default() - } - }, - BaseEvent::Discussion(discussion_event) => { - match discussion_event { - GithubDiscussionEvent::Created { discussion } => { - SendableEmbed { - icon_url: Some(event.sender.avatar_url), - url: Some(event.sender.html_url), - title: Some(event.sender.login), - description: Some(format!("#### [{}] New discussion #{}: {}\n{}", event.repository.full_name, discussion.number, discussion.title, shorten_text(&discussion.body, 450))), - colour: Some(LIGHT_ORANGE.to_string()), - ..Default::default() - } - }, - GithubDiscussionEvent::Answered { discussion, answer } => { - SendableEmbed { - icon_url: Some(answer.comment.user.avatar_url), - url: Some(answer.comment.user.html_url), - title: Some(answer.comment.user.login), - description: Some(format!("#### [{}] discussion #{} marked answered: {}\n{}", event.repository.full_name, discussion.number, discussion.title, shorten_text(&answer.comment.body, 450))), - colour: Some(LIGHT_ORANGE.to_string()), - ..Default::default() - } - }, - } - }, - BaseEvent::DiscussionComment(comment_event) => { - match comment_event { - DiscussionCommentEvent::Created { comment, discussion } => { - SendableEmbed { - icon_url: Some(comment.comment.user.avatar_url), - url: Some(comment.comment.user.html_url), - title: Some(comment.comment.user.login), - description: Some(format!("[{}] New comment on discussion #{}: {}\n{}", event.repository.full_name, discussion.number, discussion.title, shorten_text(&comment.comment.body, 450))), - colour: Some(LIGHT_ORANGE.to_string()), - ..Default::default() - } - }, - _ => { return Ok(()) } - } - }, - BaseEvent::Fork(ForkEvent { forkee }) => { - SendableEmbed { - icon_url: Some(event.sender.avatar_url), - url: Some(event.sender.html_url), - title: Some(event.sender.login), - description: Some(format!("#### [[{}] Fork created: {}]({})", event.repository.full_name, forkee.full_name, forkee.html_url)), - colour: Some(GREY.to_string()), - ..Default::default() - } - }, - BaseEvent::Issues(issue_event) => { - match issue_event { - IssuesEvent::Closed { issue } => { - SendableEmbed { - icon_url: Some(event.sender.avatar_url), - url: Some(event.sender.html_url), - title: Some(event.sender.login), - description: Some(format!("#### [[{}] Issue Closed #{}: {}]({})", event.repository.full_name, issue.number, issue.title, issue.html_url)), - colour: Some(GREY.to_string()), - ..Default::default() - } - }, - IssuesEvent::Opened { issue } => { - SendableEmbed { - icon_url: Some(event.sender.avatar_url), - url: Some(event.sender.html_url), - title: Some(event.sender.login), - description: Some(format!("#### [[{}] Issue Opened #{}: {}]({})\n{}", event.repository.full_name, issue.number, issue.title, issue.html_url, shorten_text(&issue.body.unwrap_or_default(), 450))), - colour: Some(ORANGE.to_string()), - ..Default::default() - } - }, - IssuesEvent::Reopened { issue } => { - SendableEmbed { - icon_url: Some(event.sender.avatar_url), - url: Some(event.sender.html_url), - title: Some(event.sender.login), - description: Some(format!("#### [[{}] Issue Reopened #{}: {}]({})", event.repository.full_name, issue.number, issue.html_url, issue.title)), - colour: Some(GREEN.to_string()), - ..Default::default() - } - }, - _ => { return Ok(()) } - } - }, - BaseEvent::IssueComment(comment_event) => { - match comment_event { - IssueCommentEvent::Created { comment, issue } => { - SendableEmbed { - icon_url: Some(event.sender.avatar_url), - url: Some(event.sender.html_url), - title: Some(event.sender.login), - description: Some(format!("#### [[{}] New comment on issue #{}: {}]({})\n{}", event.repository.full_name, issue.number, issue.title, issue.html_url, shorten_text(&comment.body, 450))), - colour: Some(LIGHT_ORANGE.to_string()), - ..Default::default() - } - }, - _ => { return Ok(()) } - } - }, - BaseEvent::PullRequest(pull_request_event) => { - match pull_request_event { - PullRequestEvent::Closed { number, pull_request } => { - SendableEmbed { - icon_url: Some(event.sender.avatar_url), - url: Some(event.sender.html_url), - title: Some(event.sender.login), - description: Some(format!("#### [[{}] Pull Request Closed #{}: {}]({})", event.repository.full_name, number, pull_request.title, pull_request.html_url)), - colour: Some(GREY.to_string()), - ..Default::default() - } - }, - PullRequestEvent::Opened { number, pull_request } => { - SendableEmbed { - icon_url: Some(event.sender.avatar_url), - url: Some(event.sender.html_url), - title: Some(event.sender.login), - description: Some(format!("#### [[{}] Pull Request Opened #{}: {}]({})\n{}", event.repository.full_name, number, pull_request.title, pull_request.html_url, shorten_text(&pull_request.body.unwrap_or_default(), 450))), - colour: Some(ORANGE.to_string()), - ..Default::default() - } - }, - PullRequestEvent::Reopened { number, pull_request } => { - SendableEmbed { - icon_url: Some(event.sender.avatar_url), - url: Some(event.sender.html_url), - title: Some(event.sender.login), - description: Some(format!("#### [[{}] Pull Request Reopened #{}: {}]({})", event.repository.full_name, number, pull_request.html_url, pull_request.title)), - colour: Some(GREEN.to_string()), - ..Default::default() - } - }, - _ => { return Ok(()) } - } } + BaseEvent::Ping => return Ok(()), + BaseEvent::Create(CreateEvent { + r#ref, ref_type, .. + }) => SendableEmbed { + icon_url: Some(event.sender.avatar_url), + url: Some(event.sender.html_url), + title: Some(event.sender.login), + description: Some(format!( + "#### [{}] New {} created: {}", + event.repository.full_name, ref_type, r#ref + )), + colour: Some(GREY.to_string()), + ..Default::default() + }, + BaseEvent::Delete(DeleteEvent { + r#ref, ref_type, .. + }) => SendableEmbed { + icon_url: Some(event.sender.avatar_url), + url: Some(event.sender.html_url), + title: Some(event.sender.login), + description: Some(format!( + "#### [{}] {} deleted: {}", + event.repository.full_name, ref_type, r#ref + )), + colour: Some(GREY.to_string()), + ..Default::default() + }, + BaseEvent::Discussion(discussion_event) => match discussion_event { + GithubDiscussionEvent::Created { discussion } => SendableEmbed { + icon_url: Some(event.sender.avatar_url), + url: Some(event.sender.html_url), + title: Some(event.sender.login), + description: Some(format!( + "#### [{}] New discussion #{}: {}\n{}", + event.repository.full_name, + discussion.number, + discussion.title, + shorten_text(&discussion.body, 450) + )), + colour: Some(LIGHT_ORANGE.to_string()), + ..Default::default() + }, + GithubDiscussionEvent::Answered { discussion, answer } => SendableEmbed { + icon_url: Some(answer.comment.user.avatar_url), + url: Some(answer.comment.user.html_url), + title: Some(answer.comment.user.login), + description: Some(format!( + "#### [{}] discussion #{} marked answered: {}\n{}", + event.repository.full_name, + discussion.number, + discussion.title, + shorten_text(&answer.comment.body, 450) + )), + colour: Some(LIGHT_ORANGE.to_string()), + ..Default::default() + }, + }, + BaseEvent::DiscussionComment(comment_event) => match comment_event { + DiscussionCommentEvent::Created { + comment, + discussion, + } => SendableEmbed { + icon_url: Some(comment.comment.user.avatar_url), + url: Some(comment.comment.user.html_url), + title: Some(comment.comment.user.login), + description: Some(format!( + "[{}] New comment on discussion #{}: {}\n{}", + event.repository.full_name, + discussion.number, + discussion.title, + shorten_text(&comment.comment.body, 450) + )), + colour: Some(LIGHT_ORANGE.to_string()), + ..Default::default() + }, + _ => return Ok(()), + }, + BaseEvent::Fork(ForkEvent { forkee }) => SendableEmbed { + icon_url: Some(event.sender.avatar_url), + url: Some(event.sender.html_url), + title: Some(event.sender.login), + description: Some(format!( + "#### [[{}] Fork created: {}]({})", + event.repository.full_name, forkee.full_name, forkee.html_url + )), + colour: Some(GREY.to_string()), + ..Default::default() + }, + BaseEvent::Issues(issue_event) => match issue_event { + IssuesEvent::Closed { issue } => SendableEmbed { + icon_url: Some(event.sender.avatar_url), + url: Some(event.sender.html_url), + title: Some(event.sender.login), + description: Some(format!( + "#### [[{}] Issue Closed #{}: {}]({})", + event.repository.full_name, issue.number, issue.title, issue.html_url + )), + colour: Some(GREY.to_string()), + ..Default::default() + }, + IssuesEvent::Opened { issue } => SendableEmbed { + icon_url: Some(event.sender.avatar_url), + url: Some(event.sender.html_url), + title: Some(event.sender.login), + description: Some(format!( + "#### [[{}] Issue Opened #{}: {}]({})\n{}", + event.repository.full_name, + issue.number, + issue.title, + issue.html_url, + shorten_text(&issue.body.unwrap_or_default(), 450) + )), + colour: Some(ORANGE.to_string()), + ..Default::default() + }, + IssuesEvent::Reopened { issue } => SendableEmbed { + icon_url: Some(event.sender.avatar_url), + url: Some(event.sender.html_url), + title: Some(event.sender.login), + description: Some(format!( + "#### [[{}] Issue Reopened #{}: {}]({})", + event.repository.full_name, issue.number, issue.html_url, issue.title + )), + colour: Some(GREEN.to_string()), + ..Default::default() + }, + _ => return Ok(()), + }, + BaseEvent::IssueComment(comment_event) => match comment_event { + IssueCommentEvent::Created { comment, issue } => SendableEmbed { + icon_url: Some(event.sender.avatar_url), + url: Some(event.sender.html_url), + title: Some(event.sender.login), + description: Some(format!( + "#### [[{}] New comment on issue #{}: {}]({})\n{}", + event.repository.full_name, + issue.number, + issue.title, + issue.html_url, + shorten_text(&comment.body, 450) + )), + colour: Some(LIGHT_ORANGE.to_string()), + ..Default::default() + }, + _ => return Ok(()), + }, + BaseEvent::PullRequest(pull_request_event) => match pull_request_event { + PullRequestEvent::Closed { + number, + pull_request, + } => SendableEmbed { + icon_url: Some(event.sender.avatar_url), + url: Some(event.sender.html_url), + title: Some(event.sender.login), + description: Some(format!( + "#### [[{}] Pull Request Closed #{}: {}]({})", + event.repository.full_name, number, pull_request.title, pull_request.html_url + )), + colour: Some(GREY.to_string()), + ..Default::default() + }, + PullRequestEvent::Opened { + number, + pull_request, + } => SendableEmbed { + icon_url: Some(event.sender.avatar_url), + url: Some(event.sender.html_url), + title: Some(event.sender.login), + description: Some(format!( + "#### [[{}] Pull Request Opened #{}: {}]({})\n{}", + event.repository.full_name, + number, + pull_request.title, + pull_request.html_url, + shorten_text(&pull_request.body.unwrap_or_default(), 450) + )), + colour: Some(ORANGE.to_string()), + ..Default::default() + }, + PullRequestEvent::Reopened { + number, + pull_request, + } => SendableEmbed { + icon_url: Some(event.sender.avatar_url), + url: Some(event.sender.html_url), + title: Some(event.sender.login), + description: Some(format!( + "#### [[{}] Pull Request Reopened #{}: {}]({})", + event.repository.full_name, number, pull_request.html_url, pull_request.title + )), + colour: Some(GREEN.to_string()), + ..Default::default() + }, + _ => return Ok(()), + }, }; let message_id = Ulid::new().to_string(); - let embed = sendable_embed.into_embed(db, message_id.clone()).await?; + let embed = sendable_embed + .into_embed(legacy_db, message_id.clone()) + .await?; let mut message = Message { id: message_id, author: webhook.id.clone(), - channel: webhook.channel.clone(), + channel: webhook.channel_id.clone(), embeds: Some(vec![embed]), - webhook: Some(webhook.clone().into_message_webhook()), + webhook: Some(std::convert::Into::::into(webhook.clone()).into()), ..Default::default() }; - message.create(db, &channel, Some(MessageAuthor::Webhook(&webhook))).await + message + .create( + legacy_db, + &channel, + Some(MessageAuthor::Webhook(&webhook.into())), + ) + .await } diff --git a/crates/delta/src/routes/webhooks/webhook_fetch.rs b/crates/delta/src/routes/webhooks/webhook_fetch.rs index 3808327f..e1d3ae02 100644 --- a/crates/delta/src/routes/webhooks/webhook_fetch.rs +++ b/crates/delta/src/routes/webhooks/webhook_fetch.rs @@ -1,28 +1,30 @@ -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, -} +use revolt_database::Database; +use revolt_models::v0::{ResponseWebhook, Webhook}; +use revolt_quark::{models::User, perms, Db, Error, Permission, Result}; +use rocket::{serde::json::Json, State}; /// # Gets a webhook /// -/// gets a webhook +/// Gets a webhook #[openapi(tag = "Webhooks")] -#[get("/")] -pub async fn webhook_fetch(db: &Db, target: Ref) -> Result> { - let Webhook { id, name, avatar, channel, .. } = target.as_webhook(db).await?; +#[get("/")] +pub async fn webhook_fetch( + db: &State, + legacy_db: &Db, + webhook_id: String, + user: User, +) -> Result> { + let webhook = db + .fetch_webhook(&webhook_id) + .await + .map_err(Error::from_core)?; - Ok(Json(WebhookData { id, name, avatar, channel })) + let channel = legacy_db.fetch_channel(&webhook.channel_id).await?; + + perms(&user) + .channel(&channel) + .throw_permission(legacy_db, Permission::ViewChannel) + .await?; + + Ok(Json(std::convert::Into::::into(webhook).into())) } diff --git a/crates/delta/src/routes/webhooks/webhook_fetch_token.rs b/crates/delta/src/routes/webhooks/webhook_fetch_token.rs index 4b9a3f0a..bc460f45 100644 --- a/crates/delta/src/routes/webhooks/webhook_fetch_token.rs +++ b/crates/delta/src/routes/webhooks/webhook_fetch_token.rs @@ -1,17 +1,19 @@ -use revolt_quark::{Db, Ref, Result, Error, models::Webhook}; -use rocket::serde::json::Json; +use revolt_database::Database; +use revolt_models::v0::Webhook; +use revolt_result::Result; +use rocket::{serde::json::Json, State}; /// # Gets a webhook /// -/// gets a webhook with a token +/// Gets a webhook with a token #[openapi(tag = "Webhooks")] -#[get("//")] -pub async fn webhook_fetch_token(db: &Db, target: Ref, token: String) -> Result> { - let webhook = target.as_webhook(db).await?; - - (webhook.token.as_deref() == Some(&token)) - .then_some(()) - .ok_or(Error::InvalidCredentials)?; - - Ok(Json(webhook)) +#[get("//")] +pub async fn webhook_fetch_token( + db: &State, + webhook_id: String, + token: String, +) -> Result> { + let webhook = db.fetch_webhook(&webhook_id).await?; + webhook.assert_token(&token)?; + Ok(Json(webhook.into())) }