Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
dc4ff66041 | |||
8184781b2d | |||
6abd323314 | |||
e2860b0ee9 | |||
ba4ce2f275 | |||
1f1ec5f961 | |||
2e267b7c96 | |||
05f84c134e | |||
6e99d9dd88 | |||
9f43a1c04c |
13
.gitignore
vendored
13
.gitignore
vendored
@ -1,12 +1,19 @@
|
|||||||
# ---> Rust
|
# ---> Rust
|
||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
# will have compiled files and executables
|
# will have compiled files and executables
|
||||||
backend/debug/
|
frontend/debug/
|
||||||
backend/target/
|
frontend/target/
|
||||||
|
frontend/.env
|
||||||
|
frontend/dist
|
||||||
|
|
||||||
backend/.env
|
backend/.env
|
||||||
|
backend/target
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||||
backend/*.pdb
|
frontend/*.pdb
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
.vscode/*
|
587
backend/Cargo.lock
generated
587
backend/Cargo.lock
generated
@ -30,7 +30,7 @@ dependencies = [
|
|||||||
"actix-service",
|
"actix-service",
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"ahash 0.8.3",
|
"ahash 0.8.3",
|
||||||
"base64",
|
"base64 0.21.0",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"brotli",
|
"brotli",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -277,8 +277,10 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"dotenv_codegen",
|
"dotenv_codegen",
|
||||||
|
"futures",
|
||||||
|
"mongodb",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
"pbkdf2",
|
"pbkdf2 0.12.1",
|
||||||
"rust_decimal",
|
"rust_decimal",
|
||||||
"rust_decimal_macros",
|
"rust_decimal_macros",
|
||||||
"serde",
|
"serde",
|
||||||
@ -288,6 +290,12 @@ dependencies = [
|
|||||||
"tokio-postgres",
|
"tokio-postgres",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
@ -387,6 +395,28 @@ dependencies = [
|
|||||||
"alloc-stdlib",
|
"alloc-stdlib",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bson"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9aeb8bae494e49dbc330dd23cf78f6f7accee22f640ce3ab17841badaa4ce232"
|
||||||
|
dependencies = [
|
||||||
|
"ahash 0.7.6",
|
||||||
|
"base64 0.13.1",
|
||||||
|
"bitvec",
|
||||||
|
"chrono",
|
||||||
|
"hex",
|
||||||
|
"indexmap",
|
||||||
|
"js-sys",
|
||||||
|
"lazy_static",
|
||||||
|
"rand",
|
||||||
|
"serde",
|
||||||
|
"serde_bytes",
|
||||||
|
"serde_json",
|
||||||
|
"time 0.3.20",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.12.0"
|
version = "3.12.0"
|
||||||
@ -572,6 +602,58 @@ dependencies = [
|
|||||||
"syn 2.0.12",
|
"syn 2.0.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-encoding"
|
||||||
|
version = "2.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derivative"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "0.99.17"
|
version = "0.99.17"
|
||||||
@ -581,7 +663,7 @@ dependencies = [
|
|||||||
"convert_case",
|
"convert_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustc_version",
|
"rustc_version 0.4.0",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -634,6 +716,18 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-as-inner"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fallible-iterator"
|
name = "fallible-iterator"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -671,6 +765,21 @@ version = "2.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.28"
|
version = "0.3.28"
|
||||||
@ -687,6 +796,23 @@ version = "0.3.28"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.28"
|
version = "0.3.28"
|
||||||
@ -716,10 +842,13 @@ version = "0.3.28"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
@ -774,6 +903,12 @@ dependencies = [
|
|||||||
"ahash 0.7.6",
|
"ahash 0.7.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
@ -783,6 +918,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@ -792,6 +933,17 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hostname"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"match_cfg",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
@ -839,6 +991,23 @@ dependencies = [
|
|||||||
"cxx-build",
|
"cxx-build",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
||||||
|
dependencies = [
|
||||||
|
"matches",
|
||||||
|
"unicode-bidi",
|
||||||
|
"unicode-normalization",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@ -859,6 +1028,24 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipconfig"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be"
|
||||||
|
dependencies = [
|
||||||
|
"socket2 0.4.9",
|
||||||
|
"widestring",
|
||||||
|
"winapi",
|
||||||
|
"winreg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipnet"
|
||||||
|
version = "2.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@ -889,6 +1076,12 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.140"
|
version = "0.2.140"
|
||||||
@ -904,6 +1097,12 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "local-channel"
|
name = "local-channel"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -941,6 +1140,27 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru-cache"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
|
||||||
|
dependencies = [
|
||||||
|
"linked-hash-map",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "match_cfg"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matches"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "md-5"
|
name = "md-5"
|
||||||
version = "0.10.5"
|
version = "0.10.5"
|
||||||
@ -983,6 +1203,53 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mongodb"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebe15399de63ad4294c80069967736cbb87ebe467a8cd0629df9cab88a6fbde6"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"base64 0.13.1",
|
||||||
|
"bitflags",
|
||||||
|
"bson",
|
||||||
|
"chrono",
|
||||||
|
"derivative",
|
||||||
|
"derive_more",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-util",
|
||||||
|
"hex",
|
||||||
|
"hmac",
|
||||||
|
"lazy_static",
|
||||||
|
"md-5",
|
||||||
|
"pbkdf2 0.11.0",
|
||||||
|
"percent-encoding",
|
||||||
|
"rand",
|
||||||
|
"rustc_version_runtime",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pemfile",
|
||||||
|
"serde",
|
||||||
|
"serde_bytes",
|
||||||
|
"serde_with",
|
||||||
|
"sha-1",
|
||||||
|
"sha2",
|
||||||
|
"socket2 0.4.9",
|
||||||
|
"stringprep",
|
||||||
|
"strsim",
|
||||||
|
"take_mut",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tokio-util",
|
||||||
|
"trust-dns-proto",
|
||||||
|
"trust-dns-resolver",
|
||||||
|
"typed-builder",
|
||||||
|
"uuid",
|
||||||
|
"webpki-roots",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nanoid"
|
name = "nanoid"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -1056,6 +1323,15 @@ version = "1.0.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
|
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbkdf2"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pbkdf2"
|
name = "pbkdf2"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@ -1128,7 +1404,7 @@ version = "0.6.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78b7fa9f396f51dffd61546fd8573ee20592287996568e6175ceb0f8699ad75d"
|
checksum = "78b7fa9f396f51dffd61546fd8573ee20592287996568e6175ceb0f8699ad75d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64 0.21.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
@ -1202,6 +1478,12 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.26"
|
version = "1.0.26"
|
||||||
@ -1282,6 +1564,31 @@ dependencies = [
|
|||||||
"bytecheck",
|
"bytecheck",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "resolv-conf"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00"
|
||||||
|
dependencies = [
|
||||||
|
"hostname",
|
||||||
|
"quick-error",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.16.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"spin",
|
||||||
|
"untrusted",
|
||||||
|
"web-sys",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rkyv"
|
name = "rkyv"
|
||||||
version = "0.7.42"
|
version = "0.7.42"
|
||||||
@ -1340,13 +1647,53 @@ dependencies = [
|
|||||||
"rust_decimal",
|
"rust_decimal",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
dependencies = [
|
||||||
|
"semver 0.9.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"semver",
|
"semver 1.0.17",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version_runtime"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d31b7153270ebf48bf91c65ae5b0c00e749c4cfad505f66530ac74950249582f"
|
||||||
|
dependencies = [
|
||||||
|
"rustc_version 0.2.3",
|
||||||
|
"semver 0.9.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.20.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"ring",
|
||||||
|
"sct",
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1367,18 +1714,43 @@ version = "1.0.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
|
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sct"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "seahash"
|
name = "seahash"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.159"
|
version = "1.0.159"
|
||||||
@ -1388,6 +1760,15 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_bytes"
|
||||||
|
version = "0.11.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.159"
|
version = "1.0.159"
|
||||||
@ -1405,6 +1786,7 @@ version = "1.0.95"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
|
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
@ -1422,6 +1804,39 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_with_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with_macros"
|
||||||
|
version = "1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha-1"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.5"
|
version = "0.10.5"
|
||||||
@ -1500,6 +1915,12 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stringprep"
|
name = "stringprep"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -1510,6 +1931,12 @@ dependencies = [
|
|||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
@ -1538,6 +1965,12 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "take_mut"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tap"
|
name = "tap"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -1553,6 +1986,26 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.12",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.45"
|
version = "0.1.45"
|
||||||
@ -1660,6 +2113,17 @@ dependencies = [
|
|||||||
"tokio-util",
|
"tokio-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.23.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
||||||
|
dependencies = [
|
||||||
|
"rustls",
|
||||||
|
"tokio",
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.7"
|
version = "0.7.7"
|
||||||
@ -1668,6 +2132,7 @@ checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
@ -1704,6 +2169,62 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "trust-dns-proto"
|
||||||
|
version = "0.21.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c31f240f59877c3d4bb3b3ea0ec5a6a0cff07323580ff8c7a605cd7d08b255d"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"cfg-if",
|
||||||
|
"data-encoding",
|
||||||
|
"enum-as-inner",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-io",
|
||||||
|
"futures-util",
|
||||||
|
"idna 0.2.3",
|
||||||
|
"ipnet",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"rand",
|
||||||
|
"smallvec",
|
||||||
|
"thiserror",
|
||||||
|
"tinyvec",
|
||||||
|
"tokio",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "trust-dns-resolver"
|
||||||
|
version = "0.21.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e4ba72c2ea84515690c9fcef4c6c660bb9df3036ed1051686de84605b74fd558"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"futures-util",
|
||||||
|
"ipconfig",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"lru-cache",
|
||||||
|
"parking_lot",
|
||||||
|
"resolv-conf",
|
||||||
|
"smallvec",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"trust-dns-proto",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typed-builder"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
@ -1737,6 +2258,12 @@ version = "0.1.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@ -1744,7 +2271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna 0.3.0",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1753,6 +2280,10 @@ name = "uuid"
|
|||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2"
|
checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
@ -1826,6 +2357,41 @@ version = "0.2.84"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.61"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "0.22.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
|
||||||
|
dependencies = [
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "widestring"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@ -1932,6 +2498,15 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winreg"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wyz"
|
name = "wyz"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -19,4 +19,6 @@ pbkdf2 = "0.12.1"
|
|||||||
nanoid = "0.4.0"
|
nanoid = "0.4.0"
|
||||||
sha2 = "0.10.6"
|
sha2 = "0.10.6"
|
||||||
rust_decimal = { version = "1.29", features = ["db-tokio-postgres"] }
|
rust_decimal = { version = "1.29", features = ["db-tokio-postgres"] }
|
||||||
rust_decimal_macros = "1.29"
|
rust_decimal_macros = "1.29"
|
||||||
|
mongodb = { version = "2.5.0", features = ["bson-chrono-0_4"] }
|
||||||
|
futures = "0.3"
|
@ -1,7 +1,7 @@
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use actix_web::{web, post, Responder, HttpResponse};
|
use actix_web::{web, post, Responder, HttpResponse};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use crate::{State, models::administrator};
|
use crate::State;
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct LoginInfo {
|
pub struct LoginInfo {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use actix_web::{web, get, post, patch, delete, Responder, HttpResponse, HttpRequest};
|
use actix_web::{web, get, Responder, HttpResponse};
|
||||||
use crate::State;
|
use crate::State;
|
||||||
|
|
||||||
#[get("/benchmark")]
|
#[get("/benchmark")]
|
||||||
|
@ -27,7 +27,7 @@ pub async fn get_cars(state: web::Data<Mutex<State>>, request: HttpRequest) -> i
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/{id}")]
|
#[get("/{id}")]
|
||||||
pub async fn get_car(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn get_car(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
@ -73,7 +73,7 @@ pub async fn create_car(state: web::Data<Mutex<State>>, json: web::Json<BindingC
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[patch("/{id}")]
|
#[patch("/{id}")]
|
||||||
pub async fn update_car(state: web::Data<Mutex<State>>, json: web::Json<BindingCar>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn update_car(state: web::Data<Mutex<State>>, json: web::Json<BindingCar>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
@ -96,7 +96,7 @@ pub async fn update_car(state: web::Data<Mutex<State>>, json: web::Json<BindingC
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/{id}")]
|
#[delete("/{id}")]
|
||||||
pub async fn delete_car(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn delete_car(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
|
@ -27,7 +27,7 @@ pub async fn get_car_stations(state: web::Data<Mutex<State>>, request: HttpReque
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/{id}")]
|
#[get("/{id}")]
|
||||||
pub async fn get_car_station(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn get_car_station(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
@ -73,7 +73,7 @@ pub async fn create_car_station(state: web::Data<Mutex<State>>, json: web::Json<
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[patch("/{id}")]
|
#[patch("/{id}")]
|
||||||
pub async fn update_car_station(state: web::Data<Mutex<State>>, json: web::Json<BindingCarStation>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn update_car_station(state: web::Data<Mutex<State>>, json: web::Json<BindingCarStation>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
@ -96,7 +96,7 @@ pub async fn update_car_station(state: web::Data<Mutex<State>>, json: web::Json<
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/{id}")]
|
#[delete("/{id}")]
|
||||||
pub async fn delete_car_station(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn delete_car_station(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
|
@ -27,7 +27,7 @@ pub async fn get_clients(state: web::Data<Mutex<State>>, request: HttpRequest) -
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/{id}")]
|
#[get("/{id}")]
|
||||||
pub async fn get_client(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn get_client(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
@ -73,7 +73,7 @@ pub async fn create_client(state: web::Data<Mutex<State>>, json: web::Json<Bindi
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[patch("/{id}")]
|
#[patch("/{id}")]
|
||||||
pub async fn update_client(state: web::Data<Mutex<State>>, json: web::Json<BindingClient>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn update_client(state: web::Data<Mutex<State>>, json: web::Json<BindingClient>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
@ -96,7 +96,7 @@ pub async fn update_client(state: web::Data<Mutex<State>>, json: web::Json<Bindi
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/{id}")]
|
#[delete("/{id}")]
|
||||||
pub async fn delete_client(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn delete_client(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
|
@ -27,7 +27,7 @@ pub async fn get_owners(state: web::Data<Mutex<State>>, request: HttpRequest) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/{id}")]
|
#[get("/{id}")]
|
||||||
pub async fn get_owner(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn get_owner(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
@ -73,7 +73,7 @@ pub async fn create_owner(state: web::Data<Mutex<State>>, json: web::Json<Bindin
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[patch("/{id}")]
|
#[patch("/{id}")]
|
||||||
pub async fn update_owner(state: web::Data<Mutex<State>>, json: web::Json<BindingClient>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn update_owner(state: web::Data<Mutex<State>>, json: web::Json<BindingClient>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
@ -96,7 +96,7 @@ pub async fn update_owner(state: web::Data<Mutex<State>>, json: web::Json<Bindin
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/{id}")]
|
#[delete("/{id}")]
|
||||||
pub async fn delete_owner(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn delete_owner(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
|
@ -12,8 +12,8 @@ pub async fn get_rents(state: web::Data<Mutex<State>>, request: HttpRequest) ->
|
|||||||
let token = token.unwrap();
|
let token = token.unwrap();
|
||||||
|
|
||||||
if let Ok(guard) = state.lock() {
|
if let Ok(guard) = state.lock() {
|
||||||
if guard.auth_service.administrator_by_token(guard.administrator_repository.as_ref(), token.to_str().unwrap()).await.is_err() {
|
if let Err(error) = guard.auth_service.administrator_by_token(guard.administrator_repository.as_ref(), token.to_str().unwrap()).await {
|
||||||
return HttpResponse::Unauthorized().body("Invalid token");
|
return HttpResponse::Unauthorized().body("Invalid token: ".to_owned() + error.as_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ pub async fn get_rents(state: web::Data<Mutex<State>>, request: HttpRequest) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/{id}")]
|
#[get("/{id}")]
|
||||||
pub async fn get_rent(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn get_rent(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
@ -73,7 +73,7 @@ pub async fn create_rent(state: web::Data<Mutex<State>>, json: web::Json<Binding
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[patch("/{id}")]
|
#[patch("/{id}")]
|
||||||
pub async fn update_rent(state: web::Data<Mutex<State>>, json: web::Json<BindingRent>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn update_rent(state: web::Data<Mutex<State>>, json: web::Json<BindingRent>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
@ -96,7 +96,7 @@ pub async fn update_rent(state: web::Data<Mutex<State>>, json: web::Json<Binding
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/{id}")]
|
#[delete("/{id}")]
|
||||||
pub async fn delete_rent(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn delete_rent(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
@ -119,7 +119,7 @@ pub async fn delete_rent(state: web::Data<Mutex<State>>, path: web::Path<(i32, )
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/{id}/end")]
|
#[get("/{id}/end")]
|
||||||
pub async fn end_rent(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
|
pub async fn end_rent(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
|
||||||
let token = request.headers().get("Authorization");
|
let token = request.headers().get("Authorization");
|
||||||
if token.is_none() {
|
if token.is_none() {
|
||||||
return HttpResponse::Unauthorized().body("There is no token");
|
return HttpResponse::Unauthorized().body("There is no token");
|
||||||
|
@ -6,6 +6,8 @@ use backend::endpoints::{auth::*};
|
|||||||
use backend::storages::postgres::administrator::PostgresAdministratorRepository;
|
use backend::storages::postgres::administrator::PostgresAdministratorRepository;
|
||||||
use dotenv_codegen::dotenv;
|
use dotenv_codegen::dotenv;
|
||||||
use backend::{State, services};
|
use backend::{State, services};
|
||||||
|
use mongodb::Client;
|
||||||
|
use mongodb::options::ClientOptions;
|
||||||
use tokio_postgres::NoTls;
|
use tokio_postgres::NoTls;
|
||||||
use backend::storages::postgres::car::PostgresCarRepository;
|
use backend::storages::postgres::car::PostgresCarRepository;
|
||||||
use backend::storages::postgres::car_station::PostgresCarStationRepository;
|
use backend::storages::postgres::car_station::PostgresCarStationRepository;
|
||||||
@ -13,6 +15,14 @@ use backend::storages::postgres::client::PostgresClientRepository;
|
|||||||
use backend::storages::postgres::rent::PostgresRentRepository;
|
use backend::storages::postgres::rent::PostgresRentRepository;
|
||||||
use backend::storages::postgres::owner::PostgresOwnerRepository;
|
use backend::storages::postgres::owner::PostgresOwnerRepository;
|
||||||
use backend::storages::postgres::benchmark::PostgresBenchmarkRepository;
|
use backend::storages::postgres::benchmark::PostgresBenchmarkRepository;
|
||||||
|
use backend::storages::mongo::car::MongoCarRepository;
|
||||||
|
use backend::storages::mongo::car_station::MongoCarStationRepository;
|
||||||
|
use backend::storages::mongo::administrator::MongoAdministratorRepository;
|
||||||
|
use backend::storages::mongo::client::MongoClientRepository;
|
||||||
|
use backend::storages::mongo::rent::MongoRentRepository;
|
||||||
|
use backend::storages::mongo::owner::MongoOwnerRepository;
|
||||||
|
use backend::storages::mongo::benchmark::MongoBenchmarkRepository;
|
||||||
|
use backend::storages::*;
|
||||||
use backend::endpoints::car_station::*;
|
use backend::endpoints::car_station::*;
|
||||||
use backend::endpoints::car::*;
|
use backend::endpoints::car::*;
|
||||||
use backend::endpoints::client::*;
|
use backend::endpoints::client::*;
|
||||||
@ -21,32 +31,52 @@ use backend::endpoints::owner::*;
|
|||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
let (client, connection) = tokio_postgres::connect(
|
// let (client, connection) = tokio_postgres::connect(
|
||||||
&format!("host={} user={} password={} dbname={}", dotenv!("HOST"), dotenv!("USER"), dotenv!("PASSWORD"), dotenv!("DBNAMEE")),
|
// &format!("host={} user={} password={} dbname={}", dotenv!("HOST"), dotenv!("USERR"), dotenv!("PASSWORD"), dotenv!("DBNAMEE")),
|
||||||
NoTls
|
// NoTls
|
||||||
).await.unwrap();
|
// ).await.unwrap();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
// tokio::spawn(async move {
|
||||||
if let Err(e) = connection.await {
|
// if let Err(e) = connection.await {
|
||||||
eprintln!("connection error: {}", e);
|
// eprintln!("connection error: {}", e);
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
let client = Arc::new(client);
|
// let client = Arc::new(client);
|
||||||
|
|
||||||
let car_repository = PostgresCarRepository { connection: Arc::clone(&client) };
|
// let car_repository = PostgresCarRepository { connection: Arc::clone(&client) };
|
||||||
|
|
||||||
let car_station_repository = PostgresCarStationRepository { connection: Arc::clone(&client) };
|
// let car_station_repository = PostgresCarStationRepository { connection: Arc::clone(&client) };
|
||||||
|
|
||||||
let client_repository = PostgresClientRepository { connection: Arc::clone(&client) };
|
// let client_repository = PostgresClientRepository { connection: Arc::clone(&client) };
|
||||||
|
|
||||||
let owner_repository = PostgresOwnerRepository { connection: Arc::clone(&client) };
|
// let owner_repository = PostgresOwnerRepository { connection: Arc::clone(&client) };
|
||||||
|
|
||||||
let rent_repository = PostgresRentRepository { connection: Arc::clone(&client) };
|
// let rent_repository = PostgresRentRepository { connection: Arc::clone(&client) };
|
||||||
|
|
||||||
let administrator_repository = PostgresAdministratorRepository { connection: Arc::clone(&client) };
|
// let administrator_repository = PostgresAdministratorRepository { connection: Arc::clone(&client) };
|
||||||
|
|
||||||
let benchmark_repository = PostgresBenchmarkRepository { connection: Arc::clone(&client) };
|
// let benchmark_repository = PostgresBenchmarkRepository { connection: Arc::clone(&client) };
|
||||||
|
|
||||||
|
let client_options = ClientOptions::parse(dotenv!("MONGO")).await.unwrap();
|
||||||
|
|
||||||
|
let client = Client::with_options(client_options).unwrap();
|
||||||
|
|
||||||
|
let db = Arc::new(client.database("default_db"));
|
||||||
|
|
||||||
|
let car_repository = MongoCarRepository { database: Arc::clone(&db) };
|
||||||
|
|
||||||
|
let car_station_repository = MongoCarStationRepository { database: Arc::clone(&db) };
|
||||||
|
|
||||||
|
let client_repository = MongoClientRepository { database: Arc::clone(&db) };
|
||||||
|
|
||||||
|
let owner_repository = MongoOwnerRepository { database: Arc::clone(&db) };
|
||||||
|
|
||||||
|
let rent_repository = MongoRentRepository { database: Arc::clone(&db) };
|
||||||
|
|
||||||
|
let administrator_repository = MongoAdministratorRepository { database: Arc::clone(&db) };
|
||||||
|
|
||||||
|
let benchmark_repository = MongoBenchmarkRepository { database: Arc::clone(&db) };
|
||||||
|
|
||||||
let auth_service = services::auth::AuthService::new();
|
let auth_service = services::auth::AuthService::new();
|
||||||
|
|
||||||
@ -111,7 +141,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.service(logout)
|
.service(logout)
|
||||||
).service(benchmark)
|
).service(benchmark)
|
||||||
})
|
})
|
||||||
.bind("127.0.0.1:8080")?
|
.bind("0.0.0.0:8080")?
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
@ -2,11 +2,11 @@ use serde::{Serialize, Deserialize};
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Administrator {
|
pub struct Administrator {
|
||||||
pub id: i32,
|
pub id: String,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub surname: String,
|
pub surname: String,
|
||||||
pub middlename: String,
|
pub middlename: String,
|
||||||
pub car_station_id: i32
|
pub car_station_id: String
|
||||||
}
|
}
|
@ -1,14 +1,14 @@
|
|||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct Car {
|
pub struct Car {
|
||||||
pub id: i32,
|
pub id: String,
|
||||||
pub brand: String,
|
pub brand: String,
|
||||||
pub model: String,
|
pub model: String,
|
||||||
pub price: Decimal,
|
pub price: Decimal,
|
||||||
pub owner_id: i32,
|
pub owner_id: String,
|
||||||
pub car_station_id: i32
|
pub car_station_id: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@ -16,13 +16,13 @@ pub struct BindingCar {
|
|||||||
pub brand: String,
|
pub brand: String,
|
||||||
pub model: String,
|
pub model: String,
|
||||||
pub price: Decimal,
|
pub price: Decimal,
|
||||||
pub owner_id: i32,
|
pub owner_id: String,
|
||||||
pub car_station_id: i32
|
pub car_station_id: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Report {
|
pub struct Report {
|
||||||
pub car_id: i32,
|
pub car_id: String,
|
||||||
pub brand: String,
|
pub brand: String,
|
||||||
pub model: String,
|
pub model: String,
|
||||||
pub times: i64,
|
pub times: i64,
|
||||||
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct CarStation {
|
pub struct CarStation {
|
||||||
pub id: i32,
|
pub id: String,
|
||||||
pub address: String
|
pub address: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
pub id: i32,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub surname: String,
|
pub surname: String,
|
||||||
pub middlename: Option<String>,
|
pub middlename: Option<String>,
|
||||||
|
@ -2,16 +2,16 @@ use serde::{Serialize, Deserialize};
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Rent {
|
pub struct Rent {
|
||||||
pub id: i32,
|
pub id: String,
|
||||||
pub start_time: chrono::NaiveDateTime,
|
pub start_time: chrono::NaiveDateTime,
|
||||||
pub time_amount: Option<i32>,
|
pub time_amount: Option<i32>,
|
||||||
pub client_id: i32,
|
pub client_id: String,
|
||||||
pub car_id: i32
|
pub car_id: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct BindingRent {
|
pub struct BindingRent {
|
||||||
pub time_amount: Option<i32>,
|
pub time_amount: Option<i32>,
|
||||||
pub client_id: i32,
|
pub client_id: String,
|
||||||
pub car_id: i32
|
pub car_id: String
|
||||||
}
|
}
|
@ -4,7 +4,7 @@ use sha2::Sha256;
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
pub struct AuthService {
|
pub struct AuthService {
|
||||||
authed: RefCell<Vec::<(String, i32)>>
|
authed: RefCell<Vec::<(String, String)>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthService {
|
impl AuthService {
|
||||||
@ -12,14 +12,14 @@ impl AuthService {
|
|||||||
AuthService { authed: RefCell::new(Vec::new()) }
|
AuthService { authed: RefCell::new(Vec::new()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn login(&self, storage: &dyn AdministratorRepository, username: &str, password: &str) -> Result<(String, i32, i32), String> {
|
pub async fn login(&self, storage: &dyn AdministratorRepository, username: &str, password: &str) -> Result<(String, String, String), String> {
|
||||||
let administrator = storage.find_by_username(&username).await?;
|
let administrator = storage.find_by_username(&username).await?;
|
||||||
if pbkdf2::pbkdf2_hmac_array::<Sha256, 32>(password.as_bytes(), dotenv_codegen::dotenv!("SALT").as_bytes(), 4096).iter().map(|x| format!("{:x}", x)).collect::<String>() != administrator.password {
|
if pbkdf2::pbkdf2_hmac_array::<Sha256, 32>(password.as_bytes(), dotenv_codegen::dotenv!("SALT").as_bytes(), 4096).iter().map(|x| format!("{:x}", x)).collect::<String>() != administrator.password {
|
||||||
Err("Invalid password or login!".to_owned())
|
Err("Invalid password or login!".to_owned())
|
||||||
} else {
|
} else {
|
||||||
let token = nanoid!(32);
|
let token = nanoid!(32);
|
||||||
self.authed.borrow_mut().push((token.clone(), administrator.id));
|
self.authed.borrow_mut().push((token.clone(), administrator.id.clone()));
|
||||||
Ok((token, administrator.id, administrator.car_station_id))
|
Ok((token, administrator.id.clone(), administrator.car_station_id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ impl AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn administrator_by_token(&self, storage: &dyn AdministratorRepository, token: &str) -> Result<Administrator, String> {
|
pub async fn administrator_by_token(&self, storage: &dyn AdministratorRepository, token: &str) -> Result<Administrator, String> {
|
||||||
let id = self.authed.borrow().iter().find(|&x| x.0.starts_with(token)).ok_or("Invalid token")?.1;
|
let id = self.authed.borrow().iter().find(|&x| x.0.starts_with(token)).ok_or("Invalid token")?.1.clone();
|
||||||
storage.read(id).await
|
storage.read(id).await
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,2 +1,3 @@
|
|||||||
pub mod traits;
|
pub mod traits;
|
||||||
pub mod postgres;
|
pub mod postgres;
|
||||||
|
pub mod mongo;
|
63
backend/src/storages/mongo/administrator.rs
Normal file
63
backend/src/storages/mongo/administrator.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use mongodb::Database;
|
||||||
|
use mongodb::bson::oid::ObjectId;
|
||||||
|
use mongodb::bson::{doc, Document, Bson};
|
||||||
|
use futures::stream::{TryStreamExt, StreamExt};
|
||||||
|
use mongodb::options::FindOptions;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use crate::models::administrator::*;
|
||||||
|
use crate::storages::traits::AdministratorRepository;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MongoAdministratorRepository {
|
||||||
|
pub database: Arc<Database>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AdministratorRepository for MongoAdministratorRepository {
|
||||||
|
async fn read(&self, id: String) -> Result<Administrator, String> {
|
||||||
|
let collection = self.database.collection::<Document>("administrators");
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let result = collection.find_one(filter, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(Some(document)) => {
|
||||||
|
let id = document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let username = document.get_str("username").unwrap().to_string();
|
||||||
|
let password = document.get_str("password").unwrap().to_string();
|
||||||
|
let name = document.get_str("name").unwrap().to_string();
|
||||||
|
let middlename = document.get_str("middlename").unwrap().to_string();
|
||||||
|
let surname = document.get_str("surname").unwrap().to_string();
|
||||||
|
let car_station_id = document.get_object_id("car_station_id").unwrap().to_hex();
|
||||||
|
let administrator = Administrator {id, username, password, name, surname, middlename, car_station_id};
|
||||||
|
Ok(administrator)
|
||||||
|
},
|
||||||
|
Ok(None) => Err("Administrator not found".to_string()),
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn find_by_username(&self, username: &str) -> Result<Administrator, String> {
|
||||||
|
let collection = self.database.collection::<Document>("administrators");
|
||||||
|
let filter = doc! {
|
||||||
|
"username": username.to_string()
|
||||||
|
};
|
||||||
|
let result = collection.find_one(filter, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(Some(document)) => {
|
||||||
|
let id = document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let username = document.get_str("username").unwrap().to_string();
|
||||||
|
let password = document.get_str("password").unwrap().to_string();
|
||||||
|
let name = document.get_str("name").unwrap().to_string();
|
||||||
|
let middlename = document.get_str("middlename").unwrap().to_string();
|
||||||
|
let surname = document.get_str("surname").unwrap().to_string();
|
||||||
|
let car_station_id = document.get_object_id("car_station_id").unwrap().to_hex();
|
||||||
|
let administrator = Administrator {id, username, password, name, surname, middlename, car_station_id};
|
||||||
|
Ok(administrator)
|
||||||
|
},
|
||||||
|
Ok(None) => Err("Administrator not found".to_string()),
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
backend/src/storages/mongo/benchmark.rs
Normal file
68
backend/src/storages/mongo/benchmark.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use chrono::Utc;
|
||||||
|
use mongodb::Database;
|
||||||
|
use mongodb::bson::oid::ObjectId;
|
||||||
|
use mongodb::bson::{doc, Document, Bson};
|
||||||
|
use futures::stream::{TryStreamExt, StreamExt};
|
||||||
|
use mongodb::options::{FindOptions, AggregateOptions};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use rust_decimal::prelude::{ToPrimitive, FromPrimitive};
|
||||||
|
use std::default;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use crate::storages::traits::BenchmarkRepository;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MongoBenchmarkRepository {
|
||||||
|
pub database: Arc<Database>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl BenchmarkRepository for MongoBenchmarkRepository {
|
||||||
|
async fn benchmark(&self) -> Result<i64, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
|
||||||
|
collection.delete_many(doc! {}, None).await.unwrap();
|
||||||
|
|
||||||
|
let start_time = Utc::now().naive_local();
|
||||||
|
|
||||||
|
for i in 1..2001 {
|
||||||
|
let car_id = ObjectId::new();
|
||||||
|
let client_id = ObjectId::new();
|
||||||
|
let owner_id = ObjectId::new();
|
||||||
|
let rent_id = ObjectId::new();
|
||||||
|
|
||||||
|
collection.insert_one(doc! {
|
||||||
|
"_id": client_id,
|
||||||
|
"name": format!("Иван{}", i),
|
||||||
|
"surname": format!("Иванов{}", i),
|
||||||
|
"middlename": format!("Иванович{}", i),
|
||||||
|
"phone": i.to_string(),
|
||||||
|
"rents": [{
|
||||||
|
"_id": rent_id,
|
||||||
|
"start_time": chrono::Utc::now(),
|
||||||
|
"time_amount": i,
|
||||||
|
"car_id": car_id.clone()
|
||||||
|
}]
|
||||||
|
}, None).await.unwrap();
|
||||||
|
|
||||||
|
collection.insert_one(doc! {
|
||||||
|
"_id": owner_id,
|
||||||
|
"name": format!("Петр{}", i),
|
||||||
|
"surname": format!("Петров{}", i),
|
||||||
|
"middlename": format!("Петрович{}", i),
|
||||||
|
"phone": i.to_string(),
|
||||||
|
"rents": [],
|
||||||
|
"cars": [{
|
||||||
|
"_id": car_id,
|
||||||
|
"brand": format!("Лада{}", i),
|
||||||
|
"model": format!("Гранта{}", i),
|
||||||
|
"price": i as f64,
|
||||||
|
"car_station_id": ObjectId::parse_str("6466ca4c26b3b0b31d9a45bc").unwrap()
|
||||||
|
}]
|
||||||
|
}, None).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok((Utc::now().naive_local() - start_time).num_microseconds().unwrap())
|
||||||
|
}
|
||||||
|
}
|
221
backend/src/storages/mongo/car.rs
Normal file
221
backend/src/storages/mongo/car.rs
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
use chrono::{Utc, Months};
|
||||||
|
use mongodb::Database;
|
||||||
|
use mongodb::bson::oid::ObjectId;
|
||||||
|
use mongodb::bson::{doc, Document, Bson};
|
||||||
|
use futures::stream::{TryStreamExt, StreamExt};
|
||||||
|
use mongodb::options::{FindOptions, AggregateOptions};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use rust_decimal::prelude::{ToPrimitive, FromPrimitive};
|
||||||
|
use std::default;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use crate::models::car::*;
|
||||||
|
use crate::storages::traits::*;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use super::rent::MongoRentRepository;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MongoCarRepository {
|
||||||
|
pub database: Arc<Database>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl CarRepository for MongoCarRepository {
|
||||||
|
async fn create(&self, car: BindingCar) -> Result<Car, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let document = doc! {
|
||||||
|
"_id": ObjectId::parse_str(car.owner_id.clone()).unwrap()
|
||||||
|
};
|
||||||
|
let update = doc! {
|
||||||
|
"$push": {
|
||||||
|
"cars": doc! {
|
||||||
|
"_id": ObjectId::new(),
|
||||||
|
"brand": car.brand.clone(),
|
||||||
|
"model": car.model.clone(),
|
||||||
|
"price": car.price.to_f64().unwrap(),
|
||||||
|
"car_station_id": ObjectId::parse_str(car.car_station_id.to_string()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = collection.update_one(document, update, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(result) => {
|
||||||
|
let brand = car.brand.clone();
|
||||||
|
let model = car.model.clone();
|
||||||
|
let price = car.price;
|
||||||
|
let car_station_id = car.car_station_id.clone();
|
||||||
|
let owner_id = car.owner_id.clone();
|
||||||
|
let car = Car { id: "".to_string(), brand, model, car_station_id, price, owner_id};
|
||||||
|
Ok(car)
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read(&self, id: String) -> Result<Car, String> {
|
||||||
|
match self.read_all().await.unwrap().into_iter().filter(|c| c.id.starts_with(&id)).next() {
|
||||||
|
Some(car) => Ok(car),
|
||||||
|
None => Err("Car not found".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_all(&self) -> Result<Vec<Car>, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let mut cursor = collection.find(doc! {"cars": { "$exists": true }}, None).await.map_err(|e| e.to_string())?;
|
||||||
|
let mut cars = Vec::new();
|
||||||
|
while let Some(result) = cursor.next().await {
|
||||||
|
match result {
|
||||||
|
Ok(document) => {
|
||||||
|
for car_document in document.get_array("cars").unwrap() {
|
||||||
|
let car = car_document.as_document().unwrap();
|
||||||
|
let id = car.get("_id").unwrap().as_object_id().unwrap().to_hex();
|
||||||
|
let brand = car.get_str("brand").unwrap().to_string();
|
||||||
|
let model = car.get_str("model").unwrap().to_string();
|
||||||
|
let price = Decimal::from_f64(car.get_f64("price").unwrap()).unwrap();
|
||||||
|
let car_station_id = car.get_object_id("car_station_id").unwrap().to_hex();
|
||||||
|
let owner_id = document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let car = Car { id, brand, model, car_station_id, price, owner_id};
|
||||||
|
cars.push(car);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => return Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(cars)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update(&self, id: String, car: BindingCar) -> Result<Car, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let mut cursor = collection.find(None, None).await.map_err(|e| e.to_string())?;
|
||||||
|
while let Some(result) = cursor.next().await {
|
||||||
|
match result {
|
||||||
|
Ok(document) => {
|
||||||
|
for (i, car_document) in document.get_array("cars").unwrap().into_iter().enumerate() {
|
||||||
|
if car_document.as_document().unwrap().get_object_id("_id").unwrap().to_hex().starts_with(&id) {
|
||||||
|
collection.update_one(doc! {
|
||||||
|
"_id": ObjectId::parse_str(document.get_str("_id").unwrap()).unwrap()
|
||||||
|
}, doc! {
|
||||||
|
"$set": doc! {
|
||||||
|
format!("cars.${}.brand", i): car.brand.clone(),
|
||||||
|
format!("cars.${}.model", i): car.model.clone(),
|
||||||
|
format!("cars.${}.price", i): car.price.to_f64().unwrap(),
|
||||||
|
format!("cars.${}.car_station_id", i): ObjectId::parse_str(car.car_station_id.clone()).unwrap(),
|
||||||
|
}
|
||||||
|
}, None).await.unwrap();
|
||||||
|
|
||||||
|
return Ok(Car {..Default::default()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => return Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err("Car not found".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, id: String) -> Result<(), String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let document = doc! {
|
||||||
|
"cars._id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let update = doc! {
|
||||||
|
"$pull": {
|
||||||
|
"cars": {
|
||||||
|
"_id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = collection.update_one(document, update, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(result) => {
|
||||||
|
if result.modified_count == 0 {
|
||||||
|
Err("Car not found".to_string())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_report(&self) -> Result<Vec<Report>, String> {
|
||||||
|
let mut reports = Vec::new();
|
||||||
|
|
||||||
|
let rents = (MongoRentRepository {database: Arc::clone(&self.database)}).read_all().await.unwrap();
|
||||||
|
let cars = self.read_all().await.unwrap();
|
||||||
|
|
||||||
|
for car in cars {
|
||||||
|
let mut times = 0;
|
||||||
|
let mut sum = Decimal::from_f64(0.0).unwrap();
|
||||||
|
|
||||||
|
for rent in rents.iter() {
|
||||||
|
if rent.car_id.starts_with(&car.id) && rent.time_amount.is_some() {
|
||||||
|
times += 1;
|
||||||
|
sum += car.price * Decimal::from(rent.time_amount.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reports.push(Report {car_id: car.id.clone(), brand: car.brand.clone(), model: car.model.clone(), times, income: sum})
|
||||||
|
}
|
||||||
|
// let pipeline = vec![
|
||||||
|
// doc! {
|
||||||
|
// "$match": {
|
||||||
|
// "rents.start_time": {
|
||||||
|
// "$gte": Utc::now().checked_sub_months(Months::new(1)).unwrap(),
|
||||||
|
// "$lt": Utc::now().checked_add_months(Months::new(1)).unwrap()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// doc! {
|
||||||
|
// "$lookup": {
|
||||||
|
// "from": "cars",
|
||||||
|
// "localField": "rents.car_id",
|
||||||
|
// "foreignField": "_id",
|
||||||
|
// "as": "car"
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// doc! {
|
||||||
|
// "$unwind": "$car"
|
||||||
|
// },
|
||||||
|
// doc! {
|
||||||
|
// "$group": {
|
||||||
|
// "_id": "$rents.car_id",
|
||||||
|
// "brand": { "$first": "$car.brand" },
|
||||||
|
// "model": { "$first": "$car.model" },
|
||||||
|
// "times": { "$sum": 1 },
|
||||||
|
// "income": { "$sum": { "$multiply": [ "$car.price", "$rents.time_amount" ] } }
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// doc! {
|
||||||
|
// "$project": {
|
||||||
|
// "_id": 0,
|
||||||
|
// "car_id": { "$toString": "$_id" },
|
||||||
|
// "brand": 1,
|
||||||
|
// "model": 1,
|
||||||
|
// "times": 1,
|
||||||
|
// "income": { "$round": [ "$income", 2 ] }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ];
|
||||||
|
// let collection = self.database.collection::<Document>("clients");
|
||||||
|
// let options = AggregateOptions::builder().build();
|
||||||
|
// let cursor = collection.aggregate(pipeline, options).await.unwrap();
|
||||||
|
|
||||||
|
// let reports = cursor
|
||||||
|
// .map(|doc| {
|
||||||
|
// let doc = doc.unwrap();
|
||||||
|
// let car_id = doc.get_str("car_id").unwrap().to_owned();
|
||||||
|
// let brand = doc.get_str("brand").unwrap().to_owned();
|
||||||
|
// let model = doc.get_str("model").unwrap().to_owned();
|
||||||
|
// let times = doc.get_i64("times").unwrap();
|
||||||
|
// let income = Decimal::from_f64(doc.get_f64("income").unwrap()).unwrap();
|
||||||
|
|
||||||
|
// Report { car_id, brand, model, times, income }
|
||||||
|
// })
|
||||||
|
// .collect::<Vec<_>>()
|
||||||
|
// .await;
|
||||||
|
|
||||||
|
Ok(reports)
|
||||||
|
}
|
||||||
|
}
|
101
backend/src/storages/mongo/car_station.rs
Normal file
101
backend/src/storages/mongo/car_station.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use mongodb::Database;
|
||||||
|
use mongodb::bson::oid::ObjectId;
|
||||||
|
use mongodb::bson::{doc, Document, Bson};
|
||||||
|
use futures::stream::{TryStreamExt, StreamExt};
|
||||||
|
use mongodb::options::FindOptions;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use crate::models::car_station::*;
|
||||||
|
use crate::storages::traits::CarStationRepository;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MongoCarStationRepository {
|
||||||
|
pub database: Arc<Database>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl CarStationRepository for MongoCarStationRepository {
|
||||||
|
async fn create(&self, car_station: BindingCarStation) -> Result<CarStation, String> {
|
||||||
|
let collection = self.database.collection::<Document>("car_stations");
|
||||||
|
let document = doc! {
|
||||||
|
"address": car_station.address.clone()
|
||||||
|
};
|
||||||
|
let result = collection.insert_one(document, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(result) => {
|
||||||
|
let id = result.inserted_id.as_object_id().unwrap().to_hex();
|
||||||
|
let car_station = CarStation { id, address: car_station.address };
|
||||||
|
Ok(car_station)
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read(&self, id: String) -> Result<CarStation, String> {
|
||||||
|
let collection = self.database.collection::<Document>("car_stations");
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let result = collection.find_one(filter, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(Some(document)) => {
|
||||||
|
let id = document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let address = document.get_str("address").unwrap().to_string();
|
||||||
|
let car_station = CarStation { id, address };
|
||||||
|
Ok(car_station)
|
||||||
|
},
|
||||||
|
Ok(None) => Err("Car station not found".to_string()),
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_all(&self) -> Result<Vec<CarStation>, String> {
|
||||||
|
let collection = self.database.collection::<Document>("car_stations");
|
||||||
|
let mut cursor = collection.find(None, None).await.map_err(|e| e.to_string())?;
|
||||||
|
let mut car_stations = Vec::new();
|
||||||
|
while let Some(result) = cursor.next().await {
|
||||||
|
match result {
|
||||||
|
Ok(document) => {
|
||||||
|
let id = document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let address = document.get_str("address").unwrap().to_string();
|
||||||
|
let car_station = CarStation { id, address };
|
||||||
|
car_stations.push(car_station);
|
||||||
|
},
|
||||||
|
Err(e) => return Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(car_stations)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update(&self, id: String, car_station: BindingCarStation) -> Result<CarStation, String> {
|
||||||
|
let collection = self.database.collection::<Document>("car_stations");
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let update = doc! {
|
||||||
|
"$set": {
|
||||||
|
"address": car_station.address.clone()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = collection.update_one(filter, update, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(_) => {
|
||||||
|
let car_station = CarStation { id, address: car_station.address };
|
||||||
|
Ok(car_station)
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, id: String) -> Result<(), String> {
|
||||||
|
let collection = self.database.collection::<Document>("car_stations");
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let result = collection.delete_one(filter, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
117
backend/src/storages/mongo/client.rs
Normal file
117
backend/src/storages/mongo/client.rs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
use mongodb::Database;
|
||||||
|
use mongodb::bson::oid::ObjectId;
|
||||||
|
use mongodb::bson::{doc, Document, Bson};
|
||||||
|
use futures::stream::{TryStreamExt, StreamExt};
|
||||||
|
use mongodb::options::{FindOptions, AggregateOptions};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use rust_decimal::prelude::{ToPrimitive, FromPrimitive};
|
||||||
|
use std::default;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use crate::models::client::*;
|
||||||
|
use crate::storages::traits::ClientRepository;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MongoClientRepository {
|
||||||
|
pub database: Arc<Database>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl ClientRepository for MongoClientRepository {
|
||||||
|
async fn create(&self, client: BindingClient) -> Result<Client, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let document = doc! {
|
||||||
|
"name": client.name.clone(),
|
||||||
|
"surname": client.surname.clone(),
|
||||||
|
"middlename": client.middlename.clone(),
|
||||||
|
"phone": client.phone.clone(),
|
||||||
|
"rents": Bson::Array(Vec::<Bson>::new())
|
||||||
|
};
|
||||||
|
let result = collection.insert_one(document, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(result) => {
|
||||||
|
let id = result.inserted_id.as_object_id().unwrap().to_hex();
|
||||||
|
let client = Client { id, name: client.name, surname: client.surname, middlename: client.middlename, phone: client.phone };
|
||||||
|
Ok(client)
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read(&self, id: String) -> Result<Client, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let result = collection.find_one(filter, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(Some(document)) => {
|
||||||
|
let id = document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let name = document.get_str("name").unwrap().to_string();
|
||||||
|
let surname = document.get_str("surname").unwrap().to_string();
|
||||||
|
let middlename = document.get_str("middlename").map(|s| s.to_string());
|
||||||
|
let phone = document.get_str("phone").unwrap().to_string();
|
||||||
|
let client = Client { id, name, surname, middlename: Some(middlename.unwrap()), phone };
|
||||||
|
Ok(client)
|
||||||
|
},
|
||||||
|
Ok(None) => Err("Client not found".to_string()),
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_all(&self) -> Result<Vec<Client>, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let mut cursor = collection.find(None, None).await.map_err(|e| e.to_string())?;
|
||||||
|
let mut clients = Vec::new();
|
||||||
|
while let Some(result) = cursor.next().await {
|
||||||
|
match result {
|
||||||
|
Ok(document) => {
|
||||||
|
let id = document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let name = document.get_str("name").unwrap().to_string();
|
||||||
|
let surname = document.get_str("surname").unwrap().to_string();
|
||||||
|
let middlename = document.get_str("middlename").map(|s| s.to_string());
|
||||||
|
let phone = document.get_str("phone").unwrap().to_string();
|
||||||
|
let client = Client { id, name, surname, middlename: Some(middlename.unwrap()), phone };
|
||||||
|
clients.push(client);
|
||||||
|
},
|
||||||
|
Err(e) => return Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(clients)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update(&self, id: String, client: BindingClient) -> Result<Client, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let update = doc! {
|
||||||
|
"$set": {
|
||||||
|
"name": client.name.clone(),
|
||||||
|
"surname": client.surname.clone(),
|
||||||
|
"middlename": client.middlename.clone(),
|
||||||
|
"phone": client.phone.clone()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = collection.update_one(filter, update, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(_) => {
|
||||||
|
let client = Client { id, name: client.name, surname: client.surname, middlename: client.middlename, phone: client.phone };
|
||||||
|
Ok(client)
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, id: String) -> Result<(), String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let result = collection.delete_one(filter, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
backend/src/storages/mongo/mod.rs
Normal file
7
backend/src/storages/mongo/mod.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
pub mod car_station;
|
||||||
|
pub mod administrator;
|
||||||
|
pub mod car;
|
||||||
|
pub mod client;
|
||||||
|
pub mod owner;
|
||||||
|
pub mod rent;
|
||||||
|
pub mod benchmark;
|
129
backend/src/storages/mongo/owner.rs
Normal file
129
backend/src/storages/mongo/owner.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
use mongodb::Database;
|
||||||
|
use mongodb::bson::oid::ObjectId;
|
||||||
|
use mongodb::bson::{doc, Document, Bson};
|
||||||
|
use futures::stream::{TryStreamExt, StreamExt};
|
||||||
|
use mongodb::options::{FindOptions, AggregateOptions};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use rust_decimal::prelude::{ToPrimitive, FromPrimitive};
|
||||||
|
use std::default;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use crate::models::client::*;
|
||||||
|
use crate::storages::traits::OwnerRepository;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MongoOwnerRepository {
|
||||||
|
pub database: Arc<Database>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl OwnerRepository for MongoOwnerRepository {
|
||||||
|
async fn create(&self, owner: BindingClient) -> Result<Client, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let document = doc! {
|
||||||
|
"name": owner.name.clone(),
|
||||||
|
"surname": owner.surname.clone(),
|
||||||
|
"middlename": owner.middlename.clone(),
|
||||||
|
"phone": owner.phone.clone(),
|
||||||
|
"cars": Bson::Array(Vec::<Bson>::new()),
|
||||||
|
"rents": Bson::Array(Vec::<Bson>::new())
|
||||||
|
};
|
||||||
|
let result = collection.insert_one(document, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(result) => {
|
||||||
|
let id = result.inserted_id.as_object_id().unwrap().to_hex();
|
||||||
|
let owner = Client { id, name: owner.name, surname: owner.surname, middlename: owner.middlename, phone: owner.phone };
|
||||||
|
Ok(owner)
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read(&self, id: String) -> Result<Client, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let result = collection.find_one(filter, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(Some(document)) => {
|
||||||
|
let id = document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let name = document.get_str("name").unwrap().to_string();
|
||||||
|
let surname = document.get_str("surname").unwrap().to_string();
|
||||||
|
let middlename = document.get_str("middlename").map(|s| s.to_string());
|
||||||
|
let phone = document.get_str("phone").unwrap().to_string();
|
||||||
|
let owner = Client { id, name, surname, middlename: Some(middlename.unwrap()), phone };
|
||||||
|
Ok(owner)
|
||||||
|
},
|
||||||
|
Ok(None) => Err("Owner not found".to_string()),
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_all(&self) -> Result<Vec<Client>, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let mut cursor = collection.find(doc! {"cars": { "$exists": true }}, None).await.map_err(|e| e.to_string())?;
|
||||||
|
let mut owners = Vec::new();
|
||||||
|
while let Some(result) = cursor.next().await {
|
||||||
|
match result {
|
||||||
|
Ok(document) => {
|
||||||
|
let id = document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let name = document.get_str("name").unwrap().to_string();
|
||||||
|
let surname = document.get_str("surname").unwrap().to_string();
|
||||||
|
let middlename = document.get_str("middlename").map(|s| s.to_string());
|
||||||
|
let phone = document.get_str("phone").unwrap().to_string();
|
||||||
|
let owner = Client { id, name, surname, middlename: Some(middlename.unwrap()), phone };
|
||||||
|
owners.push(owner);
|
||||||
|
},
|
||||||
|
Err(e) => return Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(owners)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update(&self, id: String, owner: BindingClient) -> Result<Client, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let update = doc! {
|
||||||
|
"$set": {
|
||||||
|
"name": owner.name.clone(),
|
||||||
|
"surname": owner.surname.clone(),
|
||||||
|
"middlename": owner.middlename.clone(),
|
||||||
|
"phone": owner.phone.clone()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = collection.update_one(filter, update, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(result) => {
|
||||||
|
if result.modified_count == 1 {
|
||||||
|
let owner = self.read(id).await?;
|
||||||
|
Ok(owner)
|
||||||
|
} else {
|
||||||
|
Err("Owner not found".to_string())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, id: String) -> Result<(), String> {
|
||||||
|
let collection = self.database.collection::<Document>("owners");
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let result = collection.delete_one(filter, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(result) => {
|
||||||
|
if result.deleted_count == 1 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("Owner not found".to_string())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
153
backend/src/storages/mongo/rent.rs
Normal file
153
backend/src/storages/mongo/rent.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
use chrono::{NaiveDateTime, Utc};
|
||||||
|
use mongodb::Database;
|
||||||
|
use mongodb::bson::oid::ObjectId;
|
||||||
|
use mongodb::bson::{doc, Document, Bson};
|
||||||
|
use futures::stream::{TryStreamExt, StreamExt};
|
||||||
|
use mongodb::options::{FindOptions, AggregateOptions, FindOneOptions};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use rust_decimal::prelude::{ToPrimitive, FromPrimitive};
|
||||||
|
use std::default;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use crate::models::rent::*;
|
||||||
|
use crate::storages::traits::RentRepository;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MongoRentRepository {
|
||||||
|
pub database: Arc<Database>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl RentRepository for MongoRentRepository {
|
||||||
|
async fn create(&self, rent: BindingRent) -> Result<Rent, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let start_time = chrono::Utc::now();
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&rent.client_id).unwrap()
|
||||||
|
};
|
||||||
|
let update = doc! {
|
||||||
|
"$push": {
|
||||||
|
"rents": {
|
||||||
|
"_id": ObjectId::new(),
|
||||||
|
"start_time": start_time,
|
||||||
|
"time_amount": Bson::Null,
|
||||||
|
"car_id": ObjectId::parse_str(&rent.car_id).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = collection.update_one(filter, update, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(result) => {
|
||||||
|
if result.modified_count == 1 {
|
||||||
|
Ok(Rent {id: "".to_string(), start_time: Default::default(), time_amount: None, car_id: "".to_string(), client_id: "".to_string()})
|
||||||
|
} else {
|
||||||
|
Err("Rent not found".to_string())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read(&self, id: String) -> Result<Rent, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let filter = doc! {
|
||||||
|
"rents._id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let projection = doc! {
|
||||||
|
"rents.$": 1
|
||||||
|
};
|
||||||
|
let options = FindOneOptions::builder().projection(projection).build();
|
||||||
|
let result = collection.find_one(filter, options).await;
|
||||||
|
match result {
|
||||||
|
Ok(Some(document)) => {
|
||||||
|
let rent_document = document.get_array("rents").unwrap().get(0).unwrap().as_document().unwrap();
|
||||||
|
let client_id = document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let id = rent_document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let start_time = rent_document.get_datetime("start_time").unwrap();
|
||||||
|
let time_amount = rent_document.get_i32("time_amount").ok();
|
||||||
|
let car_id = rent_document.get_object_id("car_id").unwrap().to_hex();
|
||||||
|
let rent = Rent { id, start_time: NaiveDateTime::from_timestamp_millis(start_time.timestamp_millis()).unwrap(), time_amount, client_id, car_id };
|
||||||
|
Ok(rent)
|
||||||
|
},
|
||||||
|
Ok(None) => Err("Rent not found".to_string()),
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_all(&self) -> Result<Vec<Rent>, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let result = collection.find(None, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(mut cursor) => {
|
||||||
|
let mut rents = Vec::new();
|
||||||
|
while let Some(document) = cursor.next().await {
|
||||||
|
let another = document.clone().unwrap();
|
||||||
|
let rents_array = another.get_array("rents").unwrap();
|
||||||
|
let client_id = document.unwrap().get_object_id("_id").unwrap().to_hex();
|
||||||
|
for rent_document in rents_array.iter().map(|x| x.as_document().unwrap()) {
|
||||||
|
let id = rent_document.get_object_id("_id").unwrap().to_hex();
|
||||||
|
let start_time = rent_document.get_datetime("start_time").unwrap();
|
||||||
|
let time_amount = rent_document.get_i32("time_amount").ok();
|
||||||
|
let car_id = rent_document.get_object_id("car_id").unwrap().to_hex();
|
||||||
|
let rent = Rent { id, start_time: NaiveDateTime::from_timestamp_millis(start_time.timestamp_millis()).unwrap(), time_amount, client_id: client_id.clone(), car_id };
|
||||||
|
rents.push(rent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(rents)
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update(&self, id: String, rent: BindingRent) -> Result<Rent, String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(&rent.client_id).unwrap(),
|
||||||
|
"rents._id": ObjectId::parse_str(&id).unwrap()
|
||||||
|
};
|
||||||
|
let update = doc! {
|
||||||
|
"$set": {
|
||||||
|
"rents.$.time_amount": rent.time_amount
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = collection.update_one(filter, update, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(success) => self.read(success.upserted_id.unwrap().as_object_id().unwrap().to_hex()).await,
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, id: String) -> Result<(), String> {
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let rent = self.read(id.clone()).await.unwrap();
|
||||||
|
let filter = doc! {
|
||||||
|
"_id": ObjectId::parse_str(rent.client_id.clone()).unwrap(),
|
||||||
|
"rents._id": ObjectId::parse_str(id.clone()).unwrap()
|
||||||
|
};
|
||||||
|
let result = collection.update_one(filter, doc! {"$pull": {"rents": {"_id": ObjectId::parse_str(id.clone()).unwrap()}}}, None).await;
|
||||||
|
match result {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn end_rent(&self, id: String) -> Result<(), String> {
|
||||||
|
let rent = self.read(id.clone()).await?;
|
||||||
|
|
||||||
|
if rent.time_amount.is_some() {
|
||||||
|
return Err("Rent is already ended".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
let time_amount = (Utc::now().naive_local() - rent.start_time).num_minutes() as i32;
|
||||||
|
|
||||||
|
let collection = self.database.collection::<Document>("clients");
|
||||||
|
let filter = doc! {"_id": ObjectId::parse_str(&rent.client_id).unwrap(), "rents._id": ObjectId::parse_str(&rent.id).unwrap()};
|
||||||
|
let update = doc! {"$set": {"rents.$.time_amount": time_amount}};
|
||||||
|
let result = collection.update_one(filter, update, None).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,21 +10,21 @@ pub struct PostgresAdministratorRepository {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AdministratorRepository for PostgresAdministratorRepository {
|
impl AdministratorRepository for PostgresAdministratorRepository {
|
||||||
async fn read(&self, id: i32) -> Result<Administrator, String> {
|
async fn read(&self, id: String) -> Result<Administrator, String> {
|
||||||
let result = self.connection.query(
|
let result = self.connection.query(
|
||||||
"SELECT * FROM administrator WHERE id = $1", &[&id]
|
"SELECT * FROM administrator WHERE id = $1", &[&id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).ok_or("Administrator not found".to_owned())?;
|
let row = rows.get(0).ok_or("Administrator not found".to_owned())?;
|
||||||
Ok(Administrator {
|
Ok(Administrator {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
username: row.get("username"),
|
username: row.get("username"),
|
||||||
password: row.get("password"),
|
password: row.get("password"),
|
||||||
name: row.get("name"),
|
name: row.get("name"),
|
||||||
surname: row.get("surname"),
|
surname: row.get("surname"),
|
||||||
middlename: row.get("middlename"),
|
middlename: row.get("middlename"),
|
||||||
car_station_id: row.get("car_station_id")
|
car_station_id: row.get::<&str, i32>("car_station_id").to_string()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(result.unwrap_err().to_string())
|
Err(result.unwrap_err().to_string())
|
||||||
@ -37,13 +37,13 @@ impl AdministratorRepository for PostgresAdministratorRepository {
|
|||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).ok_or("Administrator not found".to_owned())?;
|
let row = rows.get(0).ok_or("Administrator not found".to_owned())?;
|
||||||
Ok(Administrator {
|
Ok(Administrator {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
username: row.get("username"),
|
username: row.get("username"),
|
||||||
password: row.get("password"),
|
password: row.get("password"),
|
||||||
name: row.get("name"),
|
name: row.get("name"),
|
||||||
surname: row.get("surname"),
|
surname: row.get("surname"),
|
||||||
middlename: row.get("middlename"),
|
middlename: row.get("middlename"),
|
||||||
car_station_id: row.get("car_station_id")
|
car_station_id: row.get::<&str, i32>("car_station_id").to_string()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(result.unwrap_err().to_string())
|
Err(result.unwrap_err().to_string())
|
||||||
|
@ -17,7 +17,7 @@ impl BenchmarkRepository for PostgresBenchmarkRepository {
|
|||||||
|
|
||||||
let start_time = chrono::Utc::now().naive_local();
|
let start_time = chrono::Utc::now().naive_local();
|
||||||
|
|
||||||
for n in 1..201 {
|
for n in 1..2001 {
|
||||||
let str = format!("INSERT INTO Owner(name, surname, middlename, phone) VALUES ('Иван{0}', 'Иванов{0}', 'Иванович{0}', {0}) RETURNING id", n);
|
let str = format!("INSERT INTO Owner(name, surname, middlename, phone) VALUES ('Иван{0}', 'Иванов{0}', 'Иванович{0}', {0}) RETURNING id", n);
|
||||||
let owner_id: i32 = self.connection.query(
|
let owner_id: i32 = self.connection.query(
|
||||||
&str,
|
&str,
|
||||||
@ -36,7 +36,7 @@ impl BenchmarkRepository for PostgresBenchmarkRepository {
|
|||||||
).await.unwrap().get(0).unwrap().get(0);
|
).await.unwrap().get(0).unwrap().get(0);
|
||||||
|
|
||||||
let str = format!("INSERT INTO Rent(car_id, client_id, start_time, time_amount) VALUES ({1}, {2}, now()::timestamp, {0}) RETURNING id", n, car_id, client_id);
|
let str = format!("INSERT INTO Rent(car_id, client_id, start_time, time_amount) VALUES ({1}, {2}, now()::timestamp, {0}) RETURNING id", n, car_id, client_id);
|
||||||
let rent_id: i32 = self.connection.query(
|
let _: i32 = self.connection.query(
|
||||||
&str,
|
&str,
|
||||||
&[]
|
&[]
|
||||||
).await.unwrap().get(0).unwrap().get(0);
|
).await.unwrap().get(0).unwrap().get(0);
|
||||||
|
@ -14,38 +14,38 @@ impl CarRepository for PostgresCarRepository {
|
|||||||
let result = self.connection.query(
|
let result = self.connection.query(
|
||||||
"INSERT INTO Car(brand, model, price, owner_id, car_station_id) \
|
"INSERT INTO Car(brand, model, price, owner_id, car_station_id) \
|
||||||
VALUES ($1, $2, $3, $4, $5) RETURNING *",
|
VALUES ($1, $2, $3, $4, $5) RETURNING *",
|
||||||
&[&car.brand, &car.model, &car.price, &car.owner_id, &car.car_station_id]
|
&[&car.brand, &car.model, &car.price, &car.owner_id.parse::<i32>().unwrap(), &car.car_station_id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).unwrap();
|
let row = rows.get(0).unwrap();
|
||||||
Ok(Car {
|
Ok(Car {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
brand: row.get("brand"),
|
brand: row.get("brand"),
|
||||||
model: row.get("model"),
|
model: row.get("model"),
|
||||||
price: row.get("price"),
|
price: row.get("price"),
|
||||||
owner_id: row.get("owner_id"),
|
owner_id: row.get::<&str, i32>("owner_id").to_string(),
|
||||||
car_station_id: row.get("car_station_id")
|
car_station_id: row.get::<&str, i32>("car_station_id").to_string()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(result.unwrap_err().to_string())
|
Err(result.unwrap_err().to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read(&self, id: i32) -> Result<Car, String> {
|
async fn read(&self, id: String) -> Result<Car, String> {
|
||||||
let result = self.connection.query(
|
let result = self.connection.query(
|
||||||
"SELECT * FROM Car WHERE id = $1", &[&id]
|
"SELECT * FROM Car WHERE id = $1", &[&id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).ok_or("Car not found".to_owned())?;
|
let row = rows.get(0).ok_or("Car not found".to_owned())?;
|
||||||
Ok(Car {
|
Ok(Car {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
brand: row.get("brand"),
|
brand: row.get("brand"),
|
||||||
model: row.get("model"),
|
model: row.get("model"),
|
||||||
price: row.get("price"),
|
price: row.get("price"),
|
||||||
owner_id: row.get("owner_id"),
|
owner_id: row.get::<&str, i32>("owner_id").to_string(),
|
||||||
car_station_id: row.get("car_station_id")
|
car_station_id: row.get::<&str, i32>("car_station_id").to_string()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(result.unwrap_err().to_string())
|
Err(result.unwrap_err().to_string())
|
||||||
@ -60,12 +60,12 @@ impl CarRepository for PostgresCarRepository {
|
|||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
Ok(
|
Ok(
|
||||||
rows.into_iter().map(|r| Car {
|
rows.into_iter().map(|r| Car {
|
||||||
id: r.get("id"),
|
id: r.get::<&str, i32>("id").to_string(),
|
||||||
brand: r.get("brand"),
|
brand: r.get("brand"),
|
||||||
model: r.get("model"),
|
model: r.get("model"),
|
||||||
price: r.get("price"),
|
price: r.get("price"),
|
||||||
owner_id: r.get("owner_id"),
|
owner_id: r.get::<&str, i32>("owner_id").to_string(),
|
||||||
car_station_id: r.get("car_station_id")
|
car_station_id: r.get::<&str, i32>("car_station_id").to_string()
|
||||||
}).collect()
|
}).collect()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -73,31 +73,31 @@ impl CarRepository for PostgresCarRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update(&self, id: i32, car: BindingCar) -> Result<Car, String> {
|
async fn update(&self, id: String, car: BindingCar) -> Result<Car, String> {
|
||||||
let result = self.connection.query(
|
let result = self.connection.query(
|
||||||
"UPDATE Car SET brand = $1, model = $2, price = $3, owner_id = $4, car_station_id = $5 WHERE id = $6 RETURNING *",
|
"UPDATE Car SET brand = $1, model = $2, price = $3, owner_id = $4, car_station_id = $5 WHERE id = $6 RETURNING *",
|
||||||
&[&car.brand, &car.model, &car.price, &car.owner_id, &car.car_station_id, &id]
|
&[&car.brand, &car.model, &car.price, &car.owner_id.parse::<i32>().unwrap(), &car.car_station_id.parse::<i32>().unwrap(), &id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = &result {
|
if let Ok(rows) = &result {
|
||||||
let row = rows.get(0).unwrap();
|
let row = rows.get(0).unwrap();
|
||||||
Ok(Car {
|
Ok(Car {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
brand: row.get("brand"),
|
brand: row.get("brand"),
|
||||||
model: row.get("model"),
|
model: row.get("model"),
|
||||||
price: row.get("price"),
|
price: row.get("price"),
|
||||||
owner_id: row.get("owner_id"),
|
owner_id: row.get::<&str, i32>("owner_id").to_string(),
|
||||||
car_station_id: row.get("car_station_id")
|
car_station_id: row.get::<&str, i32>("car_station_id").to_string()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(result.unwrap_err().to_string())
|
Err(result.unwrap_err().to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete(&self, id: i32) -> Result<(), String> {
|
async fn delete(&self, id: String) -> Result<(), String> {
|
||||||
let result = self.connection.execute(
|
let result = self.connection.execute(
|
||||||
"DELETE FROM Car WHERE id = $1",
|
"DELETE FROM Car WHERE id = $1",
|
||||||
&[&id]
|
&[&id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
@ -129,7 +129,7 @@ impl CarRepository for PostgresCarRepository {
|
|||||||
Ok(
|
Ok(
|
||||||
rows.into_iter()
|
rows.into_iter()
|
||||||
.map(|row| Report {
|
.map(|row| Report {
|
||||||
car_id: row.get("car_id"),
|
car_id: row.get::<&str, i32>("car_id").to_string(),
|
||||||
brand: row.get("brand"),
|
brand: row.get("brand"),
|
||||||
model: row.get("model"),
|
model: row.get("model"),
|
||||||
times: row.get("times"),
|
times: row.get("times"),
|
||||||
|
@ -19,7 +19,7 @@ impl CarStationRepository for PostgresCarStationRepository {
|
|||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).unwrap();
|
let row = rows.get(0).unwrap();
|
||||||
Ok(CarStation {
|
Ok(CarStation {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
address: row.get("address")
|
address: row.get("address")
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -27,15 +27,15 @@ impl CarStationRepository for PostgresCarStationRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read(&self, id: i32) -> Result<CarStation, String> {
|
async fn read(&self, id: String) -> Result<CarStation, String> {
|
||||||
let result = self.connection.query(
|
let result = self.connection.query(
|
||||||
"SELECT * FROM Car_Station WHERE id = $1", &[&id]
|
"SELECT * FROM Car_Station WHERE id = $1", &[&id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).ok_or("CarStation not found".to_owned())?;
|
let row = rows.get(0).ok_or("CarStation not found".to_owned())?;
|
||||||
Ok(CarStation {
|
Ok(CarStation {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
address: row.get("address")
|
address: row.get("address")
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -51,7 +51,7 @@ impl CarStationRepository for PostgresCarStationRepository {
|
|||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
Ok(
|
Ok(
|
||||||
rows.into_iter().map(|r| CarStation {
|
rows.into_iter().map(|r| CarStation {
|
||||||
id: r.get("id"),
|
id: r.get::<&str, i32>("id").to_string(),
|
||||||
address: r.get("address")
|
address: r.get("address")
|
||||||
}).collect()
|
}).collect()
|
||||||
)
|
)
|
||||||
@ -60,16 +60,16 @@ impl CarStationRepository for PostgresCarStationRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update(&self, id: i32, car_station: BindingCarStation) -> Result<CarStation, String> {
|
async fn update(&self, id: String, car_station: BindingCarStation) -> Result<CarStation, String> {
|
||||||
let result = self.connection.query(
|
let result = self.connection.query(
|
||||||
"UPDATE Car_Station SET address = $1 WHERE id = $2 RETURNING *",
|
"UPDATE Car_Station SET address = $1 WHERE id = $2 RETURNING *",
|
||||||
&[&car_station.address, &id]
|
&[&car_station.address, &id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).unwrap();
|
let row = rows.get(0).unwrap();
|
||||||
Ok(CarStation {
|
Ok(CarStation {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
address: row.get("address")
|
address: row.get("address")
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -77,10 +77,10 @@ impl CarStationRepository for PostgresCarStationRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete(&self, id: i32) -> Result<(), String> {
|
async fn delete(&self, id: String) -> Result<(), String> {
|
||||||
let result = self.connection.execute(
|
let result = self.connection.execute(
|
||||||
"DELETE FROM Car_Station WHERE id = $1",
|
"DELETE FROM Car_Station WHERE id = $1",
|
||||||
&[&id]
|
&[&id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
|
@ -20,7 +20,7 @@ impl ClientRepository for PostgresClientRepository {
|
|||||||
if let Ok(rows) = &result {
|
if let Ok(rows) = &result {
|
||||||
let row = rows.get(0).unwrap();
|
let row = rows.get(0).unwrap();
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
name: row.get("name"),
|
name: row.get("name"),
|
||||||
surname: row.get("surname"),
|
surname: row.get("surname"),
|
||||||
middlename: row.get("middlename"),
|
middlename: row.get("middlename"),
|
||||||
@ -31,16 +31,16 @@ impl ClientRepository for PostgresClientRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read(&self, id: i32) -> Result<Client, String> {
|
async fn read(&self, id: String) -> Result<Client, String> {
|
||||||
let result= self.connection.query(
|
let result= self.connection.query(
|
||||||
"SELECT * FROM Client WHERE id = $1", &[&id]
|
"SELECT * FROM Client WHERE id = $1", &[&id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
|
|
||||||
let row = rows.get(0).ok_or("Client not found".to_owned())?;
|
let row = rows.get(0).ok_or("Client not found".to_owned())?;
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
name: row.get("name"),
|
name: row.get("name"),
|
||||||
surname: row.get("surname"),
|
surname: row.get("surname"),
|
||||||
middlename: row.get("middlename"),
|
middlename: row.get("middlename"),
|
||||||
@ -59,7 +59,7 @@ impl ClientRepository for PostgresClientRepository {
|
|||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
Ok(
|
Ok(
|
||||||
rows.into_iter().map(|r| Client {
|
rows.into_iter().map(|r| Client {
|
||||||
id: r.get("id"),
|
id: r.get::<&str, i32>("id").to_string(),
|
||||||
name: r.get("name"),
|
name: r.get("name"),
|
||||||
surname: r.get("surname"),
|
surname: r.get("surname"),
|
||||||
middlename: r.get("middlename"),
|
middlename: r.get("middlename"),
|
||||||
@ -71,17 +71,17 @@ impl ClientRepository for PostgresClientRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update(&self, id: i32, client: BindingClient) -> Result<Client, String> {
|
async fn update(&self, id: String, client: BindingClient) -> Result<Client, String> {
|
||||||
let result = self.connection.query(
|
let result = self.connection.query(
|
||||||
"UPDATE Client SET name = $1, surname = $2, middlename = $3, phone = $4 \
|
"UPDATE Client SET name = $1, surname = $2, middlename = $3, phone = $4 \
|
||||||
WHERE id = $5 RETURNING *",
|
WHERE id = $5 RETURNING *",
|
||||||
&[&client.name, &client.surname, &client.middlename, &client.phone, &id]
|
&[&client.name, &client.surname, &client.middlename, &client.phone, &id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).unwrap();
|
let row = rows.get(0).unwrap();
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
name: row.get("name"),
|
name: row.get("name"),
|
||||||
surname: row.get("surname"),
|
surname: row.get("surname"),
|
||||||
middlename: row.get("middlename"),
|
middlename: row.get("middlename"),
|
||||||
@ -92,10 +92,10 @@ impl ClientRepository for PostgresClientRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete(&self, id: i32) -> Result<(), String> {
|
async fn delete(&self, id: String) -> Result<(), String> {
|
||||||
let result = self.connection.execute(
|
let result = self.connection.execute(
|
||||||
"DELETE FROM Client WHERE id = $1",
|
"DELETE FROM Client WHERE id = $1",
|
||||||
&[&id]
|
&[&id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
|
@ -20,7 +20,7 @@ impl OwnerRepository for PostgresOwnerRepository {
|
|||||||
if let Ok(rows) = &result {
|
if let Ok(rows) = &result {
|
||||||
let row = rows.get(0).unwrap();
|
let row = rows.get(0).unwrap();
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
name: row.get("name"),
|
name: row.get("name"),
|
||||||
surname: row.get("surname"),
|
surname: row.get("surname"),
|
||||||
middlename: row.get("middlename"),
|
middlename: row.get("middlename"),
|
||||||
@ -31,16 +31,16 @@ impl OwnerRepository for PostgresOwnerRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read(&self, id: i32) -> Result<Client, String> {
|
async fn read(&self, id: String) -> Result<Client, String> {
|
||||||
let result= self.connection.query(
|
let result= self.connection.query(
|
||||||
"SELECT * FROM Owner WHERE id = $1", &[&id]
|
"SELECT * FROM Owner WHERE id = $1", &[&id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
|
|
||||||
let row = rows.get(0).ok_or("Owner not found".to_owned())?;
|
let row = rows.get(0).ok_or("Owner not found".to_owned())?;
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
name: row.get("name"),
|
name: row.get("name"),
|
||||||
surname: row.get("surname"),
|
surname: row.get("surname"),
|
||||||
middlename: row.get("middlename"),
|
middlename: row.get("middlename"),
|
||||||
@ -59,7 +59,7 @@ impl OwnerRepository for PostgresOwnerRepository {
|
|||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
Ok(
|
Ok(
|
||||||
rows.into_iter().map(|r| Client {
|
rows.into_iter().map(|r| Client {
|
||||||
id: r.get("id"),
|
id: r.get::<&str, i32>("id").to_string(),
|
||||||
name: r.get("name"),
|
name: r.get("name"),
|
||||||
surname: r.get("surname"),
|
surname: r.get("surname"),
|
||||||
middlename: r.get("middlename"),
|
middlename: r.get("middlename"),
|
||||||
@ -71,17 +71,17 @@ impl OwnerRepository for PostgresOwnerRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update(&self, id: i32, client: BindingClient) -> Result<Client, String> {
|
async fn update(&self, id: String, client: BindingClient) -> Result<Client, String> {
|
||||||
let result = self.connection.query(
|
let result = self.connection.query(
|
||||||
"UPDATE Owner SET name = $1, surname = $2, middlename = $3, phone = $4 \
|
"UPDATE Owner SET name = $1, surname = $2, middlename = $3, phone = $4 \
|
||||||
WHERE id = $5 RETURNING *",
|
WHERE id = $5 RETURNING *",
|
||||||
&[&client.name, &client.surname, &client.middlename, &client.phone, &id]
|
&[&client.name, &client.surname, &client.middlename, &client.phone, &id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).unwrap();
|
let row = rows.get(0).unwrap();
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
name: row.get("name"),
|
name: row.get("name"),
|
||||||
surname: row.get("surname"),
|
surname: row.get("surname"),
|
||||||
middlename: row.get("middlename"),
|
middlename: row.get("middlename"),
|
||||||
@ -92,10 +92,10 @@ impl OwnerRepository for PostgresOwnerRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete(&self, id: i32) -> Result<(), String> {
|
async fn delete(&self, id: String) -> Result<(), String> {
|
||||||
let result = self.connection.execute(
|
let result = self.connection.execute(
|
||||||
"DELETE FROM Owner WHERE id = $1",
|
"DELETE FROM Owner WHERE id = $1",
|
||||||
&[&id]
|
&[&id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
|
@ -13,7 +13,7 @@ impl RentRepository for PostgresRentRepository {
|
|||||||
async fn create(&self, rent: BindingRent) -> Result<Rent, String> {
|
async fn create(&self, rent: BindingRent) -> Result<Rent, String> {
|
||||||
let query_result = self.connection.query(
|
let query_result = self.connection.query(
|
||||||
"SELECT * FROM Rent WHERE car_id=$1 AND time_amount IS NULL",
|
"SELECT * FROM Rent WHERE car_id=$1 AND time_amount IS NULL",
|
||||||
&[&rent.car_id]
|
&[&rent.car_id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = query_result {
|
if let Ok(rows) = query_result {
|
||||||
@ -27,36 +27,36 @@ impl RentRepository for PostgresRentRepository {
|
|||||||
let result = self.connection.query(
|
let result = self.connection.query(
|
||||||
"INSERT INTO Rent(time_amount, client_id, car_id) \
|
"INSERT INTO Rent(time_amount, client_id, car_id) \
|
||||||
VALUES ($1, $2, $3) RETURNING *",
|
VALUES ($1, $2, $3) RETURNING *",
|
||||||
&[&rent.time_amount, &rent.client_id, &rent.car_id]
|
&[&rent.time_amount, &rent.client_id.parse::<i32>().unwrap(), &rent.car_id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).unwrap();
|
let row = rows.get(0).unwrap();
|
||||||
Ok(Rent {
|
Ok(Rent {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
start_time: row.get("start_time"),
|
start_time: row.get("start_time"),
|
||||||
time_amount: row.get("time_amount"),
|
time_amount: row.get("time_amount"),
|
||||||
client_id: row.get("client_id"),
|
client_id: row.get::<&str, i32>("client_id").to_string(),
|
||||||
car_id: row.get("car_id")
|
car_id: row.get::<&str, i32>("car_id").to_string()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(result.unwrap_err().to_string())
|
Err(result.unwrap_err().to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read(&self, id: i32) -> Result<Rent, String> {
|
async fn read(&self, id: String) -> Result<Rent, String> {
|
||||||
let result = self.connection.query(
|
let result = self.connection.query(
|
||||||
"SELECT * FROM Rent WHERE id = $1", &[&id]
|
"SELECT * FROM Rent WHERE id = $1", &[&id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).ok_or("Rent not found".to_owned())?;
|
let row = rows.get(0).ok_or("Rent not found".to_owned())?;
|
||||||
Ok(Rent {
|
Ok(Rent {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
start_time: row.get("start_time"),
|
start_time: row.get("start_time"),
|
||||||
time_amount: row.get("time_amount"),
|
time_amount: row.get("time_amount"),
|
||||||
client_id: row.get("client_id"),
|
client_id: row.get::<&str, i32>("client_id").to_string(),
|
||||||
car_id: row.get("car_id")
|
car_id: row.get::<&str, i32>("car_id").to_string()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(result.unwrap_err().to_string())
|
Err(result.unwrap_err().to_string())
|
||||||
@ -71,11 +71,11 @@ impl RentRepository for PostgresRentRepository {
|
|||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
Ok(
|
Ok(
|
||||||
rows.into_iter().map(|r| Rent {
|
rows.into_iter().map(|r| Rent {
|
||||||
id: r.get("id"),
|
id: r.get::<&str, i32>("id").to_string(),
|
||||||
start_time: r.get("start_time"),
|
start_time: r.get("start_time"),
|
||||||
time_amount: r.get("time_amount"),
|
time_amount: r.get("time_amount"),
|
||||||
client_id: r.get("client_id"),
|
client_id: r.get::<&str, i32>("client_id").to_string(),
|
||||||
car_id: r.get("car_id")
|
car_id: r.get::<&str, i32>("car_id").to_string()
|
||||||
}).collect()
|
}).collect()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -83,29 +83,29 @@ impl RentRepository for PostgresRentRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update(&self, id: i32, rent: BindingRent) -> Result<Rent, String> {
|
async fn update(&self, id: String, rent: BindingRent) -> Result<Rent, String> {
|
||||||
let result = self.connection.query(
|
let result = self.connection.query(
|
||||||
"UPDATE Rent SET time_amount = $2, client_id = $3, car_id = $4 WHERE id = $5 RETURNING *",
|
"UPDATE Rent SET time_amount = $2, client_id = $3, car_id = $4 WHERE id = $5 RETURNING *",
|
||||||
&[&rent.time_amount, &rent.client_id, &rent.car_id, &id]
|
&[&rent.time_amount, &rent.client_id.parse::<i32>().unwrap(), &rent.car_id.parse::<i32>().unwrap(), &id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(rows) = result {
|
if let Ok(rows) = result {
|
||||||
let row = rows.get(0).unwrap();
|
let row = rows.get(0).unwrap();
|
||||||
Ok(Rent {
|
Ok(Rent {
|
||||||
id: row.get("id"),
|
id: row.get::<&str, i32>("id").to_string(),
|
||||||
start_time: row.get("start_time"),
|
start_time: row.get("start_time"),
|
||||||
time_amount: row.get("time_amount"),
|
time_amount: row.get("time_amount"),
|
||||||
client_id: row.get("client_id"),
|
client_id: row.get::<&str, i32>("client_id").to_string(),
|
||||||
car_id: row.get("car_id")
|
car_id: row.get::<&str, i32>("car_id").to_string()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(result.unwrap_err().to_string())
|
Err(result.unwrap_err().to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete(&self, id: i32) -> Result<(), String> {
|
async fn delete(&self, id: String) -> Result<(), String> {
|
||||||
let result = self.connection.execute(
|
let result = self.connection.execute(
|
||||||
"DELETE FROM Rent WHERE id = $1", &[&id]
|
"DELETE FROM Rent WHERE id = $1", &[&id.parse::<i32>().unwrap()]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Ok(_) = result {
|
if let Ok(_) = result {
|
||||||
@ -115,8 +115,8 @@ impl RentRepository for PostgresRentRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn end_rent(&self, id: i32) -> Result<(), String> {
|
async fn end_rent(&self, id: String) -> Result<(), String> {
|
||||||
let current_rent = self.read(id).await?;
|
let current_rent = self.read(id.clone()).await?;
|
||||||
|
|
||||||
if current_rent.time_amount.is_some() {
|
if current_rent.time_amount.is_some() {
|
||||||
return Err("Rent is already ended".to_owned());
|
return Err("Rent is already ended".to_owned());
|
||||||
@ -124,7 +124,7 @@ impl RentRepository for PostgresRentRepository {
|
|||||||
|
|
||||||
let time_amount = (self.connection.query("SELECT now()::timestamp", &[]).await.unwrap().get(0).unwrap().get::<usize, chrono::NaiveDateTime>(0) - current_rent.start_time).num_minutes() as i32;
|
let time_amount = (self.connection.query("SELECT now()::timestamp", &[]).await.unwrap().get(0).unwrap().get::<usize, chrono::NaiveDateTime>(0) - current_rent.start_time).num_minutes() as i32;
|
||||||
|
|
||||||
match self.connection.execute("UPDATE Rent SET time_amount = $2 WHERE id = $1", &[&id, &time_amount]).await {
|
match self.connection.execute("UPDATE Rent SET time_amount = $2 WHERE id = $1", &[&id.parse::<i32>().unwrap(), &time_amount]).await {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(err) => Err(err.to_string())
|
Err(err) => Err(err.to_string())
|
||||||
}
|
}
|
||||||
|
@ -8,53 +8,53 @@ use async_trait::async_trait;
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait ClientRepository {
|
pub trait ClientRepository {
|
||||||
async fn create(&self, client: BindingClient) -> Result<Client, String>;
|
async fn create(&self, client: BindingClient) -> Result<Client, String>;
|
||||||
async fn read(&self, id: i32) -> Result<Client, String>;
|
async fn read(&self, id: String) -> Result<Client, String>;
|
||||||
async fn read_all(&self) -> Result<Vec<Client>, String>;
|
async fn read_all(&self) -> Result<Vec<Client>, String>;
|
||||||
async fn update(&self, id: i32, client: BindingClient) -> Result<Client, String>;
|
async fn update(&self, id: String, client: BindingClient) -> Result<Client, String>;
|
||||||
async fn delete(&self, id: i32) -> Result<(), String>;
|
async fn delete(&self, id: String) -> Result<(), String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait CarRepository {
|
pub trait CarRepository {
|
||||||
async fn create(&self, car: BindingCar) -> Result<Car, String>;
|
async fn create(&self, car: BindingCar) -> Result<Car, String>;
|
||||||
async fn read(&self, id: i32) -> Result<Car, String>;
|
async fn read(&self, id: String) -> Result<Car, String>;
|
||||||
async fn read_all(&self) -> Result<Vec<Car>, String>;
|
async fn read_all(&self) -> Result<Vec<Car>, String>;
|
||||||
async fn update(&self, id: i32, car: BindingCar) -> Result<Car, String>;
|
async fn update(&self, id: String, car: BindingCar) -> Result<Car, String>;
|
||||||
async fn delete(&self, id: i32) -> Result<(), String>;
|
async fn delete(&self, id: String) -> Result<(), String>;
|
||||||
async fn get_report(&self) -> Result<Vec<Report>, String>;
|
async fn get_report(&self) -> Result<Vec<Report>, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait CarStationRepository {
|
pub trait CarStationRepository {
|
||||||
async fn create(&self, car_station: BindingCarStation) -> Result<CarStation, String>;
|
async fn create(&self, car_station: BindingCarStation) -> Result<CarStation, String>;
|
||||||
async fn read(&self, id: i32) -> Result<CarStation, String>;
|
async fn read(&self, id: String) -> Result<CarStation, String>;
|
||||||
async fn read_all(&self) -> Result<Vec<CarStation>, String>;
|
async fn read_all(&self) -> Result<Vec<CarStation>, String>;
|
||||||
async fn update(&self, id: i32, car_station: BindingCarStation) -> Result<CarStation, String>;
|
async fn update(&self, id: String, car_station: BindingCarStation) -> Result<CarStation, String>;
|
||||||
async fn delete(&self, id: i32) -> Result<(), String>;
|
async fn delete(&self, id: String) -> Result<(), String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait RentRepository {
|
pub trait RentRepository {
|
||||||
async fn create(&self, rent: BindingRent) -> Result<Rent, String>;
|
async fn create(&self, rent: BindingRent) -> Result<Rent, String>;
|
||||||
async fn read(&self, id: i32) -> Result<Rent, String>;
|
async fn read(&self, id: String) -> Result<Rent, String>;
|
||||||
async fn read_all(&self) -> Result<Vec<Rent>, String>;
|
async fn read_all(&self) -> Result<Vec<Rent>, String>;
|
||||||
async fn update(&self, id: i32, rent: BindingRent) -> Result<Rent, String>;
|
async fn update(&self, id: String, rent: BindingRent) -> Result<Rent, String>;
|
||||||
async fn delete(&self, id: i32) -> Result<(), String>;
|
async fn delete(&self, id: String) -> Result<(), String>;
|
||||||
async fn end_rent(&self, id: i32) -> Result<(), String>;
|
async fn end_rent(&self, id: String) -> Result<(), String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait OwnerRepository {
|
pub trait OwnerRepository {
|
||||||
async fn create(&self, client: BindingClient) -> Result<Client, String>;
|
async fn create(&self, client: BindingClient) -> Result<Client, String>;
|
||||||
async fn read(&self, id: i32) -> Result<Client, String>;
|
async fn read(&self, id: String) -> Result<Client, String>;
|
||||||
async fn read_all(&self) -> Result<Vec<Client>, String>;
|
async fn read_all(&self) -> Result<Vec<Client>, String>;
|
||||||
async fn update(&self, id: i32, client: BindingClient) -> Result<Client, String>;
|
async fn update(&self, id: String, client: BindingClient) -> Result<Client, String>;
|
||||||
async fn delete(&self, id: i32) -> Result<(), String>;
|
async fn delete(&self, id: String) -> Result<(), String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait AdministratorRepository {
|
pub trait AdministratorRepository {
|
||||||
async fn read(&self, id: i32) -> Result<Administrator, String>;
|
async fn read(&self, id: String) -> Result<Administrator, String>;
|
||||||
async fn find_by_username(&self, username: &str) -> Result<Administrator, String>;
|
async fn find_by_username(&self, username: &str) -> Result<Administrator, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
429
frontend/app.py
Normal file
429
frontend/app.py
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
from flask import Flask, request, render_template, session, redirect, url_for
|
||||||
|
import requests
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.secret_key = 'Kill me already'
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
|
def login():
|
||||||
|
if request.method == 'POST':
|
||||||
|
if not request.form.get('username') or not request.form.get('password'):
|
||||||
|
return render_template('login.html', title='Авторизация', errors='Необходимо заполнить все поля',
|
||||||
|
logged_in=False)
|
||||||
|
|
||||||
|
response = requests.post("http://localhost:8080/api/login",
|
||||||
|
json={
|
||||||
|
'username': request.form.get('username'),
|
||||||
|
'password': request.form.get('password')
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template('login.html', title='Авторизация', errors='Неверный логин или пароль',
|
||||||
|
logged_in=False)
|
||||||
|
|
||||||
|
session['token'] = response.json()['token']
|
||||||
|
session['administrator_id'] = response.json()['administrator_id']
|
||||||
|
session['car_station_id'] = response.json()['car_station_id']
|
||||||
|
|
||||||
|
return redirect(url_for('rents'))
|
||||||
|
|
||||||
|
return render_template('login.html', title='Авторизация', logged_in=False)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/logout')
|
||||||
|
def logout():
|
||||||
|
session.pop('token')
|
||||||
|
session.pop('administrator_id')
|
||||||
|
session.pop('car_station_id')
|
||||||
|
|
||||||
|
requests.post('http://localhost:8080/api/logout')
|
||||||
|
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/rents', methods=['GET', 'POST'])
|
||||||
|
def rents():
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
a = requests.get('http://localhost:8080/api/rents/', headers={'Authorization': session['token']})
|
||||||
|
rents_data = requests.get('http://localhost:8080/api/rents/', headers={'Authorization': session['token']}).json()
|
||||||
|
cars = list(
|
||||||
|
filter(lambda c: c['car_station_id'] == session['car_station_id'],
|
||||||
|
requests.get('http://localhost:8080/api/cars/', headers={'Authorization': session['token']}).json()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
clients = requests.get('http://localhost:8080/api/clients/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
for rent in rents_data:
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
date_format = '%Y-%m-%dT%H:%M:%S.%f'
|
||||||
|
date_format_another = '%Y-%m-%dT%H:%M:%S'
|
||||||
|
|
||||||
|
try:
|
||||||
|
parsed_date = datetime.strptime(rent['start_time'], date_format)
|
||||||
|
except ValueError:
|
||||||
|
parsed_date = datetime.strptime(rent['start_time'], date_format_another)
|
||||||
|
|
||||||
|
rent['start_time'] = parsed_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
for car in cars:
|
||||||
|
if car['id'] == rent['car_id']:
|
||||||
|
rent['car'] = car
|
||||||
|
if not rent['time_amount']:
|
||||||
|
cars.remove(car)
|
||||||
|
break
|
||||||
|
for client in clients:
|
||||||
|
if client['id'] == rent['client_id']:
|
||||||
|
rent['client'] = client
|
||||||
|
break
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if not request.form.get('id') and (not request.form.get('client_id') or not request.form.get('car_id')):
|
||||||
|
return render_template(
|
||||||
|
'rents.html',
|
||||||
|
errors='Необходимо заполнить все поля',
|
||||||
|
title='Аренды',
|
||||||
|
rents=rents_data,
|
||||||
|
cars=cars,
|
||||||
|
clients=clients,
|
||||||
|
logged_in=True,
|
||||||
|
selected_client=request.args.get('client') if request.args.get('client') else None,
|
||||||
|
selected_car=request.args.get('car') if request.args.get('car') else None
|
||||||
|
)
|
||||||
|
|
||||||
|
response = None
|
||||||
|
if request.form.get('id'):
|
||||||
|
response = requests.get(f'http://localhost:8080/api/rents/{request.form.get("id")}/end',
|
||||||
|
headers={'Authorization': session['token']})
|
||||||
|
else:
|
||||||
|
response = requests.post('http://localhost:8080/api/rents/', headers={'Authorization': session['token']}, json={
|
||||||
|
"client_id": request.form.get('client_id'),
|
||||||
|
"car_id": request.form.get('car_id')
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template(
|
||||||
|
'rents.html',
|
||||||
|
title='Аренды',
|
||||||
|
errors=response.json(),
|
||||||
|
rents=rents_data,
|
||||||
|
cars=cars,
|
||||||
|
clients=clients,
|
||||||
|
logged_in=True,
|
||||||
|
selected_client=request.args.get('client') if request.args.get('client') else None,
|
||||||
|
selected_car=request.args.get('car') if request.args.get('car') else None
|
||||||
|
)
|
||||||
|
|
||||||
|
if request.form.get('id'):
|
||||||
|
return redirect(url_for('rent', id=request.form.get('id')))
|
||||||
|
|
||||||
|
rents_data = requests.get('http://localhost:8080/api/rents/',
|
||||||
|
headers={'Authorization': session['token']}).json()
|
||||||
|
cars = list(
|
||||||
|
filter(lambda c: c['car_station_id'] == session['car_station_id'],
|
||||||
|
requests.get('http://localhost:8080/api/cars/', headers={'Authorization': session['token']}).json()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
clients = requests.get('http://localhost:8080/api/clients/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
for rent in rents_data:
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
date_format = '%Y-%m-%dT%H:%M:%S.%f'
|
||||||
|
|
||||||
|
parsed_date = datetime.strptime(rent['start_time'], date_format)
|
||||||
|
rent['start_time'] = parsed_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
for car in cars:
|
||||||
|
if car['id'] == rent['car_id']:
|
||||||
|
rent['car'] = car
|
||||||
|
if not rent['time_amount']:
|
||||||
|
cars.remove(car)
|
||||||
|
break
|
||||||
|
for client in clients:
|
||||||
|
if client['id'] == rent['client_id']:
|
||||||
|
rent['client'] = client
|
||||||
|
break
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
'rents.html',
|
||||||
|
title='Аренды',
|
||||||
|
rents=rents_data,
|
||||||
|
cars=cars,
|
||||||
|
clients=clients,
|
||||||
|
logged_in=True,
|
||||||
|
selected_client=request.args.get('client') if request.args.get('client') else None,
|
||||||
|
selected_car=request.args.get('car') if request.args.get('car') else None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/rents/<id>', methods=['GET', 'POST'])
|
||||||
|
def rent(id):
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
rent = requests.get(f'http://localhost:8080/api/rents/{id}', headers={'Authorization': session['token']}).json()
|
||||||
|
client = requests.get(f'http://localhost:8080/api/clients/{rent["client_id"]}', headers={'Authorization': session['token']}).json()
|
||||||
|
car = requests.get(f'http://localhost:8080/api/cars/{rent["car_id"]}', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
rent['client'] = client
|
||||||
|
rent['car'] = car
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
date_format = '%Y-%m-%dT%H:%M:%S.%f'
|
||||||
|
|
||||||
|
parsed_date = datetime.strptime(rent['start_time'], date_format)
|
||||||
|
rent['start_time'] = parsed_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
import math
|
||||||
|
return render_template('rent.html', title='Просмотр аренды', rent=rent, logged_in=True, float=float, int=int, math=math)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/clients', defaults={'id': 0}, methods=['GET', 'POST'])
|
||||||
|
@app.route('/clients/<id>', methods=['GET', 'POST'])
|
||||||
|
def clients(id):
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
clients = requests.get('http://localhost:8080/api/clients/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if id != 0:
|
||||||
|
if not request.form.get('name') or not request.form.get('surname') or not request.form.get('middlename') or not request.form.get('phone'):
|
||||||
|
return render_template(
|
||||||
|
'clients.html',
|
||||||
|
title='Изменение клиента',
|
||||||
|
errors='Должны быть заполнены все поля!',
|
||||||
|
current_client=list(filter(lambda c: c['id'] == id, clients))[0],
|
||||||
|
clients=clients,
|
||||||
|
logged_in=True
|
||||||
|
)
|
||||||
|
|
||||||
|
response = requests.patch(f'http://localhost:8080/api/clients/{id}', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'name': request.form.get('name'),
|
||||||
|
'surname': request.form.get('surname'),
|
||||||
|
'middlename': request.form.get('middlename'),
|
||||||
|
'phone': request.form.get('phone')
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template(
|
||||||
|
'clients.html',
|
||||||
|
title='Изменение клиента',
|
||||||
|
errors=response.json(),
|
||||||
|
clients=clients,
|
||||||
|
current_client=list(filter(lambda c: c['id'] == id, clients))[0],
|
||||||
|
logged_in=True
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('clients'))
|
||||||
|
else:
|
||||||
|
if not request.form.get('name') or not request.form.get('surname') or not request.form.get('middlename') or not request.form.get('phone'):
|
||||||
|
return render_template('clients.html', title='Клиенты', errors='Должны быть заполнены все поля!',
|
||||||
|
clients=clients, logged_in=True)
|
||||||
|
|
||||||
|
response = requests.post('http://localhost:8080/api/clients/', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'name': request.form.get('name'),
|
||||||
|
'surname': request.form.get('surname'),
|
||||||
|
'middlename': request.form.get('middlename'),
|
||||||
|
'phone': request.form.get('phone')
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template('clients.html', title='Клиенты', errors=response.json(), clients=clients,
|
||||||
|
logged_in=True)
|
||||||
|
|
||||||
|
clients = requests.get('http://localhost:8080/api/clients/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
if id != 0:
|
||||||
|
return render_template('clients.html', title='Изменение клиента', clients=clients, logged_in=True, current_client=list(filter(lambda c: c['id'] == id, clients))[0])
|
||||||
|
|
||||||
|
return render_template('clients.html', title='Клиенты', clients=clients, logged_in=True)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/owners', defaults={'id': 0}, methods=['GET', 'POST'])
|
||||||
|
@app.route('/owners/<id>', methods=['GET', 'POST'])
|
||||||
|
def owners(id):
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
owners = requests.get('http://localhost:8080/api/owners/', headers={'Authorization': session['token']}).json()
|
||||||
|
cars = list(
|
||||||
|
filter(lambda c: c['car_station_id'] == session['car_station_id'],
|
||||||
|
requests.get('http://localhost:8080/api/cars/', headers={'Authorization': session['token']}).json()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if id != 0:
|
||||||
|
if not request.form.get('name') or not request.form.get('surname') or not request.form.get('middlename') or not request.form.get('phone'):
|
||||||
|
return render_template(
|
||||||
|
'owners.html',
|
||||||
|
title='Изменение владельца',
|
||||||
|
errors='Должны быть заполнены все поля!',
|
||||||
|
current_owner=list(filter(lambda c: c['id'] == id, owners))[0],
|
||||||
|
owners=owners,
|
||||||
|
cars=cars,
|
||||||
|
logged_in=True
|
||||||
|
)
|
||||||
|
|
||||||
|
response = requests.patch(f'http://localhost:8080/api/owners/{id}', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'name': request.form.get('name'),
|
||||||
|
'surname': request.form.get('surname'),
|
||||||
|
'middlename': request.form.get('middlename'),
|
||||||
|
'phone': request.form.get('phone')
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template(
|
||||||
|
'owners.html',
|
||||||
|
title='Изменение владельца',
|
||||||
|
errors=response.json(),
|
||||||
|
owners=owners,
|
||||||
|
cars=cars,
|
||||||
|
current_owner=list(filter(lambda c: c['id'] == id, owners))[0],
|
||||||
|
logged_in=True
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('owners'))
|
||||||
|
else:
|
||||||
|
if not request.form.get('name') or not request.form.get('surname') or not request.form.get('middlename') or not request.form.get('phone'):
|
||||||
|
return render_template('owners.html', title='Владельцы', errors='Должны быть заполнены все поля!',
|
||||||
|
cars=cars,
|
||||||
|
owners=owners, logged_in=True)
|
||||||
|
|
||||||
|
response = requests.post('http://localhost:8080/api/owners/', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'name': request.form.get('name'),
|
||||||
|
'surname': request.form.get('surname'),
|
||||||
|
'middlename': request.form.get('middlename'),
|
||||||
|
'phone': request.form.get('phone')
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template('owners.html', title='Владельцы', errors=response.json(), owners=owners,
|
||||||
|
cars=cars,
|
||||||
|
logged_in=True)
|
||||||
|
|
||||||
|
owners = requests.get('http://localhost:8080/api/owners/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
if id != 0:
|
||||||
|
return render_template('owners.html', title='Изменение владельца', owners=owners, cars=cars, logged_in=True, current_owner=list(filter(lambda c: c['id'] == id, owners))[0])
|
||||||
|
|
||||||
|
return render_template('owners.html', title='Владельца', owners=owners, cars=cars, logged_in=True)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/cars', defaults={'id': 0}, methods=['GET', 'POST'])
|
||||||
|
@app.route('/cars/<id>', methods=['GET', 'POST'])
|
||||||
|
def cars(id):
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
owners = requests.get('http://localhost:8080/api/owners/', headers={'Authorization': session['token']}).json()
|
||||||
|
cars = list(
|
||||||
|
filter(lambda c: c['car_station_id'] == session['car_station_id'],
|
||||||
|
requests.get('http://localhost:8080/api/cars/', headers={'Authorization': session['token']}).json()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if id != 0:
|
||||||
|
if not request.form.get('brand') or not request.form.get('model') or not request.form.get('price') or not request.form.get('owner_id'):
|
||||||
|
return render_template(
|
||||||
|
'cars.html',
|
||||||
|
title='Изменение автомобиля',
|
||||||
|
errors='Должны быть заполнены все поля!',
|
||||||
|
current_car=list(filter(lambda c: c['id'] == id, cars))[0],
|
||||||
|
cars=cars,
|
||||||
|
owners=owners,
|
||||||
|
logged_in=True,
|
||||||
|
selected_owner=request.args.get('owner') if request.args.get('owner') else None
|
||||||
|
)
|
||||||
|
|
||||||
|
response = requests.patch(f'http://localhost:8080/api/cars/{id}', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'brand': request.form.get('brand'),
|
||||||
|
'model': request.form.get('model'),
|
||||||
|
'price': float(request.form.get('price')),
|
||||||
|
'owner_id': request.form.get('owner_id'),
|
||||||
|
'car_station_id': session['car_station_id']
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template(
|
||||||
|
'cars.html',
|
||||||
|
title='Изменение автомобиля',
|
||||||
|
errors=response.json(),
|
||||||
|
cars=cars,
|
||||||
|
owners=owners,
|
||||||
|
current_car=list(filter(lambda c: c['id'] == id, cars))[0],
|
||||||
|
logged_in=True,
|
||||||
|
selected_owner=request.args.get('owner') if request.args.get('owner') else None
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('cars'))
|
||||||
|
else:
|
||||||
|
if not request.form.get('brand') or not request.form.get('model') or not request.form.get('price') or not request.form.get('owner_id'):
|
||||||
|
return render_template('cars.html', title='Автомобили', errors='Должны быть заполнены все поля!',
|
||||||
|
owners=owners, selected_owner=request.args.get('owner') if request.args.get('owner') else None,
|
||||||
|
cars=cars, logged_in=True)
|
||||||
|
|
||||||
|
response = requests.post('http://localhost:8080/api/cars/', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'brand': request.form.get('brand'),
|
||||||
|
'model': request.form.get('model'),
|
||||||
|
'price': float(request.form.get('price')),
|
||||||
|
'owner_id': request.form.get('owner_id'),
|
||||||
|
'car_station_id': session['car_station_id']
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template('cars.html', title='Автомобили', errors=response.json(), cars=cars,
|
||||||
|
owners=owners, selected_owner=request.args.get('owner') if request.args.get('owner') else None,
|
||||||
|
logged_in=True)
|
||||||
|
|
||||||
|
cars = requests.get('http://localhost:8080/api/cars/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
if id != 0:
|
||||||
|
return render_template('cars.html', title='Изменение автомобиля', cars=cars, logged_in=True, owners=owners, selected_owner=request.args.get('owner') if request.args.get('owner') else None, current_car=list(filter(lambda c: c['id'] == id, cars))[0])
|
||||||
|
|
||||||
|
return render_template('cars.html', title='Автомобили', cars=cars, logged_in=True, owners=owners, selected_owner=request.args.get('owner') if request.args.get('owner') else None)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/report')
|
||||||
|
def report():
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
response = requests.get('http://localhost:8080/api/cars/report', headers={'Authorization': session['token']})
|
||||||
|
reports = list()
|
||||||
|
for report in response.json():
|
||||||
|
report['income'] = float(report['income'])
|
||||||
|
reports.append(report)
|
||||||
|
|
||||||
|
return render_template('report.html', title='Отчёт', reports=reports, logged_in=True)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/admin', methods=['GET', 'POST'])
|
||||||
|
def admin():
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
response = requests.get('http://localhost:8080/benchmark', headers={'Authorization': session['token']})
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
|
return render_template('admin.html', title='Панель администратора', logged_in=True, result=result)
|
||||||
|
|
||||||
|
return render_template('admin.html', title='Панель администратора', logged_in=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', port='5000')
|
BIN
frontend/requirements.txt
Normal file
BIN
frontend/requirements.txt
Normal file
Binary file not shown.
18
frontend/templates/admin.html
Normal file
18
frontend/templates/admin.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col card is-vertical-align" method="post">
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">Произвести прирост населения машин и клиентов</button>
|
||||||
|
</div>
|
||||||
|
{% if result %}
|
||||||
|
<div class="alert alert-primary mb-3 h4" role="alert">
|
||||||
|
Это заняло вот столько микросекунд: {{ result }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
36
frontend/templates/base.html
Normal file
36
frontend/templates/base.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/chota@latest">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.0.slim.min.js" integrity="sha256-tG5mcZUtJsZvyKAxYLVXrmjKBVLd6VpVccqz/r4ypFE=" crossorigin="anonymous"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container mt-5">
|
||||||
|
{% if logged_in %}
|
||||||
|
<div class="nav mb-5">
|
||||||
|
<div class="nav-left">
|
||||||
|
<a href="/rents" class="button primary outline">Аренды</a>
|
||||||
|
<a href="/clients" class="button primary outline">Клиенты</a>
|
||||||
|
<a href="/owners" class="button primary outline">Владельцы</a>
|
||||||
|
<a href="/cars" class="button primary outline">Автомобили</a>
|
||||||
|
<a href="/report" class="button primary outline">Отчёт</a>
|
||||||
|
</div>
|
||||||
|
<div class="nav-right">
|
||||||
|
<a href="/logout" class="button danger outline">Выйти</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
{% block script %}
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
74
frontend/templates/cars.html
Normal file
74
frontend/templates/cars.html
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col-6 card is-vertical-align" method="post">
|
||||||
|
<h3 class="is-center mb-5">{% if current_car %} Просмотр автомобиля {% else %} Регистрация автомобиля {% endif %}</h3>
|
||||||
|
{% if errors %}
|
||||||
|
<div class="alert alert-danger mb-3 h4" role="alert">
|
||||||
|
{{ errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Марка: <input type="text" name="brand" {% if current_car %} value="{{ current_car.brand }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Модель: <input type="text" name="model" {% if current_car %} value="{{ current_car.model }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Цена: <input type="text" name="price" pattern="\d+\.\d{2}" {% if current_car %} value="{{ current_car.price }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">
|
||||||
|
Владелец:
|
||||||
|
<select id="owner_id" name="owner_id" class="mt-2">
|
||||||
|
{% for owner in owners %}
|
||||||
|
<option {% if selected_owner and selected_owner == owner['id'] %} selected {% endif %} value="{{ owner['id'] }}">ФИО: {{ owner['surname'] }} {{ owner['name'][0] }}. {{ owner['middlename'][0] }}. Телефон: {{ owner['phone'] }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">{% if current_car %} Изменить {% else %} Зарегистрировать автомобиль {% endif %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Все автомобили на стоянке</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Марка</th>
|
||||||
|
<th class="h4">Модель</th>
|
||||||
|
<th class="h4">Цена</th>
|
||||||
|
<th class="h4">Владелец</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for car in cars|sort(attribute="brand")|sort(attribute="model")|sort(attribute="price") %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ car.brand }}</td>
|
||||||
|
<td class="h4">{{ car.model }}</td>
|
||||||
|
<td class="h4">{{ car.price }}</td>
|
||||||
|
<td class="h4"><a href="{{ url_for('owners', id=car.owner_id) }}">Перейти к владельцу</a></td>
|
||||||
|
<td>
|
||||||
|
<div class="row">
|
||||||
|
<a href="{{ url_for("rents", car=car.id) }}" class="button primary outline col">Арендовать</a>
|
||||||
|
<a href="{{ url_for("cars", id=car.id) }}" class="button primary outline col">Посмотреть</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#owner_id').select2();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
59
frontend/templates/clients.html
Normal file
59
frontend/templates/clients.html
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col card is-vertical-align" method="post">
|
||||||
|
<h3 class="is-center mb-5">{% if current_client %} Просмотр клиента {% else %} Регистрация клиента {% endif %}</h3>
|
||||||
|
{% if errors %}
|
||||||
|
<div class="alert alert-danger mb-3 h4" role="alert">
|
||||||
|
{{ errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Имя: <input type="text" name="name" {% if current_client %} value="{{ current_client.name }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Фамилия: <input type="text" name="surname" {% if current_client %} value="{{ current_client.surname }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Отчество: <input type="text" name="middlename" {% if current_client %} value="{{ current_client.middlename }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Номер телефона: <input type="text" name="phone" pattern="8\d{10}" {% if current_client %} value="{{ current_client.phone }}" {% else %} value="8" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">{% if current_client %} Изменить {% else %} Зарегистрировать клиента {% endif %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Все клиенты</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Номер телефона</th>
|
||||||
|
<th class="h4">Фамилия</th>
|
||||||
|
<th class="h4">Имя</th>
|
||||||
|
<th class="h4">Отчество</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for client in clients|sort(attribute="surname")|sort(attribute="name")|sort(attribute="middlename") %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ client.phone }}</td>
|
||||||
|
<td class="h4">{{ client.surname }}</td>
|
||||||
|
<td class="h4">{{ client.name }}</td>
|
||||||
|
<td class="h4">{{ client.middlename }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="row">
|
||||||
|
<a href="{{ url_for("rents", client=client.id) }}" class="button primary outline col">Перейти к аренде</a>
|
||||||
|
<a href="{{ url_for("clients", id=client.id) }}" class="button primary outline col">Посмотреть</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
24
frontend/templates/login.html
Normal file
24
frontend/templates/login.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col card is-vertical-align" method="post">
|
||||||
|
{% if errors %}
|
||||||
|
<div class="alert alert-danger mb-3 h4" role="alert">
|
||||||
|
{{ errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Логин: <input type="text" name="username"/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Пароль: <input type="password" name="password"/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">Войти</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
91
frontend/templates/owners.html
Normal file
91
frontend/templates/owners.html
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col card is-vertical-align" method="post">
|
||||||
|
<h3 class="is-center mb-5">{% if current_owner %} Просмотр владельца {% else %} Регистрация владельца {% endif %}</h3>
|
||||||
|
{% if errors %}
|
||||||
|
<div class="alert alert-danger mb-3 h4" role="alert">
|
||||||
|
{{ errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Имя: <input type="text" name="name" {% if current_owner %} value="{{ current_owner.name }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Фамилия: <input type="text" name="surname" {% if current_owner %} value="{{ current_owner.surname }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Отчество: <input type="text" name="middlename" {% if current_owner %} value="{{ current_owner.middlename }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Номер телефона: <input type="text" name="phone" pattern="8\d{10}" {% if current_owner %} value="{{ current_owner.phone }}" {% else %} value="8" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">{% if current_owner %} Изменить {% else %} Зарегистрировать клиента {% endif %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
{% if current_owner %}
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Автомобили владельца</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Марка</th>
|
||||||
|
<th class="h4">Модель</th>
|
||||||
|
<th class="h4">Цена</th>
|
||||||
|
<th class="h4">Владелец</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for car in cars|sort(attribute="brand")|sort(attribute="model")|sort(attribute="price") %}
|
||||||
|
{% if car.owner_id == current_owner.id %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ car.brand }}</td>
|
||||||
|
<td class="h4">{{ car.model }}</td>
|
||||||
|
<td class="h4">{{ car.price }}</td>
|
||||||
|
<td class="h4"><a href="{{ url_for('owners', id=car.owner_id) }}">Перейти к владельцу</a></td>
|
||||||
|
<td>
|
||||||
|
<div class="row">
|
||||||
|
<a href="{{ url_for("rents", car=car.id) }}" class="button primary outline col">Арендовать</a>
|
||||||
|
<a href="{{ url_for("cars", id=car.id) }}" class="button primary outline col">Посмотреть</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Все владельцы</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Номер телефона</th>
|
||||||
|
<th class="h4">Фамилия</th>
|
||||||
|
<th class="h4">Имя</th>
|
||||||
|
<th class="h4">Отчество</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for owner in owners|sort(attribute="surname")|sort(attribute="name")|sort(attribute="middlename") %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ owner.phone }}</td>
|
||||||
|
<td class="h4">{{ owner.surname }}</td>
|
||||||
|
<td class="h4">{{ owner.name }}</td>
|
||||||
|
<td class="h4">{{ owner.middlename }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="row">
|
||||||
|
<a href="{{ url_for("cars", owner=owner.id) }}" class="button primary outline col">Добавить автомобиль</a>
|
||||||
|
<a href="{{ url_for("owners", id=owner.id) }}" class="button primary outline col">Посмотреть</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
34
frontend/templates/rent.html
Normal file
34
frontend/templates/rent.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col-6 card h4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
Дата начала: {{ rent.start_time }}
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
Номер телефона клиента: {{ rent.client.phone }}
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
<span>Клиент: <a href="{{ url_for('clients', id=rent.client_id) }}">{{ rent.client.surname }} {{ rent.client.name }} {{ rent.client.middlename }}</a></span>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
<span>Автомобиль: <a href="{{ url_for('cars', id=rent.car_id) }}">{{ rent.car.brand }} {{ rent.car.model }}</a></span>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
Количество минут: {{ rent.time_amount }}
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
<span>Итоговая стоимость: <strong>{{ math.ceil(float(rent.car.price) * int(rent.time_amount) * 100) / 100 }}</strong></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
109
frontend/templates/rents.html
Normal file
109
frontend/templates/rents.html
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align mb-5">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col-6 card is-vertical-align" method="post">
|
||||||
|
<h3 class="is-center mb-5">Создание аренды</h3>
|
||||||
|
{% if errors %}
|
||||||
|
<div class="alert alert-danger mb-3 h4" role="alert">
|
||||||
|
{{ errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">
|
||||||
|
Клиент:
|
||||||
|
<select id="client_id" name="client_id" class="mt-2">
|
||||||
|
{% for client in clients %}
|
||||||
|
<option {% if selected_client and selected_client == client['id'] %} selected {% endif %} value="{{ client['id'] }}">ФИО: {{ client['surname'] }} {{ client['name'][0] }}. {{ client['middlename'][0] }}. Телефон: {{ client['phone'] }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">
|
||||||
|
Машина:
|
||||||
|
<select id="car_id" name="car_id" class="mt-2">
|
||||||
|
{% for car in cars %}
|
||||||
|
<option {% if selected_car and selected_car == car['id'] %} selected {% endif %} value="{{ car['id'] }}">Марка: {{ car['brand'] }}. Модель: {{ car['model'] }}. Цена: {{ car['price'] }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">Создать запись об аренде</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Действительные аренды</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Дата начала</th>
|
||||||
|
<th class="h4">Номер телефона клиента</th>
|
||||||
|
<th class="h4">Клиент</th>
|
||||||
|
<th class="h4">Автомобиль</th>
|
||||||
|
<th class="h4">Цена за минуту</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for rent in rents|sort(reverse=true, attribute="start_time") %}
|
||||||
|
{% if not rent.time_amount %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ rent.start_time }}</td>
|
||||||
|
<td class="h4">{{ rent.client.phone }}</td>
|
||||||
|
<td class="h4"><a href="{{ url_for('clients', id=rent.client_id) }}">{{ rent.client.surname }} {{ rent.client.name }} {{ rent.client.middlename }}</a></td>
|
||||||
|
<td class="h4"><a href="{{ url_for('cars', id=rent.car_id) }}">{{ rent.car.brand }} {{ rent.car.model }}</a></td>
|
||||||
|
<td class="h4">{{ rent.car.price }}</td>
|
||||||
|
<td>
|
||||||
|
<form method="post" class="row">
|
||||||
|
<input name="id" value="{{ rent.id }}" style="visibility: hidden">
|
||||||
|
<button class="button primary outline col">Завершить</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Завершённые аренды</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Дата начала</th>
|
||||||
|
<th class="h4">Номер телефона клиента</th>
|
||||||
|
<th class="h4">Клиент</th>
|
||||||
|
<th class="h4">Автомобиль</th>
|
||||||
|
<th class="h4">Цена за минуту</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for rent in rents|sort(reverse=true, attribute="start_time") %}
|
||||||
|
{% if rent.time_amount %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ rent.start_time }}</td>
|
||||||
|
<td class="h4">{{ rent.client.phone }}</td>
|
||||||
|
<td class="h4"><a href="{{ url_for('clients', id=rent.client_id) }}">{{ rent.client.surname }} {{ rent.client.name }} {{ rent.client.middlename }}</a></td>
|
||||||
|
<td class="h4"><a href="{{ url_for('cars', id=rent.car_id) }}">{{ rent.car.brand }} {{ rent.car.model }}</a></td>
|
||||||
|
<td class="h4">{{ rent.car.price }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('rent', id=rent.id) }}" class="button primary outline">Посмотреть</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#client_id').select2();
|
||||||
|
$('#car_id').select2();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
23
frontend/templates/report.html
Normal file
23
frontend/templates/report.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Отчёт по машинам</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Автомобиль</th>
|
||||||
|
<th class="h4">Взят в аренду в этом месяце (раз)</th>
|
||||||
|
<th class="h4">Доход с машины за этот месяц</th>
|
||||||
|
</tr>
|
||||||
|
{% for report in reports|sort(reverse=true, attribute="income") %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4"><a href="{{ url_for('cars', id=report.car_id) }}">{{ report.brand }} {{ report.model }}</a></td>
|
||||||
|
<td class="h4">{{ report.times }}</td>
|
||||||
|
<td class="h4"> {{ report.income }} </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user