diff --git a/api/Cargo.lock b/api/Cargo.lock index 49388f9..0548642 100644 --- a/api/Cargo.lock +++ b/api/Cargo.lock @@ -331,8 +331,11 @@ dependencies = [ "chrono", "derive_more", "dotenv", + "env_logger", "futures", "futures-util", + "mongodb", + "musty", "once_cell", "reqwest", "serde", @@ -341,14 +344,14 @@ dependencies = [ "thiserror", "tokio", "tokio-stream", - "uuid", + "uuid 1.2.2", ] [[package]] name = "api-macro" version = "0.1.0" dependencies = [ - "darling", + "darling 0.14.2", "proc-macro2", "quote", "syn", @@ -431,7 +434,7 @@ checksum = "4fa579c7cea32030600994d579554b257e10d5ad87705f3d150b49ee08bd629d" dependencies = [ "Inflector", "async-graphql-parser", - "darling", + "darling 0.14.2", "proc-macro-crate", "proc-macro2", "quote", @@ -560,7 +563,7 @@ dependencies = [ "serde_bytes", "serde_json", "time 0.3.17", - "uuid", + "uuid 1.2.2", ] [[package]] @@ -767,14 +770,38 @@ dependencies = [ "syn", ] +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + [[package]] name = "darling" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.14.2", + "darling_macro 0.14.2", +] + +[[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", ] [[package]] @@ -791,13 +818,41 @@ dependencies = [ "syn", ] +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn", +] + [[package]] name = "darling_macro" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" dependencies = [ - "darling_core", + "darling_core 0.14.2", + "quote", + "syn", +] + +[[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", ] @@ -811,7 +866,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.0", "syn", ] @@ -823,6 +878,7 @@ checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -840,6 +896,52 @@ 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 = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -1039,6 +1141,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1048,12 +1156,41 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +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" @@ -1088,6 +1225,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.23" @@ -1155,6 +1298,17 @@ 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" @@ -1185,12 +1339,46 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +dependencies = [ + "libc", + "windows-sys 0.42.0", +] + +[[package]] +name = "ipconfig" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" +dependencies = [ + "socket2", + "widestring", + "winapi", + "winreg", +] + [[package]] name = "ipnet" version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys 0.42.0", +] + [[package]] name = "itoa" version = "1.0.4" @@ -1242,6 +1430,18 @@ 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 = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "local-channel" version = "0.1.3" @@ -1279,6 +1479,36 @@ 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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] + [[package]] name = "memchr" version = "2.5.0" @@ -1312,6 +1542,52 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "mongodb" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a1df476ac9541b0e4fdc8e2cc48884e66c92c933cd17a1fd75e68caf75752e" +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", + "rustls-pemfile", + "serde", + "serde_bytes", + "serde_with", + "sha-1", + "sha2", + "socket2", + "stringprep", + "strsim", + "take_mut", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "trust-dns-proto", + "trust-dns-resolver", + "typed-builder", + "uuid 0.8.2", + "webpki-roots", +] + [[package]] name = "multer" version = "2.0.4" @@ -1326,10 +1602,40 @@ dependencies = [ "log", "memchr", "mime", - "spin", + "spin 0.9.4", "version_check", ] +[[package]] +name = "musty" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eba224c3912cb9922a8426a0f8c44951e24fefbc180b8b4040f3730b8a0ada1" +dependencies = [ + "anyhow", + "async-graphql", + "async-trait", + "bson", + "futures", + "mongodb", + "musty-proc-macro", + "serde", + "thiserror", +] + +[[package]] +name = "musty-proc-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0eb36ed05e03204642b9d9eb7fc6f91701af93062bc5047343c4bc681d8389d" +dependencies = [ + "darling 0.14.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -1373,7 +1679,7 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -1428,6 +1734,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "os_info" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4750134fb6a5d49afc80777394ad5d95b04bc12068c6abb92fae8f43817270f" +dependencies = [ + "log", + "winapi", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -1457,6 +1773,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +[[package]] +name = "pbkdf2" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +dependencies = [ + "digest", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -1508,6 +1833,30 @@ dependencies = [ "toml", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.47" @@ -1517,6 +1866,12 @@ dependencies = [ "unicode-ident", ] +[[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" @@ -1628,13 +1983,92 @@ dependencies = [ "winreg", ] +[[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 0.5.2", + "untrusted", + "web-sys", + "winapi", +] + +[[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.14", +] + +[[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 = "rustix" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.42.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 = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" +dependencies = [ + "base64", ] [[package]] @@ -1665,6 +2099,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "security-framework" version = "2.7.0" @@ -1688,12 +2132,27 @@ dependencies = [ "libc", ] +[[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.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +[[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.147" @@ -1747,6 +2206,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 0.13.4", + "proc-macro2", + "quote", + "syn", +] + +[[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" @@ -1758,6 +2250,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -1792,6 +2295,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spin" version = "0.9.4" @@ -1804,12 +2313,28 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.103" @@ -1821,6 +2346,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" @@ -1958,6 +2489,17 @@ dependencies = [ "tokio", ] +[[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-stream" version = "0.1.11" @@ -2019,12 +2561,68 @@ 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 = "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" @@ -2064,6 +2662,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" @@ -2071,10 +2675,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna", + "idna 0.3.0", "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.2.2" @@ -2195,6 +2808,31 @@ dependencies = [ "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" diff --git a/api/Cargo.toml b/api/Cargo.toml index 95dc711..81f7ae2 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -30,4 +30,7 @@ async-stream = "0.3.0" futures-util = "0.3.0" serde_json = "1.0.87" actix-cors = "0.6.4" -api-macro = { path = "../api-macro" } \ No newline at end of file +api-macro = { path = "../api-macro" } +musty = { version = "0.5.2", features = ["graphql"] } +mongodb = "2" +env_logger = "0.10.0" \ No newline at end of file diff --git a/api/src/auth/actor.rs b/api/src/auth/actor.rs index bd7d37f..a59cae7 100644 --- a/api/src/auth/actor.rs +++ b/api/src/auth/actor.rs @@ -1,11 +1,11 @@ use actix_web::{web, HttpRequest}; use async_graphql::Context; +use musty::Model; +use mongodb::bson::doc; use serde::{Deserialize, Serialize}; use crate::{ - model::user::User, - store::{DataStore, DataStoreEntry}, - types::id::Id, + model::user::User, Database, }; use super::action::Action; @@ -14,35 +14,31 @@ use super::action::Action; #[serde(untagged)] pub enum Actor { None, - User(Id), + User(User), Internal, } impl Actor { - pub fn identify(user_store: web::Data>, request: HttpRequest) -> Self { + pub async fn identify(db: &Database, request: HttpRequest) -> Self { if let Some(identifier) = request.headers().get("Authorization") { let identifier = identifier.to_str().unwrap(); - return Self::identify_with_token(user_store, identifier); + return Self::identify_with_token(db, identifier).await; } Self::None } - pub fn identify_with_token( - user_store: web::Data>, + pub async fn identify_with_token( + db: &Database, identifier: impl ToString, ) -> Self { let identifier = identifier.to_string(); if identifier == option_env!("API_TOKEN").unwrap_or("ORBT_INTERNAL") { return Self::Internal; } else { - if let Some(user) = user_store - .data - .lock() - .unwrap() - .values() - .find(|u| u.token.check(&identifier)) + let user = User::find_one(&db, doc! { "token": identifier }).await; + if let Ok(Some(user)) = user { - return Self::User(user.id.clone()); + return Self::User(user); } } Self::None @@ -73,15 +69,11 @@ impl Actor { } } - pub fn user<'ctx>( + pub async fn user<'ctx>( self, ctx: &Context<'ctx>, - ) -> async_graphql::Result> { - let Self::User(user_id) = self else { return Err("Requires 'user' actor type.".into()) }; - let user_store = ctx.data::>()?; - let user = user_store - .get(user_id) - .ok_or::("User not found.".into())?; + ) -> async_graphql::Result { + let Self::User(user) = self else { return Err("Requires 'user' actor type.".into()) }; Ok(user) } } @@ -90,7 +82,7 @@ impl Actor { mod tests { use super::*; - #[test] + /* #[test] fn actor_identify_with_token() { let user = User::new("tester".to_string()); let user_store = DataStore::::new(); @@ -101,5 +93,5 @@ mod tests { } else { panic!("Expected Actor::User, got {:?}", actor); } - } + }*/ } diff --git a/api/src/auth/authority.rs b/api/src/auth/authority.rs index ec91f46..be553be 100644 --- a/api/src/auth/authority.rs +++ b/api/src/auth/authority.rs @@ -1,18 +1,20 @@ -use async_graphql::{Context, Error}; +use async_graphql::{Context, Error, async_trait::async_trait}; +use musty::Model; use crate::{ - model::{user::User, Model, room::Room}, - store::{DataStore, DataStoreEntry}, + model::{room::Room, user::User}, Database, }; use super::{action::Action, actor::Actor}; +#[async_trait] pub trait Authority { fn require_act(&self, action: impl Action, model: &M) -> Result; - fn user(&self) -> Result, Error>; - fn room(&self) -> Result, Error>; + async fn user(&self) -> Result; + async fn room(&self) -> Result; } +#[async_trait] impl Authority for Context<'_> { fn require_act(&self, action: impl Action, model: &M) -> Result { let Ok(actor) = self.data::() else { return Err("Not authenticated".into()) }; @@ -26,37 +28,38 @@ impl Authority for Context<'_> { } } - fn user(&self) -> Result, Error> { - User::from_context(&self) + + async fn user(&self) -> Result { + User::from_context(&self).await } - fn room(&self) -> Result, Error> { - Room::from_context(&self) + async fn room(&self) -> Result { + Room::from_context(&self).await } } -pub trait FromContext where Self: Model { - fn from_context<'a>(ctx: &Context<'a>) -> async_graphql::Result>; +#[async_trait] +pub trait FromContext where Self: Sized { + async fn from_context<'a>(ctx: &Context<'a>) -> async_graphql::Result; } +#[async_trait] impl FromContext for User { - fn from_context<'a>(ctx: &Context<'a>) -> async_graphql::Result> { + async fn from_context<'a>(ctx: &Context<'a>) -> async_graphql::Result { let actor = ctx.data::()?; - let Actor::User(user_id) = actor else { return Err("Requires 'user' actor type.".into()) }; - let user_store = ctx.data::>()?; - let user = user_store - .get(user_id) - .ok_or::("User not found.".into())?; - Ok(user) + let Actor::User(user) = actor else { return Err("Requires 'user' actor type.".into()) }; + Ok(user.clone()) } } +#[async_trait] impl FromContext for Room { - fn from_context<'a>(ctx: &Context<'a>) -> async_graphql::Result> { + async fn from_context<'a>(ctx: &Context<'a>) -> async_graphql::Result { let actor = ctx.data::()?; - let Actor::User(user_id) = actor else { return Err("Requires 'user' actor type.".into()) }; - let room_store = ctx.data::>()?; - let room = Room::get_by_member_user_id(room_store, user_id).ok_or::("User is not in a room".into())?; + let Actor::User(user) = actor else { return Err("Requires 'user' actor type.".into()) }; + let db = ctx.data::()?; + let room = Room::get_by_member_user_id(&db, &user.id).await + .ok_or::("User is not in a room".into())?; Ok(room) } } diff --git a/api/src/lib.rs b/api/src/lib.rs index 2953bff..249dba7 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -4,7 +4,6 @@ mod auth; mod model; mod schema; mod server; -mod store; mod stream; mod types; @@ -24,21 +23,16 @@ pub mod prelude { use async_graphql::Schema; use model::{room::Room, user::User}; -use store::DataStore; +use server::OrbtData; use stream::StreamControl; +pub type Database = musty::Musty; + pub async fn start_api_server(callback: Option Fut>) -> std::io::Result<()> where Fut: Future, { - let user_store = DataStore::::new(); - let room_store = DataStore::::new(); let stream_user_room_ctl = StreamControl::::new(); - let orbt_data = server::OrbtData::new( - user_store.clone(), - room_store.clone(), - stream_user_room_ctl.clone(), - ); let address = std::env::var("ADDRESS").unwrap_or("0.0.0.0".to_string()); let port = std::env::var("PORT").unwrap_or("8080".to_string()); @@ -62,6 +56,15 @@ where } } + let db = musty::prelude::Musty::new(mongodb::Client::with_uri_str( + &std::env::var("MONGODB_URI").unwrap_or_else(|f| "mongodb://localhost:27017".to_string()), + ) + .await + .unwrap() + .database("orbt")); + + let app_data = OrbtData::new(db.clone(), stream_user_room_ctl.clone()); + let server = HttpServer::new(move || { App::new() .wrap( @@ -70,9 +73,8 @@ where .allow_any_method() .allow_any_origin(), ) - .app_data(Data::new(orbt_data.clone())) - .app_data(Data::new(user_store.clone())) - .app_data(Data::new(room_store.clone())) + .app_data(Data::new(app_data.clone())) + .app_data(Data::new(db.clone())) .app_data(Data::new(stream_user_room_ctl.clone())) .service( web::resource("/") diff --git a/api/src/main.rs b/api/src/main.rs index 43f56a3..beee21c 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -8,6 +8,8 @@ use dotenv::dotenv; #[tokio::main] async fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "debug"); + env_logger::init(); dotenv().ok(); api::start_api_server(Some(|| async { diff --git a/api/src/model/mod.rs b/api/src/model/mod.rs index 0eeb3c9..2ed4512 100644 --- a/api/src/model/mod.rs +++ b/api/src/model/mod.rs @@ -1,9 +1,2 @@ -use crate::types::id::Id; - pub mod room; -pub mod user; - -pub trait Model: Send + Sync + Clone + 'static { - const NODE_SUFFIX: &'static str; - fn model_id(&self) -> &Id; -} \ No newline at end of file +pub mod user; \ No newline at end of file diff --git a/api/src/model/room.rs b/api/src/model/room.rs index 7ab5918..86ea6df 100644 --- a/api/src/model/room.rs +++ b/api/src/model/room.rs @@ -1,28 +1,28 @@ -use api_macro::Model; use async_graphql::{connection::*, ComplexObject, Context, Error, SimpleObject}; use crate::{ auth::authority::Authority, - store::{DataStore, DataStoreEntry}, types::{ color::{Color, ColorType}, - id::{Id}, time::Time, token::Token, }, + Database }; -use super::{user::User, Model}; +use musty::prelude::{Id, model, Model}; +use super::{user::User}; +use mongodb::bson::{doc, Bson}; pub const MAX_ROOM_SIZE: usize = 5; pub const INVITE_EXPIRY_MINUTES: usize = 5; -#[derive(Debug, Clone, SimpleObject, Model)] -#[model(node_suffix = "r")] +#[derive(Clone, SimpleObject)] #[graphql(complex)] +#[model(mongo())] pub struct Room { #[graphql(skip)] - pub id: Id, + pub id: String, pub name: String, pub owner: Option>, pub members: Vec, @@ -34,13 +34,9 @@ pub struct Room { #[ComplexObject] impl Room { - pub async fn id(&self) -> Id { - self.id.clone() - } - async fn get_my_member<'ctx>(&self, ctx: &Context<'ctx>) -> async_graphql::Result { - let user = ctx.user()?; - if let Some(member) = self.members.iter().find(|m| &m.user == &user.id) { + let user = ctx.user().await?; + if let Some(member) = self.members.iter().find(|m| &m.id == &user.id) { return async_graphql::Result::Ok(member.clone()); } else { return async_graphql::Result::Err("You are not a member of this room".into()); @@ -98,7 +94,7 @@ impl Room { impl Room { pub fn new(name: String) -> Self { Self { - id: Id::new(), + id: "hi".to_string().into(), name, owner: None, members: vec![], @@ -126,7 +122,7 @@ impl Room { } pub fn join(&mut self, user: &User, color: Option) -> Result { - if self.members.iter().any(|m| m.user == user.id) { + if self.members.iter().any(|m| m.id == user.id) { return Err("User already in room".into()); } if let Some(color) = color { @@ -140,23 +136,21 @@ impl Room { } pub fn leave(&mut self, user: &Id) -> Result { - if let Some(index) = self.members.iter().position(|m| &m.user == user) { + if let Some(index) = self.members.iter().position(|m| &m.id == user) { let room_member = self.members.remove(index); return Ok(room_member); } Err("User not in room".into()) } - pub fn is_member_user(&self, id: &Id) -> bool { - self.members.iter().any(|m| &m.user == id) - } - - pub fn is_member(&self, id: &Id) -> bool { + pub fn is_member(&self, id: &Id) -> bool { self.members.iter().any(|m| &m.id == id) } pub fn is_connected(&self, id: &Id) -> bool { - self.get_member_by_user_id(id).map(|m| m.connected).unwrap_or(false) + self.get_member(id) + .map(|m| m.connected) + .unwrap_or(false) } pub fn is_owner(&self, id: &Id) -> bool { @@ -168,7 +162,7 @@ impl Room { } pub fn can_pass_remote(&self, from: &Id, to: &Id) -> bool { - if !self.is_member_user(to) { + if !self.is_member(to) { return false; } if let Some(owner) = &self.owner { @@ -184,14 +178,17 @@ impl Room { false } - pub fn pass_remote(&mut self, from: &Id, to: Id) -> Result<(), Error> { - if !self.is_member_user(&from) || !self.is_member_user(&to) { + pub fn pass_remote(&mut self, from: &Id, to: &Id) -> Result<(), Error> { + if !self.is_member(&from) || !self.is_member(&to) { return Err("User not in room".into()); } + if !self.can_pass_remote(&from, &to) { return Err("You don't have the remote".into()); } - self.remote = Some(to); + + self.remote = Some(to.clone()); + Ok(()) } @@ -213,24 +210,16 @@ impl Room { !self.members.iter().any(|m| m.color.name == color) } - pub fn get_member_by_user_id(&self, id: &Id) -> Option<&RoomMember> { - self.members.iter().find(|m| &m.user == id) - } - - pub fn get_member_by_user_id_mut(&mut self, id: &Id) -> Option<&mut RoomMember> { - self.members.iter_mut().find(|m| &m.user == id) - } - - pub fn get_member(&self, id: &Id) -> Option<&RoomMember> { + pub fn get_member(&self, id: &Id) -> Option<&RoomMember> { self.members.iter().find(|m| &m.id == id) } - pub fn get_member_mut(&mut self, id: &Id) -> Option<&mut RoomMember> { + pub fn get_member_mut(&mut self, id: &Id) -> Option<&mut RoomMember> { self.members.iter_mut().find(|m| &m.id == id) } pub fn create_invite(&mut self, user_id: Id) -> Result { - if !self.is_member_user(&user_id) { + if !self.is_member(&user_id) { return Err("User not in room".into()); } @@ -250,7 +239,7 @@ impl Room { pub fn revoke_invite(&mut self, token: impl ToString) -> Result<(), Error> { let token = token.to_string(); - if let Some(index) = self.invites.iter().position(|i| i.token.check(&token)) { + if let Some(index) = self.invites.iter().position(|i| i.token.validate(&token)) { self.invites.remove(index); return Ok(()); } @@ -259,7 +248,7 @@ impl Room { pub fn check_invite(&self, token: impl ToString) -> bool { let token = token.to_string(); - self.invites.iter().any(|i| i.token.check(&token)) + self.invites.iter().any(|i| i.token.validate(&token)) } pub fn is_invited_or_owner(&self, user: &User, token: Option) -> bool { @@ -281,60 +270,37 @@ impl Room { author: &User, msg: impl ToString, ) -> Result { - if !self.is_member_user(&author.id) { + if !self.is_member(&author.id) { return Err("User not in room".into()); } - let msg = RoomChatMsg::new( - Id::new(), - author.id.clone(), - msg.to_string(), - Time::now(), - ); + let msg = RoomChatMsg::new(author.id.clone(), msg.to_string(), Time::now()); Ok(msg) } } // helpers impl Room { - pub fn any_room bool>(room_store: &DataStore, func: F) -> bool { - room_store.data.lock().unwrap().values().any(|r| func(r)) - } - - pub fn find_room bool>( - room_store: &DataStore, - func: F, - ) -> Option> { - let id = room_store - .data - .lock() - .unwrap() - .values() - .find(|r| func(r)) - .map(|r| r.id.clone()); - if let Some(id) = id { - room_store.get(&id) - } else { - None - } + pub async fn get_by_member_user_id<'a>( + db: &Database, + user: &'a Id, + ) -> Option { + let id: Bson = user.try_into().unwrap(); + Room::find_one(&db, doc!{ "members.user": id }).await.unwrap() } - pub fn get_by_member_user_id<'a>(room_store: &'a DataStore, user: &'a Id) -> Option> { - Self::find_room(room_store, |r| r.is_member_user(user)) - } - - pub fn get_by_member<'a>(room_store: &'a DataStore, id: &'a Id) -> Option> { - Self::find_room(room_store, |r| r.is_member(id)) + pub async fn get_by_invite_token<'a>( + db: &Database, + token: &'a str, + ) -> Option { + Room::find_one(&db, doc!{ "invites.token": token }).await.unwrap() } } -#[derive(Debug, Clone, SimpleObject, Model)] -#[model(node_suffix = "rm")] +#[derive(Debug, Clone, SimpleObject, serde::Serialize, serde::Deserialize)] #[graphql(complex)] pub struct RoomMember { #[graphql(skip)] - pub id: Id, - #[graphql(skip)] - pub user: Id, + pub id: Id, pub color: Color, pub connected: bool, pub typing: bool, @@ -343,8 +309,7 @@ pub struct RoomMember { impl RoomMember { pub fn new(user: Id, room: &Room, color: Option) -> Self { Self { - id: Id::new(), - user, + id: user, color: color.unwrap_or(room.pick_available_color()).into(), connected: false, typing: false, @@ -354,29 +319,27 @@ impl RoomMember { #[ComplexObject] impl RoomMember { - pub async fn id(&self) -> Id { + pub async fn id(&self) -> Id { self.id.clone() } async fn user(&self, ctx: &Context<'_>) -> async_graphql::Result { - let user_store = ctx.data::>()?; - let user = user_store.get(&self.user).ok_or("User not found")?; - Ok(user.clone()) + let db = ctx.data::()?; + let user = User::get_by_id(&db, self.id.clone()).await?; + Ok(user.unwrap()) } } -#[derive(Debug, Clone, SimpleObject)] +#[derive(Debug, Clone, SimpleObject, serde::Serialize, serde::Deserialize)] pub struct RoomChatMsg { - pub id: Id, pub author: Id, pub msg: String, pub time: Time, } impl RoomChatMsg { - pub fn new(id: Id, author: Id, msg: String, time: Time) -> Self { + pub fn new(author: Id, msg: String, time: Time) -> Self { Self { - id, author, msg, time, @@ -384,14 +347,14 @@ impl RoomChatMsg { } } -impl Model for RoomChatMsg { +/*impl Model for RoomChatMsg { const NODE_SUFFIX: &'static str = "msg"; fn model_id(&self) -> &Id { &self.id } -} +}*/ -#[derive(Debug, Clone, SimpleObject)] +#[derive(Debug, Clone, SimpleObject, serde::Serialize, serde::Deserialize)] pub struct RoomInvite { pub token: Token, pub inviter: Id, // user id @@ -434,8 +397,8 @@ mod tests { fn room_join() { let (room, owner, friend) = create_room_with_members(); assert_eq!(room.members.len(), 2); - assert!(room.is_member_user(&owner.id)); - assert!(room.is_member_user(&friend.id)); + assert!(room.is_member(&owner.id)); + assert!(room.is_member(&friend.id)); } #[test] @@ -443,7 +406,7 @@ mod tests { let (mut room, owner, friend) = create_room_with_members(); room.leave(&friend.id).unwrap(); assert_eq!(room.members.len(), 1); - assert!(room.is_member_user(&owner.id)); - assert!(!room.is_member_user(&friend.id)); + assert!(room.is_member(&owner.id)); + assert!(!room.is_member(&friend.id)); } } diff --git a/api/src/model/user.rs b/api/src/model/user.rs index 86aa32b..1c4eebe 100644 --- a/api/src/model/user.rs +++ b/api/src/model/user.rs @@ -1,22 +1,20 @@ -use api_macro::Model; use async_graphql::*; -use serde::{Deserialize, Serialize}; use crate::{ - types::{ - id::{Id}, - token::Token, - }, auth::authority::Authority, + auth::authority::Authority, + types::{token::Token}, Database, }; use super::room::{Room, RoomMember}; +use mongodb::bson::{doc, Bson}; +use musty::{prelude::{model, Id}, Model}; -#[derive(Debug, Clone, SimpleObject, Serialize, Deserialize, Model)] -#[model(node_suffix = "u")] +#[derive(Clone, SimpleObject)] #[graphql(complex)] +#[model(mongo())] pub struct User { #[graphql(skip)] - pub id: Id, + pub id: String, pub name: String, //#[graphql(skip)] pub token: Token, @@ -25,27 +23,36 @@ pub struct User { impl User { pub fn new(name: String) -> Self { Self { - id: Id::new(), + id: name.to_string().into(), name, token: Token::new_with_length(16), } } + + pub async fn get_owned_room(&self, db: &Database) -> Option { + let id: &Bson = &self.id.clone().try_into().unwrap(); + + let room = Room::find_one(&db, doc! {"owner": &id }).await.unwrap(); + room + } } #[ComplexObject] impl User { - pub async fn id(&self) -> Id { - self.id.clone() + async fn id<'ctx>(&self) -> Id { + self.id.clone().into() } async fn room<'ctx>(&self, ctx: &Context<'ctx>) -> Result { - let room = ctx.room()?; + let room = ctx.room().await?; Ok(room.clone()) } async fn room_member<'ctx>(&self, ctx: &Context<'ctx>) -> Result { - let room = ctx.room()?; - let member = room.get_member_by_user_id(&self.id).ok_or::("You are not in a room".into())?; + let room = ctx.room().await?; + let member = room + .get_member(&self.id) + .ok_or::("You are not in a room".into())?; Ok(member.clone()) } } diff --git a/api/src/schema/mod.rs b/api/src/schema/mod.rs index 05d76aa..f091c22 100644 --- a/api/src/schema/mod.rs +++ b/api/src/schema/mod.rs @@ -1,11 +1,9 @@ pub mod room; pub mod user; + use crate::auth::authority::Authority; -use crate::model::Model; use crate::model::room::RoomMember; -use crate::model::{user::User, room::Room}; -use crate::store::DataStore; -use crate::types::id::{Id}; +use crate::model::{room::Room, user::User}; use self::room::RoomAction; use self::{ @@ -15,14 +13,14 @@ use self::{ use async_graphql::*; #[derive(MergedObject, Default)] -pub struct Query(UserQuery, RoomQuery, NodeQuery); +pub struct Query(UserQuery, RoomQuery, /*NodeQuery*/); #[derive(MergedObject, Default)] pub struct Mutation(UserMutation, RoomMutation); #[derive(MergedSubscription, Default)] pub struct Subscription(RoomSubscription); - +/* #[derive(Interface, Clone)] #[graphql(field(name = "id", type = "Id"))] pub enum Node { @@ -82,4 +80,5 @@ impl NodeQuery { async fn node<'ctx>(&self, ctx: &Context<'ctx>, id: Id) -> Option { Node::fetch_node(id, ctx).ok().flatten() } -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/api/src/schema/room.rs b/api/src/schema/room.rs index f396833..8ee0704 100644 --- a/api/src/schema/room.rs +++ b/api/src/schema/room.rs @@ -1,14 +1,16 @@ -use async_graphql::*; +use async_graphql::{*, async_trait::async_trait}; use futures::Stream; use crate::{ auth::{action::Action, actor::Actor, authority::Authority}, model::{room::Room, user::User}, - store::DataStore, stream::{StreamControl, Subscriber}, - types::{color::ColorType, id::Id, token::Token}, + types::{color::ColorType, token::Token}, + Database, }; +use musty::prelude::{Id, Model}; + #[derive(Default)] pub struct RoomQuery; @@ -20,23 +22,25 @@ pub struct RoomSubscription; #[Object] impl RoomQuery { - async fn room<'ctx>(&self, ctx: &Context<'ctx>, room: Id) -> Result> { - let room_store = ctx.data::>()?; - let room = room_store.get(&room); + async fn room<'ctx>(&self, ctx: &Context<'ctx>, id: Id) -> Result> { + let db = ctx.data::()?; + let room = Room::get_by_id(&db, id).await?; + if let Some(room) = &room { ctx.require_act(RoomAction::Get, &*room)?; } - Ok(room.as_deref().cloned()) + + Ok(room) } } #[Object] impl RoomMutation { async fn create_room<'ctx>(&self, ctx: &Context<'ctx>, name: Option) -> Result { - let user = ctx.user()?; - let room_store = ctx.data::>()?; + let user: User = ctx.user().await?; + let db = ctx.data::()?; - if Room::any_room(room_store, |r| r.is_owner(&user.id)) { + if user.get_owned_room(&db).await.is_some() { return Err("You have already created a room".into()); } @@ -51,45 +55,49 @@ impl RoomMutation { let mut room = Room::new(room_name); room.init_owner(&user); + room.save(&db).await?; - room_store.insert(room.clone()); Ok(room) } async fn send_chat_message<'ctx>(&self, ctx: &Context<'ctx>, msg: String) -> Result { - let user = ctx.user()?; - let mut room = ctx.room()?; + let db = ctx.data::()?; + let user = ctx.user().await?; + let mut room = ctx.room().await?; ctx.require_act(RoomAction::SendChat, &room)?; let room_chat_msg = room.create_chat_msg(&user, msg)?; room.add_chat_msg(room_chat_msg); + room.save(&db).await?; let stream_ctl = ctx.data::>()?; stream_ctl.publish(room.clone()); - Ok(room.clone()) + Ok(room) } async fn pass_room_remote<'ctx>(&self, ctx: &Context<'ctx>, to_user: Id) -> Result { - let user = ctx.user()?; - let mut room = ctx.room()?; + let db = ctx.data::()?; + let user = ctx.user().await?; + let mut room = ctx.room().await?; ctx.require_act(RoomAction::PassRemote(&to_user), &room)?; - if !room.is_member_user(&user.id) { + if !room.is_member(&user.id) { return Err("You are not a member of that room".into()); } - if !room.is_member_user(&to_user) { + if !room.is_member(&to_user) { return Err("That user is not a member of the room".into()); } - room.pass_remote(&user.id, to_user.clone())?; + room.pass_remote(&user.id, &to_user)?; + room.save(&db).await?; let stream_ctl = ctx.data::>()?; stream_ctl.publish(room.clone()); - Ok(room.clone()) + Ok(room) } async fn join_room<'ctx>( @@ -98,44 +106,39 @@ impl RoomMutation { invite_token: Option, color: Option, ) -> Result { - let room_store = ctx.data::>()?; - - let user = ctx.user()?; + let db = ctx.data::()?; + let user = ctx.user().await?; if user.name.len() == 0 { return Err("You must set a name before joining a room".into()); } - if Room::any_room(room_store, |r| r.is_member_user(&user.id)) { + if Room::get_by_member_user_id(&db, &user.id).await.is_some() { return Err("You are already a member of another room".into()); } - let room_id = if let Some(invite_token) = invite_token { - Room::find_room(room_store, |r| r.check_invite(&invite_token)) - .map(|r| r.id.clone()) - .ok_or::("Room not found".into())? + let mut room = if let Some(invite_token) = invite_token { + Room::get_by_invite_token(&db, &invite_token).await.ok_or::("Room not found".into()) } else { - Room::find_room(room_store, |r| r.is_owner(&user.id)) - .map(|r| r.id.clone()) - .ok_or::("You are not the owner of any room".into())? - }; - - let Some(mut room) = room_store.get(&room_id) else { return Err("Room not found".into()) }; + user.get_owned_room(&db).await.ok_or::("Room not found".into()) + }?; if room.members.len() >= crate::model::room::MAX_ROOM_SIZE { return Err("Room is full".into()); }; room.join(&user, color)?; + room.save(&db).await?; let stream_ctl = ctx.data::>()?; stream_ctl.publish(room.clone()); - Ok(room.clone()) + Ok(room) } async fn leave_room<'ctx>(&self, ctx: &Context<'ctx>) -> Result { - let user = ctx.user()?; - let mut room = ctx.room()?; + let db = ctx.data::()?; + let user = ctx.user().await?; + let mut room = ctx.room().await?; if room.is_owner(&user.id) { return Err("You cannot leave a room you own".into()); @@ -143,41 +146,48 @@ impl RoomMutation { ctx.require_act(RoomAction::Leave, &room)?; room.leave(&user.id)?; + room.save(&db).await?; let stream_ctl = ctx.data::>()?; stream_ctl.disconnect(&user.id); stream_ctl.publish(room.clone()); - Ok(user.clone()) + Ok(user) } async fn create_room_invite<'ctx>(&self, ctx: &Context<'ctx>) -> Result { - let user = ctx.user()?; - let mut room = ctx.room()?; + let db = ctx.data::()?; + let user = ctx.user().await?; + let mut room = ctx.room().await?; ctx.require_act(RoomAction::CreateInvite, &room)?; let room_invite = room.create_invite(user.id.clone())?; + room.save(&db).await?; + let stream_ctl = ctx.data::>()?; stream_ctl.publish(room.clone()); - Ok(room_invite.token.clone()) + Ok(room_invite.token) } async fn revoke_room_invite<'ctx>(&self, ctx: &Context<'ctx>, invite: String) -> Result { - let mut room = ctx.room()?; + let db = ctx.data::()?; + let mut room = ctx.room().await?; ctx.require_act(RoomAction::RevokeInvite(&invite), &room)?; room.revoke_invite(&invite)?; + room.save(&db).await?; let stream_ctl = ctx.data::>()?; stream_ctl.publish(room.clone()); - Ok(room.clone()) + Ok(room) } async fn kick_member<'ctx>(&self, ctx: &Context<'ctx>, member: Id) -> Result { - let mut room = ctx.room()?; + let db = ctx.data::()?; + let mut room = ctx.room().await?; ctx.require_act(RoomAction::KickMember(&member), &room)?; if room.is_owner(&member) { @@ -185,28 +195,32 @@ impl RoomMutation { } let room_member = room.leave(&member)?; + room.save(&db).await?; let stream_ctl = ctx.data::>()?; - stream_ctl.disconnect(&room_member.user); + stream_ctl.disconnect(&room_member.id); stream_ctl.publish(room.clone()); - Ok(room.clone()) + Ok(room) } async fn set_typing_status<'ctx>(&self, ctx: &Context<'ctx>, typing: bool) -> Result { - let user = ctx.user()?; - let mut room = ctx.room()?; + let db = ctx.data::()?; + let user = ctx.user().await?; + let mut room = ctx.room().await?; ctx.require_act(RoomAction::SetTypingStatus, &room)?; - let Some(member) = room.get_member_by_user_id_mut(&user.id) else { return Err("You are not in this room".into()) }; + let Some(member) = room.get_member_mut(&user.id) else { return Err("You are not in this room".into()) }; member.typing = typing; + room.save(&db).await?; + let stream_ctl = ctx.data::>()?; stream_ctl.publish(room.clone()); - Ok(room.clone()) + Ok(room) } } @@ -216,17 +230,17 @@ impl RoomSubscription { &'ctx self, ctx: &Context<'ctx>, ) -> Result + 'ctx> { - let room_store = ctx.data::>()?; - let mut room = ctx.room()?; + let db = ctx.data::()?; + let mut room = ctx.room().await?; let user = ctx .require_act(RoomAction::SubscribeChat, &room)? - .user(ctx)?; + .user(ctx).await?; let room_id = room.id.clone(); // room id let stream_ctl = ctx.data::>()?; - let Some(room_member) = room.get_member_by_user_id_mut(&user.id) else { return Err("You are not a member of this room".into()) }; + let Some(room_member) = room.get_member_mut(&user.id) else { return Err("You are not a member of this room".into()) }; if room_member.connected { return Err("You are already subscribed to this room".into()); } @@ -236,8 +250,8 @@ impl RoomSubscription { Ok(stream_ctl.subscribe(UserRoomSubscriber::new( user.id.clone(), room_id, - room_store.clone(), - stream_ctl.clone(), + db.clone(), + stream_ctl.clone() ))) } } @@ -245,7 +259,7 @@ impl RoomSubscription { pub struct UserRoomSubscriber { user: Id, room: Id, - room_store: DataStore, + db: Database, stream_ctl: StreamControl, } @@ -253,18 +267,19 @@ impl UserRoomSubscriber { pub fn new( user: Id, room: Id, - room_store: DataStore, + db: Database, stream_ctl: StreamControl, ) -> Self { Self { user, room, - room_store, + db, stream_ctl, } } } +#[async_trait] impl Subscriber for UserRoomSubscriber { fn subscriber_id(&self) -> &Id { &self.user @@ -274,9 +289,13 @@ impl Subscriber for UserRoomSubscriber { &self.room } - fn on_disconnect(&mut self) { - let Some(mut room) = self.room_store.get(&self.room) else { return }; - let Some(room_member) = room.get_member_by_user_id_mut(&self.user) else { return }; + async fn on_disconnect(&mut self) { + let db = &self.db; + let id = self.user.clone(); + let room = self.room.clone(); + + let Some(mut room) = Room::get_by_id(&db, room).await.unwrap() else { return }; + let Some(room_member) = room.get_member_mut(&id) else { return }; room_member.connected = false; room_member.typing = false; if let Some(remote) = room.remote.as_ref() { @@ -284,7 +303,7 @@ impl Subscriber for UserRoomSubscriber { room.remote = room.owner.clone(); } } - self.stream_ctl.publish(room.clone()) + self.stream_ctl.publish(room); } fn map_msg(&self, msg: Room) -> Option { @@ -314,42 +333,42 @@ impl<'a> Action for RoomAction<'a> { match actor { Actor::None => false, Actor::Internal => true, - Actor::User(user_id) => match self { + Actor::User(user) => match self { Self::Get | Self::SendChat | Self::SubscribeChat | Self::SubscribeMembers | Self::SubscribeRemote - | Self::Leave => room.is_member_user(user_id), - Self::GetMember(id) => room.is_member_user(user_id) && room.is_member_user(id), - Self::PassRemote(to_user_id) => room.can_pass_remote(user_id, to_user_id), + | Self::Leave => room.is_member(&user.id), + Self::GetMember(id) => room.is_member(&user.id) && room.is_member(id), + Self::PassRemote(to_user_id) => room.can_pass_remote(&user.id, to_user_id), Self::Join(token) => { room.check_invite(token) && room.members.len() < crate::model::room::MAX_ROOM_SIZE } Self::CreateInvite => { - room.is_member_user(user_id) - /*&& !room - .invites - .iter() - .any(|i| &i.inviter == user_id && i.token.is_valid())*/ + room.is_member(&user.id) + /*&& !room + .invites + .iter() + .any(|i| &i.inviter == user_id && i.token.is_valid())*/ } Self::RevokeInvite(token) => { - let invite_exists = room.invites.iter().any(|i| i.token.check(&token)); - let is_member_or_owner = room.is_member_user(user_id) || room.is_owner(user_id); - let is_owner_or_created_invite = room.is_owner(user_id) + let invite_exists = room.invites.iter().any(|i| i.token.validate(&token)); + let is_member_or_owner = room.is_member(&user.id) || room.is_owner(&user.id); + let is_owner_or_created_invite = room.is_owner(&user.id) || room .invites .iter() - .any(|i| &i.inviter == user_id && i.token.check(&token)); + .any(|i| &i.inviter == &user.id && i.token.validate(&token)); invite_exists && is_member_or_owner && is_owner_or_created_invite } - Self::KickMember(id) => room.is_owner(user_id) && room.is_member_user(id), + Self::KickMember(id) => room.is_owner(&user.id) && room.is_member(id), Self::SetTypingStatus => { - room.is_member_user(user_id) + room.is_member(&user.id) && room - .get_member_by_user_id(user_id) + .get_member(&user.id) .map(|m| m.connected) .unwrap_or(false) } diff --git a/api/src/schema/user.rs b/api/src/schema/user.rs index b5b9519..125bfb6 100644 --- a/api/src/schema/user.rs +++ b/api/src/schema/user.rs @@ -1,5 +1,6 @@ -use crate::{auth::authority::Authority, model::user::User, store::DataStore, types::id::Id}; +use crate::{auth::authority::Authority, model::user::User, Database}; use async_graphql::*; +use musty::prelude::{Model, Id}; #[derive(Default)] pub struct UserQuery; @@ -10,31 +11,33 @@ pub struct UserMutation; #[Object] impl UserQuery { async fn user<'ctx>(&self, ctx: &Context<'ctx>, id: Id) -> Result> { - let user_store = ctx.data::>()?; - let user = user_store.get(&id); - Ok(user.as_deref().cloned()) + let db = ctx.data::()?; + Ok(User::get_by_id(&db, id).await?) } async fn me<'ctx>(&self, ctx: &Context<'ctx>) -> Result { - let user = ctx.user()?; - Ok(user.clone()) + let user = ctx.user().await?; + Ok(user) } } #[Object] impl UserMutation { async fn create_user<'ctx>(&self, ctx: &Context<'ctx>, name: Option) -> Result { + let db = ctx.data::()?; let name = name.unwrap_or("".to_string()); - let user_store = ctx.data::>()?; - let user = User::new(name); - user_store.insert(user.clone()); + let mut user = User::new(name); + user.save(&db).await?; Ok(user) } async fn set_user_name<'ctx>(&self, ctx: &Context<'ctx>, name: String) -> Result { - let mut user = ctx.user()?; + let db = ctx.data::()?; + let mut user = ctx.user().await?; user.name = name; - // saved implicitly when dropped - Ok(user.clone()) + + user.save(&db).await?; + + Ok(user) } } diff --git a/api/src/server.rs b/api/src/server.rs index 1c9900a..bb3d574 100644 --- a/api/src/server.rs +++ b/api/src/server.rs @@ -1,15 +1,16 @@ use actix_web::{web, HttpRequest, HttpResponse}; +use async_graphql::extensions::ApolloTracing; use async_graphql::http::{playground_source, GraphQLPlaygroundConfig}; use async_graphql::{Data, Schema}; use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse, GraphQLSubscription}; -use async_graphql::extensions::ApolloTracing; +use crate::Database; use crate::auth::actor::Actor; use crate::model::room::Room; use crate::model::user::User; use crate::schema::{Mutation, Query, Subscription}; -use crate::store::DataStore; use crate::stream::StreamControl; +use musty::prelude::Backend; #[derive(Clone)] pub struct OrbtData { @@ -18,8 +19,7 @@ pub struct OrbtData { impl OrbtData { pub fn new( - user_store: DataStore, - room_store: DataStore, + db: Database, stream_user_room_ctl: StreamControl, ) -> Self { Self { @@ -28,8 +28,7 @@ impl OrbtData { Mutation::default(), Subscription::default(), ) - .data(user_store) - .data(room_store) + .data(db) .data(stream_user_room_ctl) .extension(ApolloTracing) .finish(), @@ -39,13 +38,13 @@ impl OrbtData { pub async fn graphql_root( data: web::Data, - user_store: web::Data>, + db: web::Data, http: HttpRequest, req: GraphQLRequest, ) -> GraphQLResponse { let mut inner = req.into_inner(); - let actor = Actor::identify(user_store, http); + let actor = Actor::identify(&db, http).await; inner.data.insert(actor); data.schema.execute(inner).await.into() @@ -61,12 +60,12 @@ pub async fn graphql_playground() -> actix_web::Result { pub async fn graphql_ws( orbt_data: web::Data, - user_store: web::Data>, + db: web::Data, http: HttpRequest, payload: web::Payload, ) -> actix_web::Result { GraphQLSubscription::new(orbt_data.schema.clone()) - .on_connection_init(|value| on_connection_init(user_store, value)) + .on_connection_init(|value| on_connection_init(db, value)) .start(&http, payload) } @@ -76,13 +75,13 @@ pub async fn graphql_ws( } */ async fn on_connection_init( - user_store: web::Data>, + db: web::Data, value: serde_json::Value, ) -> async_graphql::Result { let Some(value) = value.as_object() else { return Err("connection_init payload must be an object".into()) }; let Some(authorization) = value.get("Authorization") else { return Err("connection_init payload must have 'Authorization' key".into()) }; let Some(token) = authorization.as_str() else { return Err("connection_init payload key 'Authorization' must have a string value".into()) }; - let actor = Actor::identify_with_token(user_store, token); + let actor = Actor::identify_with_token(&db, token).await; let mut data = Data::default(); data.insert(actor); Ok(data) diff --git a/api/src/store/mod.rs b/api/src/store/mod.rs deleted file mode 100644 index 9883059..0000000 --- a/api/src/store/mod.rs +++ /dev/null @@ -1,150 +0,0 @@ -use crate::model::Model; -use crate::types::id::{ToId, Id}; -use std::ops::{Deref, DerefMut}; -use std::sync::{Mutex, MutexGuard}; -use std::{collections::HashMap, sync::Arc}; - -#[derive(Debug, Clone)] -pub struct DataStore { - pub data: Arc, M>>>, -} - -impl DataStore { - pub fn new() -> Self { - Self { - data: Arc::new(Mutex::new(HashMap::new())), - } - } - - pub fn get<'a>( - &'a self, - id: impl ToId, - ) -> Option> { - let mut lock = self.data.lock().unwrap(); - let Some(id) = id.to_id() else { return None }; - let item = lock.remove(&id); - if let Some(model) = item { - let entry = DataStoreEntry::new(id, model, true, lock); - return Some(entry) - } - None - } - - pub fn insert(&self, model: M) { - (&*self.data) - .lock() - .unwrap() - .insert(::model_id(&model).clone(), model); - } - - pub fn delete>(&self, id: &I) -> Option<()> { - let mut lock = self.data.lock().unwrap(); - let id = id.to_id()?; - lock.remove(&id); - Some(()) - } -} - -#[derive(Debug)] -pub struct DataStoreEntry<'a, M: Model> { - id: Id, - model: Option, - save: bool, - lock: MutexGuard<'a, HashMap, M>>, -} - -impl<'a, M: Model> DataStoreEntry<'a, M> { - fn new( - id: Id, - model: M, - save: bool, - lock: MutexGuard<'a, HashMap, M>>, - ) -> Self { - Self { - id, - model: Some(model), - save, - lock, - } - } -} - -impl<'a, M: Model> Deref for DataStoreEntry<'a, M> { - type Target = M; - fn deref(&self) -> &Self::Target { - self.model - .as_ref() - .expect("Failed to deref for DataStoreEntry Model") // this will never panic since Option is always Some before value is dropped - } -} - -impl<'a, M: Model> DerefMut for DataStoreEntry<'a, M> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.model - .as_mut() - .expect("Failed to deref_mut for DataStoreEntry Model") - } -} - -impl<'a, M: Model> Drop for DataStoreEntry<'a, M> { - fn drop(&mut self) { - if self.save { - if let Some(model) = self.model.take() { - self.lock.insert(self.id.clone(), model); - } - } - // self.lock implicitly dropped - } -} - -#[cfg(test)] -mod test { - use crate::model::user::User; - - use super::*; - #[test] - fn store() { - let user_data_store = DataStore::::new(); - let user = User::new("tester123".to_string()); - let id = user.id.clone(); - user_data_store.insert(user); - - assert!(user_data_store.data.lock().unwrap().contains_key(&id)); - - { - let mut set = user_data_store.get(&id).unwrap(); - set.name = "jonah".to_string(); - println!("set2"); - // drop is called here, so the value gets saved - } - - let get2 = user_data_store.get(&id).unwrap(); - - assert_eq!("jonah", &get2.name); - } - - #[test] - fn store_multiple_models() { - let store = DataStore::::new(); - - let user1 = User::new("user1".to_string()); - let user2 = User::new("user2".to_string()); - - let id1 = user1.id.clone(); - let id2 = user2.id.clone(); - - store.insert(user1); - store.insert(user2); - - let user1 = store.get(&id1).unwrap(); - let user1id = user1.id.clone(); - drop(user1); - let user2 = store.get(&id2).unwrap(); - let user2id = user2.id.clone(); - drop(user2); - - assert_eq!(id1, user1id); - assert_eq!(id2, user2id); - // Note: current implementation of store causes the mutex's lock to be poisoned when getting two DataStoreEntry in the same scope - } -} diff --git a/api/src/stream/mod.rs b/api/src/stream/mod.rs index a7b0a92..e23715c 100644 --- a/api/src/stream/mod.rs +++ b/api/src/stream/mod.rs @@ -4,17 +4,18 @@ use std::{ task::{Context, Poll}, }; +use async_graphql::async_trait::async_trait; use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; use futures_util::{Stream, StreamExt}; -use crate::{model::Model, types::id::Id}; +use musty::prelude::{Model, Id}; #[derive(Clone)] -pub struct StreamControl { +pub struct StreamControl { publishers: Arc>>>, } -impl StreamControl { +impl StreamControl { pub fn new() -> Self { Self { publishers: Arc::new(Mutex::new(Vec::new())), @@ -38,16 +39,18 @@ impl StreamControl { subscriber_id: &Id, topic_id: &Id, ) -> Option> { - self.publishers + /*self.publishers .lock() .unwrap() .iter() .find(|s| &s.subscriber_id == subscriber_id && &s.topic_id == topic_id) - .cloned() + .cloned()*/ + + None } pub fn publish(&self, msg: T) { - let topic_id = msg.model_id().clone(); + let topic_id = msg.id().clone(); let mut publishers = self.publishers.lock().unwrap(); for publisher in publishers.iter_mut().filter(|p| p.topic_id == topic_id) { publisher.publish(msg.clone()); @@ -99,7 +102,7 @@ impl ModelPublisher { } } -pub struct ModelSubscriber<'a, S: Model + 'static, T: Model + 'static> { +pub struct ModelSubscriber<'a, S: Model + 'static, T: Model + 'static + Clone> { subscriber: Box>, subscriber_id: Id, topic_id: Id, @@ -107,9 +110,9 @@ pub struct ModelSubscriber<'a, S: Model + 'static, T: Model + 'static> { ctl: &'a StreamControl, } -impl<'a, S: Model + 'static, T: Model + 'static> Unpin for ModelSubscriber<'a, S, T> {} +impl<'a, S: Model + 'static, T: Model + 'static + Clone> Unpin for ModelSubscriber<'a, S, T> {} -impl<'a, S: Model + 'static, T: Model + 'static> ModelSubscriber<'a, S, T> { +impl<'a, S: Model + 'static, T: Model + 'static + Clone> ModelSubscriber<'a, S, T> { fn new( sub: Box>, receiver: UnboundedReceiver, @@ -127,7 +130,7 @@ impl<'a, S: Model + 'static, T: Model + 'static> ModelSubscriber<'a, S, T> { } } -impl<'a, S: Model + 'static, T: Model + 'static> Stream for ModelSubscriber<'a, S, T> { +impl<'a, S: Model + 'static, T: Model + 'static + Clone> Stream for ModelSubscriber<'a, S, T> { type Item = T; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -140,25 +143,25 @@ impl<'a, S: Model + 'static, T: Model + 'static> Stream for ModelSubscriber<'a, } } -impl<'a, S: Model + 'static, T: Model + 'static> Drop for ModelSubscriber<'a, S, T> { +impl<'a, S: Model + 'static, T: Model + 'static + Clone> Drop for ModelSubscriber<'a, S, T> { fn drop(&mut self) { self.subscriber.on_disconnect(); self.ctl.handle_disconnect(&self.subscriber_id); } } -pub trait Subscriber: Send + Sync { +#[async_trait] +pub trait Subscriber: Send + Sync { fn subscriber_id(&self) -> &Id; fn topic_id(&self) -> &Id; - fn on_disconnect(&mut self); + async fn on_disconnect(&mut self); fn map_msg(&self, msg: T) -> Option; } -#[cfg(test)] +/*#[cfg(test)] mod tests { use crate::{ model::{room::Room, user::User}, - store::DataStore, }; use super::*; @@ -198,7 +201,7 @@ mod tests { } } - #[tokio::test] + /*#[tokio::test] async fn test_subscribe_publish() { // SETUP CONTROL, STORE, MODELS let ctl = StreamControl::::new(); @@ -371,5 +374,5 @@ mod tests { let (s1, s2) = futures::join!(task_subscribe1, task_subscribe2); s1.unwrap(); s2.unwrap(); - } -} + }*/ +}*/ diff --git a/api/src/types/id.rs b/api/src/types/id.rs deleted file mode 100644 index 1f056a3..0000000 --- a/api/src/types/id.rs +++ /dev/null @@ -1,222 +0,0 @@ -use crate::model::Model; -use std::{fmt::Display, hash::Hash, marker::PhantomData, any::Any}; - -use async_graphql::{InputValueError, Scalar, ScalarType, Value}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug)] -pub struct Id { - pub id: String, - pub node_suffix: String, - _phantom: PhantomData -} - -impl Id { - pub fn new_from_id(id: String, node_suffix: String) -> Self { - Self { id, node_suffix, _phantom: PhantomData } - } - - pub fn as_ref(id: &Id) -> &Id { - unsafe { &*(id as *const Id as *const Id) } - } -} - -impl Id { - pub fn new() -> Self { - let uid = uuid::Uuid::new_v4().to_string().replace("-", "")[..6].to_string(); - Self::new_from_id(uid, M::NODE_SUFFIX.to_string()) - } - - pub fn from_str(id: impl ToString) -> Self { - let id = id.to_string(); - if id.contains("$") { - if let Ok((id, node_suffix)) = parse_id_parts(id.clone()) { - return Self::new_from_id(id, node_suffix) - } - } - Self::new_from_id(id, M::NODE_SUFFIX.to_string()) - } - - pub fn try_from_str(id: impl ToString) -> Option { - let id = id.to_string(); - if id.contains("$") { - if let Ok((id, node_suffix)) = parse_id_parts(id.clone()) { - if node_suffix == M::NODE_SUFFIX || M::NODE_SUFFIX == "n" { - return Some(Self::new_from_id(id, node_suffix)) - } - } - } - None - } -} - -impl Display for Id { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}${}", self.id, self.node_suffix) - } -} - -impl PartialEq for Id { - fn eq(&self, other: &Self) -> bool { - self.id == other.id && self.node_suffix == other.node_suffix - } -} - -impl Eq for Id {} - -impl Clone for Id { - fn clone(&self) -> Self { - Self::new_from_id(self.id.clone(), self.node_suffix.clone()) - } -} - -impl Hash for Id { - fn hash(&self, state: &mut H) { - self.id.hash(state); - self.node_suffix.hash(state); - } -} - -#[Scalar] -impl ScalarType for Id { - fn parse(value: async_graphql::Value) -> async_graphql::InputValueResult { - if let Value::String(value_str) = &value { - if let Some(id) = Self::try_from_str(value_str) { - Ok(id) - } else { - Err(InputValueError::expected_type(value)) - } - } else { - Err(InputValueError::expected_type(value)) - } - } - - fn to_value(&self) -> async_graphql::Value { - Value::String(self.to_string()) - } -} - -impl Serialize for Id { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -struct IdVisitor(std::marker::PhantomData); - -impl<'de, M: Model> serde::de::Visitor<'de> for IdVisitor { - type Value = Id; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a string") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - Id::try_from_str(value).ok_or(E::custom("invalid id")) - } -} - -impl<'de, M: Model> Deserialize<'de> for Id { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_string(IdVisitor(std::marker::PhantomData)) - } -} - -pub trait ToId { - fn to_id(&self) -> Option>; -} - -impl ToId for Id { - fn to_id(&self) -> Option> { - Some(self.clone()) - } -} - -impl ToId for &Id { - fn to_id(&self) -> Option> { - (*self).to_id() - } -} - -impl ToId for String { - fn to_id(&self) -> Option> { - Id::::try_from_str(&self) - } -} - -impl ToId for &str { - fn to_id(&self) -> Option> { - Id::::try_from_str(&self) - } -} - -impl From> for String { - fn from(id: Id) -> Self { - id.to_string() - } -} - -impl From<&Id> for String { - fn from(id: &Id) -> Self { - id.to_string() - } -} - -impl From for Id { - fn from(s: String) -> Self { - Id::::from_str(s) - } -} - - -/*impl From> for Id { - fn from(i: Id) -> Self { - Id::::new_from_id(i.id, i.node_suffix) - } -} - -impl<'a> From<&'a Id> for &'a Id { - fn from(i: &'a Id) -> Self { - Id::::as_ref(i) - } -} - -impl From> for Id { - fn from(i: Id) -> Self { - Id::::new_from_id(i.id, i.node_suffix) - } -} - -impl<'a> From<&'a Id> for &'a Id { - fn from(i: &'a Id) -> Self { - Id::::as_ref(i) - } -} - -impl From> for Id { - fn from(i: Id) -> Self { - Id::::new_from_id(i.id, i.node_suffix) - } -} - -impl<'a> From<&'a Id> for &'a Id { - fn from(i: &'a Id) -> Self { - Id::::as_ref(i) - } -}*/ - -fn parse_id_parts(id: String) -> Result<(String, String), anyhow::Error> { // id, suffix - let mut split = id.split("$"); - let id = split.next().ok_or(anyhow::anyhow!("Missing part 1 (id) for id"))?.to_string(); - let suffix = split.next().ok_or(anyhow::anyhow!("Missing part 2 (suffix) for id"))?.to_string(); - Ok((id, suffix)) -} \ No newline at end of file diff --git a/api/src/types/mod.rs b/api/src/types/mod.rs index 53d88dc..935e863 100644 --- a/api/src/types/mod.rs +++ b/api/src/types/mod.rs @@ -1,6 +1,3 @@ pub mod color; -pub mod id; pub mod time; -pub mod token; - -pub use id::{Id, ToId}; \ No newline at end of file +pub mod token; \ No newline at end of file diff --git a/api/src/types/token.rs b/api/src/types/token.rs index 0e5ff6d..4baf5d8 100644 --- a/api/src/types/token.rs +++ b/api/src/types/token.rs @@ -51,7 +51,7 @@ impl Token { } } - pub fn check(&self, other: impl ToString) -> bool { + pub fn validate(&self, other: impl ToString) -> bool { if !self.is_valid() { return false; }