This commit is contained in:
2025-09-02 01:24:56 +02:00
parent 744cc14d1b
commit d2166ae752
14 changed files with 576 additions and 37 deletions

325
Cargo.lock generated
View File

@@ -52,6 +52,18 @@ dependencies = [
"subtle",
]
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -85,12 +97,15 @@ dependencies = [
"leptos_meta",
"leptos_router",
"leptos_transition_group",
"lettre",
"log",
"rand 0.8.5",
"send_wrapper",
"serde",
"serde_json",
"simple_logger",
"sqlx",
"tera",
"thaw",
"thaw_components",
"thaw_macro",
@@ -453,6 +468,16 @@ dependencies = [
"cipher",
]
[[package]]
name = "bstr"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.19.0"
@@ -513,6 +538,16 @@ dependencies = [
"windows-link",
]
[[package]]
name = "chumsky"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9"
dependencies = [
"hashbrown 0.14.5",
"stacker",
]
[[package]]
name = "cipher"
version = "0.4.4"
@@ -693,6 +728,25 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.12"
@@ -833,6 +887,22 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "email-encoding"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9298e6504d9b9e780ed3f7dfd43a61be8cd0e09eb07f7706a945b0072b6670b6"
dependencies = [
"base64",
"memchr",
]
[[package]]
name = "email_address"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
[[package]]
name = "encoding_rs"
version = "0.8.35"
@@ -892,6 +962,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1"
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "flume"
version = "0.11.1"
@@ -1098,6 +1174,30 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "globset"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "globwalk"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
dependencies = [
"bitflags",
"ignore",
"walkdir",
]
[[package]]
name = "gloo-net"
version = "0.6.0"
@@ -1143,6 +1243,10 @@ name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashbrown"
@@ -1209,6 +1313,17 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "hostname"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65"
dependencies = [
"cfg-if",
"libc",
"windows-link",
]
[[package]]
name = "html-escape"
version = "0.2.13"
@@ -1469,6 +1584,22 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "ignore"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
dependencies = [
"crossbeam-deque",
"globset",
"log",
"memchr",
"regex-automata",
"same-file",
"walkdir",
"winapi-util",
]
[[package]]
name = "indexmap"
version = "2.11.0"
@@ -1781,6 +1912,36 @@ dependencies = [
"web-sys",
]
[[package]]
name = "lettre"
version = "0.11.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cb54db6ff7a89efac87dba5baeac57bb9ccd726b49a9b6f21fb92b3966aaf56"
dependencies = [
"async-trait",
"base64",
"chumsky",
"email-encoding",
"email_address",
"fastrand",
"futures-io",
"futures-util",
"hostname",
"httpdate",
"idna",
"mime",
"nom",
"percent-encoding",
"quoted_printable",
"rustls",
"socket2",
"tokio",
"tokio-rustls",
"tracing",
"url",
"webpki-roots 1.0.2",
]
[[package]]
name = "libc"
version = "0.2.175"
@@ -1947,6 +2108,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60993920e071b0c9b66f14e2b32740a4e27ffc82854dcd72035887f336a09a28"
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]]
name = "nonempty"
version = "0.7.0"
@@ -2142,6 +2312,50 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pest"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
dependencies = [
"memchr",
"thiserror 2.0.16",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5"
dependencies = [
"pest",
"sha2",
]
[[package]]
name = "phf"
version = "0.11.3"
@@ -2344,6 +2558,15 @@ dependencies = [
"yansi",
]
[[package]]
name = "psm"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f"
dependencies = [
"cc",
]
[[package]]
name = "pure-rust-locales"
version = "0.8.1"
@@ -2381,6 +2604,12 @@ dependencies = [
"syn",
]
[[package]]
name = "quoted_printable"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "640c9bd8497b02465aeef5375144c26062e0dcd5939dfcbb0f5db76cb8c17c73"
[[package]]
name = "r-efi"
version = "5.3.0"
@@ -2613,6 +2842,7 @@ version = "0.23.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
@@ -3144,6 +3374,19 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "stacker"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b"
dependencies = [
"cc",
"cfg-if",
"libc",
"psm",
"windows-sys 0.59.0",
]
[[package]]
name = "stringprep"
version = "0.1.5"
@@ -3235,6 +3478,22 @@ dependencies = [
"web-sys",
]
[[package]]
name = "tera"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee"
dependencies = [
"globwalk",
"lazy_static",
"pest",
"pest_derive",
"regex",
"serde",
"serde_json",
"unic-segment",
]
[[package]]
name = "thaw"
version = "0.5.0-beta"
@@ -3436,6 +3695,16 @@ dependencies = [
"syn",
]
[[package]]
name = "tokio-rustls"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
dependencies = [
"rustls",
"tokio",
]
[[package]]
name = "tokio-stream"
version = "0.1.17"
@@ -3632,6 +3901,62 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]]
name = "ucd-trie"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
[[package]]
name = "unic-char-property"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
dependencies = [
"unic-char-range",
]
[[package]]
name = "unic-char-range"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
[[package]]
name = "unic-common"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
[[package]]
name = "unic-segment"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23"
dependencies = [
"unic-ucd-segment",
]
[[package]]
name = "unic-ucd-segment"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700"
dependencies = [
"unic-char-property",
"unic-char-range",
"unic-ucd-version",
]
[[package]]
name = "unic-ucd-version"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
dependencies = [
"unic-common",
]
[[package]]
name = "unicase"
version = "2.8.1"

