Implemented mail notifications.

main
Josef Rokos 1 year ago
parent d18ef72d03
commit 1b6f544e55

456
Cargo.lock generated

@ -331,6 +331,54 @@ dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
[[package]]
name = "anstyle-parse"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
]
[[package]]
name = "anyhow"
version = "1.0.71"
@ -668,7 +716,17 @@ dependencies = [
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets",
"windows-targets 0.48.0",
]
[[package]]
name = "chumsky"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23170228b96236b5a7299057ac284a321457700bc8c41a4476052f0f4ba5349d"
dependencies = [
"hashbrown 0.12.3",
"stacker",
]
[[package]]
@ -723,6 +781,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "config"
version = "0.13.3"
@ -817,6 +881,16 @@ dependencies = [
"version_check",
]
[[package]]
name = "core-foundation"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
@ -1077,6 +1151,22 @@ dependencies = [
"serde",
]
[[package]]
name = "email-encoding"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75"
dependencies = [
"base64 0.21.2",
"memchr",
]
[[package]]
name = "email_address"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2153bd83ebc09db15bcbdc3e2194d901804952e3dc96967e1cd3b0c5c32d112"
[[package]]
name = "encoding_rs"
version = "0.8.32"
@ -1086,17 +1176,27 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "env_filter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.10.1"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
checksum = "05e7cf40684ae96ade6232ed84582f40ce0a66efcd43a5117aef610534f8e0b8"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"is-terminal",
"log",
"regex",
"termcolor",
]
[[package]]
@ -1113,7 +1213,7 @@ checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
@ -1134,7 +1234,7 @@ checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
dependencies = [
"cfg-if",
"home",
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
@ -1177,6 +1277,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.2.0"
@ -1493,12 +1608,6 @@ dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
name = "hex"
version = "0.4.3"
@ -1539,7 +1648,18 @@ version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
dependencies = [
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
name = "hostname"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
dependencies = [
"libc",
"match_cfg",
"winapi",
]
[[package]]
@ -1636,6 +1756,16 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "idna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "if_chain"
version = "1.0.2"
@ -1692,17 +1822,6 @@ version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a53088c87cf71c9d4f3372a2cb9eea1e7b8a0b1bf8b7f7d23fe5b76dbb07e63b"
[[package]]
name = "is-terminal"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi 0.3.3",
"rustix",
"windows-sys",
]
[[package]]
name = "itertools"
version = "0.10.5"
@ -2012,6 +2131,35 @@ dependencies = [
"tracing",
]
[[package]]
name = "lettre"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a48c2e9831b370bc2d7233c2620298c45f3a158ed6b4b8d7416b2ada5a268fd8"
dependencies = [
"async-trait",
"base64 0.21.2",
"chumsky",
"email-encoding",
"email_address",
"fastrand",
"futures-io",
"futures-util",
"hostname",
"httpdate",
"idna 0.5.0",
"mime",
"native-tls",
"nom",
"once_cell",
"quoted_printable",
"socket2 0.5.4",
"tokio",
"tokio-native-tls",
"url",
"uuid",
]
[[package]]
name = "libc"
version = "0.2.147"
@ -2117,6 +2265,12 @@ dependencies = [
"quote",
]
[[package]]
name = "match_cfg"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
[[package]]
name = "md-5"
version = "0.9.1"
@ -2183,7 +2337,25 @@ dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
name = "native-tls"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
dependencies = [
"lazy_static",
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
@ -2261,7 +2433,7 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi 0.2.6",
"hermit-abi",
"libc",
]
@ -2277,6 +2449,50 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8"
dependencies = [
"bitflags 2.4.0",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "pad-adapter"
version = "0.1.1"
@ -2303,7 +2519,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
"windows-targets 0.48.0",
]
[[package]]
@ -2486,6 +2702,15 @@ dependencies = [
"yansi",
]
[[package]]
name = "psm"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874"
dependencies = [
"cc",
]
[[package]]
name = "ptr_meta"
version = "0.1.4"
@ -2542,6 +2767,12 @@ dependencies = [
"syn 2.0.48",
]
[[package]]
name = "quoted_printable"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79ec282e887b434b68c18fe5c121d38e72a5cf35119b59e54ec5b992ea9c8eb0"
[[package]]
name = "radium"
version = "0.7.0"
@ -2644,6 +2875,7 @@ dependencies = [
"leptos_actix",
"leptos_meta",
"leptos_router",
"lettre",
"log",
"pwhash",
"regex",
@ -2778,7 +3010,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
@ -2836,6 +3068,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "schannel"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -2858,6 +3099,29 @@ version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "security-framework"
version = "2.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "self_cell"
version = "1.0.1"
@ -3132,7 +3396,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
@ -3381,6 +3645,19 @@ dependencies = [
"uuid",
]
[[package]]
name = "stacker"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
dependencies = [
"cc",
"cfg-if",
"libc",
"psm",
"winapi",
]
[[package]]
name = "stringprep"
version = "0.1.3"
@ -3453,16 +3730,7 @@ dependencies = [
"fastrand",
"redox_syscall",
"rustix",
"windows-sys",
]
[[package]]
name = "termcolor"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
dependencies = [
"winapi-util",
"windows-sys 0.48.0",
]
[[package]]
@ -3541,7 +3809,17 @@ dependencies = [
"pin-project-lite",
"signal-hook-registry",
"socket2 0.4.9",
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]]
@ -3748,7 +4026,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
dependencies = [
"form_urlencoded",
"idna",
"idna 0.4.0",
"percent-encoding",
]
@ -3758,6 +4036,12 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "1.4.1"
@ -3774,7 +4058,7 @@ version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd"
dependencies = [
"idna",
"idna 0.4.0",
"lazy_static",
"regex",
"serde",
@ -3979,7 +4263,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets",
"windows-targets 0.48.0",
]
[[package]]
@ -3988,7 +4272,16 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
"windows-targets 0.48.0",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
@ -3997,13 +4290,28 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
@ -4012,42 +4320,84 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.19"

@ -30,9 +30,10 @@ futures-util = "0.3.28"
regex = "1.10.2"
toml = "0.8.8"
log = "0.4.20"
env_logger = "0.10.1"
env_logger = "0.11"
getopts = "0.2.21"
leptos-use = "0.10.1"
lettre = {version = "0.11", features = ["tokio1-native-tls", "smtp-transport", "file-transport"], optional = true}
[features]
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
@ -43,6 +44,7 @@ ssr = [
"dep:leptos_actix",
"dep:actix-session",
"dep:sqlx",
"dep:lettre",
"leptos/ssr",
"leptos_meta/ssr",
"leptos_router/ssr",

@ -10,6 +10,13 @@ cfg_if! { if #[cfg(feature = "ssr")] {
use crate::backend::get_pool;
use crate::error::AppError;
use log::info;
use crate::config::Mailing;
use crate::config::MailTransport;
use crate::backend::data::ResSumWithItems;
use lettre::message::Message as LettreMessage;
use lettre::{AsyncSmtpTransport, AsyncFileTransport, AsyncTransport, Tokio1Executor};
use lettre::transport::smtp::authentication::Credentials;
use std::ops::Add;
pub async fn message_for_type(msg_type: &MessageType, pool: &PgPool) -> Result<Message, Error> {
Ok(query_as::<_, Message>("SELECT * FROM message WHERE msg_type = $1")
@ -48,6 +55,73 @@ cfg_if! { if #[cfg(feature = "ssr")] {
Ok(())
}
pub struct MailMessage {
reply_to: String,
to: String,
subject: String,
text: String
}
impl MailMessage {
pub fn new(reply_to: String, to: String, message: Message, reservation: &ResSumWithItems) -> Self {
Self {
reply_to,
to,
subject: message.subject,
text: Self::replace_body_vars(message.text, &reservation)
}
}
fn replace_body_vars(text: String, reservation: &ResSumWithItems) -> String {
text
.replace("#date#", &reservation.summary.date.format("%d. %m. %Y").to_string())
.replace("#summary#", &{
let mut sum = "".to_string();
for p in &reservation.reservations {
sum = sum.add(&format!("{}: {} - {}",
p.property.name,
p.reservation.from.format("%H:%M").to_string(), p.reservation.to.format("%H:%M").to_string()));
}
sum
}
)
}
pub fn build_mail(&self, from: String) -> Result<LettreMessage, AppError> {
Ok(LettreMessage::builder()
.from(from.parse()?)
.reply_to(self.reply_to.parse()?)
.to(self.to.parse()?)
.subject(&self.subject)
.body(self.text.clone())?)
}
}
impl Mailing {
pub async fn send_mail(&self, msg: MailMessage) -> Result<(), AppError> {
match self.transport() {
MailTransport::Smtp => {
let transport = if self.tls().unwrap_or(false) {
AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&self.server().clone().unwrap_or_default())
} else {
AsyncSmtpTransport::<Tokio1Executor>::relay(&self.server().clone().unwrap_or_default())
}.expect("Cannot create SMTP mail transport");
if self.user().is_some() && self.password().is_some() {
let cred = Credentials::new(self.user().clone().unwrap(), self.password().clone().unwrap());
transport.credentials(cred).build().send(msg.build_mail(self.from().to_string())?).await?;
} else {
transport.build().send(msg.build_mail(self.from().to_string())?).await?;
}
}
MailTransport::File => {
AsyncFileTransport::<Tokio1Executor>::new(self.path().clone().unwrap_or_default())
.send(msg.build_mail(self.from().to_string())?).await?;
}
}
Ok(())
}
}
}}
#[server]

@ -54,27 +54,39 @@ cfg_if!{
use actix_web::web::Data;
use leptos_actix::extract;
use leptos::ServerFnError;
use crate::config::Mailing;
#[derive(Clone)]
pub struct AppData {
db_pool: PgPool
db_pool: PgPool,
mailer: Mailing
}
impl AppData {
pub fn new(db_pool: PgPool) -> Self {
pub fn new(db_pool: PgPool, mailer: Mailing) -> Self {
Self {
db_pool
db_pool,
mailer
}
}
pub fn db_pool(&self) -> &PgPool {
&self.db_pool
}
pub fn mailer(&self) -> &Mailing {
&self.mailer
}
}
pub async fn get_pool() -> Result<PgPool, ServerFnError> {
let data = extract::<Data<AppData>>().await?;
Ok(data.db_pool().clone())
}
pub async fn get_mailing() -> Result<Mailing, ServerFnError> {
let data = extract::<Data<AppData>>().await?;
Ok(data.mailer().clone())
}
}
}

@ -13,8 +13,16 @@ cfg_if! { if #[cfg(feature = "ssr")] {
use std::ops::DerefMut;
use std::str::FromStr;
use futures_util::future::join_all;
use crate::backend::data::{ReservationSum, ReservationState, ResWithProperty, Customer};
use log::warn;
use crate::backend::data::{ReservationSum, ReservationState, ResWithProperty, Customer, Message, MessageType};
use crate::backend::get_pool;
use crate::backend::get_mailing;
use crate::backend::mail::MailMessage;
use crate::backend::mail::get_message;
use crate::error::AppError;
use sqlx::PgPool;
use crate::backend::user::admin_email;
use crate::backend::user::emails_for_notify;
async fn find_sum_by_uuid(uuid: &Uuid, tx: &mut Transaction<'_, Postgres>) -> Result<ReservationSum, Error> {
let reservation = query_as::<_, ReservationSum>("SELECT * FROM reservation_sum WHERE uuid = $1")
@ -43,6 +51,39 @@ cfg_if! { if #[cfg(feature = "ssr")] {
}
}
async fn reservation_by_uuid(uuid: Uuid) -> Result<ResSumWithItems, ServerFnError> {
let pool = get_pool().await?;
let summary = query_as::<_, ReservationSum>("SELECT * FROM reservation_sum WHERE uuid = $1")
.bind(uuid)
.fetch_one(&pool)
.await?;
let sum_id = summary.id();
let cust_id = summary.customer;
Ok(ResSumWithItems{
summary,
customer: customer_for_reservation(cust_id, &pool).await?,
reservations: items_for_reservation(sum_id, &pool).await?
})
}
async fn items_for_reservation(id: i32, pool: &PgPool) -> Result<Vec<ResWithProperty>, ServerFnError> {
Ok(query_as::<_, ResWithProperty>(
"SELECT r.id, r.from, r.to, r.property, r.summary, p.name, p,description \
FROM reservation as r \
JOIN property as p ON r.property = p.id WHERE r.summary = $1")
.bind(id)
.fetch_all(pool)
.await?)
}
async fn customer_for_reservation(id: i32, pool: &PgPool) -> Result<Customer, ServerFnError> {
Ok(query_as::<_, Customer>("SELECT * FROM customer WHERE id = $1")
.bind(id)
.fetch_one(pool)
.await?)
}
async fn reservations_in_range(from: &NaiveDate, to: &NaiveDate, state: Option<ReservationState>) -> Result<Vec<ResSumWithItems>, ServerFnError> {
let pool = get_pool().await?;
let sums = if let Some(s) = state {
@ -74,18 +115,9 @@ cfg_if! { if #[cfg(feature = "ssr")] {
return Ok(vec![])
}
let res: Result<Vec<ResSumWithItems>, Error> = join_all(sums.into_iter().map(|s| async {
let reservations = query_as::<_, ResWithProperty>(
"SELECT r.id, r.from, r.to, r.property, r.summary, p.name, p,description \
FROM reservation as r \
JOIN property as p ON r.property = p.id WHERE r.summary = $1")
.bind(s.id())
.fetch_all(&pool)
.await?;
let customer = query_as::<_, Customer>("SELECT * FROM customer WHERE id = $1")
.bind(s.customer)
.fetch_one(&pool)
.await?;
let res: Result<Vec<ResSumWithItems>, ServerFnError> = join_all(sums.into_iter().map(|s| async {
let reservations = items_for_reservation(s.id(), &pool).await?;
let customer = customer_for_reservation(s.customer, &pool).await?;
Ok(ResSumWithItems {
summary: s,
customer,
@ -106,6 +138,51 @@ cfg_if! { if #[cfg(feature = "ssr")] {
Ok(())
}
async fn notify_new_all(admin_mail: String, reservation: &ResSumWithItems) -> Result<(), AppError> {
let mailing = get_mailing().await?;
let msg = get_message(MessageType::NewReservation).await?;
for m in emails_for_notify().await? {
mailing.send_mail(MailMessage::new(admin_mail.clone(), m, msg.clone(), reservation)).await?;
}
Ok(())
}
async fn notify_new(uuid: Uuid) -> Result<(), AppError> {
let mailing = get_mailing().await?;
let msg = get_message(MessageType::NewReservationCust).await?;
let reservation = reservation_by_uuid(uuid).await?;
let admin_mail = admin_email().await;
if admin_mail.is_none() {
return Err(AppError::MailSendError("No admin mail".to_string()))
}
mailing.send_mail(MailMessage::new(admin_mail.clone().unwrap(), reservation.customer.email.clone(), msg, &reservation)).await?;
notify_new_all(admin_mail.unwrap(), &reservation).await
}
async fn send_notify(uuid: Uuid, msg: Message) -> Result<(), AppError> {
let mailing = get_mailing().await?;
let reservation = reservation_by_uuid(uuid).await?;
let admin_mail = admin_email().await;
if admin_mail.is_none() {
return Err(AppError::MailSendError("No admin mail".to_string()))
}
mailing.send_mail(MailMessage::new(admin_mail.unwrap(), reservation.customer.email.clone(), msg, &reservation)).await
}
async fn notify_approve(uuid: Uuid) -> Result<(), AppError> {
send_notify(uuid, get_message(MessageType::ReservationApp).await?).await
}
async fn notify_cancel(uuid: Uuid) -> Result<(), AppError> {
send_notify(uuid, get_message(MessageType::ReservationCanceled).await?).await
}
}}
#[server]
@ -206,6 +283,10 @@ pub async fn create_reservation(reservation: CrReservation) -> Result<ApiRespons
tx.commit().await?;
if let Err(e) = notify_new(res_uuid).await {
warn!("Notification not send: {}", e);
}
Ok(ApiResponse::Data(reservation.date()))
}
@ -247,7 +328,12 @@ pub async fn approve(uuid: String) -> Result<ApiResponse<()>, ServerFnError> {
use crate::backend::data::ReservationState;
perm_check!(is_logged_in);
set_state(Uuid::parse_str(&uuid)?, ReservationState::Approved).await?;
let uuid = Uuid::parse_str(&uuid)?;
set_state(uuid, ReservationState::Approved).await?;
if let Err(e) = notify_approve(uuid).await {
warn!("Approve notification not send: {}", e);
}
Ok(ApiResponse::Data(()))
}
@ -258,7 +344,12 @@ pub async fn cancel(uuid: String) -> Result<ApiResponse<()>, ServerFnError> {
use crate::backend::data::ReservationState;
perm_check!(is_logged_in);
set_state(Uuid::parse_str(&uuid)?, ReservationState::Canceled).await?;
let uuid = Uuid::parse_str(&uuid)?;
set_state(uuid, ReservationState::Canceled).await?;
if let Err(e) = notify_cancel(uuid).await {
warn!("Cancel notification not send: {}", e);
}
Ok(ApiResponse::Data(()))
}

@ -10,6 +10,7 @@ cfg_if! { if #[cfg(feature = "ssr")] {
use leptos_actix::{extract, redirect};
use log::{info, warn};
use crate::error::AppError;
use crate::backend::get_pool;
pub async fn has_admin_user(pool: &PgPool) -> Result<bool, Error> {
let count: (i64,) = query_as(r#"SELECT COUNT(id) FROM "user" WHERE admin = $1"#)
@ -68,6 +69,36 @@ cfg_if! { if #[cfg(feature = "ssr")] {
false
}
}
pub async fn admin_email() -> Option<String> {
let pool = get_pool().await.ok()?;
let mail: Result<(String,), Error> = query_as(r#"SELECT email FROM "user" WHERE login = 'admin'"#)
.fetch_one(&pool)
.await;
if let Ok(m) = mail {
Some(m.0)
} else {
None
}
}
pub async fn emails_for_notify() -> Result<Vec<String>, ServerFnError> {
let pool = get_pool().await?;
let mails: Result<Vec<(String,)>, Error> = query_as(r#"SELECT email FROM "user" WHERE (email IS NOT NULL OR email <> '') AND get_emails = true"#)
.fetch_all(&pool)
.await;
if let Err(e) = mails {
if matches!(e, Error::RowNotFound) {
Ok(vec![])
} else {
Err(e.into())
}
} else {
Ok(mails.unwrap().into_iter().map(|m| m.0).collect())
}
}
}}
#[server]

@ -51,12 +51,58 @@ impl Database {
}
}
#[cfg(feature = "ssr")]
#[derive(Deserialize, Clone)]
pub enum MailTransport {
Smtp,
File
}
#[cfg(feature = "ssr")]
#[derive(Deserialize, Clone)]
pub struct Mailing {
transport: MailTransport,
from: String,
path: Option<String>,
server: Option<String>,
user: Option<String>,
password: Option<String>,
tls: Option<bool>
}
#[cfg(feature = "ssr")]
impl Mailing {
pub fn transport(&self) -> &MailTransport {
&self.transport
}
pub fn from(&self) -> &str {
&self.from
}
pub fn path(&self) -> &Option<String> {
&self.path
}
pub fn server(&self) -> &Option<String> {
&self.server
}
pub fn user(&self) -> &Option<String> {
&self.user
}
pub fn password(&self) -> &Option<String> {
&self.password
}
pub fn tls(&self) -> Option<bool> {
self.tls
}
}
#[cfg(feature = "ssr")]
#[derive(Deserialize)]
pub struct Configuration {
session: Session,
network: Network,
database: Database
database: Database,
mailing: Mailing
}
#[cfg(feature = "ssr")]
@ -70,6 +116,9 @@ impl Configuration {
pub fn database(&self) -> &Database {
&self.database
}
pub fn mailing(&self) -> &Mailing {
&self.mailing
}
}
#[cfg(feature = "ssr")]

@ -8,7 +8,9 @@ pub enum AppError {
HourParseError,
ServerError(String),
FatalError(String),
SlotParseError
SlotParseError,
MailAddrParseErr(String),
MailSendError(String)
}
impl AppError {
@ -17,7 +19,9 @@ impl AppError {
AppError::HourParseError => {"Hour parse error".to_string()},
AppError::ServerError(e) => {format!("Server error: {}", e)},
AppError::FatalError(e) => {format!("Fatal error: {}", e)},
AppError::SlotParseError => {"Book slot parse error".to_string()}
AppError::SlotParseError => {"Book slot parse error".to_string()},
AppError::MailAddrParseErr(e) => {format!("Cannot parse email address: {}", e)},
AppError::MailSendError(e) => {format!("Cannot send email: {}", e)}
}
}
}
@ -48,3 +52,31 @@ impl From<ParseError> for AppError {
AppError::HourParseError
}
}
#[cfg(feature = "ssr")]
impl From<lettre::address::AddressError> for AppError {
fn from(value: lettre::address::AddressError) -> Self {
AppError::MailAddrParseErr(value.to_string())
}
}
#[cfg(feature = "ssr")]
impl From<lettre::error::Error> for AppError {
fn from(value: lettre::error::Error) -> Self {
AppError::MailSendError(value.to_string())
}
}
#[cfg(feature = "ssr")]
impl From<lettre::transport::smtp::Error> for AppError {
fn from(value: lettre::transport::smtp::Error) -> Self {
AppError::MailSendError(value.to_string())
}
}
#[cfg(feature = "ssr")]
impl From<lettre::transport::file::Error> for AppError {
fn from(value: lettre::transport::file::Error) -> Self {
AppError::MailSendError(value.to_string())
}
}

@ -51,9 +51,10 @@ async fn main() -> std::io::Result<()> {
let pool = PgPoolOptions::new()
.max_connections(10)
.connect(&srv_conf.database().con_string()).await.unwrap();
migrate!().run(&pool).await.expect("could not run SQLx migrations");
let mailing = srv_conf.mailing().clone();
if let Err(e) = create_admin(&pool).await {
error!("Error while checking admin user: {:?}", e);
}
@ -69,7 +70,7 @@ async fn main() -> std::io::Result<()> {
let site_root = &leptos_options.site_root;
App::new()
.app_data(Data::new(AppData::new(pool.clone())))
.app_data(Data::new(AppData::new(pool.clone(), mailing.clone())))
.wrap(Authentication)
.wrap(SessionMiddleware::new(
CookieSessionStore::default(),

Loading…
Cancel
Save