diff --git a/Cargo.lock b/Cargo.lock index 457e46a4aebc01c1e186c1ed4dbd193cbb0c7398..e0464563b8a13decf14aa280c22b898c978df7f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,6 +42,108 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "async-channel" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59740d83946db6a5af71ae25ddf9562c2b176b2ca42cf99a455f09f4a220d6b9" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb877970c7b440ead138f6321a3b5395d6061183af779340b65e20c0fede9146" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "vec-arena", +] + +[[package]] +name = "async-global-executor" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73079b49cd26b8fd5a15f68fc7707fc78698dc2a3d61430f2a7a9430230dfa04" +dependencies = [ + "async-executor", + "async-io", + "futures-lite", + "num_cpus", + "once_cell", + "tokio", +] + +[[package]] +name = "async-io" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9315f8f07556761c3e48fec2e6b276004acf426e6dc068b2c2251854d65ee0fd" +dependencies = [ + "concurrent-queue", + "fastrand", + "futures-lite", + "libc", + "log", + "nb-connect", + "once_cell", + "parking", + "polling", + "vec-arena", + "waker-fn", + "winapi 0.3.9", +] + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-std" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f9f84f1280a2b436a2c77c2582602732b6c2f4321d5494d6e799e6c367859a8" +dependencies = [ + "async-channel", + "async-global-executor", + "async-io", + "async-mutex", + "blocking", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "num_cpus", + "once_cell", + "pin-project-lite 0.2.0", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" + [[package]] name = "async-trait" version = "0.1.42" @@ -53,6 +155,20 @@ dependencies = [ "syn 1.0.56", ] +[[package]] +name = "async-tungstenite" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39eca8dd578b18e557361e50ca767df55c5e62f690a5e53868c3c7a8123145b7" +dependencies = [ + "async-std", + "futures-io", + "futures-util", + "log", + "pin-project 1.0.2", + "tungstenite", +] + [[package]] name = "atomic" version = "0.5.0" @@ -62,6 +178,12 @@ dependencies = [ "autocfg 1.0.1", ] +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + [[package]] name = "atty" version = "0.2.14" @@ -188,6 +310,15 @@ dependencies = [ "generic-array 0.12.3", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.4", +] + [[package]] name = "block-cipher" version = "0.8.0" @@ -206,6 +337,20 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "blocking" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + [[package]] name = "blowfish" version = "0.6.0" @@ -267,6 +412,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" +[[package]] +name = "cache-padded" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" + [[package]] name = "cc" version = "1.0.66" @@ -309,6 +460,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + [[package]] name = "const_fn" version = "0.4.4" @@ -347,6 +507,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + [[package]] name = "crossbeam-utils" version = "0.8.1" @@ -368,6 +534,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "ctrlc" +version = "3.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b57a92e9749e10f25a171adcebfafe72991d45e7ec2dcb853e8f83d9dafaeb08" +dependencies = [ + "nix", + "winapi 0.3.9", +] + [[package]] name = "darling" version = "0.10.2" @@ -453,6 +629,15 @@ dependencies = [ "generic-array 0.12.3", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", +] + [[package]] name = "discard" version = "1.0.4" @@ -525,12 +710,27 @@ dependencies = [ "synstructure", ] +[[package]] +name = "event-listener" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" + [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +[[package]] +name = "fastrand" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3" +dependencies = [ + "instant", +] + [[package]] name = "figment" version = "0.10.0" @@ -662,6 +862,21 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" +[[package]] +name = "futures-lite" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite 0.2.0", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.8" @@ -751,6 +966,19 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "gloo-timers" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "h2" version = "0.2.7" @@ -825,7 +1053,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" dependencies = [ "crypto-mac", - "digest", + "digest 0.8.1", ] [[package]] @@ -991,6 +1219,15 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3094308123a0e9fd59659ce45e22de9f53fc1d2ac6e1feb9fef988e4f76cad77" +[[package]] +name = "input_buffer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" +dependencies = [ + "bytes 0.5.6", +] + [[package]] name = "instant" version = "0.1.9" @@ -1029,9 +1266,9 @@ checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" [[package]] name = "itoa" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "js-sys" @@ -1058,6 +1295,15 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "language-tags" version = "0.2.2" @@ -1180,8 +1426,8 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a18af3dcaf2b0219366cdb4e2af65a6101457b415c3d1a5c71dd9c2b7c77b9c8" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.7.3", + "digest 0.8.1", "opaque-debug 0.2.3", ] @@ -1299,7 +1545,7 @@ dependencies = [ "serde", "serde_bytes", "serde_with", - "sha-1", + "sha-1 0.8.2", "sha2", "socket2", "stringprep", @@ -1344,6 +1590,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nb-connect" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8123a81538e457d44b933a02faf885d3fe8408806b23fa700e8f01c6c3a98998" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "net2" version = "0.2.37" @@ -1355,6 +1611,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "nix" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", +] + [[package]] name = "nom" version = "6.0.1" @@ -1484,6 +1752,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + [[package]] name = "parking_lot" version = "0.10.2" @@ -1636,6 +1910,19 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +[[package]] +name = "polling" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "log", + "wepoll-sys", + "winapi 0.3.9", +] + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1950,18 +2237,18 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "ref-cast" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17626b2f4bcf35b84bf379072a66e28cfe5c3c6ae58b38e4914bb8891dabece" +checksum = "e84b8a3c77dd38893c11b59284a40f304a1346d4da020e603fab3671727df95d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c523ccaed8ac4b0288948849a350b37d3035827413c458b6a40ddb614bb4f72" +checksum = "99d5173fc07aa6595363a38ca7d69d438cc32cca4216ccd1a3a8f2d4b10bbcd0" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.8", @@ -2049,11 +2336,15 @@ dependencies = [ name = "revolt" version = "0.3.0-alpha" dependencies = [ + "async-std", + "async-tungstenite", "bcrypt", "bitfield", "chrono", + "ctrlc", "dotenv", "env_logger", + "futures", "hive_pubsub", "lazy_static", "lettre", @@ -2073,7 +2364,6 @@ dependencies = [ "serde_json", "snafu", "time 0.2.23", - "tokio", "ulid", "validator", "ws", @@ -2097,7 +2387,7 @@ dependencies = [ [[package]] name = "rocket" version = "0.5.0-dev" -source = "git+https://github.com/SergioBenitez/Rocket#9671115796e42865eaebd020ac27542802027b02" +source = "git+https://github.com/SergioBenitez/Rocket?branch=master#9671115796e42865eaebd020ac27542802027b02" dependencies = [ "async-trait", "atomic", @@ -2126,7 +2416,7 @@ dependencies = [ [[package]] name = "rocket_codegen" version = "0.5.0-dev" -source = "git+https://github.com/SergioBenitez/Rocket#9671115796e42865eaebd020ac27542802027b02" +source = "git+https://github.com/SergioBenitez/Rocket?branch=master#9671115796e42865eaebd020ac27542802027b02" dependencies = [ "devise", "glob", @@ -2138,7 +2428,7 @@ dependencies = [ [[package]] name = "rocket_contrib" version = "0.5.0-dev" -source = "git+https://github.com/SergioBenitez/Rocket#9671115796e42865eaebd020ac27542802027b02" +source = "git+https://github.com/SergioBenitez/Rocket?branch=master#9671115796e42865eaebd020ac27542802027b02" dependencies = [ "log", "rocket", @@ -2150,7 +2440,7 @@ dependencies = [ [[package]] name = "rocket_cors" version = "0.5.2" -source = "git+https://github.com/lawliet89/rocket_cors#305971023d0edf29d10bf5af1d385481b0b5245f" +source = "git+https://github.com/lawliet89/rocket_cors?branch=master#305971023d0edf29d10bf5af1d385481b0b5245f" dependencies = [ "log", "regex", @@ -2165,7 +2455,7 @@ dependencies = [ [[package]] name = "rocket_http" version = "0.5.0-dev" -source = "git+https://github.com/SergioBenitez/Rocket#9671115796e42865eaebd020ac27542802027b02" +source = "git+https://github.com/SergioBenitez/Rocket?branch=master#9671115796e42865eaebd020ac27542802027b02" dependencies = [ "cookie", "either", @@ -2406,12 +2696,25 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.7.3", + "digest 0.8.1", "fake-simd", "opaque-debug 0.2.3", ] +[[package]] +name = "sha-1" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpuid-bool", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "sha1" version = "0.6.0" @@ -2424,8 +2727,8 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.7.3", + "digest 0.8.1", "fake-simd", "opaque-debug 0.2.3", ] @@ -2653,18 +2956,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" +checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" +checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.8", @@ -2918,6 +3221,25 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "tungstenite" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23" +dependencies = [ + "base64 0.12.3", + "byteorder", + "bytes 0.5.6", + "http", + "httparse", + "input_buffer", + "log", + "rand 0.7.3", + "sha-1 0.9.2", + "url", + "utf-8", +] + [[package]] name = "typed-builder" version = "0.3.0" @@ -3037,6 +3359,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" + [[package]] name = "uuid" version = "0.8.1" @@ -3091,12 +3419,24 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" +[[package]] +name = "vec-arena" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d" + [[package]] name = "version_check" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "want" version = "0.3.0" @@ -3225,6 +3565,15 @@ dependencies = [ "webpki", ] +[[package]] +name = "wepoll-sys" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff" +dependencies = [ + "cc", +] + [[package]] name = "widestring" version = "0.4.3" @@ -3305,7 +3654,7 @@ dependencies = [ "mio", "mio-extras", "rand 0.7.3", - "sha-1", + "sha-1 0.8.2", "slab", "url", ] diff --git a/Cargo.toml b/Cargo.toml index a36df61fb138d3536785c6901cffa2383d8fee35..74028354a0cc57feb16b0ca801d7e0739fe8a7e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,12 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = "0.2.22" +futures = "0.3.8" many-to-many = "0.1.2" +ctrlc = { version = "3.0", features = ["termination"] } +async-tungstenite = { version = "0.10.0", features = ["async-std-runtime"] } rauth = { git = "https://gitlab.insrt.uk/insert/rauth" } +async-std = { version = "1.8.0", features = ["tokio02"] } hive_pubsub = { version = "0.4.1", features = ["mongo"] } rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", branch = "master" } diff --git a/src/main.rs b/src/main.rs index 415f629cd792ba7fe1fb2d96b9f2047b077d4fff..7d9e043b3eb48eb8f783e0cadf0992b5911492fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,21 +6,25 @@ extern crate rocket; #[macro_use] extern crate rocket_contrib; #[macro_use] -extern crate bitfield; -#[macro_use] extern crate lazy_static; +extern crate ctrlc; +pub mod notifications; pub mod database; -pub mod pubsub; pub mod routes; pub mod util; use rauth; use log::info; +use futures::join; +use async_std::task; use rocket_cors::AllowedOrigins; -#[tokio::main] -async fn main() { +fn main() { + task::block_on(entry()) +} + +async fn entry() { dotenv::dotenv().ok(); env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "info")); @@ -28,10 +32,16 @@ async fn main() { util::variables::preflight_checks(); database::connect().await; + + ctrlc::set_handler(move || { + // Force ungraceful exit to avoid hang. + std::process::exit(0); + }).expect("Error setting Ctrl-C handler"); - pubsub::hive::init_hive(); - //pubsub::websocket::launch_server(); - + join!(launch_web(), notifications::websocket::launch_server()); +} + +async fn launch_web() { let cors = rocket_cors::CorsOptions { allowed_origins: AllowedOrigins::All, ..Default::default() diff --git a/src/notifications/events.rs b/src/notifications/events.rs new file mode 100644 index 0000000000000000000000000000000000000000..5ed7960eae45b565b8a5711a7a4a810a80695b42 --- /dev/null +++ b/src/notifications/events.rs @@ -0,0 +1,67 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "type")] +pub enum Notification { + MessageCreate { + id: String, + nonce: Option<String>, + channel: String, + author: String, + content: String, + }, + + MessageEdit { + id: String, + channel: String, + author: String, + content: String, + }, + + MessageDelete { + id: String, + }, + + GroupUserJoin { + id: String, + user: String, + }, + + GroupUserLeave { + id: String, + user: String, + }, + + GuildUserJoin { + id: String, + user: String, + }, + + GuildUserLeave { + id: String, + user: String, + banned: bool, + }, + + GuildChannelCreate { + id: String, + channel: String, + name: String, + description: String, + }, + + GuildChannelDelete { + id: String, + channel: String, + }, + + GuildDelete { + id: String, + }, + + UserRelationship { + id: String, + user: String, + status: i32, + } +} diff --git a/src/pubsub/hive.rs b/src/notifications/hive.rs similarity index 69% rename from src/pubsub/hive.rs rename to src/notifications/hive.rs index 2ffadd03cdab882571bbb93cf67579285cfcfdf7..ff2f04a35f887f2314fe8b03229eb86416a34743 100644 --- a/src/pubsub/hive.rs +++ b/src/notifications/hive.rs @@ -10,14 +10,12 @@ use log::{error, debug}; static HIVE: OnceCell<MongodbPubSub<String, String, Notification>> = OnceCell::new(); -pub fn init_hive() { +pub async fn init_hive() { let hive = MongodbPubSub::new( |_ids, notification| { if let Ok(data) = to_string(¬ification) { debug!("Pushing out notification. {}", data); - // if let Err(err) = websocket::publish(ids, data) { - // error!("Failed to publish notification through WebSocket! {}", err); - // } + // ! FIXME: push to websocket } else { error!("Failed to serialise notification."); } @@ -25,20 +23,18 @@ pub fn init_hive() { get_collection("hive"), ); - //listen_thread(hive.clone()); - if HIVE.set(hive).is_err() { panic!("Failed to set global pubsub instance."); } } pub fn publish(topic: &String, data: Notification) -> Result<(), String> { - let hive = HIVE.get().expect("Global pubsub instance not available."); + let hive = HIVE.get().unwrap(); hive.publish(topic, data) } pub fn subscribe(user: String, topics: Vec<String>) -> Result<(), String> { - let hive = HIVE.get().expect("Global pubsub instance not available."); + let hive = HIVE.get().unwrap(); for topic in topics { hive.subscribe(user.clone(), topic)?; } @@ -47,14 +43,14 @@ pub fn subscribe(user: String, topics: Vec<String>) -> Result<(), String> { } pub fn drop_user(user: &String) -> Result<(), String> { - let hive = HIVE.get().expect("Global pubsub instance not available."); + let hive = HIVE.get().unwrap(); hive.drop_client(user)?; Ok(()) } pub fn drop_topic(topic: &String) -> Result<(), String> { - let hive = HIVE.get().expect("Global pubsub instance not available."); + let hive = HIVE.get().unwrap(); hive.drop_topic(topic)?; Ok(()) diff --git a/src/pubsub/mod.rs b/src/notifications/mod.rs similarity index 57% rename from src/pubsub/mod.rs rename to src/notifications/mod.rs index d898b56f713f910cbec91b3de2ce4ded58bc552b..0312f43a7a7ff5fb831583512b732c6183ecb4f6 100644 --- a/src/pubsub/mod.rs +++ b/src/notifications/mod.rs @@ -1,3 +1,3 @@ +pub mod websocket; pub mod events; pub mod hive; -// pub mod websocket; diff --git a/src/notifications/websocket.rs b/src/notifications/websocket.rs new file mode 100644 index 0000000000000000000000000000000000000000..dd98bd3834eb243a49a9f1a01a6ca404c23a5f53 --- /dev/null +++ b/src/notifications/websocket.rs @@ -0,0 +1,28 @@ +use crate::util::variables::WS_HOST; + +use log::info; +use async_std::task; +use futures::prelude::*; +use async_std::net::{TcpListener, TcpStream}; + +pub async fn launch_server() { + let try_socket = TcpListener::bind(WS_HOST.to_string()).await; + let listener = try_socket.expect("Failed to bind"); + info!("Listening on: {}", *WS_HOST); + + while let Ok((stream, _)) = listener.accept().await { + task::spawn(accept(stream)); + } +} + +async fn accept(stream: TcpStream) { + let addr = stream.peer_addr().expect("Connected streams should have a peer address."); + let ws_stream = async_tungstenite::accept_async(stream) + .await + .expect("Error during websocket handshake."); + + info!("User established WebSocket connection from {}.", addr); + + let (write, read) = ws_stream.split(); + read.forward(write).await.expect("Failed to forward message") +} diff --git a/src/pubsub/events/groups.rs b/src/pubsub/events/groups.rs deleted file mode 100644 index 876f13d163faa0a0b8545a6196301038926fb064..0000000000000000000000000000000000000000 --- a/src/pubsub/events/groups.rs +++ /dev/null @@ -1,13 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct UserJoin { - pub id: String, - pub user: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct UserLeave { - pub id: String, - pub user: String, -} diff --git a/src/pubsub/events/guilds.rs b/src/pubsub/events/guilds.rs deleted file mode 100644 index 3b1b95d2052ca750b17aa1f89d6b7151e8c31acb..0000000000000000000000000000000000000000 --- a/src/pubsub/events/guilds.rs +++ /dev/null @@ -1,33 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct UserJoin { - pub id: String, - pub user: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct UserLeave { - pub id: String, - pub user: String, - pub banned: bool, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ChannelCreate { - pub id: String, - pub channel: String, - pub name: String, - pub description: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ChannelDelete { - pub id: String, - pub channel: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Delete { - pub id: String, -} diff --git a/src/pubsub/events/message.rs b/src/pubsub/events/message.rs deleted file mode 100644 index ffec878984282a769efff93bee04a98c3ddea214..0000000000000000000000000000000000000000 --- a/src/pubsub/events/message.rs +++ /dev/null @@ -1,23 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Create { - pub id: String, - pub nonce: Option<String>, - pub channel: String, - pub author: String, - pub content: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Edit { - pub id: String, - pub channel: String, - pub author: String, - pub content: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Delete { - pub id: String, -} diff --git a/src/pubsub/events/mod.rs b/src/pubsub/events/mod.rs deleted file mode 100644 index 0769eafcd1441f3ffda1d843f3541c2b169d1414..0000000000000000000000000000000000000000 --- a/src/pubsub/events/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -use serde::{Deserialize, Serialize}; - -pub mod groups; -pub mod guilds; -pub mod message; -pub mod users; - -#[allow(non_camel_case_types)] -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(tag = "type")] -pub enum Notification { - message_create(message::Create), - message_edit(message::Edit), - message_delete(message::Delete), - group_user_join(groups::UserJoin), - group_user_leave(groups::UserLeave), - guild_user_join(guilds::UserJoin), - guild_user_leave(guilds::UserLeave), - guild_channel_create(guilds::ChannelCreate), - guild_channel_delete(guilds::ChannelDelete), - guild_delete(guilds::Delete), - user_friend_status(users::FriendStatus), -} - -impl Notification { - pub fn push_to_cache(&self) { - //crate::database::channel::process_event(&self); - //crate::database::guild::process_event(&self); - //crate::database::user::process_event(&self); - } -} diff --git a/src/pubsub/events/users.rs b/src/pubsub/events/users.rs deleted file mode 100644 index 7a23446e295c3e76534c9a69a1a1d31b9436ae67..0000000000000000000000000000000000000000 --- a/src/pubsub/events/users.rs +++ /dev/null @@ -1,8 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct FriendStatus { - pub id: String, - pub user: String, - pub status: i32, -} diff --git a/src/pubsub/websocket.disabled/client.rs b/src/pubsub/websocket.disabled/client.rs deleted file mode 100644 index ff4c7c684e049dd3dd58f12b159985b4b235ff3a..0000000000000000000000000000000000000000 --- a/src/pubsub/websocket.disabled/client.rs +++ /dev/null @@ -1,200 +0,0 @@ -use super::state; -use crate::database::get_collection; -use crate::pubsub::hive; - -use log::{error, info}; -use mongodb::bson::doc; -use mongodb::options::FindOneOptions; -use serde_json::{from_str, json, Value}; -use ulid::Ulid; -use ws::{CloseCode, Error, Handler, Handshake, Message, Result, Sender}; - -pub struct Client { - id: String, - sender: Sender, - user_id: Option<String>, -} - -impl Client { - pub fn new(sender: Sender) -> Client { - Client { - id: Ulid::new().to_string(), - user_id: None, - sender, - } - } -} - -impl Handler for Client { - fn on_open(&mut self, handshake: Handshake) -> Result<()> { - info!("Client connected. [{}] {:?}", self.id, handshake.peer_addr); - - Ok(()) - } - - // Client sends { "type": "authenticate", "token": token }. - // Receives { "type": "authorised" } and waits. - // Client then receives { "type": "ready", "data": payload }. - // If at any point we hit an error, send { "type": "error", "error": error }. - fn on_message(&mut self, msg: Message) -> Result<()> { - if let Message::Text(text) = msg { - if let Ok(data) = from_str(&text) as std::result::Result<Value, _> { - if let Value::String(packet_type) = &data["type"] { - if packet_type == "authenticate" { - if self.user_id.is_some() { - return self.sender.send( - json!({ - "type": "error", - "error": "Already authenticated!" - }) - .to_string(), - ); - } else if let Value::String(token) = &data["token"] { - let user = get_collection("users").find_one( - doc! { - "access_token": token - }, - FindOneOptions::builder() - .projection(doc! { "_id": 1 }) - .build(), - ).await; - - if let Ok(result) = user { - if let Some(doc) = result { - self.sender.send( - json!({ - "type": "authorised" - }) - .to_string(), - )?; - - // FIXME: fetch above when we switch to new token system - // or auth cache system, something like that - let user = crate::database::user::fetch_user( - doc.get_str("_id").unwrap(), - ) - .unwrap() - .unwrap(); // this should be guranteed, I think, maybe? I'm getting rid of it later. FIXME - - self.user_id = Some(user.id.clone()); - - match user.create_payload() { - Ok(payload) => { - // ! Grab the ids from the payload, - // ! there's probably a better way to - // ! do this. I'll rewrite it at some point. - let mut ids = vec![ - self.user_id.as_ref().unwrap().clone() - ]; - - { - // This is bad code. But to be fair - // it should work just fine. - for user in payload.get("users").unwrap().as_array().unwrap() { - ids.push(user.as_object().unwrap().get("id").unwrap().as_str().unwrap().to_string()); - } - - for channel in payload.get("channels").unwrap().as_array().unwrap() { - ids.push(channel.as_object().unwrap().get("id").unwrap().as_str().unwrap().to_string()); - } - - for guild in payload.get("guilds").unwrap().as_array().unwrap() { - ids.push(guild.as_object().unwrap().get("id").unwrap().as_str().unwrap().to_string()); - } - } - - if let Err(err) = hive::subscribe(self.user_id.as_ref().unwrap().clone(), ids) { - error!("Failed to subscribe someone to the Hive! {}", err); - self.sender.send( - json!({ - "type": "warn", - "error": "Failed to subscribe you to the Hive. You may not receive all notifications." - }) - .to_string(), - )?; - } - - self.sender.send( - json!({ - "type": "ready", - "data": payload - }) - .to_string(), - )?; - - if state::accept( - self.id.clone(), - self.user_id.as_ref().unwrap().clone(), - self.sender.clone(), - ) - .is_err() - { - self.sender.send( - json!({ - "type": "warn", - "error": "Failed to accept your connection. You will not receive any notifications." - }) - .to_string(), - )?; - } - } - Err(error) => { - error!("Failed to create payload! {}", error); - self.sender.send( - json!({ - "type": "error", - "error": "Failed to create payload." - }) - .to_string(), - )?; - } - } - } else { - self.sender.send( - json!({ - "type": "error", - "error": "Invalid token." - }) - .to_string(), - )?; - } - } else { - self.sender.send( - json!({ - "type": "error", - "error": "Failed to fetch from database." - }) - .to_string(), - )?; - } - } else { - self.sender.send( - json!({ - "type": "error", - "error": "Missing token." - }) - .to_string(), - )?; - } - } - } - } - } - - Ok(()) - } - - fn on_close(&mut self, _code: CloseCode, reason: &str) { - info!("Client disconnected. [{}] {}", self.id, reason); - if let Err(error) = state::drop(&self.id) { - error!("Also failed to drop client from state! {}", error); - } - } - - fn on_error(&mut self, err: Error) { - error!( - "A client disconnected due to an error. [{}] {}", - self.id, err - ); - } -} diff --git a/src/pubsub/websocket.disabled/mod.rs b/src/pubsub/websocket.disabled/mod.rs deleted file mode 100644 index e161253ea783e1404995fb139ddaae82e94e58ec..0000000000000000000000000000000000000000 --- a/src/pubsub/websocket.disabled/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::util::variables::WS_HOST; - -use log::{error, info}; -use std::thread; -use ws::listen; - -mod client; -mod state; - -pub use state::publish; - -pub fn launch_server() { - thread::spawn(|| { - if listen(WS_HOST.to_string(), |sender| client::Client::new(sender)).is_err() { - error!( - "Failed to listen for WebSocket connections on {:?}!", - *WS_HOST - ); - } else { - info!("Listening for WebSocket connections on {:?}", *WS_HOST); - } - }); -} diff --git a/src/pubsub/websocket.disabled/state.rs b/src/pubsub/websocket.disabled/state.rs deleted file mode 100644 index ec02d9fedf29b5e4541faad90516377f8711f74c..0000000000000000000000000000000000000000 --- a/src/pubsub/websocket.disabled/state.rs +++ /dev/null @@ -1,89 +0,0 @@ -use crate::pubsub::hive; - -use many_to_many::ManyToMany; -use std::collections::HashMap; -use std::sync::{Arc, RwLock}; -use log::{error, info}; -use ws::Sender; - -lazy_static! { - static ref CONNECTIONS: Arc<RwLock<HashMap<String, Sender>>> = - Arc::new(RwLock::new(HashMap::new())); - static ref CLIENTS: Arc<RwLock<ManyToMany<String, String>>> = - Arc::new(RwLock::new(ManyToMany::new())); -} - -pub fn accept(id: String, user_id: String, sender: Sender) -> Result<(), String> { - let mut conns = CONNECTIONS - .write() - .map_err(|_| "Failed to lock connections for writing.")?; - - conns.insert(id.clone(), sender); - - let mut clients = CLIENTS - .write() - .map_err(|_| "Failed to lock clients for writing.")?; - - clients.insert(user_id.clone(), id.clone()); - - info!("Accepted user [{}] for connection {}.", user_id, id); - Ok(()) -} - -pub fn drop(id: &String) -> Result<(), String> { - let mut conns = CONNECTIONS - .write() - .map_err(|_| "Failed to lock connections for writing.")?; - - conns.remove(id); - - let mut clients = CLIENTS - .write() - .map_err(|_| "Failed to lock clients for writing.")?; - - let uid = if let Some(ids) = clients.get_right(id) { - let user_id: String = ids.into_iter().next().unwrap(); - info!("Dropped user [{}] for connection {}.", user_id, id); - Some(user_id) - } else { - None - }; - - clients.remove_right(id); - - if let Some(user_id) = &uid { - if let None = clients.get_left(user_id) { - if let Err(error) = hive::drop_user(user_id) { - error!("Failed to drop user from hive! {}", error); - } else { - info!("User [{}] has completed disconnected from node.", user_id); - } - } - } - - Ok(()) -} - -pub fn publish(clients: Vec<String>, data: String) -> Result<(), String> { - let conns = CONNECTIONS - .read() - .map_err(|_| "Failed to lock connections for reading.")?; - - let client_map = CLIENTS - .read() - .map_err(|_| "Failed to lock clients for reading.")?; - - for client in clients { - if let Some(targets) = client_map.get_left(&client) { - for target in &targets { - if let Some(connection) = conns.get(target) { - if let Err(err) = connection.send(data.clone()) { - error!("Failed to publish notification to client [{}]! {}", target, err); - } - } - } - } - } - - Ok(()) -} diff --git a/src/routes/account.disabled.rs b/src/routes/account.disabled.rs deleted file mode 100644 index 24a90e94f9ff20652a10c522d0ce7b6a25defe52..0000000000000000000000000000000000000000 --- a/src/routes/account.disabled.rs +++ /dev/null @@ -1,344 +0,0 @@ -use super::Response; -use crate::database; -use crate::util::variables::{DISABLE_REGISTRATION, USE_EMAIL}; -use crate::util::{captcha, email, gen_token}; - -use bcrypt::{hash, verify}; -use chrono::prelude::*; -use database::user::User; -use log::error; -use mongodb::bson::{doc, from_bson, Bson}; -use rocket_contrib::json::Json; -use serde::{Deserialize, Serialize}; -use ulid::Ulid; -use validator::validate_email; - -#[derive(Serialize, Deserialize)] -pub struct Create { - username: String, - password: String, - email: String, - captcha: Option<String>, -} - -/// create a new Revolt account -/// (1) validate input -/// [username] 2 to 32 characters -/// [password] 8 to 72 characters -/// [email] validate against RFC -/// (2) check email existence -/// (3) add user and send email verification -#[post("/create", data = "<info>")] -pub async fn create(info: Json<Create>) -> Response { - if let Err(error) = captcha::verify(&info.captcha).await { - return Response::BadRequest(json!({ "error": error })); - } - - if *DISABLE_REGISTRATION { - return Response::BadRequest(json!({ "error": "Registration disabled." })); - } - - let col = database::get_collection("users"); - - if info.username.len() < 2 || info.username.len() > 32 { - return Response::NotAcceptable( - json!({ "error": "Username needs to be at least 2 chars and less than 32 chars." }), - ); - } - - if info.password.len() < 8 || info.password.len() > 72 { - return Response::NotAcceptable( - json!({ "error": "Password needs to be at least 8 chars and at most 72." }), - ); - } - - if !validate_email(info.email.clone()) { - return Response::UnprocessableEntity(json!({ "error": "Invalid email." })); - } - - if let Some(_) = col - .find_one(doc! { "email": info.email.clone() }, None) - .await - .expect("Failed user lookup") - { - return Response::Conflict(json!({ "error": "Email already in use!" })); - } - - if let Some(_) = col - .find_one(doc! { "username": info.username.clone() }, None) - .await - .expect("Failed user lookup") - { - return Response::Conflict(json!({ "error": "Username already in use!" })); - } - - if let Ok(hashed) = hash(info.password.clone(), 10) { - let access_token = gen_token(92); - let code = gen_token(48); - - let email_verification = match *USE_EMAIL { - true => doc! { - "verified": false, - "target": info.email.clone(), - "expiry": Bson::DateTime(Utc::now() + chrono::Duration::days(1)), - "rate_limit": Bson::DateTime(Utc::now() + chrono::Duration::minutes(1)), - "code": code.clone(), - }, - false => doc! { - "verified": true - }, - }; - - let id = Ulid::new().to_string(); - match col.insert_one( - doc! { - "_id": &id, - "email": info.email.clone(), - "username": info.username.clone(), - "display_name": info.username.clone(), - "password": hashed, - "access_token": &access_token, - "email_verification": email_verification - }, - None, - ) - .await { - Ok(_) => { - if *USE_EMAIL { - let sent = email::send_verification_email(info.email.clone(), code); - - Response::Success(json!({ - "email_sent": sent, - })) - } else { - Response::Success(json!({ - "id": id, - "access_token": access_token - })) - } - } - Err(_) => { - Response::InternalServerError(json!({ "error": "Failed to create account." })) - } - } - } else { - Response::InternalServerError(json!({ "error": "Failed to hash." })) - } -} - -/// verify an email for a Revolt account -/// (1) check if code is valid -/// (2) check if it expired yet -/// (3) set account as verified -#[get("/verify/<code>")] -pub async fn verify_email(code: String) -> Response { - let col = database::get_collection("users"); - - if let Some(u) = col - .find_one(doc! { "email_verification.code": code.clone() }, None) - .await - .expect("Failed user lookup") - { - let user: User = from_bson(Bson::Document(u)).expect("Failed to unwrap user."); - let ev = user.email_verification; - - if Utc::now() > *ev.expiry.unwrap() { - Response::Gone(json!({ - "success": false, - "error": "Token has expired!", - })) - } else { - let target = ev.target.unwrap(); - col.update_one( - doc! { "_id": user.id }, - doc! { - "$unset": { - "email_verification.code": "", - "email_verification.expiry": "", - "email_verification.target": "", - "email_verification.rate_limit": "", - }, - "$set": { - "email_verification.verified": true, - "email": target.clone(), - }, - }, - None, - ) - .await - .expect("Failed to update user!"); - - if *USE_EMAIL { - if let Err(err) = email::send_welcome_email(target.to_string(), user.username) { - error!("Failed to send welcome email! {}", err); - } - } - - Response::Redirect(super::Redirect::to("https://app.revolt.chat")) - } - } else { - Response::BadRequest(json!({ "error": "Invalid code." })) - } -} - -#[derive(Serialize, Deserialize)] -pub struct Resend { - email: String, - captcha: Option<String>, -} - -/// resend a verification email -/// (1) check if verification is pending for x email -/// (2) check for rate limit -/// (3) resend the email -#[post("/resend", data = "<info>")] -pub async fn resend_email(info: Json<Resend>) -> Response { - if let Err(error) = captcha::verify(&info.captcha).await { - return Response::BadRequest(json!({ "error": error })); - } - - let col = database::get_collection("users"); - - if let Some(u) = col - .find_one( - doc! { "email_verification.target": info.email.clone() }, - None, - ) - .await - .expect("Failed user lookup.") - { - let user: User = from_bson(Bson::Document(u)).expect("Failed to unwrap user."); - let ev = user.email_verification; - - let expiry = ev.expiry.unwrap(); - let rate_limit = ev.rate_limit.unwrap(); - - if Utc::now() < *rate_limit { - Response::TooManyRequests( - json!({ "error": "You are being rate limited, please try again in a while." }), - ) - } else { - let mut new_expiry = Bson::DateTime(Utc::now() + chrono::Duration::days(1)); - if info.email.clone() != user.email { - if Utc::now() > *expiry { - return Response::Gone( - json!({ "error": "To help protect your account, please login and change your email again. The original request was made over one day ago." }), - ); - } - - new_expiry = Bson::DateTime(*expiry); - } - - let code = gen_token(48); - col.update_one( - doc! { "_id": user.id }, - doc! { - "$set": { - "email_verification.code": code.clone(), - "email_verification.expiry": new_expiry, - "email_verification.rate_limit": Bson::DateTime(Utc::now() + chrono::Duration::minutes(1)), - }, - }, - None, - ) - .await - .expect("Failed to update user!"); - - if let Err(err) = email::send_verification_email(info.email.clone(), code) { - return Response::InternalServerError(json!({ "error": err })); - } - - Response::Result(super::Status::Ok) - } - } else { - Response::NotFound(json!({ "error": "Email not found or pending verification!" })) - } -} - -#[derive(Serialize, Deserialize)] -pub struct Login { - email: String, - password: String, - captcha: Option<String>, -} - -/// login to a Revolt account -/// (1) find user by email -/// (2) verify password -/// (3) return access token -#[post("/login", data = "<info>")] -pub async fn login(info: Json<Login>) -> Response { - if let Err(error) = captcha::verify(&info.captcha).await { - return Response::BadRequest(json!({ "error": error })); - } - - let col = database::get_collection("users"); - - if let Some(u) = col - .find_one(doc! { "email": info.email.clone() }, None) - .await - .expect("Failed user lookup") - { - let user: User = from_bson(Bson::Document(u)).expect("Failed to unwrap user."); - - match verify(info.password.clone(), &user.password) - .expect("Failed to check hash of password.") - { - true => { - let token = match user.access_token { - Some(t) => t.to_string(), - None => { - let token = gen_token(92); - if col - .update_one( - doc! { "_id": &user.id }, - doc! { "$set": { "access_token": token.clone() } }, - None, - ) - .await - .is_err() - { - return Response::InternalServerError( - json!({ "error": "Failed database operation." }), - ); - } - - token - } - }; - - Response::Success(json!({ "access_token": token, "id": user.id })) - } - false => Response::Unauthorized(json!({ "error": "Invalid password." })), - } - } else { - Response::NotFound(json!({ "error": "Email is not registered." })) - } -} - -#[derive(Serialize, Deserialize)] -pub struct Token { - token: String, -} - -/// login to a Revolt account via token -#[post("/token", data = "<info>")] -pub async fn token(info: Json<Token>) -> Response { - let col = database::get_collection("users"); - - if let Ok(result) = col.find_one(doc! { "access_token": info.token.clone() }, None).await { - if let Some(user) = result { - Response::Success(json!({ - "id": user.get_str("_id").unwrap(), - })) - } else { - Response::Unauthorized(json!({ - "error": "Invalid token!", - })) - } - } else { - Response::InternalServerError(json!({ - "error": "Failed database query.", - })) - } -}