Implemented user authentication.
This commit is contained in:
Generated
+159
-38
@@ -44,9 +44,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-http"
|
||||
version = "3.3.1"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74"
|
||||
checksum = "a92ef85799cba03f76e4f7c10f533e66d87c9a7e7055f3391f09000ad8351bc9"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-rt",
|
||||
@@ -54,7 +54,7 @@ dependencies = [
|
||||
"actix-utils",
|
||||
"ahash 0.8.3",
|
||||
"base64 0.21.2",
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 2.4.0",
|
||||
"brotli",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
@@ -127,7 +127,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"socket2",
|
||||
"socket2 0.4.9",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
@@ -145,9 +145,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-session"
|
||||
version = "0.7.2"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43da8b818ae1f11049a4d218975345fe8e56ce5a5f92c11f972abcff5ff80e87"
|
||||
checksum = "2e6a28f813a6671e1847d005cad0be36ae4d016287690f765c303379837c13d6"
|
||||
dependencies = [
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
@@ -172,9 +172,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-web"
|
||||
version = "4.3.1"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96"
|
||||
checksum = "0e4a5b5e29603ca8c94a77c65cf874718ceb60292c5a5c3e5f4ace041af462b9"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-http",
|
||||
@@ -185,7 +185,7 @@ dependencies = [
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"actix-web-codegen",
|
||||
"ahash 0.7.6",
|
||||
"ahash 0.8.3",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"cfg-if",
|
||||
@@ -194,7 +194,6 @@ dependencies = [
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"itoa",
|
||||
"language-tags",
|
||||
"log",
|
||||
@@ -206,7 +205,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"smallvec",
|
||||
"socket2",
|
||||
"socket2 0.5.4",
|
||||
"time 0.3.22",
|
||||
"url",
|
||||
]
|
||||
@@ -246,7 +245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cipher 0.4.4",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
@@ -258,7 +257,7 @@ checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cipher",
|
||||
"cipher 0.4.4",
|
||||
"ctr",
|
||||
"ghash",
|
||||
"subtle",
|
||||
@@ -466,6 +465,15 @@ dependencies = [
|
||||
"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]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@@ -475,6 +483,17 @@ dependencies = [
|
||||
"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]]
|
||||
name = "borsh"
|
||||
version = "0.10.3"
|
||||
@@ -685,6 +704,15 @@ dependencies = [
|
||||
"half",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
@@ -786,10 +814,10 @@ dependencies = [
|
||||
"aes-gcm",
|
||||
"base64 0.20.0",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"hmac 0.12.1",
|
||||
"percent-encoding",
|
||||
"rand",
|
||||
"sha2",
|
||||
"sha2 0.10.7",
|
||||
"subtle",
|
||||
"time 0.3.22",
|
||||
"version_check",
|
||||
@@ -864,13 +892,23 @@ dependencies = [
|
||||
"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]]
|
||||
name = "ctr"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
"cipher 0.4.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -943,13 +981,22 @@ dependencies = [
|
||||
"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]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"block-buffer 0.10.4",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
@@ -1342,7 +1389,17 @@ version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
|
||||
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]]
|
||||
@@ -1351,7 +1408,7 @@ version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1429,7 +1486,7 @@ dependencies = [
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"socket2 0.4.9",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -1906,13 +1963,24 @@ dependencies = [
|
||||
"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]]
|
||||
name = "md-5"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2162,7 +2230,7 @@ checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
"sha2",
|
||||
"sha2 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2338,6 +2406,21 @@ dependencies = [
|
||||
"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]]
|
||||
name = "quote"
|
||||
version = "1.0.28"
|
||||
@@ -2474,11 +2557,13 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"console_error_panic_hook",
|
||||
"futures-util",
|
||||
"lazy_static",
|
||||
"leptos",
|
||||
"leptos_actix",
|
||||
"leptos_meta",
|
||||
"leptos_router",
|
||||
"pwhash",
|
||||
"rust_decimal",
|
||||
"serde",
|
||||
"sqlx",
|
||||
@@ -2550,7 +2635,7 @@ checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"const-oid",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
@@ -2846,6 +2931,19 @@ dependencies = [
|
||||
"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]]
|
||||
name = "sha1"
|
||||
version = "0.10.5"
|
||||
@@ -2854,7 +2952,20 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"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]]
|
||||
@@ -2865,7 +2976,7 @@ checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2883,7 +2994,7 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
@@ -2928,6 +3039,16 @@ dependencies = [
|
||||
"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]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
@@ -3011,7 +3132,7 @@ dependencies = [
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.10.7",
|
||||
"smallvec",
|
||||
"sqlformat",
|
||||
"thiserror",
|
||||
@@ -3050,7 +3171,7 @@ dependencies = [
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.10.7",
|
||||
"sqlx-core",
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
@@ -3074,7 +3195,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"futures-channel",
|
||||
@@ -3084,10 +3205,10 @@ dependencies = [
|
||||
"generic-array",
|
||||
"hex",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"hmac 0.12.1",
|
||||
"itoa",
|
||||
"log",
|
||||
"md-5",
|
||||
"md-5 0.10.5",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
@@ -3096,7 +3217,7 @@ dependencies = [
|
||||
"rust_decimal",
|
||||
"serde",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"sha2 0.10.7",
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
@@ -3125,11 +3246,11 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hex",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"hmac 0.12.1",
|
||||
"home",
|
||||
"itoa",
|
||||
"log",
|
||||
"md-5",
|
||||
"md-5 0.10.5",
|
||||
"memchr",
|
||||
"num-bigint",
|
||||
"once_cell",
|
||||
@@ -3138,7 +3259,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"sha2 0.10.7",
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
@@ -3188,9 +3309,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.5.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
@@ -3331,7 +3452,7 @@ dependencies = [
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"socket2 0.4.9",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
|
||||
+5
-3
@@ -7,9 +7,9 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
actix-session = { version = "0.7.2", optional = true, features = ["cookie-session"] }
|
||||
actix-files = { version = "0.6.2", optional = true }
|
||||
actix-web = { version = "4.4.0", optional = true, features = ["macros"] }
|
||||
actix-session = { version = "0.8.0", optional = true, features = ["cookie-session"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
cfg-if = "1"
|
||||
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"
|
||||
uuid = {version = "1.4.1", features = ["v4"]}
|
||||
validator = {version = "0.16.1", features = ["derive"]}
|
||||
pwhash = "1.0.0"
|
||||
futures-util = "0.3.28"
|
||||
|
||||
[features]
|
||||
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
|
||||
|
||||
+18
-199
@@ -1,9 +1,13 @@
|
||||
use crate::locales::{init_locales, trl};
|
||||
use crate::locales::init_locales;
|
||||
use crate::pages::home_page::HomePage;
|
||||
use crate::pages::settings::Settings;
|
||||
use leptos::*;
|
||||
use leptos_meta::*;
|
||||
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]
|
||||
pub fn App() -> impl IntoView {
|
||||
@@ -12,210 +16,25 @@ pub fn App() -> impl IntoView {
|
||||
init_locales();
|
||||
|
||||
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"/>
|
||||
<Header/>
|
||||
|
||||
//<!-- 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>
|
||||
<main>
|
||||
<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>
|
||||
</main>
|
||||
</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)) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
+11
-5
@@ -1,11 +1,14 @@
|
||||
use crate::backend::data::Company;
|
||||
use leptos::*;
|
||||
use crate::backend::data::{ApiResponse, 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 actix_web::web::Data;
|
||||
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?;
|
||||
|
||||
@@ -13,14 +16,17 @@ pub async fn get_company() -> Result<Company, ServerFnError> {
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
|
||||
Ok(cmp)
|
||||
Ok(ApiResponse::Data(cmp))
|
||||
}
|
||||
|
||||
#[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 actix_web::web::Data;
|
||||
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?;
|
||||
|
||||
@@ -36,5 +42,5 @@ pub async fn update_company(company: Company) -> Result<(), ServerFnError> {
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
Ok(ApiResponse::Data(()))
|
||||
}
|
||||
|
||||
+26
-12
@@ -1,9 +1,15 @@
|
||||
use chrono::{NaiveDate, NaiveTime, Weekday};
|
||||
use rust_decimal::Decimal;
|
||||
//use chrono::{NaiveDate, NaiveTime, Weekday};
|
||||
//use rust_decimal::Decimal;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
//use uuid::Uuid;
|
||||
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)]
|
||||
#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]
|
||||
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 {
|
||||
id: u16,
|
||||
login: String,
|
||||
password: String,
|
||||
full_name: String,
|
||||
email: String,
|
||||
admin: bool,
|
||||
get_emails: bool,
|
||||
id: i32,
|
||||
pub login: String,
|
||||
pub password: String,
|
||||
pub full_name: Option<String>,
|
||||
pub email: Option<String>,
|
||||
pub admin: bool,
|
||||
pub get_emails: bool,
|
||||
}
|
||||
|
||||
pub struct Property {
|
||||
impl User {
|
||||
pub fn id(&self) -> i32 {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
/*pub struct Property {
|
||||
id: u16,
|
||||
name: String,
|
||||
description: String,
|
||||
@@ -92,4 +106,4 @@ pub struct ReservationSum {
|
||||
customer: Customer,
|
||||
price: Decimal,
|
||||
state: ReservationState,
|
||||
}
|
||||
}*/
|
||||
|
||||
@@ -2,6 +2,24 @@ use cfg_if::cfg_if;
|
||||
|
||||
pub mod data;
|
||||
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!{
|
||||
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 server_err;
|
||||
pub mod validation_err;
|
||||
pub mod header;
|
||||
pub mod admin_portal;
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use crate::components::modal_box::DialogOpener;
|
||||
use leptos::*;
|
||||
use crate::backend::data::ApiResponse;
|
||||
|
||||
#[component]
|
||||
pub fn ServerErr(
|
||||
result: RwSignal<Option<Result<(), ServerFnError>>>,
|
||||
result: RwSignal<Option<Result<ApiResponse<()>, ServerFnError>>>,
|
||||
opener: DialogOpener,
|
||||
) -> impl IntoView {
|
||||
view! {{move || {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod app;
|
||||
pub mod server_fn;
|
||||
pub mod locales;
|
||||
pub mod backend;
|
||||
mod pages;
|
||||
|
||||
+1
-2
@@ -1,5 +1,3 @@
|
||||
use std::ops::Deref;
|
||||
use leptos::*;
|
||||
use crate::locales::catalogues::get_dictionary;
|
||||
|
||||
mod catalogues;
|
||||
@@ -10,6 +8,7 @@ pub struct Locales(pub Vec<Option<String>>);
|
||||
pub fn init_locales() {
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
{
|
||||
use leptos::*;
|
||||
let loc = Locales(
|
||||
window()
|
||||
.navigator()
|
||||
|
||||
@@ -13,6 +13,7 @@ async fn main() -> std::io::Result<()> {
|
||||
use actix_web::web::Data;
|
||||
use sqlx::migrate;
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use rezervator::backend::auth_middleware::Authentication;
|
||||
|
||||
let conf = get_configuration(None).await.unwrap();
|
||||
let addr = conf.leptos_options.site_addr;
|
||||
@@ -32,6 +33,7 @@ async fn main() -> std::io::Result<()> {
|
||||
|
||||
App::new()
|
||||
.app_data(Data::new(AppData::new(pool.clone())))
|
||||
.wrap(Authentication)
|
||||
.wrap(SessionMiddleware::new(
|
||||
CookieSessionStore::default(),
|
||||
key.clone()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::backend::data::Company;
|
||||
use crate::backend::company::UpdateCompany;
|
||||
use crate::components::modal_box::{
|
||||
DialogOpener, DlgNotLoaded, ModalBody, ModalDialog, ModalFooter,
|
||||
DialogOpener, ModalBody, ModalDialog, ModalFooter,
|
||||
};
|
||||
use crate::components::server_err::ServerErr;
|
||||
use crate::locales::trl;
|
||||
|
||||
+16
-12
@@ -1,7 +1,6 @@
|
||||
use leptos::*;
|
||||
use serde::de::Unexpected::Option;
|
||||
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::locales::trl;
|
||||
use crate::pages::company_edit::CompanyEdit;
|
||||
@@ -20,18 +19,23 @@ pub fn CompanyInfo() -> impl IntoView {
|
||||
<p class="card-text">
|
||||
<Transition fallback=move || view! {<p>{trl("Loading...")}</p> }>
|
||||
{move || {
|
||||
company.read().map(|c| match c {
|
||||
Err(e) => {view! {<p>{trl("Error loading data")}</p>
|
||||
<p>{e.to_string()}</p>
|
||||
company.get().map(|c| match c {
|
||||
Err(e) => {view! {<div><p>{trl("Error loading data")}</p>
|
||||
<p>{e.to_string()}</p></div>
|
||||
}}
|
||||
Ok(c) => {
|
||||
set_cmp.update(|cmp| *cmp = c.clone());
|
||||
view! {
|
||||
<p><b>{c.name}</b></p>
|
||||
<p>{c.street}" "{c.house_number}<br/>
|
||||
{c.zip_code}" "{c.city}
|
||||
</p>
|
||||
}}
|
||||
match c {
|
||||
ApiResponse::Data(d) => {set_cmp.update(|cmp| *cmp = d.clone());
|
||||
view! {<div>
|
||||
<p><b>{d.name}</b></p>
|
||||
<p>{d.street}" "{d.house_number}<br/>
|
||||
{d.zip_code}" "{d.city}
|
||||
</p></div>}
|
||||
}
|
||||
ApiResponse::Error(_) => {view! {<div><p>{trl("Error loading data")}</p></div>}}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}}
|
||||
</Transition>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use leptos::*;
|
||||
use crate::components::modal_box::{DialogOpener, ModalDialog, ModalBody, ModalFooter};
|
||||
use crate::server_fn::*;
|
||||
use crate::locales::trl;
|
||||
|
||||
/// Renders the home page of your application.
|
||||
@@ -65,16 +64,6 @@ pub fn HomePage() -> impl IntoView {
|
||||
<h1>"Welcome to Leptos!"</h1>
|
||||
<button on:click=on_click>"Click Me: " {count}</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>
|
||||
}
|
||||
}
|
||||
@@ -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 company_info;
|
||||
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(())
|
||||
}
|
||||
+8
-8
@@ -3,8 +3,8 @@ use validator::Validate;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Validator {
|
||||
message: ReadSignal<Option<String>>,
|
||||
set_message: WriteSignal<Option<String>>,
|
||||
//message: ReadSignal<Option<String>>,
|
||||
//set_message: WriteSignal<Option<String>>,
|
||||
valid: ReadSignal<bool>,
|
||||
set_valid: WriteSignal<bool>,
|
||||
messages: ReadSignal<Option<Vec<String>>>,
|
||||
@@ -14,11 +14,11 @@ pub struct Validator {
|
||||
impl Validator {
|
||||
pub fn new() -> Self {
|
||||
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);
|
||||
Self {
|
||||
message,
|
||||
set_message,
|
||||
//message,
|
||||
//set_message,
|
||||
valid,
|
||||
set_valid,
|
||||
messages,
|
||||
@@ -29,7 +29,7 @@ impl Validator {
|
||||
pub fn check(&self, entity: &impl Validate, ev: &web_sys::Event) {
|
||||
if let Err(val_err) = entity.validate() {
|
||||
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 = {
|
||||
let mut out: Vec<String> = vec![];
|
||||
val_err.field_errors().drain().for_each(|e| {
|
||||
@@ -51,9 +51,9 @@ impl Validator {
|
||||
self.valid.get()
|
||||
}
|
||||
|
||||
pub fn message(&self) -> Option<String> {
|
||||
/*pub fn message(&self) -> Option<String> {
|
||||
self.message.get()
|
||||
}
|
||||
}*/
|
||||
|
||||
pub fn messages(&self) -> Option<Vec<String>> {
|
||||
self.messages.get()
|
||||
|
||||
Reference in New Issue
Block a user