EOD
This commit is contained in:
325
Cargo.lock
generated
325
Cargo.lock
generated
@@ -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"
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -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]
|
||||
|
||||
BIN
airsoftfotos.db
BIN
airsoftfotos.db
Binary file not shown.
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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! {
|
||||
|
||||
@@ -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>
|
||||
|
||||
32
src/auth.rs
32
src/auth.rs
@@ -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
118
src/dangerous_lettre.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
27
src/main.rs
27
src/main.rs
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
18
templates/user/created.email.html
Normal file
18
templates/user/created.email.html
Normal 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>
|
||||
3
templates/user/created.email.txt
Normal file
3
templates/user/created.email.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Bedankt voor het aanmaken van een gebruiker op AirsoftFotos.nl
|
||||
|
||||
Ga naar {{ activate_url }} om je account te activeren.
|
||||
Reference in New Issue
Block a user