feat(sreez): Add subscribe button that sends a notification to discord

This commit is contained in:
jeangab 2024-04-17 16:44:25 -04:00
parent acbc8a20d6
commit 3bcb92829a
12 changed files with 419 additions and 13 deletions

261
sreez/Cargo.lock generated
View File

@ -142,6 +142,27 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "actix-tls"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4cce60a2f2b477bc72e5cde0af1812a6e82d8fd85b5570a5dcf2a5bf2c5be5f"
dependencies = [
"actix-rt",
"actix-service",
"actix-utils",
"futures-core",
"http 0.2.12",
"http 1.1.0",
"impl-more",
"pin-project-lite",
"tokio",
"tokio-rustls",
"tokio-util",
"tracing",
"webpki-roots",
]
[[package]]
name = "actix-utils"
version = "3.0.1"
@ -262,6 +283,21 @@ version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.82"
@ -315,6 +351,40 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]]
name = "awc"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68c09cc97310b926f01621faee652f3d1b0962545a3cec6c9ac07def9ea36c2c"
dependencies = [
"actix-codec",
"actix-http",
"actix-rt",
"actix-service",
"actix-tls",
"actix-utils",
"base64 0.21.7",
"bytes",
"cfg-if",
"cookie",
"derive_more",
"futures-core",
"futures-util",
"h2",
"http 0.2.12",
"itoa",
"log",
"mime",
"percent-encoding",
"pin-project-lite",
"rand",
"rustls",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
]
[[package]]
name = "backtrace"
version = "0.3.71"
@ -459,6 +529,20 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets 0.52.5",
]
[[package]]
name = "ciborium"
version = "0.2.2"
@ -562,6 +646,12 @@ dependencies = [
"version_check",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "cpufeatures"
version = "0.2.12"
@ -985,6 +1075,29 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
@ -1001,6 +1114,12 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "impl-more"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d"
[[package]]
name = "indexmap"
version = "2.2.6"
@ -1452,6 +1571,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-traits"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
]
[[package]]
name = "object"
version = "0.32.2"
@ -1730,6 +1858,36 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin 0.5.2",
"untrusted 0.7.1",
"web-sys",
"winapi",
]
[[package]]
name = "ring"
version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
"cc",
"cfg-if",
"getrandom",
"libc",
"spin 0.9.8",
"untrusted 0.9.0",
"windows-sys 0.52.0",
]
[[package]]
name = "rstml"
version = "0.11.2"
@ -1765,6 +1923,18 @@ dependencies = [
"semver",
]
[[package]]
name = "rustls"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
dependencies = [
"log",
"ring 0.16.20",
"sct",
"webpki",
]
[[package]]
name = "ryu"
version = "1.0.17"
@ -1786,6 +1956,16 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sct"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
"ring 0.17.8",
"untrusted 0.9.0",
]
[[package]]
name = "self_cell"
version = "1.0.3"
@ -1809,9 +1989,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.197"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
dependencies = [
"serde_derive",
]
@ -1829,9 +2009,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.197"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
dependencies = [
"proc-macro2",
"quote",
@ -1840,9 +2020,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.115"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
dependencies = [
"itoa",
"ryu",
@ -2000,18 +2180,36 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "sreez"
version = "0.1.0"
dependencies = [
"actix-files",
"actix-web",
"awc",
"chrono",
"console_error_panic_hook",
"http 0.2.12",
"leptos",
"leptos_actix",
"leptos_meta",
"leptos_router",
"regex",
"serde",
"serde_json",
"toml",
"wasm-bindgen",
]
@ -2138,6 +2336,17 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-rustls"
version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
dependencies = [
"rustls",
"tokio",
"webpki",
]
[[package]]
name = "tokio-util"
version = "0.7.10"
@ -2286,6 +2495,18 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.5.0"
@ -2429,6 +2650,25 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
dependencies = [
"ring 0.17.8",
"untrusted 0.9.0",
]
[[package]]
name = "webpki-roots"
version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
dependencies = [
"webpki",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -2460,6 +2700,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.5",
]
[[package]]
name = "windows-sys"
version = "0.48.0"

View File

@ -16,6 +16,12 @@ leptos_meta = { version = "0.6", features = ["nightly"] }
leptos_actix = { version = "0.6", optional = true }
leptos_router = { version = "0.6", features = ["nightly"] }
wasm-bindgen = "=0.2.92"
toml = "0.8.12"
serde = "1.0.198"
chrono = "0.4.38"
awc = { version = "3.4.0", optional = true, features = ["rustls"] }
serde_json = { version = "1.0.116", optional = true }
regex = "1.10.4"
[features]
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
@ -27,7 +33,10 @@ ssr = [
"leptos/ssr",
"leptos_meta/ssr",
"leptos_router/ssr",
"awc",
"serde_json",
]
awc = ["dep:awc"]
# Defines a size-optimized profile for the WASM bundle in release mode
[profile.wasm-release]

1
sreez/config.toml Normal file
View File

@ -0,0 +1 @@
discord_webhook_url = "https://discord.com/api/webhooks/1230205994055762081/IUfZThaKpHCdlALdRr9orDBoJwlYepIKa2s8p_LvPk1hOBDYxpSyfBW98jGLLxGlOs2i"

View File

@ -1,7 +1,10 @@
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
use logging::log;
use chrono::Utc;
use crate::config::get_discord_webhook_url;
use crate::pages::ShortLandingPage;
use crate::pages::OkdInstallationOverview1;
use crate::pages::HomePage;
@ -11,6 +14,7 @@ use crate::components::Matomo;
#[component]
pub fn App() -> impl IntoView {
// Provides context that manages stylesheets, titles, meta tags, etc.
log!("Got a hit {}", Utc::now());
provide_meta_context();
view! {

View File

@ -1,6 +1,8 @@
mod book_a_demo;
mod footer;
mod matomo;
mod subscribe;
pub use subscribe::*;
pub use book_a_demo::*;
pub use footer::*;
pub use matomo::*;

View File

@ -0,0 +1,55 @@
use leptos::*;
use leptos_router::use_location;
use leptos::logging::log;
#[cfg(feature = "ssr")]
use crate::infra::discord::send_discord_message;
use regex::Regex;
fn is_valid_email(email: &str) -> bool {
let regex = Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap();
return regex.is_match(email);
}
#[server(Subscribe, "/subscribe")]
pub async fn add_subscriber(message: String, email: String) -> Result<(), ServerFnError> {
let notification = &format!("New subscriber email : {} , message : {}", email, message);
log!("Sending new subscriber notification {} ", notification);
#[cfg(feature = "ssr")]
send_discord_message(notification).await?;
Ok(())
}
#[component]
pub fn SubscribeButton() -> impl IntoView {
let FORM_INITIAL = "INITIAL";
let FORM_SUCCESS ="SUCCESS";
let FORM_ERROR ="ERROR";
let (form_status, set_form_status) = create_signal(FORM_INITIAL);
let email : NodeRef<html::Input> = create_node_ref();
let send = move |ev: leptos::ev::SubmitEvent | {
ev.prevent_default();
let email_value = email.get().expect("Email input should be mounted").value();
spawn_local(async move {
if is_valid_email(&email_value) {
let path = use_location().pathname.get();
let _ = add_subscriber(path, email_value).await;
set_form_status(FORM_SUCCESS);
} else {
log!("Invalid email {}", email_value);
set_form_status(FORM_ERROR);
}
});
};
view! {
<form on:submit=send class="margin-y-3">
<label for="email" class="margin-right-1">"Email : "</label>
<input type="text" value="email" node_ref=email class="margin-right-3 input"/>
<input type="Submit" value="Get notified !" class="btn"/>
<p class=format!("form-message initial status-{}", form_status.get())><span>"Status is "</span><span>{form_status}</span></p>
</form>
}
}

28
sreez/src/config.rs Normal file
View File

@ -0,0 +1,28 @@
use std::env;
use toml;
use std::fs;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Config {
discord_webhook_url: String,
}
fn get_full_config() -> Option<Config> {
if let Ok(config) = fs::read_to_string("config.toml") {
return toml::from_str(&config).unwrap_or(None);
}
None
}
pub fn get_discord_webhook_url() -> Option<String> {
if let Ok(url) = env::var("DISCORD_WEBHOOK_URL") {
return Some(url);
}
if let Some(config) = get_full_config() {
return Some(config.discord_webhook_url);
}
return None;
}

View File

@ -0,0 +1,25 @@
use crate::config::get_discord_webhook_url;
#[cfg(feature = "ssr")]
use awc::{error::SendRequestError, Client};
#[cfg(feature = "ssr")]
use serde_json::json;
#[cfg(feature = "ssr")]
pub async fn send_discord_message(content: &str) -> Result<(), SendRequestError> {
let webhook_url = get_discord_webhook_url().expect("Webhook url configuration is available");
let client = Client::default();
let mut request = client.post(webhook_url);
request = request.insert_header(("User-Agent", "Actix-web"));
let body = json!({
"content": content,
});
let response = request.send_json(&body).await?;
println!("Response: {:?}", response);
Ok(())
}

1
sreez/src/infra/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod discord;

View File

@ -1,6 +1,8 @@
pub mod app;
mod pages;
mod components;
mod config;
mod infra;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]

View File

@ -1,4 +1,5 @@
use leptos::*;
use crate::components::SubscribeButton;
/// Renders the home page of your application.
#[component]
pub fn OkdInstallationOverview1() -> impl IntoView {
@ -95,6 +96,7 @@ pub fn OkdInstallationOverview1() -> impl IntoView {
</div>
</div>
<SubscribeButton/>
</div>
}
}

View File

@ -177,6 +177,11 @@ ol {
margin-bottom: 8rem;
}
.margin-y-3 {
margin-top: 3rem;
margin-bottom: 3rem;
}
.margin-y-4 {
margin-top: 4rem;
margin-bottom: 4rem;
@ -236,6 +241,13 @@ ol {
margin: 3rem;
}
.margin-right-1 {
margin-right: 1rem;
}
.margin-right-3 {
margin-right: 3rem;
}
.max-width-300 {
max-width: 300px;
@ -454,14 +466,25 @@ ol.huge-list-markers li{
font-weight: 700;
font-size: inherit;
cursor: pointer;
background-color: #e72235;
color: white;
background-color: #e72235;
color: white;
&:hover {
// background-color: #eee;
// color: black;
box-shadow: 5px 5px 27px 10px rgba(231,34,53,0.21);
//background-color: #66d500;
//background-color: white;
box-shadow: 5px 5px 17px 5px rgba(231,34,53,0.21);
}
}
.input {
border: solid 2px #eee;
padding: 1rem 1.5rem;
border-radius: 0.75rem;
font-weight: 400;
font-size: inherit;
cursor: pointer;
background-color: white;
color: black;
&:focus {
box-shadow: 5px 5px 17px -5px rgba(0,0,0,0.21);
outline: none;
}
}
@ -496,6 +519,11 @@ ol.huge-list-markers li{
}
}
.form-message {
display: none;
// TODO handle dynamic class
}
@media screen and (min-width: 960px) {
.lg-margin-x-auto {