Implemented user authentication.

main
Josef Rokos 2 years ago
parent 0bac17f2eb
commit 17f628739f

197
Cargo.lock generated

@ -44,9 +44,9 @@ dependencies = [
[[package]] [[package]]
name = "actix-http" name = "actix-http"
version = "3.3.1" version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74" checksum = "a92ef85799cba03f76e4f7c10f533e66d87c9a7e7055f3391f09000ad8351bc9"
dependencies = [ dependencies = [
"actix-codec", "actix-codec",
"actix-rt", "actix-rt",
@ -54,7 +54,7 @@ dependencies = [
"actix-utils", "actix-utils",
"ahash 0.8.3", "ahash 0.8.3",
"base64 0.21.2", "base64 0.21.2",
"bitflags 1.3.2", "bitflags 2.4.0",
"brotli", "brotli",
"bytes", "bytes",
"bytestring", "bytestring",
@ -127,7 +127,7 @@ dependencies = [
"futures-util", "futures-util",
"mio", "mio",
"num_cpus", "num_cpus",
"socket2", "socket2 0.4.9",
"tokio", "tokio",
"tracing", "tracing",
] ]
@ -145,9 +145,9 @@ dependencies = [
[[package]] [[package]]
name = "actix-session" name = "actix-session"
version = "0.7.2" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43da8b818ae1f11049a4d218975345fe8e56ce5a5f92c11f972abcff5ff80e87" checksum = "2e6a28f813a6671e1847d005cad0be36ae4d016287690f765c303379837c13d6"
dependencies = [ dependencies = [
"actix-service", "actix-service",
"actix-utils", "actix-utils",
@ -172,9 +172,9 @@ dependencies = [
[[package]] [[package]]
name = "actix-web" name = "actix-web"
version = "4.3.1" version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96" checksum = "0e4a5b5e29603ca8c94a77c65cf874718ceb60292c5a5c3e5f4ace041af462b9"
dependencies = [ dependencies = [
"actix-codec", "actix-codec",
"actix-http", "actix-http",
@ -185,7 +185,7 @@ dependencies = [
"actix-service", "actix-service",
"actix-utils", "actix-utils",
"actix-web-codegen", "actix-web-codegen",
"ahash 0.7.6", "ahash 0.8.3",
"bytes", "bytes",
"bytestring", "bytestring",
"cfg-if", "cfg-if",
@ -194,7 +194,6 @@ dependencies = [
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
"futures-util", "futures-util",
"http",
"itoa", "itoa",
"language-tags", "language-tags",
"log", "log",
@ -206,7 +205,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"smallvec", "smallvec",
"socket2", "socket2 0.5.4",
"time 0.3.22", "time 0.3.22",
"url", "url",
] ]
@ -246,7 +245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cipher", "cipher 0.4.4",
"cpufeatures", "cpufeatures",
] ]
@ -258,7 +257,7 @@ checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237"
dependencies = [ dependencies = [
"aead", "aead",
"aes", "aes",
"cipher", "cipher 0.4.4",
"ctr", "ctr",
"ghash", "ghash",
"subtle", "subtle",
@ -466,6 +465,15 @@ dependencies = [
"wyz", "wyz",
] ]
[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.10.4" version = "0.10.4"
@ -475,6 +483,17 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "blowfish"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32fa6a061124e37baba002e496d203e23ba3d7b73750be82dbfbc92913048a5b"
dependencies = [
"byteorder",
"cipher 0.2.5",
"opaque-debug",
]
[[package]] [[package]]
name = "borsh" name = "borsh"
version = "0.10.3" version = "0.10.3"
@ -685,6 +704,15 @@ dependencies = [
"half", "half",
] ]
[[package]]
name = "cipher"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "cipher" name = "cipher"
version = "0.4.4" version = "0.4.4"
@ -786,10 +814,10 @@ dependencies = [
"aes-gcm", "aes-gcm",
"base64 0.20.0", "base64 0.20.0",
"hkdf", "hkdf",
"hmac", "hmac 0.12.1",
"percent-encoding", "percent-encoding",
"rand", "rand",
"sha2", "sha2 0.10.7",
"subtle", "subtle",
"time 0.3.22", "time 0.3.22",
"version_check", "version_check",
@ -864,13 +892,23 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "crypto-mac"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
dependencies = [
"generic-array",
"subtle",
]
[[package]] [[package]]
name = "ctr" name = "ctr"
version = "0.9.2" version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [ dependencies = [
"cipher", "cipher 0.4.4",
] ]
[[package]] [[package]]
@ -943,13 +981,22 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [ dependencies = [
"block-buffer", "block-buffer 0.10.4",
"const-oid", "const-oid",
"crypto-common", "crypto-common",
"subtle", "subtle",
@ -1342,7 +1389,17 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
dependencies = [ dependencies = [
"hmac", "hmac 0.12.1",
]
[[package]]
name = "hmac"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
dependencies = [
"crypto-mac",
"digest 0.9.0",
] ]
[[package]] [[package]]
@ -1351,7 +1408,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [ dependencies = [
"digest", "digest 0.10.7",
] ]
[[package]] [[package]]
@ -1429,7 +1486,7 @@ dependencies = [
"httpdate", "httpdate",
"itoa", "itoa",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2 0.4.9",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
@ -1906,13 +1963,24 @@ dependencies = [
"hashbrown 0.14.0", "hashbrown 0.14.0",
] ]
[[package]]
name = "md-5"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
dependencies = [
"block-buffer 0.9.0",
"digest 0.9.0",
"opaque-debug",
]
[[package]] [[package]]
name = "md-5" name = "md-5"
version = "0.10.5" version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca"
dependencies = [ dependencies = [
"digest", "digest 0.10.7",
] ]
[[package]] [[package]]
@ -2162,7 +2230,7 @@ checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"pest", "pest",
"sha2", "sha2 0.10.7",
] ]
[[package]] [[package]]
@ -2338,6 +2406,21 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "pwhash"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "419a3ad8fa9f9d445e69d9b185a24878ae6e6f55c96e4512f4a0e28cd3bc5c56"
dependencies = [
"blowfish",
"byteorder",
"hmac 0.10.1",
"md-5 0.9.1",
"rand",
"sha-1",
"sha2 0.9.9",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.28" version = "1.0.28"
@ -2474,11 +2557,13 @@ dependencies = [
"cfg-if", "cfg-if",
"chrono", "chrono",
"console_error_panic_hook", "console_error_panic_hook",
"futures-util",
"lazy_static", "lazy_static",
"leptos", "leptos",
"leptos_actix", "leptos_actix",
"leptos_meta", "leptos_meta",
"leptos_router", "leptos_router",
"pwhash",
"rust_decimal", "rust_decimal",
"serde", "serde",
"sqlx", "sqlx",
@ -2550,7 +2635,7 @@ checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"const-oid", "const-oid",
"digest", "digest 0.10.7",
"num-bigint-dig", "num-bigint-dig",
"num-integer", "num-integer",
"num-iter", "num-iter",
@ -2846,6 +2931,19 @@ dependencies = [
"syn 2.0.28", "syn 2.0.28",
] ]
[[package]]
name = "sha-1"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cpufeatures",
"digest 0.9.0",
"opaque-debug",
]
[[package]] [[package]]
name = "sha1" name = "sha1"
version = "0.10.5" version = "0.10.5"
@ -2854,7 +2952,20 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
"digest", "digest 0.10.7",
]
[[package]]
name = "sha2"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cpufeatures",
"digest 0.9.0",
"opaque-debug",
] ]
[[package]] [[package]]
@ -2865,7 +2976,7 @@ checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
"digest", "digest 0.10.7",
] ]
[[package]] [[package]]
@ -2883,7 +2994,7 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
dependencies = [ dependencies = [
"digest", "digest 0.10.7",
"rand_core", "rand_core",
] ]
@ -2928,6 +3039,16 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "socket2"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e"
dependencies = [
"libc",
"windows-sys",
]
[[package]] [[package]]
name = "spin" name = "spin"
version = "0.5.2" version = "0.5.2"
@ -3011,7 +3132,7 @@ dependencies = [
"rustls-pemfile", "rustls-pemfile",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2 0.10.7",
"smallvec", "smallvec",
"sqlformat", "sqlformat",
"thiserror", "thiserror",
@ -3050,7 +3171,7 @@ dependencies = [
"quote", "quote",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2 0.10.7",
"sqlx-core", "sqlx-core",
"sqlx-mysql", "sqlx-mysql",
"sqlx-postgres", "sqlx-postgres",
@ -3074,7 +3195,7 @@ dependencies = [
"bytes", "bytes",
"chrono", "chrono",
"crc", "crc",
"digest", "digest 0.10.7",
"dotenvy", "dotenvy",
"either", "either",
"futures-channel", "futures-channel",
@ -3084,10 +3205,10 @@ dependencies = [
"generic-array", "generic-array",
"hex", "hex",
"hkdf", "hkdf",
"hmac", "hmac 0.12.1",
"itoa", "itoa",
"log", "log",
"md-5", "md-5 0.10.5",
"memchr", "memchr",
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
@ -3096,7 +3217,7 @@ dependencies = [
"rust_decimal", "rust_decimal",
"serde", "serde",
"sha1", "sha1",
"sha2", "sha2 0.10.7",
"smallvec", "smallvec",
"sqlx-core", "sqlx-core",
"stringprep", "stringprep",
@ -3125,11 +3246,11 @@ dependencies = [
"futures-util", "futures-util",
"hex", "hex",
"hkdf", "hkdf",
"hmac", "hmac 0.12.1",
"home", "home",
"itoa", "itoa",
"log", "log",
"md-5", "md-5 0.10.5",
"memchr", "memchr",
"num-bigint", "num-bigint",
"once_cell", "once_cell",
@ -3138,7 +3259,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sha1", "sha1",
"sha2", "sha2 0.10.7",
"smallvec", "smallvec",
"sqlx-core", "sqlx-core",
"stringprep", "stringprep",
@ -3188,9 +3309,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.5.0" version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]] [[package]]
name = "syn" name = "syn"
@ -3331,7 +3452,7 @@ dependencies = [
"parking_lot", "parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2 0.4.9",
"windows-sys", "windows-sys",
] ]

@ -7,9 +7,9 @@ edition = "2021"
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
actix-files = { version = "0.6", optional = true } actix-files = { version = "0.6.2", optional = true }
actix-web = { version = "4", optional = true, features = ["macros"] } actix-web = { version = "4.4.0", optional = true, features = ["macros"] }
actix-session = { version = "0.7.2", optional = true, features = ["cookie-session"] } actix-session = { version = "0.8.0", optional = true, features = ["cookie-session"] }
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
cfg-if = "1" cfg-if = "1"
leptos = { version = "0.5.0-rc1" } leptos = { version = "0.5.0-rc1" }
@ -25,6 +25,8 @@ sqlx = { version = "0.7.1", optional = true, features = ["runtime-tokio-rustls",
rust_decimal = "1.31.0" rust_decimal = "1.31.0"
uuid = {version = "1.4.1", features = ["v4"]} uuid = {version = "1.4.1", features = ["v4"]}
validator = {version = "0.16.1", features = ["derive"]} validator = {version = "0.16.1", features = ["derive"]}
pwhash = "1.0.0"
futures-util = "0.3.28"
[features] [features]
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"] csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]

@ -1,9 +1,13 @@
use crate::locales::{init_locales, trl}; use crate::locales::init_locales;
use crate::pages::home_page::HomePage; use crate::pages::home_page::HomePage;
use crate::pages::settings::Settings; use crate::pages::settings::Settings;
use leptos::*; use leptos::*;
use leptos_meta::*; use leptos_meta::*;
use leptos_router::*; use leptos_router::*;
use crate::components::admin_portal::AdminPortal;
use crate::components::header::Header;
use crate::pages::login::Login;
use crate::pages::public::Public;
#[component] #[component]
pub fn App() -> impl IntoView { pub fn App() -> impl IntoView {
@ -12,210 +16,25 @@ pub fn App() -> impl IntoView {
init_locales(); init_locales();
view! { view! {
<Html <Header/>
lang="cz"
dir="ltr"
attributes=AdditionalAttributes::from(vec![
("data-theme", "theme-default"),
("class", "light-style layout-menu-fixed"),
("data-template", "vertical-menu-template-free"),
("data-assets-path", "/")])
/>
<Meta charset="utf-8"/>
<Meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"/>
//<!-- Fonts -->
<Link rel="preconnect" href="https://fonts.googleapis.com" />
<Link rel="preconnect" href="https://fonts.gstatic.com" />
<Link
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
//<!-- Icons. Uncomment required icon fonts -->
<Link rel="stylesheet" href="/vendor/fonts/boxicons.css" />
//<!-- Core CSS -->
<Link rel="stylesheet" href="/vendor/css/core.css" />
<Link rel="stylesheet" href="/vendor/css/theme-default.css" />
<Link rel="stylesheet" href="/css/demo.css" />
//<!-- Vendors CSS -->
<Link rel="stylesheet" href="/vendor/libs/perfect-scrollbar/perfect-scrollbar.css" />
<Body class="testik"/>
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
//<!-- Menu -->
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
<div class="app-brand demo">
<a href="javascript:void(0);" class="layout-menu-toggle menu-link text-large ms-auto d-block d-xl-none">
<i class="bx bx-chevron-left bx-sm align-middle"></i>
</a>
</div>
<div class="menu-inner-shadow"></div>
<ul class="menu-inner py-1">
//<!-- Dashboard -->
<li class="menu-item">
<a href="/" class="menu-link">
<i class="menu-icon tf-icons bx bx-home-circle"></i>
<div data-i18n="Analytics">{trl("Dashboard")}</div>
</a>
</li>
<li class="menu-item">
<a href="/" class="menu-link">
<i class="menu-icon tf-icons bx bx-time"></i>
<div data-i18n="Analytics">"Opening hours"</div>
</a>
</li>
<li class="menu-item">
<a href="/" class="menu-link">
<i class="menu-icon tf-icons bx bx-layer"></i>
<div data-i18n="Analytics">"Places"</div>
</a>
</li>
<li class="menu-item">
<a href="/" class="menu-link">
<i class="menu-icon tf-icons bx bx-info-circle"></i>
<div data-i18n="Analytics">"About"</div>
</a>
</li>
</ul>
</aside>
//<!-- Layout container -->
<div class="layout-page">
//<!-- Navbar -->
<nav
class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar">
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-3 me-xl-0 d-xl-none">
<a class="nav-item nav-link px-0 me-xl-4" href="javascript:void(0)">
<i class="bx bx-menu bx-sm"></i>
</a>
</div>
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
//<!-- Search -->
<div class="navbar-nav align-items-center ms-auto mt-auto">
<div class="nav-item d-flex align-items-center mt-auto">
<h4 class="mt-3"><i class="bx bx-desktop fs-4 lh-0"></i>" Admin portal"</h4>
</div>
</div>
//<!-- /Search -->
<ul class="navbar-nav flex-row align-items-center ms-auto">
<li class="nav-item navbar-dropdown dropdown-user dropdown">
<a class="nav-link dropdown-toggle hide-arrow" href="/settings" data-bs-toggle="dropdown">
<i class="bx bx-cog fs-3 lh-0"></i>
</a>
</li>
//<!-- User -->
<li class="nav-item navbar-dropdown dropdown-user dropdown">
<a class="nav-link dropdown-toggle hide-arrow" href="#" data-bs-toggle="dropdown">
//<div class="avatar avatar-online">
// <img src="/img/avatars/1.png" alt class="w-px-40 h-auto rounded-circle" />
//</div>
<i class="bx bx-user fs-3 lh-0"></i>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="#">
<div class="d-flex">
<div class="flex-shrink-0 me-3">
<div class="avatar avatar-online">
<img src="/img/avatars/1.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</div>
<div class="flex-grow-1">
<span class="fw-semibold d-block">"John Doe"</span>
<small class="text-muted">"Admin"</small>
</div>
</div>
</a>
</li>
<li>
<div class="dropdown-divider"></div>
</li>
<li>
<a class="dropdown-item" href="#">
<i class="bx bx-user me-2"></i>
<span class="align-middle">"My Profile"</span>
</a>
</li>
<li>
<a class="dropdown-item" href="#">
<i class="bx bx-cog me-2"></i>
<span class="align-middle">"Settings"</span>
</a>
</li>
<li>
<div class="dropdown-divider"></div>
</li>
<li>
<a class="dropdown-item" href="auth-login-basic.html">
<i class="bx bx-power-off me-2"></i>
<span class="align-middle">"Log Out"</span>
</a>
</li>
</ul>
</li>
//<!--/ User -->
</ul>
</div>
</nav>
//<!-- Content wrapper -->
<div class="content-wrapper">
//<!-- Content -->
<div class="container-xxl flex-grow-1 container-p-y">
<Router>
<main>
<Routes>
<Route path="" view=|| view! { <HomePage/> }/>
<Route path="settings" view=|| view! { <Settings/> }/>
</Routes>
</main>
</Router>
</div>
</div>
</div>
</div>
</div>
/*<Html lang="cz"/>
// injects a stylesheet into the document <head>
// id=leptos means cargo-leptos will hot-reload this stylesheet
<Stylesheet id="leptos" href="/pkg/leptos_start.css"/>
<Stylesheet href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"/>
// sets the document title
<Title text="Welcome to Leptos"/>
<div class="topbar">
<img src="/logo.png" width=40 height=40/><h1>"Rezervator admin"</h1>
</div>
<div class="main">
<div class="sidebar">
<p></p>
<a class="active" href="#home">"Dashboard"</a>
<a href="#news">"Opening hours"</a>
<a href="#contact">"Places"</a>
<a href="#about">"About"</a>
</div>
// content for this welcome page
<div class="content">
<Router> <Router>
<main> <main>
<Routes> <Routes>
<Route path="" view=|| view! { <HomePage/> }/> <Route path="" view=|| view! { <Public/> }/>
<Route path="login" view=|| view! { <Login/> }/>
<Route path="admin" view=|| view! {
<AdminPortal>
<HomePage/>
</AdminPortal>
}/>
<Route path="admin/settings" view=|| view! {
<AdminPortal>
<Settings/>
</AdminPortal>
}/>
</Routes> </Routes>
</main> </main>
</Router> </Router>
</div>
</div>*/
} }
} }

@ -0,0 +1,71 @@
use cfg_if::cfg_if;
cfg_if! { if #[cfg(feature = "ssr")] {
use std::future::{Ready, ready};
use actix_session::SessionExt;
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform, forward_ready};
use actix_web::Error;
use actix_web::HttpResponse;
use actix_web::body::EitherBody;
use futures_util::future::LocalBoxFuture;
use actix_web::http::header::LOCATION;
use crate::backend::data::User;
pub struct Authentication;
impl<S, B> Transform<S, ServiceRequest> for Authentication
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<EitherBody<B>>;
type Error = Error;
type Transform = AuthenticationMiddleware<S>;
type InitError = ();
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(AuthenticationMiddleware { service }))
}
}
pub struct AuthenticationMiddleware<S> {
service: S,
}
impl<S, B> Service<ServiceRequest> for AuthenticationMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<EitherBody<B>>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
forward_ready!(service);
fn call(&self, req: ServiceRequest) -> Self::Future {
let session = req.get_session();
let authenticate_pass = !req.path().starts_with("/admin") ||
session.get::<User>("user").unwrap_or(None).is_some();
if authenticate_pass {
let res = self.service.call(req);
Box::pin(async move {
res.await.map(ServiceResponse::map_into_left_body)
})
} else {
let (request, _pl) = req.into_parts();
let response = HttpResponse::Found()
.insert_header((LOCATION, "/login"))
.finish()
.map_into_right_body();
Box::pin(async { Ok(ServiceResponse::new(request, response)) })
}
}
}
}}

@ -1,11 +1,14 @@
use crate::backend::data::Company;
use leptos::*; use leptos::*;
use crate::backend::data::{ApiResponse, Company};
#[server(GetCompany, "/api", "Url", "get_company")] #[server(GetCompany, "/api", "Url", "get_company")]
pub async fn get_company() -> Result<Company, ServerFnError> { pub async fn get_company() -> Result<ApiResponse<Company>, ServerFnError> {
use crate::backend::AppData; use crate::backend::AppData;
use actix_web::web::Data; use actix_web::web::Data;
use leptos_actix::extract; use leptos_actix::extract;
use crate::perm_check;
perm_check!(is_logged_in);
let pool = extract(|data: Data<AppData>| async move { data.db_pool().clone() }).await?; let pool = extract(|data: Data<AppData>| async move { data.db_pool().clone() }).await?;
@ -13,14 +16,17 @@ pub async fn get_company() -> Result<Company, ServerFnError> {
.fetch_one(&pool) .fetch_one(&pool)
.await?; .await?;
Ok(cmp) Ok(ApiResponse::Data(cmp))
} }
#[server(UpdateCompany, "/api", "Url", "update_company")] #[server(UpdateCompany, "/api", "Url", "update_company")]
pub async fn update_company(company: Company) -> Result<(), ServerFnError> { pub async fn update_company(company: Company) -> Result<ApiResponse<()>, ServerFnError> {
use crate::backend::AppData; use crate::backend::AppData;
use actix_web::web::Data; use actix_web::web::Data;
use leptos_actix::extract; use leptos_actix::extract;
use crate::perm_check;
perm_check!(is_admin);
let pool = extract(|data: Data<AppData>| async move { data.db_pool().clone() }).await?; let pool = extract(|data: Data<AppData>| async move { data.db_pool().clone() }).await?;
@ -36,5 +42,5 @@ pub async fn update_company(company: Company) -> Result<(), ServerFnError> {
.execute(&pool) .execute(&pool)
.await?; .await?;
Ok(()) Ok(ApiResponse::Data(()))
} }

@ -1,9 +1,15 @@
use chrono::{NaiveDate, NaiveTime, Weekday}; //use chrono::{NaiveDate, NaiveTime, Weekday};
use rust_decimal::Decimal; //use rust_decimal::Decimal;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; //use uuid::Uuid;
use validator::Validate; use validator::Validate;
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum ApiResponse<T> {
Data(T),
Error(String)
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Validate, Default)] #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Validate, Default)]
#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))] #[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]
pub struct Company { pub struct Company {
@ -24,17 +30,25 @@ impl Company {
} }
} }
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Validate, Default)]
#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]
pub struct User { pub struct User {
id: u16, id: i32,
login: String, pub login: String,
password: String, pub password: String,
full_name: String, pub full_name: Option<String>,
email: String, pub email: Option<String>,
admin: bool, pub admin: bool,
get_emails: bool, pub get_emails: bool,
} }
pub struct Property { impl User {
pub fn id(&self) -> i32 {
self.id
}
}
/*pub struct Property {
id: u16, id: u16,
name: String, name: String,
description: String, description: String,
@ -92,4 +106,4 @@ pub struct ReservationSum {
customer: Customer, customer: Customer,
price: Decimal, price: Decimal,
state: ReservationState, state: ReservationState,
} }*/

@ -2,6 +2,24 @@ use cfg_if::cfg_if;
pub mod data; pub mod data;
pub mod company; pub mod company;
pub mod user;
pub mod auth_middleware;
#[macro_export]
macro_rules! perm_check {
($check:ident) => {
use crate::backend::user::$check;
use actix_web::http::StatusCode;
use leptos_actix::ResponseOptions;
if !$check().await {
let response = expect_context::<ResponseOptions>();
response.set_status(StatusCode::FORBIDDEN);
return Ok(ApiResponse::Error("Forbidden".to_string()))
}
}
}
cfg_if!{ cfg_if!{
if #[cfg(feature = "ssr")] { if #[cfg(feature = "ssr")] {

@ -0,0 +1,102 @@
use cfg_if::cfg_if;
use leptos::*;
cfg_if! { if #[cfg(feature = "ssr")] {
use sqlx::{query_as, Error, PgPool, query};
use actix_session::*;
use leptos_actix::{extract, redirect};
use crate::backend::data::User;
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"#)
.bind(true)
.fetch_one(pool)
.await?;
Ok(count.0 > 0)
}
pub async fn create_admin(pool: &PgPool) -> Result<(), Error> {
if !has_admin_user(pool).await? {
let pwd = pwhash::bcrypt::hash("admin");
query(r#"INSERT INTO "user"(login, password, full_name, admin) VALUES($1, $2, $3, $4)"#)
.bind("admin")
.bind(pwd.unwrap())
.bind("Admin User")
.bind(true)
.execute(pool).await?;
}
Ok(())
}
pub async fn user_from_login(pool: &PgPool, login: &str) -> Result<User, Error> {
let usr = query_as::<_, User>(r#"SELECT * FROM "user" WHERE login=$1"#)
.bind(login)
.fetch_one(pool).await?;
Ok(usr)
}
pub async fn logged_in_user() -> Option<User> {
extract(|session: Session| async move {
session.get::<User>("user").unwrap_or(None)
}).await.unwrap_or(None)
}
pub async fn is_logged_in() -> bool {
logged_in_user().await.is_some()
}
pub async fn is_admin() -> bool {
if let Some(user) = logged_in_user().await {
user.admin
} else {
false
}
}
}}
#[server(Login, "/api")]
pub async fn login(username: String, password: String) -> Result<(), ServerFnError> {
use crate::backend::AppData;
use actix_session::*;
use actix_web::web::Data;
use leptos_actix::extract;
let pool = extract(|data: Data<AppData>| async move { data.db_pool().clone() }).await?;
let user = user_from_login(&pool, &username).await?;
if pwhash::bcrypt::verify(password, &user.password) {
extract(|session: Session| async move {
let _ = session.insert("user", user);
})
.await?;
redirect("/admin");
return Ok(());
}
Err(ServerFnError::ServerError("Bad login".to_string()))
}
#[server]
pub async fn logout() -> Result<(), ServerFnError> {
extract(|session: Session| async move {
session.clear();
}).await?;
redirect("/login");
Ok(())
}
#[server]
pub async fn auth_check() -> Result<bool, ServerFnError> {
Ok(is_logged_in().await)
}
#[server]
pub async fn admin_check() -> Result<bool, ServerFnError> {
Ok(is_admin().await)
}

@ -0,0 +1,140 @@
use leptos::*;
use crate::locales::trl;
#[component]
pub fn AdminPortal(children: Children) -> impl IntoView {
view! {
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
//<!-- Menu -->
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
<div class="app-brand demo">
<a href="javascript:void(0);" class="layout-menu-toggle menu-link text-large ms-auto d-block d-xl-none">
<i class="bx bx-chevron-left bx-sm align-middle"></i>
</a>
</div>
<div class="menu-inner-shadow"></div>
<ul class="menu-inner py-1">
//<!-- Dashboard -->
<li class="menu-item">
<a href="/admin" class="menu-link">
<i class="menu-icon tf-icons bx bx-home-circle"></i>
<div data-i18n="Analytics">{trl("Dashboard")}</div>
</a>
</li>
<li class="menu-item">
<a href="/" class="menu-link">
<i class="menu-icon tf-icons bx bx-time"></i>
<div data-i18n="Analytics">"Opening hours"</div>
</a>
</li>
<li class="menu-item">
<a href="/" class="menu-link">
<i class="menu-icon tf-icons bx bx-layer"></i>
<div data-i18n="Analytics">"Places"</div>
</a>
</li>
<li class="menu-item">
<a href="/" class="menu-link">
<i class="menu-icon tf-icons bx bx-info-circle"></i>
<div data-i18n="Analytics">"About"</div>
</a>
</li>
</ul>
</aside>
//<!-- Layout container -->
<div class="layout-page">
//<!-- Navbar -->
<nav
class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar">
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-3 me-xl-0 d-xl-none">
<a class="nav-item nav-link px-0 me-xl-4" href="javascript:void(0)">
<i class="bx bx-menu bx-sm"></i>
</a>
</div>
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
//<!-- Search -->
<div class="navbar-nav align-items-center ms-auto mt-auto">
<div class="nav-item d-flex align-items-center mt-auto">
<h4 class="mt-3"><i class="bx bx-desktop fs-4 lh-0"></i>" Admin portal"</h4>
</div>
</div>
//<!-- /Search -->
<ul class="navbar-nav flex-row align-items-center ms-auto">
<li class="nav-item navbar-dropdown dropdown-user dropdown">
<a class="nav-link dropdown-toggle hide-arrow" href="/admin/settings" data-bs-toggle="dropdown">
<i class="bx bx-cog fs-3 lh-0"></i>
</a>
</li>
//<!-- User -->
<li class="nav-item navbar-dropdown dropdown-user dropdown">
<a class="nav-link dropdown-toggle hide-arrow" href="#" data-bs-toggle="dropdown">
//<div class="avatar avatar-online">
// <img src="/img/avatars/1.png" alt class="w-px-40 h-auto rounded-circle" />
//</div>
<i class="bx bx-user fs-3 lh-0"></i>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="#">
<div class="d-flex">
<div class="flex-shrink-0 me-3">
<div class="avatar avatar-online">
<img src="/img/avatars/1.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</div>
<div class="flex-grow-1">
<span class="fw-semibold d-block">"John Doe"</span>
<small class="text-muted">"Admin"</small>
</div>
</div>
</a>
</li>
<li>
<div class="dropdown-divider"></div>
</li>
<li>
<a class="dropdown-item" href="#">
<i class="bx bx-user me-2"></i>
<span class="align-middle">"My Profile"</span>
</a>
</li>
<li>
<a class="dropdown-item" href="#">
<i class="bx bx-cog me-2"></i>
<span class="align-middle">"Settings"</span>
</a>
</li>
<li>
<div class="dropdown-divider"></div>
</li>
<li>
<a class="dropdown-item" href="auth-login-basic.html">
<i class="bx bx-power-off me-2"></i>
<span class="align-middle">"Log Out"</span>
</a>
</li>
</ul>
</li>
//<!--/ User -->
</ul>
</div>
</nav>
//<!-- Content wrapper -->
<div class="content-wrapper">
//<!-- Content -->
<div class="container-xxl flex-grow-1 container-p-y">
{children()}
</div>
</div>
</div>
</div>
</div>
}
}

@ -0,0 +1,38 @@
use leptos::*;
use leptos_meta::*;
#[component]
pub fn Header() -> impl IntoView {
view! {
<Html
lang="cz"
dir="ltr"
attributes=AdditionalAttributes::from(vec![
("data-theme", "theme-default"),
("class", "light-style layout-menu-fixed"),
("data-template", "vertical-menu-template-free"),
("data-assets-path", "/")])
/>
<Meta charset="utf-8"/>
<Meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"/>
//<!-- Fonts -->
<Link rel="preconnect" href="https://fonts.googleapis.com" />
<Link rel="preconnect" href="https://fonts.gstatic.com" />
<Link
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
//<!-- Icons. Uncomment required icon fonts -->
<Link rel="stylesheet" href="/vendor/fonts/boxicons.css" />
//<!-- Core CSS -->
<Link rel="stylesheet" href="/vendor/css/core.css" />
<Link rel="stylesheet" href="/vendor/css/theme-default.css" />
<Link rel="stylesheet" href="/css/demo.css" />
//<!-- Vendors CSS -->
<Link rel="stylesheet" href="/vendor/libs/perfect-scrollbar/perfect-scrollbar.css" />
}
}

@ -1,4 +1,6 @@
pub mod modal_box; pub mod modal_box;
pub mod server_err; pub mod server_err;
pub mod validation_err; pub mod validation_err;
pub mod header;
pub mod admin_portal;

@ -1,9 +1,10 @@
use crate::components::modal_box::DialogOpener; use crate::components::modal_box::DialogOpener;
use leptos::*; use leptos::*;
use crate::backend::data::ApiResponse;
#[component] #[component]
pub fn ServerErr( pub fn ServerErr(
result: RwSignal<Option<Result<(), ServerFnError>>>, result: RwSignal<Option<Result<ApiResponse<()>, ServerFnError>>>,
opener: DialogOpener, opener: DialogOpener,
) -> impl IntoView { ) -> impl IntoView {
view! {{move || { view! {{move || {

@ -1,5 +1,4 @@
pub mod app; pub mod app;
pub mod server_fn;
pub mod locales; pub mod locales;
pub mod backend; pub mod backend;
mod pages; mod pages;

@ -1,5 +1,3 @@
use std::ops::Deref;
use leptos::*;
use crate::locales::catalogues::get_dictionary; use crate::locales::catalogues::get_dictionary;
mod catalogues; mod catalogues;
@ -10,6 +8,7 @@ pub struct Locales(pub Vec<Option<String>>);
pub fn init_locales() { pub fn init_locales() {
#[cfg(not(feature = "ssr"))] #[cfg(not(feature = "ssr"))]
{ {
use leptos::*;
let loc = Locales( let loc = Locales(
window() window()
.navigator() .navigator()

@ -13,6 +13,7 @@ async fn main() -> std::io::Result<()> {
use actix_web::web::Data; use actix_web::web::Data;
use sqlx::migrate; use sqlx::migrate;
use sqlx::postgres::PgPoolOptions; use sqlx::postgres::PgPoolOptions;
use rezervator::backend::auth_middleware::Authentication;
let conf = get_configuration(None).await.unwrap(); let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_addr; let addr = conf.leptos_options.site_addr;
@ -32,6 +33,7 @@ async fn main() -> std::io::Result<()> {
App::new() App::new()
.app_data(Data::new(AppData::new(pool.clone()))) .app_data(Data::new(AppData::new(pool.clone())))
.wrap(Authentication)
.wrap(SessionMiddleware::new( .wrap(SessionMiddleware::new(
CookieSessionStore::default(), CookieSessionStore::default(),
key.clone() key.clone()

@ -1,7 +1,7 @@
use crate::backend::data::Company; use crate::backend::data::Company;
use crate::backend::company::UpdateCompany; use crate::backend::company::UpdateCompany;
use crate::components::modal_box::{ use crate::components::modal_box::{
DialogOpener, DlgNotLoaded, ModalBody, ModalDialog, ModalFooter, DialogOpener, ModalBody, ModalDialog, ModalFooter,
}; };
use crate::components::server_err::ServerErr; use crate::components::server_err::ServerErr;
use crate::locales::trl; use crate::locales::trl;

@ -1,7 +1,6 @@
use leptos::*; use leptos::*;
use serde::de::Unexpected::Option;
use crate::backend::company::get_company; use crate::backend::company::get_company;
use crate::backend::data::Company; use crate::backend::data::{ApiResponse, Company};
use crate::components::modal_box::DialogOpener; use crate::components::modal_box::DialogOpener;
use crate::locales::trl; use crate::locales::trl;
use crate::pages::company_edit::CompanyEdit; use crate::pages::company_edit::CompanyEdit;
@ -20,18 +19,23 @@ pub fn CompanyInfo() -> impl IntoView {
<p class="card-text"> <p class="card-text">
<Transition fallback=move || view! {<p>{trl("Loading...")}</p> }> <Transition fallback=move || view! {<p>{trl("Loading...")}</p> }>
{move || { {move || {
company.read().map(|c| match c { company.get().map(|c| match c {
Err(e) => {view! {<p>{trl("Error loading data")}</p> Err(e) => {view! {<div><p>{trl("Error loading data")}</p>
<p>{e.to_string()}</p> <p>{e.to_string()}</p></div>
}} }}
Ok(c) => { Ok(c) => {
set_cmp.update(|cmp| *cmp = c.clone()); match c {
view! { ApiResponse::Data(d) => {set_cmp.update(|cmp| *cmp = d.clone());
<p><b>{c.name}</b></p> view! {<div>
<p>{c.street}" "{c.house_number}<br/> <p><b>{d.name}</b></p>
{c.zip_code}" "{c.city} <p>{d.street}" "{d.house_number}<br/>
</p> {d.zip_code}" "{d.city}
}} </p></div>}
}
ApiResponse::Error(_) => {view! {<div><p>{trl("Error loading data")}</p></div>}}
}
}
}) })
}} }}
</Transition> </Transition>

@ -1,6 +1,5 @@
use leptos::*; use leptos::*;
use crate::components::modal_box::{DialogOpener, ModalDialog, ModalBody, ModalFooter}; use crate::components::modal_box::{DialogOpener, ModalDialog, ModalBody, ModalFooter};
use crate::server_fn::*;
use crate::locales::trl; use crate::locales::trl;
/// Renders the home page of your application. /// Renders the home page of your application.
@ -65,16 +64,6 @@ pub fn HomePage() -> impl IntoView {
<h1>"Welcome to Leptos!"</h1> <h1>"Welcome to Leptos!"</h1>
<button on:click=on_click>"Click Me: " {count}</button> <button on:click=on_click>"Click Me: " {count}</button>
<button on:click=move |_| dialog.show()>"Dialog"</button> <button on:click=move |_| dialog.show()>"Dialog"</button>
<button on:click=move |_| {
spawn_local(async move {
set_session().await;
});
}>"Session"</button>
<button on:click=move |_| {
spawn_local(async move {
get_session().await;
});
}>"Session get"</button>
<p>{trl("testik!")}</p> <p>{trl("testik!")}</p>
} }
} }

@ -0,0 +1,74 @@
use leptos::*;
use leptos_meta::*;
use leptos_router::ActionForm;
use crate::backend::user::Login;
#[component]
pub fn Login() -> impl IntoView {
let login = create_server_action::<Login>();
view! {
<Link rel="stylesheet" href="/vendor/css/pages/page-auth.css" />
<div class="authentication-wrapper authentication-basic container-p-y">
<div class="authentication-inner">
<div class="card">
<div class="card-body">
//<!-- Logo -->
<div class="app-brand justify-content-center">
<a href="index.html" class="app-brand-link gap-2">
<span class="app-brand-logo demo">
</span>
//<span class="app-brand-text demo text-body fw-bolder">Sneat</span>
</a>
</div>
//<!-- /Logo -->
<h4 class="mb-2">"Welcome to Rezervator 👋"</h4>
<p class="mb-4">"Please sign-in to your account and start the adventure"</p>
<ActionForm action=login>
<div class="mb-3">
<label for="username" class="form-label">"Username"</label>
<input
type="text"
class="form-control"
id="username"
name="username"
placeholder="Enter your username"
autofocus
/>
</div>
<div class="mb-3 form-password-toggle">
<div class="d-flex justify-content-between">
<label class="form-label" for="password">"Password"</label>
<a href="auth-forgot-password-basic.html">
<small>"Forgot Password?"</small>
</a>
</div>
<div class="input-group input-group-merge">
<input
type="password"
id="password"
class="form-control"
name="password"
aria-describedby="password"
/>
<span class="input-group-text cursor-pointer"><i class="bx bx-hide"></i></span>
</div>
</div>
<div class="mb-3">
</div>
<div class="mb-3">
<button class="btn btn-primary d-grid w-100" type="submit">"Sign in"</button>
</div>
</ActionForm>
</div>
</div>
</div>
</div>
}
}

@ -2,3 +2,5 @@ pub mod home_page;
pub mod settings; pub mod settings;
pub mod company_info; pub mod company_info;
mod company_edit; mod company_edit;
pub mod login;
pub mod public;

@ -0,0 +1,8 @@
use leptos::*;
#[component]
pub fn Public() -> impl IntoView {
view! {
<div>"public"</div>
}
}

@ -1,33 +0,0 @@
use leptos::*;
#[server(SetSession, "/api", "Url", "set_session")]
pub async fn set_session() -> Result<(), ServerFnError> {
use leptos_actix::extract;
use actix_session::*;
use actix_web::web::Data;
use leptos::logging::log;
//use crate::DataPok;
extract(|session: Session| async move {
log!("extract");
let pok = session.insert("user", "uzivatel");
log!("{pok:?}");
}).await
//Ok(())
}
#[server(GetSession, "/api")]
pub async fn get_session() -> Result<(), ServerFnError> {
use leptos_actix::extract;
use actix_session::*;
use leptos::logging::log;
extract(|session: Session| async move {
log!("extract");
let pok = session.get::<String>("user");
log!("{pok:?}");
}).await
//Ok(())
}

@ -3,8 +3,8 @@ use validator::Validate;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct Validator { pub struct Validator {
message: ReadSignal<Option<String>>, //message: ReadSignal<Option<String>>,
set_message: WriteSignal<Option<String>>, //set_message: WriteSignal<Option<String>>,
valid: ReadSignal<bool>, valid: ReadSignal<bool>,
set_valid: WriteSignal<bool>, set_valid: WriteSignal<bool>,
messages: ReadSignal<Option<Vec<String>>>, messages: ReadSignal<Option<Vec<String>>>,
@ -14,11 +14,11 @@ pub struct Validator {
impl Validator { impl Validator {
pub fn new() -> Self { pub fn new() -> Self {
let (valid, set_valid) = create_signal(true); let (valid, set_valid) = create_signal(true);
let (message, set_message) = create_signal(None); //let (message, set_message) = create_signal(None);
let (messages, set_messages) = create_signal(None); let (messages, set_messages) = create_signal(None);
Self { Self {
message, //message,
set_message, //set_message,
valid, valid,
set_valid, set_valid,
messages, messages,
@ -29,7 +29,7 @@ impl Validator {
pub fn check(&self, entity: &impl Validate, ev: &web_sys::Event) { pub fn check(&self, entity: &impl Validate, ev: &web_sys::Event) {
if let Err(val_err) = entity.validate() { if let Err(val_err) = entity.validate() {
ev.prevent_default(); ev.prevent_default();
self.set_message.update(|m| *m = Some(val_err.to_string().clone())); //self.set_message.update(|m| *m = Some(val_err.to_string().clone()));
self.set_messages.update(|m| *m = { self.set_messages.update(|m| *m = {
let mut out: Vec<String> = vec![]; let mut out: Vec<String> = vec![];
val_err.field_errors().drain().for_each(|e| { val_err.field_errors().drain().for_each(|e| {
@ -51,9 +51,9 @@ impl Validator {
self.valid.get() self.valid.get()
} }
pub fn message(&self) -> Option<String> { /*pub fn message(&self) -> Option<String> {
self.message.get() self.message.get()
} }*/
pub fn messages(&self) -> Option<Vec<String>> { pub fn messages(&self) -> Option<Vec<String>> {
self.messages.get() self.messages.get()

Loading…
Cancel
Save