Compare commits

..

No commits in common. "main" and "backend" have entirely different histories.

45 changed files with 168 additions and 2539 deletions

13
.gitignore vendored
View File

@ -1,19 +1,12 @@
# ---> Rust
# Generated by Cargo
# will have compiled files and executables
frontend/debug/
frontend/target/
frontend/.env
frontend/dist
backend/debug/
backend/target/
backend/.env
backend/target
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
frontend/*.pdb
.vscode
.vscode/*
backend/*.pdb

587
backend/Cargo.lock generated
View File

@ -30,7 +30,7 @@ dependencies = [
"actix-service",
"actix-utils",
"ahash 0.8.3",
"base64 0.21.0",
"base64",
"bitflags",
"brotli",
"bytes",
@ -277,10 +277,8 @@ dependencies = [
"chrono",
"dotenv",
"dotenv_codegen",
"futures",
"mongodb",
"nanoid",
"pbkdf2 0.12.1",
"pbkdf2",
"rust_decimal",
"rust_decimal_macros",
"serde",
@ -290,12 +288,6 @@ dependencies = [
"tokio-postgres",
]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.21.0"
@ -395,28 +387,6 @@ dependencies = [
"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]]
name = "bumpalo"
version = "3.12.0"
@ -602,58 +572,6 @@ dependencies = [
"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]]
name = "derive_more"
version = "0.99.17"
@ -663,7 +581,7 @@ dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version 0.4.0",
"rustc_version",
"syn 1.0.109",
]
@ -716,18 +634,6 @@ dependencies = [
"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]]
name = "fallible-iterator"
version = "0.2.0"
@ -765,21 +671,6 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "futures-channel"
version = "0.3.28"
@ -796,23 +687,6 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "futures-macro"
version = "0.3.28"
@ -842,13 +716,10 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
@ -903,12 +774,6 @@ dependencies = [
"ahash 0.7.6",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.2.6"
@ -918,12 +783,6 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.12.1"
@ -933,17 +792,6 @@ dependencies = [
"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]]
name = "http"
version = "0.2.9"
@ -991,23 +839,6 @@ dependencies = [
"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]]
name = "idna"
version = "0.3.0"
@ -1028,24 +859,6 @@ dependencies = [
"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]]
name = "itoa"
version = "1.0.6"
@ -1076,12 +889,6 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.140"
@ -1097,12 +904,6 @@ dependencies = [
"cc",
]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "local-channel"
version = "0.1.3"
@ -1140,27 +941,6 @@ dependencies = [
"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]]
name = "md-5"
version = "0.10.5"
@ -1203,53 +983,6 @@ dependencies = [
"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]]
name = "nanoid"
version = "0.4.0"
@ -1323,15 +1056,6 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
[[package]]
name = "pbkdf2"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
dependencies = [
"digest",
]
[[package]]
name = "pbkdf2"
version = "0.12.1"
@ -1404,7 +1128,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b7fa9f396f51dffd61546fd8573ee20592287996568e6175ceb0f8699ad75d"
dependencies = [
"base64 0.21.0",
"base64",
"byteorder",
"bytes",
"fallible-iterator",
@ -1478,12 +1202,6 @@ dependencies = [
"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]]
name = "quote"
version = "1.0.26"
@ -1564,31 +1282,6 @@ dependencies = [
"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]]
name = "rkyv"
version = "0.7.42"
@ -1647,53 +1340,13 @@ dependencies = [
"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]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"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",
"semver",
]
[[package]]
@ -1714,43 +1367,18 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "seahash"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "semver"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.159"
@ -1760,15 +1388,6 @@ dependencies = [
"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]]
name = "serde_derive"
version = "1.0.159"
@ -1786,7 +1405,6 @@ version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
@ -1804,39 +1422,6 @@ dependencies = [
"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]]
name = "sha1"
version = "0.10.5"
@ -1915,12 +1500,6 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "stringprep"
version = "0.1.2"
@ -1931,12 +1510,6 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "subtle"
version = "2.4.1"
@ -1965,12 +1538,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "take_mut"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
[[package]]
name = "tap"
version = "1.0.1"
@ -1986,26 +1553,6 @@ dependencies = [
"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]]
name = "time"
version = "0.1.45"
@ -2113,17 +1660,6 @@ dependencies = [
"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]]
name = "tokio-util"
version = "0.7.7"
@ -2132,7 +1668,6 @@ checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
dependencies = [
"bytes",
"futures-core",
"futures-io",
"futures-sink",
"pin-project-lite",
"tokio",
@ -2169,62 +1704,6 @@ dependencies = [
"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]]
name = "typenum"
version = "1.16.0"
@ -2258,12 +1737,6 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "url"
version = "2.3.1"
@ -2271,7 +1744,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna 0.3.0",
"idna",
"percent-encoding",
]
@ -2280,10 +1753,6 @@ name = "uuid"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2"
dependencies = [
"getrandom",
"serde",
]
[[package]]
name = "version_check"
@ -2357,41 +1826,6 @@ version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "winapi"
version = "0.3.9"
@ -2498,15 +1932,6 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]
[[package]]
name = "wyz"
version = "0.5.1"

View File

@ -19,6 +19,4 @@ pbkdf2 = "0.12.1"
nanoid = "0.4.0"
sha2 = "0.10.6"
rust_decimal = { version = "1.29", features = ["db-tokio-postgres"] }
rust_decimal_macros = "1.29"
mongodb = { version = "2.5.0", features = ["bson-chrono-0_4"] }
futures = "0.3"
rust_decimal_macros = "1.29"

View File

@ -1,7 +1,7 @@
use std::sync::Mutex;
use actix_web::{web, post, Responder, HttpResponse};
use serde_json::json;
use crate::State;
use crate::{State, models::administrator};
#[derive(serde::Deserialize)]
pub struct LoginInfo {

View File

@ -1,5 +1,5 @@
use std::sync::Mutex;
use actix_web::{web, get, Responder, HttpResponse};
use actix_web::{web, get, post, patch, delete, Responder, HttpResponse, HttpRequest};
use crate::State;
#[get("/benchmark")]

View File

@ -27,7 +27,7 @@ pub async fn get_cars(state: web::Data<Mutex<State>>, request: HttpRequest) -> i
}
#[get("/{id}")]
pub async fn get_car(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn get_car(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
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}")]
pub async fn update_car(state: web::Data<Mutex<State>>, json: web::Json<BindingCar>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn update_car(state: web::Data<Mutex<State>>, json: web::Json<BindingCar>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
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}")]
pub async fn delete_car(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn delete_car(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
return HttpResponse::Unauthorized().body("There is no token");

View File

@ -27,7 +27,7 @@ pub async fn get_car_stations(state: web::Data<Mutex<State>>, request: HttpReque
}
#[get("/{id}")]
pub async fn get_car_station(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn get_car_station(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
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}")]
pub async fn update_car_station(state: web::Data<Mutex<State>>, json: web::Json<BindingCarStation>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn update_car_station(state: web::Data<Mutex<State>>, json: web::Json<BindingCarStation>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
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}")]
pub async fn delete_car_station(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn delete_car_station(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
return HttpResponse::Unauthorized().body("There is no token");

View File

@ -27,7 +27,7 @@ pub async fn get_clients(state: web::Data<Mutex<State>>, request: HttpRequest) -
}
#[get("/{id}")]
pub async fn get_client(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn get_client(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
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}")]
pub async fn update_client(state: web::Data<Mutex<State>>, json: web::Json<BindingClient>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn update_client(state: web::Data<Mutex<State>>, json: web::Json<BindingClient>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
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}")]
pub async fn delete_client(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn delete_client(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
return HttpResponse::Unauthorized().body("There is no token");

View File

@ -27,7 +27,7 @@ pub async fn get_owners(state: web::Data<Mutex<State>>, request: HttpRequest) ->
}
#[get("/{id}")]
pub async fn get_owner(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn get_owner(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
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}")]
pub async fn update_owner(state: web::Data<Mutex<State>>, json: web::Json<BindingClient>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn update_owner(state: web::Data<Mutex<State>>, json: web::Json<BindingClient>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
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}")]
pub async fn delete_owner(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn delete_owner(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
return HttpResponse::Unauthorized().body("There is no token");

View File

@ -12,8 +12,8 @@ pub async fn get_rents(state: web::Data<Mutex<State>>, request: HttpRequest) ->
let token = token.unwrap();
if let Ok(guard) = state.lock() {
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: ".to_owned() + error.as_str());
if guard.auth_service.administrator_by_token(guard.administrator_repository.as_ref(), token.to_str().unwrap()).await.is_err() {
return HttpResponse::Unauthorized().body("Invalid token");
}
}
@ -27,7 +27,7 @@ pub async fn get_rents(state: web::Data<Mutex<State>>, request: HttpRequest) ->
}
#[get("/{id}")]
pub async fn get_rent(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn get_rent(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
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}")]
pub async fn update_rent(state: web::Data<Mutex<State>>, json: web::Json<BindingRent>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn update_rent(state: web::Data<Mutex<State>>, json: web::Json<BindingRent>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
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}")]
pub async fn delete_rent(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn delete_rent(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
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<(String
}
#[get("/{id}/end")]
pub async fn end_rent(state: web::Data<Mutex<State>>, path: web::Path<(String, )>, request: HttpRequest) -> impl Responder {
pub async fn end_rent(state: web::Data<Mutex<State>>, path: web::Path<(i32, )>, request: HttpRequest) -> impl Responder {
let token = request.headers().get("Authorization");
if token.is_none() {
return HttpResponse::Unauthorized().body("There is no token");

View File

@ -6,8 +6,6 @@ use backend::endpoints::{auth::*};
use backend::storages::postgres::administrator::PostgresAdministratorRepository;
use dotenv_codegen::dotenv;
use backend::{State, services};
use mongodb::Client;
use mongodb::options::ClientOptions;
use tokio_postgres::NoTls;
use backend::storages::postgres::car::PostgresCarRepository;
use backend::storages::postgres::car_station::PostgresCarStationRepository;
@ -15,14 +13,6 @@ use backend::storages::postgres::client::PostgresClientRepository;
use backend::storages::postgres::rent::PostgresRentRepository;
use backend::storages::postgres::owner::PostgresOwnerRepository;
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::*;
use backend::endpoints::client::*;
@ -31,52 +21,32 @@ use backend::endpoints::owner::*;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// let (client, connection) = tokio_postgres::connect(
// &format!("host={} user={} password={} dbname={}", dotenv!("HOST"), dotenv!("USERR"), dotenv!("PASSWORD"), dotenv!("DBNAMEE")),
// NoTls
// ).await.unwrap();
let (client, connection) = tokio_postgres::connect(
&format!("host={} user={} password={} dbname={}", dotenv!("HOST"), dotenv!("USER"), dotenv!("PASSWORD"), dotenv!("DBNAMEE")),
NoTls
).await.unwrap();
// tokio::spawn(async move {
// if let Err(e) = connection.await {
// eprintln!("connection error: {}", e);
// }
// });
tokio::spawn(async move {
if let Err(e) = connection.await {
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 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 benchmark_repository = PostgresBenchmarkRepository { connection: Arc::clone(&client) };
let auth_service = services::auth::AuthService::new();
@ -141,7 +111,7 @@ async fn main() -> std::io::Result<()> {
.service(logout)
).service(benchmark)
})
.bind("0.0.0.0:8080")?
.bind("127.0.0.1:8080")?
.run()
.await
}

View File

@ -2,11 +2,11 @@ use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Administrator {
pub id: String,
pub id: i32,
pub username: String,
pub password: String,
pub name: String,
pub surname: String,
pub middlename: String,
pub car_station_id: String
pub car_station_id: i32
}

View File

@ -1,14 +1,14 @@
use rust_decimal::Decimal;
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Car {
pub id: String,
pub id: i32,
pub brand: String,
pub model: String,
pub price: Decimal,
pub owner_id: String,
pub car_station_id: String
pub owner_id: i32,
pub car_station_id: i32
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -16,13 +16,13 @@ pub struct BindingCar {
pub brand: String,
pub model: String,
pub price: Decimal,
pub owner_id: String,
pub car_station_id: String
pub owner_id: i32,
pub car_station_id: i32
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Report {
pub car_id: String,
pub car_id: i32,
pub brand: String,
pub model: String,
pub times: i64,

View File

@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CarStation {
pub id: String,
pub id: i32,
pub address: String
}

View File

@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Client {
pub id: String,
pub id: i32,
pub name: String,
pub surname: String,
pub middlename: Option<String>,

View File

@ -2,16 +2,16 @@ use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Rent {
pub id: String,
pub id: i32,
pub start_time: chrono::NaiveDateTime,
pub time_amount: Option<i32>,
pub client_id: String,
pub car_id: String
pub client_id: i32,
pub car_id: i32
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BindingRent {
pub time_amount: Option<i32>,
pub client_id: String,
pub car_id: String
pub client_id: i32,
pub car_id: i32
}

View File

@ -4,7 +4,7 @@ use sha2::Sha256;
use std::cell::RefCell;
pub struct AuthService {
authed: RefCell<Vec::<(String, String)>>
authed: RefCell<Vec::<(String, i32)>>
}
impl AuthService {
@ -12,14 +12,14 @@ impl AuthService {
AuthService { authed: RefCell::new(Vec::new()) }
}
pub async fn login(&self, storage: &dyn AdministratorRepository, username: &str, password: &str) -> Result<(String, String, String), String> {
pub async fn login(&self, storage: &dyn AdministratorRepository, username: &str, password: &str) -> Result<(String, i32, i32), String> {
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 {
Err("Invalid password or login!".to_owned())
} else {
let token = nanoid!(32);
self.authed.borrow_mut().push((token.clone(), administrator.id.clone()));
Ok((token, administrator.id.clone(), administrator.car_station_id))
self.authed.borrow_mut().push((token.clone(), administrator.id));
Ok((token, administrator.id, 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> {
let id = self.authed.borrow().iter().find(|&x| x.0.starts_with(token)).ok_or("Invalid token")?.1.clone();
let id = self.authed.borrow().iter().find(|&x| x.0.starts_with(token)).ok_or("Invalid token")?.1;
storage.read(id).await
}
}

View File

@ -1,3 +1,2 @@
pub mod traits;
pub mod postgres;
pub mod mongo;
pub mod postgres;

View File

@ -1,63 +0,0 @@
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())
}
}
}

View File

@ -1,68 +0,0 @@
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())
}
}

View File

@ -1,221 +0,0 @@
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)
}
}

View File

@ -1,101 +0,0 @@
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())
}
}
}

View File

@ -1,117 +0,0 @@
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())
}
}
}

View File

@ -1,7 +0,0 @@
pub mod car_station;
pub mod administrator;
pub mod car;
pub mod client;
pub mod owner;
pub mod rent;
pub mod benchmark;

View File

@ -1,129 +0,0 @@
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())
}
}
}

View File

@ -1,153 +0,0 @@
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())
}
}
}

View File

@ -10,21 +10,21 @@ pub struct PostgresAdministratorRepository {
#[async_trait]
impl AdministratorRepository for PostgresAdministratorRepository {
async fn read(&self, id: String) -> Result<Administrator, String> {
async fn read(&self, id: i32) -> Result<Administrator, String> {
let result = self.connection.query(
"SELECT * FROM administrator WHERE id = $1", &[&id.parse::<i32>().unwrap()]
"SELECT * FROM administrator WHERE id = $1", &[&id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).ok_or("Administrator not found".to_owned())?;
Ok(Administrator {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
username: row.get("username"),
password: row.get("password"),
name: row.get("name"),
surname: row.get("surname"),
middlename: row.get("middlename"),
car_station_id: row.get::<&str, i32>("car_station_id").to_string()
car_station_id: row.get("car_station_id")
})
} else {
Err(result.unwrap_err().to_string())
@ -37,13 +37,13 @@ impl AdministratorRepository for PostgresAdministratorRepository {
if let Ok(rows) = result {
let row = rows.get(0).ok_or("Administrator not found".to_owned())?;
Ok(Administrator {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
username: row.get("username"),
password: row.get("password"),
name: row.get("name"),
surname: row.get("surname"),
middlename: row.get("middlename"),
car_station_id: row.get::<&str, i32>("car_station_id").to_string()
car_station_id: row.get("car_station_id")
})
} else {
Err(result.unwrap_err().to_string())

View File

@ -17,7 +17,7 @@ impl BenchmarkRepository for PostgresBenchmarkRepository {
let start_time = chrono::Utc::now().naive_local();
for n in 1..2001 {
for n in 1..201 {
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(
&str,
@ -36,7 +36,7 @@ impl BenchmarkRepository for PostgresBenchmarkRepository {
).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 _: i32 = self.connection.query(
let rent_id: i32 = self.connection.query(
&str,
&[]
).await.unwrap().get(0).unwrap().get(0);

View File

@ -14,38 +14,38 @@ impl CarRepository for PostgresCarRepository {
let result = self.connection.query(
"INSERT INTO Car(brand, model, price, owner_id, car_station_id) \
VALUES ($1, $2, $3, $4, $5) RETURNING *",
&[&car.brand, &car.model, &car.price, &car.owner_id.parse::<i32>().unwrap(), &car.car_station_id.parse::<i32>().unwrap()]
&[&car.brand, &car.model, &car.price, &car.owner_id, &car.car_station_id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).unwrap();
Ok(Car {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
brand: row.get("brand"),
model: row.get("model"),
price: row.get("price"),
owner_id: row.get::<&str, i32>("owner_id").to_string(),
car_station_id: row.get::<&str, i32>("car_station_id").to_string()
owner_id: row.get("owner_id"),
car_station_id: row.get("car_station_id")
})
} else {
Err(result.unwrap_err().to_string())
}
}
async fn read(&self, id: String) -> Result<Car, String> {
async fn read(&self, id: i32) -> Result<Car, String> {
let result = self.connection.query(
"SELECT * FROM Car WHERE id = $1", &[&id.parse::<i32>().unwrap()]
"SELECT * FROM Car WHERE id = $1", &[&id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).ok_or("Car not found".to_owned())?;
Ok(Car {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
brand: row.get("brand"),
model: row.get("model"),
price: row.get("price"),
owner_id: row.get::<&str, i32>("owner_id").to_string(),
car_station_id: row.get::<&str, i32>("car_station_id").to_string()
owner_id: row.get("owner_id"),
car_station_id: row.get("car_station_id")
})
} else {
Err(result.unwrap_err().to_string())
@ -60,12 +60,12 @@ impl CarRepository for PostgresCarRepository {
if let Ok(rows) = result {
Ok(
rows.into_iter().map(|r| Car {
id: r.get::<&str, i32>("id").to_string(),
id: r.get("id"),
brand: r.get("brand"),
model: r.get("model"),
price: r.get("price"),
owner_id: r.get::<&str, i32>("owner_id").to_string(),
car_station_id: r.get::<&str, i32>("car_station_id").to_string()
owner_id: r.get("owner_id"),
car_station_id: r.get("car_station_id")
}).collect()
)
} else {
@ -73,31 +73,31 @@ impl CarRepository for PostgresCarRepository {
}
}
async fn update(&self, id: String, car: BindingCar) -> Result<Car, String> {
async fn update(&self, id: i32, car: BindingCar) -> Result<Car, String> {
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 *",
&[&car.brand, &car.model, &car.price, &car.owner_id.parse::<i32>().unwrap(), &car.car_station_id.parse::<i32>().unwrap(), &id.parse::<i32>().unwrap()]
&[&car.brand, &car.model, &car.price, &car.owner_id, &car.car_station_id, &id]
).await;
if let Ok(rows) = &result {
let row = rows.get(0).unwrap();
Ok(Car {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
brand: row.get("brand"),
model: row.get("model"),
price: row.get("price"),
owner_id: row.get::<&str, i32>("owner_id").to_string(),
car_station_id: row.get::<&str, i32>("car_station_id").to_string()
owner_id: row.get("owner_id"),
car_station_id: row.get("car_station_id")
})
} else {
Err(result.unwrap_err().to_string())
}
}
async fn delete(&self, id: String) -> Result<(), String> {
async fn delete(&self, id: i32) -> Result<(), String> {
let result = self.connection.execute(
"DELETE FROM Car WHERE id = $1",
&[&id.parse::<i32>().unwrap()]
&[&id]
).await;
if let Ok(rows) = result {
@ -129,7 +129,7 @@ impl CarRepository for PostgresCarRepository {
Ok(
rows.into_iter()
.map(|row| Report {
car_id: row.get::<&str, i32>("car_id").to_string(),
car_id: row.get("car_id"),
brand: row.get("brand"),
model: row.get("model"),
times: row.get("times"),

View File

@ -19,7 +19,7 @@ impl CarStationRepository for PostgresCarStationRepository {
if let Ok(rows) = result {
let row = rows.get(0).unwrap();
Ok(CarStation {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
address: row.get("address")
})
} else {
@ -27,15 +27,15 @@ impl CarStationRepository for PostgresCarStationRepository {
}
}
async fn read(&self, id: String) -> Result<CarStation, String> {
async fn read(&self, id: i32) -> Result<CarStation, String> {
let result = self.connection.query(
"SELECT * FROM Car_Station WHERE id = $1", &[&id.parse::<i32>().unwrap()]
"SELECT * FROM Car_Station WHERE id = $1", &[&id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).ok_or("CarStation not found".to_owned())?;
Ok(CarStation {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
address: row.get("address")
})
} else {
@ -51,7 +51,7 @@ impl CarStationRepository for PostgresCarStationRepository {
if let Ok(rows) = result {
Ok(
rows.into_iter().map(|r| CarStation {
id: r.get::<&str, i32>("id").to_string(),
id: r.get("id"),
address: r.get("address")
}).collect()
)
@ -60,16 +60,16 @@ impl CarStationRepository for PostgresCarStationRepository {
}
}
async fn update(&self, id: String, car_station: BindingCarStation) -> Result<CarStation, String> {
async fn update(&self, id: i32, car_station: BindingCarStation) -> Result<CarStation, String> {
let result = self.connection.query(
"UPDATE Car_Station SET address = $1 WHERE id = $2 RETURNING *",
&[&car_station.address, &id.parse::<i32>().unwrap()]
&[&car_station.address, &id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).unwrap();
Ok(CarStation {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
address: row.get("address")
})
} else {
@ -77,10 +77,10 @@ impl CarStationRepository for PostgresCarStationRepository {
}
}
async fn delete(&self, id: String) -> Result<(), String> {
async fn delete(&self, id: i32) -> Result<(), String> {
let result = self.connection.execute(
"DELETE FROM Car_Station WHERE id = $1",
&[&id.parse::<i32>().unwrap()]
&[&id]
).await;
if let Ok(rows) = result {

View File

@ -20,7 +20,7 @@ impl ClientRepository for PostgresClientRepository {
if let Ok(rows) = &result {
let row = rows.get(0).unwrap();
Ok(Client {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
name: row.get("name"),
surname: row.get("surname"),
middlename: row.get("middlename"),
@ -31,16 +31,16 @@ impl ClientRepository for PostgresClientRepository {
}
}
async fn read(&self, id: String) -> Result<Client, String> {
async fn read(&self, id: i32) -> Result<Client, String> {
let result= self.connection.query(
"SELECT * FROM Client WHERE id = $1", &[&id.parse::<i32>().unwrap()]
"SELECT * FROM Client WHERE id = $1", &[&id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).ok_or("Client not found".to_owned())?;
Ok(Client {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
name: row.get("name"),
surname: row.get("surname"),
middlename: row.get("middlename"),
@ -59,7 +59,7 @@ impl ClientRepository for PostgresClientRepository {
if let Ok(rows) = result {
Ok(
rows.into_iter().map(|r| Client {
id: r.get::<&str, i32>("id").to_string(),
id: r.get("id"),
name: r.get("name"),
surname: r.get("surname"),
middlename: r.get("middlename"),
@ -71,17 +71,17 @@ impl ClientRepository for PostgresClientRepository {
}
}
async fn update(&self, id: String, client: BindingClient) -> Result<Client, String> {
async fn update(&self, id: i32, client: BindingClient) -> Result<Client, String> {
let result = self.connection.query(
"UPDATE Client SET name = $1, surname = $2, middlename = $3, phone = $4 \
WHERE id = $5 RETURNING *",
&[&client.name, &client.surname, &client.middlename, &client.phone, &id.parse::<i32>().unwrap()]
&[&client.name, &client.surname, &client.middlename, &client.phone, &id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).unwrap();
Ok(Client {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
name: row.get("name"),
surname: row.get("surname"),
middlename: row.get("middlename"),
@ -92,10 +92,10 @@ impl ClientRepository for PostgresClientRepository {
}
}
async fn delete(&self, id: String) -> Result<(), String> {
async fn delete(&self, id: i32) -> Result<(), String> {
let result = self.connection.execute(
"DELETE FROM Client WHERE id = $1",
&[&id.parse::<i32>().unwrap()]
&[&id]
).await;
if let Ok(rows) = result {

View File

@ -20,7 +20,7 @@ impl OwnerRepository for PostgresOwnerRepository {
if let Ok(rows) = &result {
let row = rows.get(0).unwrap();
Ok(Client {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
name: row.get("name"),
surname: row.get("surname"),
middlename: row.get("middlename"),
@ -31,16 +31,16 @@ impl OwnerRepository for PostgresOwnerRepository {
}
}
async fn read(&self, id: String) -> Result<Client, String> {
async fn read(&self, id: i32) -> Result<Client, String> {
let result= self.connection.query(
"SELECT * FROM Owner WHERE id = $1", &[&id.parse::<i32>().unwrap()]
"SELECT * FROM Owner WHERE id = $1", &[&id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).ok_or("Owner not found".to_owned())?;
Ok(Client {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
name: row.get("name"),
surname: row.get("surname"),
middlename: row.get("middlename"),
@ -59,7 +59,7 @@ impl OwnerRepository for PostgresOwnerRepository {
if let Ok(rows) = result {
Ok(
rows.into_iter().map(|r| Client {
id: r.get::<&str, i32>("id").to_string(),
id: r.get("id"),
name: r.get("name"),
surname: r.get("surname"),
middlename: r.get("middlename"),
@ -71,17 +71,17 @@ impl OwnerRepository for PostgresOwnerRepository {
}
}
async fn update(&self, id: String, client: BindingClient) -> Result<Client, String> {
async fn update(&self, id: i32, client: BindingClient) -> Result<Client, String> {
let result = self.connection.query(
"UPDATE Owner SET name = $1, surname = $2, middlename = $3, phone = $4 \
WHERE id = $5 RETURNING *",
&[&client.name, &client.surname, &client.middlename, &client.phone, &id.parse::<i32>().unwrap()]
&[&client.name, &client.surname, &client.middlename, &client.phone, &id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).unwrap();
Ok(Client {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
name: row.get("name"),
surname: row.get("surname"),
middlename: row.get("middlename"),
@ -92,10 +92,10 @@ impl OwnerRepository for PostgresOwnerRepository {
}
}
async fn delete(&self, id: String) -> Result<(), String> {
async fn delete(&self, id: i32) -> Result<(), String> {
let result = self.connection.execute(
"DELETE FROM Owner WHERE id = $1",
&[&id.parse::<i32>().unwrap()]
&[&id]
).await;
if let Ok(rows) = result {

View File

@ -13,7 +13,7 @@ impl RentRepository for PostgresRentRepository {
async fn create(&self, rent: BindingRent) -> Result<Rent, String> {
let query_result = self.connection.query(
"SELECT * FROM Rent WHERE car_id=$1 AND time_amount IS NULL",
&[&rent.car_id.parse::<i32>().unwrap()]
&[&rent.car_id]
).await;
if let Ok(rows) = query_result {
@ -27,36 +27,36 @@ impl RentRepository for PostgresRentRepository {
let result = self.connection.query(
"INSERT INTO Rent(time_amount, client_id, car_id) \
VALUES ($1, $2, $3) RETURNING *",
&[&rent.time_amount, &rent.client_id.parse::<i32>().unwrap(), &rent.car_id.parse::<i32>().unwrap()]
&[&rent.time_amount, &rent.client_id, &rent.car_id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).unwrap();
Ok(Rent {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
start_time: row.get("start_time"),
time_amount: row.get("time_amount"),
client_id: row.get::<&str, i32>("client_id").to_string(),
car_id: row.get::<&str, i32>("car_id").to_string()
client_id: row.get("client_id"),
car_id: row.get("car_id")
})
} else {
Err(result.unwrap_err().to_string())
}
}
async fn read(&self, id: String) -> Result<Rent, String> {
async fn read(&self, id: i32) -> Result<Rent, String> {
let result = self.connection.query(
"SELECT * FROM Rent WHERE id = $1", &[&id.parse::<i32>().unwrap()]
"SELECT * FROM Rent WHERE id = $1", &[&id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).ok_or("Rent not found".to_owned())?;
Ok(Rent {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
start_time: row.get("start_time"),
time_amount: row.get("time_amount"),
client_id: row.get::<&str, i32>("client_id").to_string(),
car_id: row.get::<&str, i32>("car_id").to_string()
client_id: row.get("client_id"),
car_id: row.get("car_id")
})
} else {
Err(result.unwrap_err().to_string())
@ -71,11 +71,11 @@ impl RentRepository for PostgresRentRepository {
if let Ok(rows) = result {
Ok(
rows.into_iter().map(|r| Rent {
id: r.get::<&str, i32>("id").to_string(),
id: r.get("id"),
start_time: r.get("start_time"),
time_amount: r.get("time_amount"),
client_id: r.get::<&str, i32>("client_id").to_string(),
car_id: r.get::<&str, i32>("car_id").to_string()
client_id: r.get("client_id"),
car_id: r.get("car_id")
}).collect()
)
} else {
@ -83,29 +83,29 @@ impl RentRepository for PostgresRentRepository {
}
}
async fn update(&self, id: String, rent: BindingRent) -> Result<Rent, String> {
async fn update(&self, id: i32, rent: BindingRent) -> Result<Rent, String> {
let result = self.connection.query(
"UPDATE Rent SET time_amount = $2, client_id = $3, car_id = $4 WHERE id = $5 RETURNING *",
&[&rent.time_amount, &rent.client_id.parse::<i32>().unwrap(), &rent.car_id.parse::<i32>().unwrap(), &id.parse::<i32>().unwrap()]
&[&rent.time_amount, &rent.client_id, &rent.car_id, &id]
).await;
if let Ok(rows) = result {
let row = rows.get(0).unwrap();
Ok(Rent {
id: row.get::<&str, i32>("id").to_string(),
id: row.get("id"),
start_time: row.get("start_time"),
time_amount: row.get("time_amount"),
client_id: row.get::<&str, i32>("client_id").to_string(),
car_id: row.get::<&str, i32>("car_id").to_string()
client_id: row.get("client_id"),
car_id: row.get("car_id")
})
} else {
Err(result.unwrap_err().to_string())
}
}
async fn delete(&self, id: String) -> Result<(), String> {
async fn delete(&self, id: i32) -> Result<(), String> {
let result = self.connection.execute(
"DELETE FROM Rent WHERE id = $1", &[&id.parse::<i32>().unwrap()]
"DELETE FROM Rent WHERE id = $1", &[&id]
).await;
if let Ok(_) = result {
@ -115,8 +115,8 @@ impl RentRepository for PostgresRentRepository {
}
}
async fn end_rent(&self, id: String) -> Result<(), String> {
let current_rent = self.read(id.clone()).await?;
async fn end_rent(&self, id: i32) -> Result<(), String> {
let current_rent = self.read(id).await?;
if current_rent.time_amount.is_some() {
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;
match self.connection.execute("UPDATE Rent SET time_amount = $2 WHERE id = $1", &[&id.parse::<i32>().unwrap(), &time_amount]).await {
match self.connection.execute("UPDATE Rent SET time_amount = $2 WHERE id = $1", &[&id, &time_amount]).await {
Ok(_) => Ok(()),
Err(err) => Err(err.to_string())
}

View File

@ -8,53 +8,53 @@ use async_trait::async_trait;
#[async_trait]
pub trait ClientRepository {
async fn create(&self, client: BindingClient) -> Result<Client, String>;
async fn read(&self, id: String) -> Result<Client, String>;
async fn read(&self, id: i32) -> Result<Client, String>;
async fn read_all(&self) -> Result<Vec<Client>, String>;
async fn update(&self, id: String, client: BindingClient) -> Result<Client, String>;
async fn delete(&self, id: String) -> Result<(), String>;
async fn update(&self, id: i32, client: BindingClient) -> Result<Client, String>;
async fn delete(&self, id: i32) -> Result<(), String>;
}
#[async_trait]
pub trait CarRepository {
async fn create(&self, car: BindingCar) -> Result<Car, String>;
async fn read(&self, id: String) -> Result<Car, String>;
async fn read(&self, id: i32) -> Result<Car, String>;
async fn read_all(&self) -> Result<Vec<Car>, String>;
async fn update(&self, id: String, car: BindingCar) -> Result<Car, String>;
async fn delete(&self, id: String) -> Result<(), String>;
async fn update(&self, id: i32, car: BindingCar) -> Result<Car, String>;
async fn delete(&self, id: i32) -> Result<(), String>;
async fn get_report(&self) -> Result<Vec<Report>, String>;
}
#[async_trait]
pub trait CarStationRepository {
async fn create(&self, car_station: BindingCarStation) -> Result<CarStation, String>;
async fn read(&self, id: String) -> Result<CarStation, String>;
async fn read(&self, id: i32) -> Result<CarStation, String>;
async fn read_all(&self) -> Result<Vec<CarStation>, String>;
async fn update(&self, id: String, car_station: BindingCarStation) -> Result<CarStation, String>;
async fn delete(&self, id: String) -> Result<(), String>;
async fn update(&self, id: i32, car_station: BindingCarStation) -> Result<CarStation, String>;
async fn delete(&self, id: i32) -> Result<(), String>;
}
#[async_trait]
pub trait RentRepository {
async fn create(&self, rent: BindingRent) -> Result<Rent, String>;
async fn read(&self, id: String) -> Result<Rent, String>;
async fn read(&self, id: i32) -> Result<Rent, String>;
async fn read_all(&self) -> Result<Vec<Rent>, String>;
async fn update(&self, id: String, rent: BindingRent) -> Result<Rent, String>;
async fn delete(&self, id: String) -> Result<(), String>;
async fn end_rent(&self, id: String) -> Result<(), String>;
async fn update(&self, id: i32, rent: BindingRent) -> Result<Rent, String>;
async fn delete(&self, id: i32) -> Result<(), String>;
async fn end_rent(&self, id: i32) -> Result<(), String>;
}
#[async_trait]
pub trait OwnerRepository {
async fn create(&self, client: BindingClient) -> Result<Client, String>;
async fn read(&self, id: String) -> Result<Client, String>;
async fn read(&self, id: i32) -> Result<Client, String>;
async fn read_all(&self) -> Result<Vec<Client>, String>;
async fn update(&self, id: String, client: BindingClient) -> Result<Client, String>;
async fn delete(&self, id: String) -> Result<(), String>;
async fn update(&self, id: i32, client: BindingClient) -> Result<Client, String>;
async fn delete(&self, id: i32) -> Result<(), String>;
}
#[async_trait]
pub trait AdministratorRepository {
async fn read(&self, id: String) -> Result<Administrator, String>;
async fn read(&self, id: i32) -> Result<Administrator, String>;
async fn find_by_username(&self, username: &str) -> Result<Administrator, String>;
}

View File

@ -1,429 +0,0 @@
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')

Binary file not shown.

View File

@ -1,18 +0,0 @@
{% 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 %}

View File

@ -1,36 +0,0 @@
<!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>

View File

@ -1,74 +0,0 @@
{% 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 %}

View File

@ -1,59 +0,0 @@
{% 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 %}

View File

@ -1,24 +0,0 @@
{% 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 %}

View File

@ -1,91 +0,0 @@
{% 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 %}

View File

@ -1,34 +0,0 @@
{% 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 %}

View File

@ -1,109 +0,0 @@
{% 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 %}

View File

@ -1,23 +0,0 @@
{% 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 %}