View File

@@ -20,6 +20,7 @@ leptos_axum = { version = "0.8", optional = true }
log = "0.4.0"
simple_logger = "5.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
axum = { version = "0.8.0", optional = true, features = ["macros"] }
tower = { version = "0.5.0", optional = true }
tower-http = { version = "0.6.0", features = ["fs", "trace"], optional = true }
@@ -66,6 +67,18 @@ icondata_core = "0.1.0"
icondata_ai = "0.0.10"
chrono = { version = "0.4.41", features = ["serde"] }
tracing = "0.1.41"
# Email
lettre = { version = "0.11.18", default-features = false, features = [
"builder",
"hostname",
"pool",
"rustls-tls",
"smtp-transport",
"tokio1",
"tokio1-rustls-tls",
"tracing",
], optional = true }
tera = { version = "1.20.0", default-features = false, optional = true }
[features]
default = ["ssr"]
@@ -87,6 +100,8 @@ ssr = [
"leptos_router/ssr",
"dep:leptos_axum",
"thaw/ssr",
"lettre",
"tera",
]
[package.metadata.cargo-all-features]

Binary file not shown.

View File

@@ -14,7 +14,7 @@ use thaw::{ConfigProvider, Layout, LayoutHeader, Theme};
#[cfg(feature = "ssr")]
pub mod ssr {
use crate::{auth::ssr::AuthSession, state::AppState};
use crate::{auth::ssr::AuthSession, dangerous_lettre::DangerousLettre, state::AppState};
use leptos::prelude::*;
use sqlx::SqlitePool;
@@ -23,6 +23,11 @@ pub mod ssr {
.ok_or_else(|| ServerFnError::ServerError("Pool missing.".into()))
}
pub fn get_lettre() -> Result<DangerousLettre, ServerFnError> {
with_context::<AppState, _>(|state| state.lettre.clone())
.ok_or_else(|| ServerFnError::ServerError("Lettre missing".into()))
}
pub async fn auth() -> Result<AuthSession, ServerFnError> {
let auth = leptos_axum::extract().await?;
Ok(auth)

View File

@@ -10,7 +10,7 @@ pub fn Category(category: Model) -> impl IntoView {
<Card class="p-2">
<CardHeader>
<Body1>
<b>{category.name}</b>" - "{format!("{}", category.updated_at.format("%d-%m-%Y"))}
<b>{category.name}</b>
</Body1>
<CardHeaderDescription slot>
<Caption1>{category.location}</Caption1>

View File

@@ -58,10 +58,6 @@ pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
/// Renders the home page of your application.
#[component]
pub fn HomePage() -> impl IntoView {
// Creates a reactive value to update the button
// let count = RwSignal::new(0);
// let on_click = move |_| *count.write() += 1;
let categories = Resource::new(move || (), move |_| get_categories());
view! {

View File

@@ -5,21 +5,50 @@ use thaw::*;
#[component]
pub fn Signup(action: ServerAction<Signup>) -> impl IntoView {
let response = action.value();
let error = move || match response.get() {
Some(Err(e)) => Some(e.to_string().replace("error running server function: ", "")),
_ => None,
};
let pending = action.pending();
let disabled = RwSignal::new(false);
Effect::new(move |_| {
pending.get();
disabled.set(true);
});
Effect::new(move |_| {
let Some(response) = response.get() else {
disabled.set(false);
return;
};
if response.is_err() {
disabled.set(false);
}
});
view! {
<ActionForm action=action>
<Flex vertical=true inline=true>
{move || error().map(|e| view! {
<MessageBar intent=MessageBarIntent::Error>
<MessageBarBody>
{e}
</MessageBarBody>
</MessageBar>
})}
{move || response.get().map(|e|
match e {
Err(e) => view! {
<MessageBar intent=MessageBarIntent::Error>
<MessageBarBody>
{e.to_string().replace("error running server function: ", "")}
</MessageBarBody>
</MessageBar>
},
Ok(e) => view! {
<MessageBar intent=MessageBarIntent::Success>
<MessageBarBody>
{e.msg}
</MessageBarBody>
</MessageBar>
}
}
)}
<Input
input_type=InputType::Email
placeholder="Email adres"
@@ -32,7 +61,7 @@ pub fn Signup(action: ServerAction<Signup>) -> impl IntoView {
/>
<Input input_type=InputType::Password placeholder="Wachtwoord" name="password"/>
<Input input_type=InputType::Password placeholder="Wachtwoord opnieuw" name="password_confirmation" />
<Button button_type=ButtonType::Submit class="button">
<Button button_type=ButtonType::Submit disabled=disabled class="button">
"Aanmelden"
</Button>
</Flex>

View File

@@ -43,7 +43,7 @@ pub mod ssr {
pub use sqlx::SqlitePool;
pub use std::collections::HashSet;
pub type AuthSession = axum_session_auth::AuthSession<User, i64, SessionSqlitePool, SqlitePool>;
pub use crate::app::ssr::{auth, pool};
pub use crate::app::ssr::{auth, get_lettre, pool};
pub use async_trait::async_trait;
pub use bcrypt::{DEFAULT_COST, hash, verify};
@@ -219,16 +219,24 @@ pub async fn login(
}
}
#[server(Signup, "/api")]
#[derive(Clone, Serialize, Deserialize)]
pub struct SignupResponse {
pub msg: String,
}
use crate::auth::server_fn::codec::Json;
#[server(Signup, "/api",
output = Json)]
pub async fn signup(
email: String,
displayname: String,
password: String,
password_confirmation: String,
) -> Result<(), ServerFnError> {
) -> Result<SignupResponse, ServerFnError> {
use self::ssr::*;
let pool = pool()?;
let lettre = get_lettre()?;
if email.is_empty() {
return Err(ServerFnError::ServerError(
@@ -277,30 +285,32 @@ pub async fn signup(
sqlx::query("INSERT INTO user_verifications (user_id, token) VALUES (?,?)")
.bind(user.id)
.bind(verify_token)
.bind(verify_token.clone())
.execute(&pool)
.await?;
// Send email with token url
// After verification auto-login
lettre.user_created(&user, &verify_token).await;
// leptos_axum::redirect("/");
Ok(())
Ok(SignupResponse {
msg: "Je account is aangemaakt. Controleer je email om je account te activeren.".into(),
})
}
use crate::auth::server_fn::codec::GetUrl;
#[server(
name = Verify,
prefix = "/auth",
endpoint = "verify",
endpoint = "activate",
input = GetUrl
)]
pub async fn verify(token: String) -> Result<(), ServerFnError> {
use self::ssr::*;
// use self::ssr::*;
let pool = pool()?;
dbg!(token);
// let pool = pool()?;
// get user id from token
// set user to verified

118
src/dangerous_lettre.rs Normal file
View File

@@ -0,0 +1,118 @@
use lettre::message::{Mailbox, MultiPart, SinglePart, header};
use lettre::AsyncSmtpTransport;
use lettre::Message;
use lettre::Tokio1Executor;
use lettre::transport::smtp::authentication::Credentials;
use tera::{Context, Tera};
use crate::auth::User;
#[derive(Debug, Clone)]
pub struct DangerousLettre {
mailer: AsyncSmtpTransport<Tokio1Executor>,
from: Mailbox,
tera: Tera,
}
impl DangerousLettre {
pub fn new(
host: &str,
username: &str,
password: &str,
from: &str,
) -> Result<Self, anyhow::Error> {
let creds = Credentials::new(username.to_string(), password.to_string());
let mailer: AsyncSmtpTransport<Tokio1Executor> =
AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(host)?
.credentials(creds)
.build();
let tera = Tera::new("templates/**/*.email.*")?;
let from = from.parse::<Mailbox>()?;
Ok(Self { mailer, from, tera })
}
pub async fn user_created(&self, user: &User, token: &str) {
use lettre::AsyncTransport;
let mut context = tera::Context::new();
let url = format!("{}/auth/activate?token={}", crate::BASE_URL, token); // Move base url to env
context.insert("activate_url", &url);
let to = user.email.parse().unwrap();
let message = self
.template("user/created", "Welkom bij AirsoftFotos.nl!", &context, to)
.unwrap();
if let Err(e) = self.mailer.send(message).await {
eprintln!("{:#?}", e);
};
}
// async fn forgot_password(&self, user: &User, token: &PasswordResetToken) {
// let mut context = tera::Context::new();
// let url = format!("{}/auth/reset/{}", BASE_URL, token); // Move base url to env
// context.insert("reset_url", &url);
// let to = format!("{}", user.email).parse().unwrap();
// let message = self
// .template(
// "user/password_reset",
// "Password reset request",
// &context,
// to,
// )
// .unwrap();
// if let Err(e) = self.mailer.send(message).await {
// eprintln!("{:#?}", e);
// };
// }
pub fn template(
&self,
template: &str,
subject: &str,
context: &Context,
to: Mailbox,
) -> Result<Message, anyhow::Error> {
let plain_text = self
.tera
.render(&format!("{}.email.txt", template), context)?;
let html = self
.tera
.render(&format!("{}.email.html", template), context)?;
let message = Message::builder()
.subject(subject)
.from(self.from.clone())
.to(to)
.multipart(
MultiPart::alternative() // This is composed of two parts.
.singlepart(
SinglePart::builder()
.header(header::ContentType::TEXT_PLAIN)
.body(plain_text), // Every message should have a plain text fallback.
)
.singlepart(
SinglePart::builder()
.header(header::ContentType::TEXT_HTML)
.body(html),
),
)?;
Ok(message)
}
}

View File

@@ -2,11 +2,17 @@
pub mod app;
pub mod auth;
#[cfg(feature = "ssr")]
pub mod dangerous_lettre;
pub mod error_template;
pub mod errors;
#[cfg(feature = "ssr")]
pub mod state;
pub static BASE_URL: &str = "https://dev.airsoftfotos.nl";
pub static PROJECT_NAME: &str = "AirsoftFotos.nl";
pub static COPYRIGHT: &str = "AirsoftFotos.nl © 2025";
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {

View File

@@ -1,6 +1,7 @@
use std::{fmt::Display, net::IpAddr, time::Duration};
use airsoftfotos::{app::*, auth::User, state::AppState};
use airsoftfotos::{app::*, auth::User, dangerous_lettre::DangerousLettre, state::AppState};
use anyhow::Context;
use axum::{Router, body::Body, extract::Request, middleware::Next, response::Response};
use axum_session::{SessionConfig, SessionLayer, SessionStore};
use axum_session_auth::{AuthConfig, AuthSessionLayer};
@@ -63,18 +64,25 @@ async fn uri_middleware(request: Request<Body>, next: Next) -> Response {
}
#[tokio::main]
async fn main() {
async fn main() -> anyhow::Result<()> {
dotenvy::dotenv().ok();
simple_logger::init_with_level(log::Level::Info).expect("couldn't initialize logging");
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL to exist");
let db_url = load_env("DATABASE_URL")?;
let pool = SqlitePoolOptions::new()
.connect(&db_url)
.await
.expect("Could not make pool.");
let dangerous_lettre = DangerousLettre::new(
&load_env("SMTP_HOST")?,
&load_env("SMTP_USERNAME")?,
&load_env("SMTP_PASSWORD")?,
&load_env("SMTP_FROM")?,
)?;
// Auth section
let session_config = SessionConfig::default().with_table_name("axum_sessions");
let auth_config = AuthConfig::<i64>::default();
@@ -99,6 +107,7 @@ async fn main() {
leptos_options,
pool: pool.clone(),
routes: routes.clone(),
lettre: dangerous_lettre,
};
// build our application with a route
@@ -127,8 +136,8 @@ async fn main() {
)
})
.on_failure(
|error: ServerErrorsFailureClass, _latency: Duration, _span: &Span| {
eprintln!("{:#?}", error);
|_error: ServerErrorsFailureClass, _latency: Duration, _span: &Span| {
// eprintln!("{:#?}", error);
},
),
)
@@ -138,7 +147,9 @@ async fn main() {
// `axum::Server` is a re-export of `hyper::Server`
log!("listening on http://{}", &addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
Ok(axum::serve(listener, app.into_make_service()).await?)
}
fn load_env(key: &str) -> anyhow::Result<String> {
std::env::var(key).with_context(|| format!("failed to load environment variable {}", key))
}

View File

@@ -3,6 +3,8 @@ use leptos::prelude::LeptosOptions;
use leptos_axum::AxumRouteListing;
use sqlx::SqlitePool;
use crate::dangerous_lettre::DangerousLettre;
/// This takes advantage of Axum's SubStates feature by deriving FromRef. This is the only way to have more than one
/// item in Axum's State. Leptos requires you to have leptosOptions in your State struct for the leptos route handlers
#[derive(FromRef, Debug, Clone)]
@@ -10,4 +12,5 @@ pub struct AppState {
pub leptos_options: LeptosOptions,
pub pool: SqlitePool,
pub routes: Vec<AxumRouteListing>,
pub lettre: DangerousLettre,
}

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AirsoftFotos.nl - Activeer je account</title>
</head>
<body>
<p>Bedankt voor het aanmaken van een gebruiker op AirsoftFotos.nl</p>
<p>Klik <a href="{{ activate_url }}" target="_BLANK">hier</a> of ga naar <a href="{{ activate_url }}"
target="_BLANK">{{ activate_url }}</a> om je account te activeren.
account.</p>
</body>
</html>

View File

@@ -0,0 +1,3 @@
Bedankt voor het aanmaken van een gebruiker op AirsoftFotos.nl
Ga naar {{ activate_url }} om je account te activeren.