diff --git a/Cargo.lock b/Cargo.lock index 513bd40..5682e32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arraydeque" @@ -662,9 +662,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" dependencies = [ "atomic-waker", "bytes", @@ -991,6 +991,7 @@ checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown 0.15.2", + "serde", ] [[package]] @@ -1059,9 +1060,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "linux-raw-sys" @@ -2111,6 +2112,42 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utoipa" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435c6f69ef38c9017b4b4eea965dfb91e71e53d869e896db40d1cf2441dd75c0" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77d306bc75294fd52f3e99b13ece67c02c1a2789190a6f31d32f736624326f7" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn", +] + +[[package]] +name = "utoipa-redoc" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6427547f6db7ec006cbbef95f7565952a16f362e298b416d2d497d9706fef72d" +dependencies = [ + "axum", + "serde", + "serde_json", + "utoipa", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -2152,8 +2189,12 @@ dependencies = [ "anyhow", "axum", "clap", + "futures", "serde", + "serde_json", "tokio", + "utoipa", + "utoipa-redoc", "veno-core", ] diff --git a/Cargo.toml b/Cargo.toml index aec08e3..8f80e19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,7 @@ resolver = "2" [workspace.dependencies] anyhow = "1.0.97" tokio = { version = "1.41.1", features = ["rt-multi-thread", "macros"] } +futures = "0.3.31" +serde = { version = "1.0", features = ["derive"] } +serde_derive = "1.0" +serde_json = "1.0" diff --git a/veno-core/Cargo.toml b/veno-core/Cargo.toml index f65e454..6736434 100644 --- a/veno-core/Cargo.toml +++ b/veno-core/Cargo.toml @@ -6,14 +6,14 @@ edition = "2021" [dependencies] anyhow = { workspace = true } tokio = { workspace = true } +futures = { workspace = true } +serde = { workspace = true } +serde_derive = { workspace = true } +serde_json = { workspace = true } config = "0.15" -serde = { version = "1.0", features = ["derive"] } -serde_derive = "1.0" -serde_json = "1.0" reqwest = { version = "0.12", features = ["json"] } once_cell = "1.20" lettre = "0.11" regex = "1.11.1" mockito = "1.6.1" -futures = "0.3.31" diff --git a/veno-core/src/app.rs b/veno-core/src/app.rs index 0624e99..83a7cf7 100644 --- a/veno-core/src/app.rs +++ b/veno-core/src/app.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result}; use futures::future::join_all; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use crate::{ artifact::Artifact, @@ -41,31 +41,6 @@ impl AppState { notifier.sink.send(¬ification).await; } } - - // This function will check all artifacts if they are behind the latest version - pub async fn check_all_artifacts(&self) -> Result { - let mut new_versions = Vec::new(); - - let check_futures = self - .artifacts - .iter() - .map(|artifact| async move { (artifact, artifact.is_version_behind().await) }); - - let checked_artifacts = join_all(check_futures).await; - - for (artifact, result) in checked_artifacts { - if let Ok(Some(latest_version)) = result { - new_versions.push(CheckedArtifact { - name: artifact.name.clone(), - current_version: artifact.current_version.clone(), - latest_version, - }); - } - } - let new_versions = - serde_json::to_string(&new_versions).context("Failed to serialize new versions")?; - Ok(new_versions) - } } async fn generate_notification(artifacts: &Vec<(&Artifact, Result>)>) -> String { @@ -90,10 +65,3 @@ async fn generate_notification(artifacts: &Vec<(&Artifact, Result messages.join("\n") } - -#[derive(Debug, Serialize)] -struct CheckedArtifact { - name: String, - current_version: String, - latest_version: String, -} diff --git a/veno-core/src/artifact/mod.rs b/veno-core/src/artifact/mod.rs index 6b65fb2..3a70685 100644 --- a/veno-core/src/artifact/mod.rs +++ b/veno-core/src/artifact/mod.rs @@ -15,7 +15,7 @@ pub struct Artifact { } impl Artifact { - pub(super) async fn is_version_behind(&self) -> Result> { + pub async fn is_version_behind(&self) -> Result> { self.source.is_version_behind(&self.current_version).await } } diff --git a/veno-core/src/artifact/source/github.rs b/veno-core/src/artifact/source/github.rs index 46b4156..4929017 100644 --- a/veno-core/src/artifact/source/github.rs +++ b/veno-core/src/artifact/source/github.rs @@ -29,11 +29,21 @@ impl SourceChecker for GitHubSource { return Err(anyhow!("Request failed: {:?}", response)); } - // Parse the JSON response - let release: Release = response - .json() - .await - .context("Failed to parse JSON response")?; + // // Parse the JSON response + // let release: Release = match response.json().await { + // Ok(json) => json, + // Err(err) => { + // let error_message = format!("Failed to parse JSON response: {}", err); + // println!("{}", error_message); + // return Err(anyhow!(error_message)); + // } + // }; + + //TODO make error responses more robust + let release: Release = response.json().await.map_err(|err| { + println!("added context: {}", err); + err + })?; // Extract and compare the version // TODO: change this logic to be used with the version checker but we first need to diff --git a/veno-core/src/lib.rs b/veno-core/src/lib.rs index 8794d77..75326e7 100644 --- a/veno-core/src/lib.rs +++ b/veno-core/src/lib.rs @@ -9,22 +9,17 @@ use anyhow::Result; use once_cell::sync::Lazy; use reqwest::{Client, ClientBuilder, Response}; -pub static CLIENT: Lazy = Lazy::new(|| { +static CLIENT: Lazy = Lazy::new(|| { ClientBuilder::new() .user_agent("veno") .build() .expect("Could not create reqwest client") }); -pub async fn get(url: &str) -> Result { +async fn get(url: &str) -> Result { CLIENT .get(url) .timeout(Duration::from_secs(10)) .send() .await } - -pub fn pretty_json(body: &str) -> Result { - let json: serde_json::Value = serde_json::from_str(body)?; - serde_json::to_string_pretty(&json) -} diff --git a/veno-core/src/notifier/webhook.rs b/veno-core/src/notifier/webhook.rs index b76c663..f9ad8bf 100644 --- a/veno-core/src/notifier/webhook.rs +++ b/veno-core/src/notifier/webhook.rs @@ -20,6 +20,7 @@ impl SinkSender for WebhookSink { } } +// TODO this needs to bubble so we can return an error to the user pub async fn call(webhook: &str, payload: &Value) { match CLIENT .post(webhook) diff --git a/veno-web/Cargo.toml b/veno-web/Cargo.toml index 7217a08..4146e6d 100644 --- a/veno-web/Cargo.toml +++ b/veno-web/Cargo.toml @@ -6,8 +6,11 @@ edition = "2021" [dependencies] anyhow = { workspace = true } tokio = { workspace = true } - +futures = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } veno-core = { path = "../veno-core" } clap = { version = "4.0", features = ["derive"] } axum = "0.8.3" -serde = { version = "1.0", features = ["derive"] } +utoipa = { version = "5.3.1", features = ["axum_extras"] } +utoipa-redoc = { version = "6.0.0", features = ["axum"] } diff --git a/veno-web/src/endpoints.rs b/veno-web/src/endpoints.rs deleted file mode 100644 index c78854e..0000000 --- a/veno-web/src/endpoints.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::sync::Arc; - -use veno_core::app::AppState; - -use axum::{ - extract::State, - http::{header, StatusCode}, - response::IntoResponse, - routing::get, - Router, -}; - -pub async fn routes(app: AppState) { - let app = Arc::new(app); - let app = Router::new() - .route("/check", get(check)) - // .route("/users", post(create_user)) - .with_state(app); - - let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); - axum::serve(listener, app).await.unwrap(); -} - -// basic handler that responds with a static string -async fn check(State(app): State>) -> impl IntoResponse { - match app.check_all_artifacts().await { - Ok(response) => ( - StatusCode::OK, - [(header::CONTENT_TYPE, "application/json")], - response, - ), - Err(e) => { - println!("Error: {}", e); - ( - StatusCode::INTERNAL_SERVER_ERROR, - [(header::CONTENT_TYPE, "text/plain")], - String::from( - "There was an error while checking for new versions. Please try again later.", - ), - ) - } - } -} - -// async fn create_user( -// // this argument tells axum to parse the request body -// // as JSON into a `CreateUser` type -// Json(payload): Json, -// ) -> (StatusCode, Json) { -// // insert your application logic here -// let user = User { -// id: 1337, -// username: payload.username, -// }; -// -// // this will be converted into a JSON response -// // with a status code of `201 Created` -// (StatusCode::CREATED, Json(user)) -// } -// -// // the input to our `create_user` handler -// #[derive(Deserialize)] -// struct CreateUser { -// username: String, -// } -// -// // the output to our `create_user` handler -// #[derive(Serialize)] -// struct User { -// id: u64, -// username: String, -// } diff --git a/veno-web/src/main.rs b/veno-web/src/main.rs index 57462cb..e7be617 100644 --- a/veno-web/src/main.rs +++ b/veno-web/src/main.rs @@ -1,10 +1,12 @@ +use std::sync::Arc; + use anyhow::Result; +use resources::serve_api; use veno_core::app::AppState; use clap::Parser; -use endpoints::routes; -mod endpoints; +mod resources; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -16,7 +18,7 @@ struct Cli { #[tokio::main] async fn main() -> Result<()> { let cli = Cli::parse(); - let config = AppState::init(&cli.config)?; - routes(config.clone()).await; + let app = Arc::new(AppState::init(&cli.config)?); + serve_api(app).await; Ok(()) } diff --git a/veno-web/src/resources/errors.rs b/veno-web/src/resources/errors.rs new file mode 100644 index 0000000..bb60e24 --- /dev/null +++ b/veno-web/src/resources/errors.rs @@ -0,0 +1,15 @@ +use serde::Serialize; + +#[derive(Serialize, Debug, Clone)] +pub struct PathParamError { + pub error_code: String, + pub resource: String, + pub param: String, + pub message: String, +} + +#[derive(Serialize, Debug, Clone)] +pub struct InternalServerError { + pub error_code: String, + pub message: String, +} diff --git a/veno-web/src/resources/mod.rs b/veno-web/src/resources/mod.rs new file mode 100644 index 0000000..b8a0296 --- /dev/null +++ b/veno-web/src/resources/mod.rs @@ -0,0 +1,26 @@ +use std::sync::Arc; + +use openapi::ApiDoc; +use utoipa::OpenApi; +use utoipa_redoc::{Redoc, Servable}; +use v1::v1_routes; +use veno_core::app::AppState; + +use axum::{routing::get, Router}; + +mod errors; +mod openapi; +mod v1; + +// fn assert_state_bounds(_: &T) {} + +pub async fn serve_api(app: Arc) { + let router = Router::new() + .merge(Redoc::with_url("/redoc", ApiDoc::openapi())) + .route("/", get(|| async { "Hello" })) + .nest("/api/v1", v1_routes()) + .with_state(app); + + let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + axum::serve(listener, router.into_make_service()).await.unwrap(); +} diff --git a/veno-web/src/resources/openapi.rs b/veno-web/src/resources/openapi.rs new file mode 100644 index 0000000..de3b612 --- /dev/null +++ b/veno-web/src/resources/openapi.rs @@ -0,0 +1,9 @@ +use utoipa::OpenApi; + +#[derive(OpenApi)] +#[openapi( + nest( + (path = "/api/v1/artifacts", api = crate::resources::v1::artifacts::handlers::ArtifactsApi) + ) +)] +pub struct ApiDoc; diff --git a/veno-web/src/resources/v1/artifacts/handlers.rs b/veno-web/src/resources/v1/artifacts/handlers.rs new file mode 100644 index 0000000..ac8997a --- /dev/null +++ b/veno-web/src/resources/v1/artifacts/handlers.rs @@ -0,0 +1,92 @@ +use std::sync::Arc; + +use axum::{ + extract::{Path, State}, + http::{header, StatusCode}, + response::IntoResponse, + Json, +}; +use serde_json::json; +use utoipa::OpenApi; +use veno_core::app::AppState; + +use crate::resources::errors::{InternalServerError, PathParamError}; + +use super::{model::ArtifactResponse, service::check_all_artifacts}; + +#[derive(OpenApi)] +#[openapi()] +pub struct ArtifactsApi; + +#[utoipa::path( + get, + path="/api/v1/artifacts/check", + responses( + (status= OK, description = "Check all artifacts for new versions", body = str) + ) +)] +pub async fn check_versions(State(app): State>) -> impl IntoResponse { + let response = check_all_artifacts(&app.artifacts).await; + match response { + Ok(Some(new_versions)) => ( + StatusCode::OK, + [(header::CONTENT_TYPE, "application/json")], + Json(new_versions), + ) + .into_response(), + Ok(None) => ( + StatusCode::OK, + [(header::CONTENT_TYPE, "application/json")], + Json(json!({"message": "There are currently no new versions of your artifacts"})), + ) + .into_response(), + Err(err) => { + let error = InternalServerError { + error_code: StatusCode::INTERNAL_SERVER_ERROR.to_string(), + message: format!("There was an error while executing your request: {}", err), + }; + + ( + StatusCode::INTERNAL_SERVER_ERROR, + [(header::CONTENT_TYPE, "application/json")], + Json(error), + ) + .into_response() + } + } +} + +pub async fn all_artifacts(State(app): State>) -> impl IntoResponse { + let artifacts: Vec = app + .artifacts + .iter() + .map(|artifact| ArtifactResponse::from(artifact.clone())) + .collect(); + Json(artifacts) +} + +pub async fn artifact_for_id( + Path(artifact_id): Path, + State(app): State>, +) -> impl IntoResponse { + let artifact = app + .artifacts + .iter() + .find(|artifact| artifact.id == artifact_id); + + match artifact { + Some(artifact) => { + let response_boddy = ArtifactResponse::from(artifact.clone()); + (StatusCode::OK, Json(response_boddy)).into_response() + } + None => { + let error = PathParamError { + error_code: StatusCode::NOT_FOUND.to_string(), + resource: String::from("artifacts"), + param: artifact_id.clone(), + message: format!("The Artifact with the id '{}' was not found", artifact_id), + }; + (StatusCode::NOT_FOUND, Json(error)).into_response() + } + } +} diff --git a/veno-web/src/resources/v1/artifacts/mod.rs b/veno-web/src/resources/v1/artifacts/mod.rs new file mode 100644 index 0000000..2e88b41 --- /dev/null +++ b/veno-web/src/resources/v1/artifacts/mod.rs @@ -0,0 +1,4 @@ +pub mod handlers; +pub mod model; +pub mod routes; +pub mod service; diff --git a/veno-web/src/resources/v1/artifacts/model.rs b/veno-web/src/resources/v1/artifacts/model.rs new file mode 100644 index 0000000..5af9c38 --- /dev/null +++ b/veno-web/src/resources/v1/artifacts/model.rs @@ -0,0 +1,72 @@ +use serde::Serialize; +use utoipa::ToSchema; +use veno_core::artifact::{source::Source, Artifact}; + +#[derive(Serialize, Debug, Clone, ToSchema)] +pub struct ArtifactResponse { + pub id: String, + pub name: String, + pub message_prefix: Option, + pub source: SourceDto, + pub current_version: String, +} + +#[derive(Serialize, Debug, Clone, ToSchema)] +#[serde(tag = "type")] // Use tag-based enum for source type +pub enum SourceDto { + #[serde(rename = "github")] + GitHub(GitHubSource), + #[serde(rename = "dockerhub")] + DockerHub(DockerHubSource), + #[serde(rename = "artifacthub")] + ArtifactHub(ArtifactHubSource), +} + +#[derive(Serialize, Debug, Clone, ToSchema)] +pub struct GitHubSource { + pub identifier: String, +} + +#[derive(Serialize, Debug, Clone, ToSchema)] +pub struct DockerHubSource { + pub identifier: String, +} + +#[derive(Serialize, Debug, Clone, ToSchema)] +pub struct ArtifactHubSource { + pub identifier: String, +} + +impl From for ArtifactResponse { + fn from(value: Artifact) -> Self { + Self { + id: value.id, + name: value.name, + message_prefix: value.message_prefix, + source: value.source.into(), + current_version: value.current_version, + } + } +} +impl From for SourceDto { + fn from(original_source: Source) -> Self { + match original_source { + Source::GitHub(gh_source) => SourceDto::GitHub(GitHubSource { + identifier: gh_source.identifier, + }), + Source::DockerHub(dh_source) => SourceDto::DockerHub(DockerHubSource { + identifier: dh_source.identifier, + }), + Source::ArtifactHub(ah_source) => SourceDto::ArtifactHub(ArtifactHubSource { + identifier: ah_source.identifier, + }), + } + } +} + +#[derive(Debug, Serialize)] +pub struct CheckedArtifact { + pub name: String, + pub current_version: String, + pub latest_version: String, +} diff --git a/veno-web/src/resources/v1/artifacts/routes.rs b/veno-web/src/resources/v1/artifacts/routes.rs new file mode 100644 index 0000000..4ea221a --- /dev/null +++ b/veno-web/src/resources/v1/artifacts/routes.rs @@ -0,0 +1,13 @@ +use std::sync::Arc; + +use axum::{routing::get, Router}; +use veno_core::app::AppState; + +use super::handlers::{all_artifacts, artifact_for_id, check_versions}; + +pub fn artifacts_routes() -> Router> { + Router::new() + .route("/", get(all_artifacts)) + .route("/{artifact_id}", get(artifact_for_id)) + .route("/check", get(check_versions)) +} diff --git a/veno-web/src/resources/v1/artifacts/service.rs b/veno-web/src/resources/v1/artifacts/service.rs new file mode 100644 index 0000000..e2a7a6f --- /dev/null +++ b/veno-web/src/resources/v1/artifacts/service.rs @@ -0,0 +1,37 @@ +use anyhow::Result; +use futures::future::join_all; +use veno_core::artifact::Artifact; + +use super::model::CheckedArtifact; + +pub async fn check_all_artifacts( + artifacts: &Vec, +) -> Result>> { + let mut new_versions = Vec::new(); + + let check_futures = artifacts + .iter() + .map(|artifact| async move { (artifact, artifact.is_version_behind().await) }); + + let checked_artifacts = join_all(check_futures).await; + + for (artifact, result) in checked_artifacts { + match result { + Ok(Some(latest_version)) => { + new_versions.push(CheckedArtifact { + name: artifact.name.clone(), + current_version: artifact.current_version.clone(), + latest_version, + }); + } + Ok(None) => {} + Err(err) => return Err(err), + } + } + + if new_versions.is_empty() { + return Ok(None); + } + + Ok(Some(new_versions)) +} diff --git a/veno-web/src/resources/v1/mod.rs b/veno-web/src/resources/v1/mod.rs new file mode 100644 index 0000000..17cc625 --- /dev/null +++ b/veno-web/src/resources/v1/mod.rs @@ -0,0 +1,15 @@ +use std::sync::Arc; + +use artifacts::routes::artifacts_routes; +use axum::Router; +use notifiers::routes::notifiers_routes; +use veno_core::app::AppState; + +pub mod artifacts; +pub mod notifiers; + +pub fn v1_routes() -> Router> { + Router::new() + .nest("/artifacts", artifacts_routes()) + .nest("/notifiers", notifiers_routes()) +} diff --git a/veno-web/src/resources/v1/notifiers/handlers.rs b/veno-web/src/resources/v1/notifiers/handlers.rs new file mode 100644 index 0000000..a6bb1a3 --- /dev/null +++ b/veno-web/src/resources/v1/notifiers/handlers.rs @@ -0,0 +1,15 @@ +use std::sync::Arc; + +use axum::{extract::State, response::IntoResponse, Json}; +use veno_core::app::AppState; + +use super::model::NotifierResponse; + +pub async fn all_notifiers(State(app): State>) -> impl IntoResponse { + let notifiers: Vec = app + .notifiers + .iter() + .map(|notifier| NotifierResponse::from(notifier.clone())) + .collect(); + Json(notifiers) +} diff --git a/veno-web/src/resources/v1/notifiers/mod.rs b/veno-web/src/resources/v1/notifiers/mod.rs new file mode 100644 index 0000000..29a5b60 --- /dev/null +++ b/veno-web/src/resources/v1/notifiers/mod.rs @@ -0,0 +1,3 @@ +pub mod handlers; +pub mod model; +pub mod routes; diff --git a/veno-web/src/resources/v1/notifiers/model.rs b/veno-web/src/resources/v1/notifiers/model.rs new file mode 100644 index 0000000..d47bae5 --- /dev/null +++ b/veno-web/src/resources/v1/notifiers/model.rs @@ -0,0 +1,79 @@ +use serde::Serialize; +use veno_core::notifier::{Notifier, Sink}; + +#[derive(Serialize, Debug, Clone)] +pub struct NotifierResponse { + pub name: String, + pub sink: SinkDto, + pub artifact_ids: Vec, +} + +#[derive(Serialize, Debug, Clone)] +#[serde(tag = "type")] // Use tag-based enum for sink type +pub enum SinkDto { + #[serde(rename = "slack")] + Slack(SlackSink), + #[serde(rename = "email")] + Email(EmailSink), + #[serde(rename = "google_chat")] + GoogleChat(GoogleChatSink), + #[serde(rename = "webhook")] + Webhook(WebhookSink), +} + +#[derive(Debug, Clone, Serialize)] +pub struct SlackSink { + pub webhook: String, +} + +#[derive(Serialize, Clone, Debug)] +pub struct EmailSink { + pub host: String, + pub port: Option, + pub username: String, + pub password: String, + pub to: Vec, +} + +#[derive(Debug, Clone, Serialize)] +pub struct GoogleChatSink { + pub webhook: String, +} + +#[derive(Debug, Clone, Serialize)] +pub struct WebhookSink { + pub webhook: String, +} + +impl From for NotifierResponse { + fn from(value: Notifier) -> Self { + Self { + name: value.name, + sink: value.sink.into(), + artifact_ids: value.artifact_ids, + } + } +} + +impl From for SinkDto { + fn from(value: Sink) -> Self { + match value { + Sink::Slack(slack) => SinkDto::Slack(SlackSink { + webhook: slack.webhook, + }), + Sink::Email(email) => SinkDto::Email(EmailSink { + host: email.host, + port: email.port, + username: String::from("[REDACTED]"), + password: String::from("[REDACTED]"), + to: email.to, + }), + Sink::GoogleChat(google) => SinkDto::GoogleChat(GoogleChatSink { + webhook: google.webhook, + }), + Sink::Webhook(webhook) => SinkDto::Webhook(WebhookSink { + webhook: webhook.webhook, + }), + } + } +} diff --git a/veno-web/src/resources/v1/notifiers/routes.rs b/veno-web/src/resources/v1/notifiers/routes.rs new file mode 100644 index 0000000..69eb274 --- /dev/null +++ b/veno-web/src/resources/v1/notifiers/routes.rs @@ -0,0 +1,10 @@ +use std::sync::Arc; + +use axum::{routing::get, Router}; +use veno_core::app::AppState; + +use super::handlers::all_notifiers; + +pub fn notifiers_routes() -> Router> { + Router::new().route("/", get(all_notifiers)) +}