diff --git a/backend/Cargo.lock b/backend/Cargo.lock index d71ffe8..12fe3f3 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -30,7 +30,7 @@ dependencies = [ "actix-service", "actix-utils", "ahash 0.8.3", - "base64", + "base64 0.21.0", "bitflags", "brotli", "bytes", @@ -277,8 +277,10 @@ dependencies = [ "chrono", "dotenv", "dotenv_codegen", + "futures", + "mongodb", "nanoid", - "pbkdf2", + "pbkdf2 0.12.1", "rust_decimal", "rust_decimal_macros", "serde", @@ -288,6 +290,12 @@ 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" @@ -387,6 +395,28 @@ 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" @@ -572,6 +602,58 @@ 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" @@ -581,7 +663,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.0", "syn 1.0.109", ] @@ -634,6 +716,18 @@ 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" @@ -671,6 +765,21 @@ 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" @@ -687,6 +796,23 @@ 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" @@ -716,10 +842,13 @@ 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", @@ -774,6 +903,12 @@ 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" @@ -783,6 +918,12 @@ 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" @@ -792,6 +933,17 @@ 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" @@ -839,6 +991,23 @@ 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" @@ -859,6 +1028,24 @@ 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" @@ -889,6 +1076,12 @@ 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" @@ -904,6 +1097,12 @@ 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" @@ -941,6 +1140,27 @@ 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" @@ -983,6 +1203,53 @@ 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" @@ -1056,6 +1323,15 @@ 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" @@ -1128,7 +1404,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b7fa9f396f51dffd61546fd8573ee20592287996568e6175ceb0f8699ad75d" dependencies = [ - "base64", + "base64 0.21.0", "byteorder", "bytes", "fallible-iterator", @@ -1202,6 +1478,12 @@ 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" @@ -1282,6 +1564,31 @@ 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" @@ -1340,13 +1647,53 @@ 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", + "semver 1.0.17", +] + +[[package]] +name = "rustc_version_runtime" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d31b7153270ebf48bf91c65ae5b0c00e749c4cfad505f66530ac74950249582f" +dependencies = [ + "rustc_version 0.2.3", + "semver 0.9.0", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.0", ] [[package]] @@ -1367,18 +1714,43 @@ 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" @@ -1388,6 +1760,15 @@ 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" @@ -1405,6 +1786,7 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ + "indexmap", "itoa", "ryu", "serde", @@ -1422,6 +1804,39 @@ 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" @@ -1500,6 +1915,12 @@ 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" @@ -1510,6 +1931,12 @@ 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" @@ -1538,6 +1965,12 @@ 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" @@ -1553,6 +1986,26 @@ 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" @@ -1660,6 +2113,17 @@ 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" @@ -1668,6 +2132,7 @@ checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -1704,6 +2169,62 @@ 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" @@ -1737,6 +2258,12 @@ 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" @@ -1744,7 +2271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna", + "idna 0.3.0", "percent-encoding", ] @@ -1753,6 +2280,10 @@ 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" @@ -1826,6 +2357,41 @@ 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" @@ -1932,6 +2498,15 @@ 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" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 6ac8bec..8ba5111 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -19,4 +19,6 @@ 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" \ No newline at end of file +rust_decimal_macros = "1.29" +mongodb = { version = "2.5.0", features = ["bson-chrono-0_4"] } +futures = "0.3" \ No newline at end of file diff --git a/backend/src/main.rs b/backend/src/main.rs index 0bffacc..096b6fb 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -6,6 +6,8 @@ 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; @@ -13,6 +15,14 @@ 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::*; @@ -21,32 +31,52 @@ 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!("USERR"), 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 benchmark_repository = PostgresBenchmarkRepository { connection: Arc::clone(&client) }; + + let client_options = ClientOptions::parse(dotenv!("MONGO")).await.unwrap(); + + let client = Client::with_options(client_options).unwrap(); + + let db = Arc::new(client.database("default_db")); + + let car_repository = MongoCarRepository { database: Arc::clone(&db) }; + + let car_station_repository = MongoCarStationRepository { database: Arc::clone(&db) }; + + let client_repository = MongoClientRepository { database: Arc::clone(&db) }; + + let owner_repository = MongoOwnerRepository { database: Arc::clone(&db) }; + + let rent_repository = MongoRentRepository { database: Arc::clone(&db) }; + + let administrator_repository = MongoAdministratorRepository { database: Arc::clone(&db) }; + + let benchmark_repository = MongoBenchmarkRepository { database: Arc::clone(&db) }; let auth_service = services::auth::AuthService::new(); diff --git a/backend/src/models/car.rs b/backend/src/models/car.rs index 1ed9756..e5ab95d 100644 --- a/backend/src/models/car.rs +++ b/backend/src/models/car.rs @@ -1,7 +1,7 @@ use rust_decimal::Decimal; use serde::{Serialize, Deserialize}; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Car { pub id: String, pub brand: String, diff --git a/backend/src/storages/mongo/administrator.rs b/backend/src/storages/mongo/administrator.rs new file mode 100644 index 0000000..b2e0d74 --- /dev/null +++ b/backend/src/storages/mongo/administrator.rs @@ -0,0 +1,63 @@ +use mongodb::Database; +use mongodb::bson::oid::ObjectId; +use mongodb::bson::{doc, Document, Bson}; +use futures::stream::{TryStreamExt, StreamExt}; +use mongodb::options::FindOptions; +use std::sync::Arc; +use crate::models::administrator::*; +use crate::storages::traits::AdministratorRepository; +use async_trait::async_trait; + + +pub struct MongoAdministratorRepository { + pub database: Arc +} + +#[async_trait] +impl AdministratorRepository for MongoAdministratorRepository { + async fn read(&self, id: String) -> Result { + let collection = self.database.collection::("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 { + let collection = self.database.collection::("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()) + } + } +} \ No newline at end of file diff --git a/backend/src/storages/mongo/benchmark.rs b/backend/src/storages/mongo/benchmark.rs new file mode 100644 index 0000000..2338871 --- /dev/null +++ b/backend/src/storages/mongo/benchmark.rs @@ -0,0 +1,68 @@ +use chrono::Utc; +use mongodb::Database; +use mongodb::bson::oid::ObjectId; +use mongodb::bson::{doc, Document, Bson}; +use futures::stream::{TryStreamExt, StreamExt}; +use mongodb::options::{FindOptions, AggregateOptions}; +use rust_decimal::Decimal; +use rust_decimal::prelude::{ToPrimitive, FromPrimitive}; +use std::default; +use std::sync::Arc; +use async_trait::async_trait; + +use crate::storages::traits::BenchmarkRepository; + + +pub struct MongoBenchmarkRepository { + pub database: Arc +} + +#[async_trait] +impl BenchmarkRepository for MongoBenchmarkRepository { + async fn benchmark(&self) -> Result { + let collection = self.database.collection::("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()) + } +} \ No newline at end of file diff --git a/backend/src/storages/mongo/car.rs b/backend/src/storages/mongo/car.rs new file mode 100644 index 0000000..1836482 --- /dev/null +++ b/backend/src/storages/mongo/car.rs @@ -0,0 +1,221 @@ +use chrono::{Utc, Months}; +use mongodb::Database; +use mongodb::bson::oid::ObjectId; +use mongodb::bson::{doc, Document, Bson}; +use futures::stream::{TryStreamExt, StreamExt}; +use mongodb::options::{FindOptions, AggregateOptions}; +use rust_decimal::Decimal; +use rust_decimal::prelude::{ToPrimitive, FromPrimitive}; +use std::default; +use std::sync::Arc; +use crate::models::car::*; +use crate::storages::traits::*; +use async_trait::async_trait; + +use super::rent::MongoRentRepository; + + +pub struct MongoCarRepository { + pub database: Arc +} + +#[async_trait] +impl CarRepository for MongoCarRepository { + async fn create(&self, car: BindingCar) -> Result { + let collection = self.database.collection::("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 { + 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, String> { + let collection = self.database.collection::("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 { + let collection = self.database.collection::("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::("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, 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::("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::>() + // .await; + + Ok(reports) + } +} \ No newline at end of file diff --git a/backend/src/storages/mongo/car_station.rs b/backend/src/storages/mongo/car_station.rs new file mode 100644 index 0000000..612a145 --- /dev/null +++ b/backend/src/storages/mongo/car_station.rs @@ -0,0 +1,101 @@ +use mongodb::Database; +use mongodb::bson::oid::ObjectId; +use mongodb::bson::{doc, Document, Bson}; +use futures::stream::{TryStreamExt, StreamExt}; +use mongodb::options::FindOptions; +use std::sync::Arc; +use crate::models::car_station::*; +use crate::storages::traits::CarStationRepository; +use async_trait::async_trait; + + +pub struct MongoCarStationRepository { + pub database: Arc +} + +#[async_trait] +impl CarStationRepository for MongoCarStationRepository { + async fn create(&self, car_station: BindingCarStation) -> Result { + let collection = self.database.collection::("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 { + let collection = self.database.collection::("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, String> { + let collection = self.database.collection::("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 { + let collection = self.database.collection::("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::("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()) + } + } +} \ No newline at end of file diff --git a/backend/src/storages/mongo/client.rs b/backend/src/storages/mongo/client.rs new file mode 100644 index 0000000..beb4c07 --- /dev/null +++ b/backend/src/storages/mongo/client.rs @@ -0,0 +1,117 @@ +use mongodb::Database; +use mongodb::bson::oid::ObjectId; +use mongodb::bson::{doc, Document, Bson}; +use futures::stream::{TryStreamExt, StreamExt}; +use mongodb::options::{FindOptions, AggregateOptions}; +use rust_decimal::Decimal; +use rust_decimal::prelude::{ToPrimitive, FromPrimitive}; +use std::default; +use std::sync::Arc; +use crate::models::client::*; +use crate::storages::traits::ClientRepository; +use async_trait::async_trait; + + +pub struct MongoClientRepository { + pub database: Arc +} + +#[async_trait] +impl ClientRepository for MongoClientRepository { + async fn create(&self, client: BindingClient) -> Result { + let collection = self.database.collection::("clients"); + let document = doc! { + "name": client.name.clone(), + "surname": client.surname.clone(), + "middlename": client.middlename.clone(), + "phone": client.phone.clone(), + "rents": Bson::Array(Vec::::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 { + let collection = self.database.collection::("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, String> { + let collection = self.database.collection::("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 { + let collection = self.database.collection::("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::("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()) + } + } +} \ No newline at end of file diff --git a/backend/src/storages/mongo/mod.rs b/backend/src/storages/mongo/mod.rs index e69de29..c746ff0 100644 --- a/backend/src/storages/mongo/mod.rs +++ b/backend/src/storages/mongo/mod.rs @@ -0,0 +1,7 @@ +pub mod car_station; +pub mod administrator; +pub mod car; +pub mod client; +pub mod owner; +pub mod rent; +pub mod benchmark; \ No newline at end of file diff --git a/backend/src/storages/mongo/owner.rs b/backend/src/storages/mongo/owner.rs new file mode 100644 index 0000000..78c6099 --- /dev/null +++ b/backend/src/storages/mongo/owner.rs @@ -0,0 +1,129 @@ +use mongodb::Database; +use mongodb::bson::oid::ObjectId; +use mongodb::bson::{doc, Document, Bson}; +use futures::stream::{TryStreamExt, StreamExt}; +use mongodb::options::{FindOptions, AggregateOptions}; +use rust_decimal::Decimal; +use rust_decimal::prelude::{ToPrimitive, FromPrimitive}; +use std::default; +use std::sync::Arc; +use crate::models::client::*; +use crate::storages::traits::OwnerRepository; +use async_trait::async_trait; + + + +pub struct MongoOwnerRepository { + pub database: Arc +} + +#[async_trait] +impl OwnerRepository for MongoOwnerRepository { + async fn create(&self, owner: BindingClient) -> Result { + let collection = self.database.collection::("clients"); + let document = doc! { + "name": owner.name.clone(), + "surname": owner.surname.clone(), + "middlename": owner.middlename.clone(), + "phone": owner.phone.clone(), + "cars": Bson::Array(Vec::::new()), + "rents": Bson::Array(Vec::::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 { + let collection = self.database.collection::("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, String> { + let collection = self.database.collection::("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 { + let collection = self.database.collection::("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::("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()) + } + } +} diff --git a/backend/src/storages/mongo/rent.rs b/backend/src/storages/mongo/rent.rs new file mode 100644 index 0000000..4205dc1 --- /dev/null +++ b/backend/src/storages/mongo/rent.rs @@ -0,0 +1,153 @@ +use chrono::{NaiveDateTime, Utc}; +use mongodb::Database; +use mongodb::bson::oid::ObjectId; +use mongodb::bson::{doc, Document, Bson}; +use futures::stream::{TryStreamExt, StreamExt}; +use mongodb::options::{FindOptions, AggregateOptions, FindOneOptions}; +use rust_decimal::Decimal; +use rust_decimal::prelude::{ToPrimitive, FromPrimitive}; +use std::default; +use std::sync::Arc; +use crate::models::rent::*; +use crate::storages::traits::RentRepository; +use async_trait::async_trait; + + +pub struct MongoRentRepository { + pub database: Arc +} + +#[async_trait] +impl RentRepository for MongoRentRepository { + async fn create(&self, rent: BindingRent) -> Result { + let collection = self.database.collection::("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 { + let collection = self.database.collection::("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, String> { + let collection = self.database.collection::("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 { + let collection = self.database.collection::("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::("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::("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()) + } + } +} \ No newline at end of file