From fc87d5d019e023ec52b6ff15afc57dc007c9cf1a Mon Sep 17 00:00:00 2001 From: Daan Vanoverloop Date: Mon, 29 Aug 2022 20:08:16 +0200 Subject: [PATCH] Switch to MongoDB --- Cargo.lock | 515 +++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 +- Rocket.toml | 2 +- src/api/event.rs | 312 ++++++++++++++-------------- src/api/mod.rs | 8 +- src/api/user.rs | 97 +++++---- src/api/util.rs | 39 +++- src/main.rs | 24 +-- start_mongodb.sh | 6 + 9 files changed, 767 insertions(+), 239 deletions(-) create mode 100755 start_mongodb.sh diff --git a/Cargo.lock b/Cargo.lock index 7385107..272279a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,6 +48,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "android_system_properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e" +dependencies = [ + "libc", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -151,6 +160,25 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bson" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d76085681585d39016f4d3841eb019201fc54d2dd0d92ad1e4fab3bfb32754" +dependencies = [ + "ahash", + "base64", + "hex", + "indexmap", + "lazy_static", + "rand", + "serde", + "serde_bytes", + "serde_json", + "time 0.3.14", + "uuid 1.1.2", +] + [[package]] name = "bumpalo" version = "3.11.0" @@ -181,6 +209,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time 0.1.44", + "wasm-bindgen", + "winapi", +] + [[package]] name = "cipher" version = "0.3.0" @@ -204,10 +247,16 @@ dependencies = [ "rand", "sha2", "subtle", - "time", + "time 0.3.14", "version_check", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "cpufeatures" version = "0.2.4" @@ -318,6 +367,23 @@ dependencies = [ "parking_lot_core 0.9.3", ] +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "devise" version = "0.3.1" @@ -392,6 +458,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", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -580,7 +658,7 @@ checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -684,6 +762,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.8" @@ -742,6 +831,19 @@ dependencies = [ "want", ] +[[package]] +name = "iana-time-zone" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "js-sys", + "wasm-bindgen", + "winapi", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -785,6 +887,24 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ipconfig" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98" +dependencies = [ + "socket2", + "widestring", + "winapi", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + [[package]] name = "itertools" version = "0.10.3" @@ -817,6 +937,7 @@ dependencies = [ "futures", "lazy_static", "okapi", + "paste", "rocket", "rocket_db_pools", "rocket_okapi", @@ -824,7 +945,7 @@ dependencies = [ "serde_json", "sqlx", "thiserror", - "uuid", + "uuid 1.1.2", ] [[package]] @@ -850,6 +971,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "lock_api" version = "0.4.8" @@ -884,6 +1011,21 @@ dependencies = [ "tracing-subscriber", ] +[[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 = "matchers" version = "0.1.0" @@ -899,6 +1041,15 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest", +] + [[package]] name = "memchr" version = "2.5.0" @@ -925,10 +1076,56 @@ checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] +[[package]] +name = "mongodb" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95afe97b0c799fdf69cd960272a2cb9662d077bd6efd84eb722bb9805d47554" +dependencies = [ + "async-trait", + "base64", + "bitflags", + "bson", + "chrono", + "derivative", + "futures-core", + "futures-executor", + "futures-util", + "hex", + "hmac", + "lazy_static", + "md-5", + "os_info", + "pbkdf2", + "percent-encoding", + "rand", + "rustc_version_runtime", + "rustls 0.20.6", + "rustls-pemfile", + "serde", + "serde_bytes", + "serde_with", + "sha-1", + "sha2", + "socket2", + "stringprep", + "strsim", + "take_mut", + "thiserror", + "tokio", + "tokio-rustls 0.23.4", + "tokio-util", + "trust-dns-proto", + "trust-dns-resolver", + "typed-builder", + "uuid 0.8.2", + "webpki-roots 0.22.4", +] + [[package]] name = "multer" version = "2.0.3" @@ -959,6 +1156,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -1011,6 +1218,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "os_info" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5209b2162b2c140df493a93689e04f8deab3a67634f5bc7a553c0a98e5b8d399" +dependencies = [ + "log", + "winapi", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -1065,6 +1282,15 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22" +[[package]] +name = "pbkdf2" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +dependencies = [ + "digest", +] + [[package]] name = "pear" version = "0.2.3" @@ -1172,6 +1398,12 @@ dependencies = [ "yansi", ] +[[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.21" @@ -1273,6 +1505,16 @@ dependencies = [ "winapi", ] +[[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" @@ -1318,7 +1560,7 @@ dependencies = [ "serde_json", "state", "tempfile", - "time", + "time 0.3.14", "tokio", "tokio-stream", "tokio-util", @@ -1349,6 +1591,7 @@ version = "0.1.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bc154f4f4985a136e2d59c336474a56da02103993f5e637e3a5424971ee4eff" dependencies = [ + "mongodb", "rocket", "rocket_db_pools_codegen", "sqlx", @@ -1387,7 +1630,7 @@ dependencies = [ "smallvec", "stable-pattern", "state", - "time", + "time 0.3.14", "tokio", "uncased", ] @@ -1422,6 +1665,25 @@ dependencies = [ "syn", ] +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "rustc_version_runtime" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d31b7153270ebf48bf91c65ae5b0c00e749c4cfad505f66530ac74950249582f" +dependencies = [ + "rustc_version", + "semver", +] + [[package]] name = "rustls" version = "0.19.1" @@ -1431,8 +1693,29 @@ dependencies = [ "base64", "log", "ring", - "sct", - "webpki", + "sct 0.6.1", + "webpki 0.21.4", +] + +[[package]] +name = "rustls" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +dependencies = [ + "log", + "ring", + "sct 0.7.0", + "webpki 0.22.0", +] + +[[package]] +name = "rustls-pemfile" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" +dependencies = [ + "base64", ] [[package]] @@ -1494,6 +1777,31 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[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-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.144" @@ -1503,6 +1811,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.144" @@ -1531,11 +1848,45 @@ version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ + "indexmap", "itoa", "ryu", "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", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.2" @@ -1658,7 +2009,7 @@ dependencies = [ "once_cell", "paste", "percent-encoding", - "rustls", + "rustls 0.19.1", "serde", "sha2", "smallvec", @@ -1668,8 +2019,8 @@ dependencies = [ "thiserror", "tokio-stream", "url", - "webpki", - "webpki-roots", + "webpki 0.21.4", + "webpki-roots 0.21.1", ] [[package]] @@ -1702,7 +2053,7 @@ checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae" dependencies = [ "once_cell", "tokio", - "tokio-rustls", + "tokio-rustls 0.22.0", ] [[package]] @@ -1756,6 +2107,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 = "tempfile" version = "3.3.0" @@ -1799,6 +2156,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "time" version = "0.3.14" @@ -1869,9 +2237,20 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ - "rustls", + "rustls 0.19.1", "tokio", - "webpki", + "webpki 0.21.4", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.6", + "tokio", + "webpki 0.22.0", ] [[package]] @@ -1976,12 +2355,68 @@ dependencies = [ "tracing-log", ] +[[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", + "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 0.12.1", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "trust-dns-proto", +] + [[package]] name = "try-lock" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[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", +] + [[package]] name = "typenum" version = "1.15.0" @@ -2074,6 +2509,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", +] + [[package]] name = "uuid" version = "1.1.2" @@ -2082,6 +2526,7 @@ checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" dependencies = [ "getrandom", "rand", + "serde", "uuid-macro-internal", ] @@ -2124,6 +2569,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2204,15 +2655,40 @@ dependencies = [ "untrusted", ] +[[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.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" dependencies = [ - "webpki", + "webpki 0.21.4", ] +[[package]] +name = "webpki-roots" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" +dependencies = [ + "webpki 0.22.0", +] + +[[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" @@ -2321,6 +2797,15 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 03559c0..cddaefc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger", "rocket_db_pools futures = "0.3" lazy_static = "1.4" serde_json = "1" +paste = "1" [dependencies.sqlx] version = "*" @@ -23,7 +24,7 @@ features = ["macros", "offline", "migrate"] [dependencies.rocket_db_pools] version = "0.1.0-rc.2" -features = ["sqlx_sqlite"] +features = ["sqlx_sqlite", "mongodb"] [dependencies.uuid] version = "1.1.2" diff --git a/Rocket.toml b/Rocket.toml index 968e31f..e961372 100644 --- a/Rocket.toml +++ b/Rocket.toml @@ -1,2 +1,2 @@ [default.databases.party] -url = "party.sqlite" +url = "mongodb://root:example@localhost:27017" diff --git a/src/api/event.rs b/src/api/event.rs index 4e52d70..05bbb51 100644 --- a/src/api/event.rs +++ b/src/api/event.rs @@ -1,181 +1,177 @@ use std::collections::HashMap; -use sqlx::FromRow; +use futures::TryStreamExt; +use rocket::serde::json::json; +use rocket_db_pools::mongodb::{bson::to_bson, options::InsertOneOptions}; +use serde_json::Value; use super::{prelude::*, util::PartyError}; -api_routes!(); +api_routes!( + create_event, + stop_event, + get_event, + update_event, + get_all_events, + event_outcome, +); -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] #[serde(crate = "rocket::serde")] pub struct EventOutcome { - points: HashMap, + points: HashMap, } -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, FromRow)] -#[serde(crate = "rocket::serde")] -pub struct FreeForAllGame {} - -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, FromRow)] -#[serde(crate = "rocket::serde")] -pub struct TeamGame {} - -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, FromRow)] -#[serde(crate = "rocket::serde")] -pub struct Test {} - -// # Event -// -// An event in which participants can win or lose points +/// # Event +/// +/// An event in which participants can win or lose points #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[serde(crate = "rocket::serde")] -pub enum Event { - FreeForAllGame(FreeForAllGame), - TeamGame(TeamGame), - Test(Test), -} - -pub struct EventRecord { - id: i64, - name: String, - event_type: String, - event_id: i64, -} - -macro_rules! dispatch { - ($event:ident) => { - dispatch!($event, - free_for_all_game => FreeForAllGame, - team_game => TeamGame, - test => Test, - ) - }; - ($event:ident, $($event_type:ident => $event_struct:ident),* $(,)?) => { - match $event.event_type.as_str() { - $(stringify!($event_type) => dispatch_run!($event_type, $event_struct),)* - _ => return Err(PartyError::Unknown("invalid event type".into())), - } - }; -} - -macro_rules! reverse_dispatch { - ($event:ident) => { - reverse_dispatch!($event, - FreeForAllGame => free_for_all_game, - TeamGame => team_game, - Test => test, - ) - }; - ($event:ident, $($event_struct:ident => $event_type:ident),* $(,)?) => { - match $event { - $(Event::$event_struct(e) => reverse_dispatch_run!($event_type, $event_struct, e),)* - } - }; -} - -impl EventRecord { - pub async fn get(db: &mut Connection, id: i64) -> Result { - Ok(sqlx::query_as!( - EventRecord, - "SELECT id, name, event_type, event_id FROM events WHERE id = ?", - id - ) - .fetch_one(&mut **db) - .await?) - } - - pub async fn remove(&self, db: &mut Connection) -> Result<(), PartyError> { - macro_rules! dispatch_run { - ($event_type:ident, $event_struct:ident) => {{ - sqlx::query(&format!( - "DELETE FROM events_{} WHERE id = {}", - stringify!($event_type), - self.event_id - )) - .fetch_one(&mut **db) - .await?; - }}; - } - - dispatch!(self); - - sqlx::query!("DELETE FROM events WHERE id = ?", self.id) - .execute(&mut **db) - .await?; - - Ok(()) - } +pub struct Event { + /// Unique identifier of the event. This will be randomly generated and cannot be modified. + #[serde(default = "uuid")] + id: String, + /// Has this event concluded? + concluded: bool, + /// Event type + event_type: EventType, } impl Event { - pub async fn register( - &self, - db: &mut Connection, - name: String, - ) -> Result { - let event_id = match self { - Self::FreeForAllGame(e) => { - unimplemented!() - /* - sqlx::query!("INSERT INTO events_free_for_all_game () VALUES ()") - .execute(&mut **db) - .await? - .last_insert_rowid() - */ - } - Self::TeamGame(e) => { - unimplemented!() - /* - sqlx::query!("INSERT INTO events_team_game () VALUES ()") - .execute(&mut **db) - .await? - .last_insert_rowid() - */ - } - Self::Test(e) => sqlx::query!("INSERT INTO events_test () VALUES ()") - .execute(&mut **db) - .await? - .last_insert_rowid(), - }; - - macro_rules! reverse_dispatch_run { - ($event_type:ident, $event_struct:ident, $inner:ident) => { - sqlx::query!( - "INSERT INTO events (name, event_type, event_id) VALUES (?, ?, ?)", - stringify!($event_type), - name, - event_id - ) - .execute(&mut **db) - .await? - }; + pub fn outcome(&self) -> EventOutcome { + match self.event_type { + _ => EventOutcome::default(), } - - let id = reverse_dispatch!(self).last_insert_rowid(); - Ok(EventRecord::get(db, id).await?) } - pub async fn get(db: &mut Connection, record: EventRecord) -> Result { - macro_rules! dispatch_run { - ($event_type:ident, $event_struct:ident) => { - Event::$event_struct( - sqlx::query_as::<_, $event_struct>(&format!( - "SELECT id, name, event_type, event_id FROM events_{} WHERE id = {}", - stringify!($event_type), - record.event_id - )) - .fetch_one(&mut **db) - .await?, - ) - }; - } - - Ok(dispatch!(record)) - } + pub fn conclude(&self) {} } +fn uuid() -> String { + uuid::Uuid::new_v4().to_string() +} + +/// # EventType +/// +/// Enumeration of all different types of events +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(crate = "rocket::serde")] +pub enum EventType { + FreeForAllGame {}, + TeamGame {}, + Test {}, +} + +/// # Create event +/// +/// If an `id` is supplied, it will be replaced by a randomly generated +/// one. +/// +/// Returns the created event. #[openapi(tag = "Event")] +#[openapi(tag = "Event")] +#[post("/", data = "")] +pub async fn create_event( + _api_key: ApiKey, + db: Connection, + mut event: Json, +) -> Result, PartyError> { + event.id = uuid(); + event.concluded = false; + db.events().insert_one(&*event, None).await?; + Ok(event) +} + +/// # Update event +/// +/// Update the supplied values in the event with the given id. The `id` of an event cannot be +/// changed and `concluded` will always be false. +#[openapi(tag = "Event")] +#[post("/", data = "")] +pub async fn update_event( + _api_key: ApiKey, + db: Connection, + id: String, + mut event: Json, // Use serde_json::Value to allow a partial struct +) -> Result { + if let Some(i) = event.get_mut("id") { + *i = json! { &id }; + } + if let Some(i) = event.get_mut("concluded") { + *i = json! { false }; + } + db.users() + .update_one(doc! { "id": &id }, doc! { "$set": to_bson(&*event)? }, None) + .await?; + + Ok(Status::Ok) +} + +/// # Get event +/// +/// Returns the event with the given id +#[openapi(tag = "Event")] +#[get("/")] +pub async fn get_event( + _api_key: ApiKey, + db: Connection, + id: String, +) -> Result, PartyError> { + Ok(Json( + db.events() + .find_one(doc! { "id": &id }, None) + .await? + .ok_or(PartyError::EventNotFound(id))?, + )) +} + +/// # Get events +/// +/// Returns the event with the given id +#[openapi(tag = "Event")] +#[get("/")] +pub async fn get_all_events( + _api_key: ApiKey, + db: Connection, +) -> Result>, PartyError> { + Ok(Json( + db.events().find(doc! {}, None).await?.try_collect().await?, + )) +} + +/// # Get outcome +/// +/// Returns the outcome of a concluded event. +#[openapi(tag = "Event")] +#[get("//outcome")] +pub async fn event_outcome( + _api_key: ApiKey, + db: Connection, + id: String, +) -> Result, PartyError> { + let event = db + .events() + .find_one(doc! { "id": &id }, None) + .await? + .ok_or(PartyError::EventNotFound(id.clone()))?; + Ok(Json(event.outcome())) +} + +/// # Stop event #[openapi(tag = "Event")] #[post("//stop")] -pub fn stop_event(id: i64) -> Result, PartyError> { - todo!() +pub async fn stop_event( + _api_key: ApiKey, + db: Connection, + id: String, +) -> Result, PartyError> { + let event = db + .events() + .find_one(doc! { "id": &id }, None) + .await? + .ok_or(PartyError::EventNotFound(id.clone()))?; + event.conclude(); + db.users() + .update_one(doc! { "id": &id }, doc! { "concluded": true }, None) + .await?; + Ok(Json(event.outcome())) } diff --git a/src/api/mod.rs b/src/api/mod.rs index 7c99554..9398aff 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -13,8 +13,12 @@ mod prelude { response::status, serde::{json::Json, Deserialize, Serialize}, }; - pub use rocket_db_pools::{sqlx, Connection}; + pub use rocket_db_pools::{ + mongodb::bson::{doc, Document}, + sqlx, Connection, + }; pub use rocket_okapi::{openapi, JsonSchema}; + pub use util::MongoClientExt; } #[macro_export] @@ -40,7 +44,7 @@ macro_rules! mount_endpoints { ) -> Rocket { mount_endpoints_and_merged_docs! { building_rocket, "/api".to_owned(), openapi_settings, - $(stringify!("/", $endpoint) => $endpoint::get_routes_and_docs(&openapi_settings),)* + $(concat!("/", stringify!($endpoint)) => $endpoint::get_routes_and_docs(&openapi_settings),)* }; building_rocket } diff --git a/src/api/user.rs b/src/api/user.rs index fff8140..daf3662 100644 --- a/src/api/user.rs +++ b/src/api/user.rs @@ -1,5 +1,6 @@ use super::prelude::*; -use sqlx::FromRow; +use futures::{StreamExt, TryStreamExt}; +use rocket_db_pools::mongodb::options::FindOptions; use util::{Ordering, PartyError}; api_routes!( @@ -14,17 +15,15 @@ api_routes!( /// # User /// /// A user that represents a person participating in the LAN party -#[derive(Clone, Debug, FromForm, Serialize, Deserialize, JsonSchema, FromRow)] +#[derive(Clone, Debug, FromForm, Serialize, Deserialize, JsonSchema)] #[serde(crate = "rocket::serde")] pub struct User { /// Name of the user name: String, /// Score of the user - #[serde(default)] score: i64, /// Unique identifier of the user - #[serde(default)] - id: i64, + id: String, } /// # Create new user with the give name @@ -34,19 +33,17 @@ pub struct User { #[post("/", data = "")] pub async fn add_user( _api_key: ApiKey, - mut db: Connection, + db: Connection, name: Json<&str>, ) -> Result>, PartyError> { - let result = sqlx::query!("INSERT INTO users (name) VALUES (?)", *name) - .execute(&mut *db) - .await?; - let user = User { - id: result.last_insert_rowid(), + id: uuid::Uuid::new_v4().to_string(), score: 0, name: name.to_string(), }; + db.users().insert_one(&user, None).await?; + Ok(status::Created::new("/").body(Json(user))) } @@ -55,12 +52,10 @@ pub async fn add_user( #[delete("/")] pub async fn delete_user( _api_key: ApiKey, - mut db: Connection, - id: i64, + db: Connection, + id: String, ) -> Result { - sqlx::query!("DELETE FROM users where (id = ?)", id) - .execute(&mut *db) - .await?; + db.users().delete_one(doc! { "id": id }, None).await?; Ok(Status::Ok) } @@ -72,12 +67,14 @@ pub async fn delete_user( #[get("/")] pub async fn get_user( _api_key: ApiKey, - mut db: Connection, - id: i64, + db: Connection, + id: String, ) -> Result, PartyError> { - let user = sqlx::query_as!(User, "SELECT id, name, score FROM users WHERE (id = ?)", id) - .fetch_one(&mut *db) - .await?; + let user = db + .users() + .find_one(doc! { "id": &id }, None) + .await? + .ok_or(PartyError::UserNotFound(id))?; Ok(Json(user)) } @@ -113,17 +110,31 @@ impl ToString for UserSort { #[get("/?&")] pub async fn get_all_users( _api_key: ApiKey, - mut db: Connection, + db: Connection, sort: Option, order: Option, ) -> Result>, PartyError> { - let users = sqlx::query_as::<_, User>(&format!( - "SELECT id, name, score FROM users ORDER BY {} {}", - sort.unwrap_or(UserSort::Id).to_string(), - order.unwrap_or(Ordering::Asc).to_string() - )) - .fetch_all(&mut *db) - .await?; + let options = if let Some(sort) = sort { + let order = order.unwrap_or(Ordering::Asc); + let mut doc = Document::new(); + doc.insert( + sort.to_string(), + match order { + Ordering::Asc => 1, + Ordering::Desc => -1, + }, + ); + FindOptions::builder().sort(doc).build() + } else { + FindOptions::builder().build() + }; + + let users = db + .users() + .find(doc! {}, Some(options)) + .await? + .try_collect() + .await?; Ok(Json(users)) } @@ -133,13 +144,18 @@ pub async fn get_all_users( #[post("//score", data = "")] pub async fn set_score( _api_key: ApiKey, - mut db: Connection, - id: i64, + db: Connection, + id: String, score: Json, ) -> Result { - sqlx::query!("UPDATE users SET score = ? WHERE id = ?", *score, id) - .execute(&mut *db) + db.users() + .update_one( + doc! { "id": id }, + doc! { "$set": { "score": *score } }, + None, + ) .await?; + Ok(Status::Ok) } @@ -150,11 +166,14 @@ pub async fn set_score( #[get("//score")] pub async fn get_score( _api_key: ApiKey, - mut db: Connection, - id: i64, + db: Connection, + id: String, ) -> Result, PartyError> { - let score = sqlx::query_scalar!("SELECT score FROM users WHERE id = ?", id) - .fetch_one(&mut *db) - .await?; - Ok(Json(score)) + Ok(Json( + db.users() + .find_one(doc! { "id": &id }, None) + .await? + .ok_or(PartyError::UserNotFound(id))? + .score, + )) } diff --git a/src/api/util.rs b/src/api/util.rs index 51eb57f..a457e15 100644 --- a/src/api/util.rs +++ b/src/api/util.rs @@ -1,8 +1,11 @@ use rocket::{http::Status, response, response::Responder, Request}; +use rocket_db_pools::mongodb; use rocket_okapi::response::OpenApiResponderInner; use schemars::JsonSchema; use thiserror::Error; +use super::{event::Event, user::User}; + /// # Ordering /// /// Ordering of data in an array, ascending or descending @@ -28,7 +31,9 @@ impl ToString for Ordering { #[derive(Error, Debug)] pub enum PartyError { #[error("user `{0}` does not exist")] - UserNotFound(i64), + UserNotFound(String), + #[error("event `{0}` does not exist")] + EventNotFound(String), #[error("unknown error: {0}")] Unknown(String), #[error("invalid parameter: {0}")] @@ -43,6 +48,16 @@ pub enum PartyError { #[from] source: sqlx::Error, }, + #[error("mongodb error {source:?}")] + MongodbError { + #[from] + source: mongodb::error::Error, + }, + #[error("failed to serialize bson: {source:?}")] + BsonSerializationError { + #[from] + source: mongodb::bson::ser::Error, + }, } impl OpenApiResponderInner for PartyError { @@ -56,9 +71,29 @@ impl OpenApiResponderInner for PartyError { impl<'r, 'o: 'r> Responder<'r, 'o> for PartyError { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { match self { - Self::UserNotFound(_) => Status::NotFound, + Self::UserNotFound(_) | Self::EventNotFound(_) => Status::NotFound, _ => Status::InternalServerError, } .respond_to(req) } } + +pub trait MongoClientExt { + fn party_db(&self) -> mongodb::Database; + fn users(&self) -> mongodb::Collection; + fn events(&self) -> mongodb::Collection; +} + +impl MongoClientExt for mongodb::Client { + fn party_db(&self) -> mongodb::Database { + self.database("lan_party") + } + + fn users(&self) -> mongodb::Collection { + self.party_db().collection("users") + } + + fn events(&self) -> mongodb::Collection { + self.party_db().collection("events") + } +} diff --git a/src/main.rs b/src/main.rs index f4a835f..c467229 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ mod api; -use rocket::{fairing, fairing::AdHoc, Build, Rocket}; -use rocket_db_pools::{sqlx, Database}; +use rocket_db_pools::{mongodb, Database}; use rocket_okapi::{ - mount_endpoints_and_merged_docs, openapi, + openapi, rapidoc::*, settings::{OpenApiSettings, UrlObject}, swagger_ui::{make_swagger_ui, SwaggerUIConfig}, @@ -14,7 +13,7 @@ extern crate rocket; #[derive(Database)] #[database("party")] -pub struct Db(sqlx::SqlitePool); +pub struct Db(mongodb::Client); #[openapi] #[get("/")] @@ -22,27 +21,10 @@ fn index() -> String { format!("Hello, world!") } -async fn run_migrations(rocket: Rocket) -> fairing::Result { - match Db::fetch(&rocket) { - Some(db) => match sqlx::migrate!("db/migrations").run(&**db).await { - Ok(_) => { - println!("Migrations completed"); - Ok(rocket) - } - Err(e) => { - error!("Failed to initialize SQLx database: {}", e); - Err(rocket) - } - }, - None => Err(rocket), - } -} - #[launch] fn rocket() -> _ { let building_rocket = rocket::build() .attach(Db::init()) - .attach(AdHoc::try_on_ignite("SQLx Migrations", run_migrations)) .mount("/", routes![index]) .mount( "/swagger", diff --git a/start_mongodb.sh b/start_mongodb.sh new file mode 100755 index 0000000..eddbe30 --- /dev/null +++ b/start_mongodb.sh @@ -0,0 +1,6 @@ + podman run -d --rm --name lan_party_db \ + -v lan-party-db:/data/db:z \ + -p 27017:27017 \ + -e MONGO_INITDB_ROOT_USERNAME=root \ + -e MONGO_INITDB_ROOT_PASSWORD=example \ + docker.io/mongo:latest