diff --git a/Cargo.lock b/Cargo.lock index 5ac696fe688f043f1df410f7fe7520441246e75f..d456ebed0c0c8f7bdf31cd64ed3b59d756734615 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,7 +72,7 @@ dependencies = [ "http", "httparse", "httpdate", - "itoa 1.0.5", + "itoa", "language-tags", "local-channel", "mime", @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "actix-rt" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ea16c295198e958ef31930a6ef37d0fb64e9ca3b6116e6b93a8bdae96ee1000" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" dependencies = [ "futures-core", "tokio", @@ -122,9 +122,9 @@ dependencies = [ [[package]] name = "actix-server" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da34f8e659ea1b077bb4637948b815cd3768ad5a188fdcd74ff4d84240cd824" +checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" dependencies = [ "actix-rt", "actix-service", @@ -184,7 +184,7 @@ dependencies = [ "futures-core", "futures-util", "http", - "itoa 1.0.5", + "itoa", "language-tags", "log", "mime", @@ -196,7 +196,7 @@ dependencies = [ "serde_urlencoded", "smallvec", "socket2", - "time 0.3.17", + "time 0.3.19", "url", ] @@ -225,7 +225,7 @@ dependencies = [ "actix-utils", "actix-web", "actix-web-lab-derive", - "ahash 0.8.2", + "ahash 0.8.3", "arc-swap", "async-trait", "bytes", @@ -304,9 +304,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", "getrandom", @@ -355,9 +355,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" dependencies = [ "backtrace", ] @@ -408,9 +408,9 @@ checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" [[package]] name = "async-trait" -version = "0.1.61" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", @@ -451,8 +451,8 @@ dependencies = [ "actix-web-lab", "anyhow", "argon2", - "bincode", "clap", + "dashmap", "dotenv", "glue", "jwt-simple", @@ -462,7 +462,6 @@ dependencies = [ "reqwest", "serde", "serde_json", - "sled", "sqlx", "thiserror", "tokio", @@ -588,9 +587,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.2" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -605,14 +604,13 @@ dependencies = [ "lazy_static", "memchr", "regex-automata", - "serde", ] [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" @@ -622,9 +620,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "bytestring" @@ -712,9 +710,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -742,14 +740,14 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.32" +version = "4.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" +checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" dependencies = [ "bitflags", "clap_derive", "clap_lex", - "is-terminal 0.4.2", + "is-terminal 0.4.4", "once_cell", "strsim", "termcolor", @@ -757,9 +755,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.21" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" dependencies = [ "heck", "proc-macro-error", @@ -770,9 +768,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" dependencies = [ "os_str_bytes", ] @@ -829,7 +827,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ "percent-encoding", - "time 0.3.17", + "time 0.3.19", "version_check", ] @@ -967,18 +965,18 @@ dependencies = [ [[package]] name = "crc" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ "crc-catalog", ] [[package]] name = "crc-catalog" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" [[package]] name = "crc32fast" @@ -1072,13 +1070,12 @@ dependencies = [ [[package]] name = "csv" -version = "1.1.6" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +checksum = "af91f40b7355f82b0a891f50e70399475945bb0b0da4f1700ce60761c9d3e359" dependencies = [ - "bstr", "csv-core", - "itoa 0.4.8", + "itoa", "ryu", "serde", ] @@ -1100,9 +1097,9 @@ checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df" [[package]] name = "cxx" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" dependencies = [ "cc", "cxxbridge-flags", @@ -1112,9 +1109,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" dependencies = [ "cc", "codespan-reporting", @@ -1127,21 +1124,34 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" [[package]] name = "cxxbridge-macro" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core 0.9.7", +] + [[package]] name = "der" version = "0.6.1" @@ -1258,14 +1268,14 @@ checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0" [[package]] name = "ecdsa" -version = "0.14.8" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +checksum = "12844141594ad74185a926d030f3b605f6a903b4e3fec351f3ea338ac5b7637e" dependencies = [ "der", "elliptic-curve", "rfc6979", - "signature", + "signature 2.0.0", ] [[package]] @@ -1280,9 +1290,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" @@ -1317,9 +1327,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if", ] @@ -1350,7 +1360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ "humantime", - "is-terminal 0.4.2", + "is-terminal 0.4.4", "log", "regex", "termcolor", @@ -1407,22 +1417,22 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] [[package]] name = "fd-lock" -version = "3.0.8" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb21c69b9fea5e15dbc1049e4b77145dd0ba1c84019c488102de0dc4ea4b0a27" +checksum = "8ef1a30ae415c3a691a4f41afddc2dbcd6d70baf338368d85ebc1e8ed92cedb9" dependencies = [ "cfg-if", - "rustix 0.36.6", - "windows-sys 0.42.0", + "rustix 0.36.8", + "windows-sys 0.45.0", ] [[package]] @@ -1502,21 +1512,11 @@ dependencies = [ "windows-sys 0.36.1", ] -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -1524,9 +1524,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-intrusive" @@ -1541,21 +1541,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-core", "futures-sink", @@ -1683,9 +1683,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" dependencies = [ "unicode-segmentation", ] @@ -1708,6 +1708,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -1764,13 +1770,13 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", - "itoa 1.0.5", + "itoa", ] [[package]] @@ -1810,9 +1816,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -1823,7 +1829,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.5", + "itoa", "pin-project-lite", "socket2", "tokio", @@ -1936,12 +1942,12 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" dependencies = [ "libc", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -1952,9 +1958,9 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "is-macro" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c068d4c6b922cd6284c609cfa6dec0e41615c9c5a1a4ba729a970d8daba05fb" +checksum = "8a7d079e129b77477a49c5c4f1cfe9ce6c2c909ef52520693e8e811a714c7b20" dependencies = [ "Inflector", "pmutil", @@ -1977,14 +1983,14 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" dependencies = [ - "hermit-abi 0.2.6", - "io-lifetimes 1.0.3", - "rustix 0.36.6", - "windows-sys 0.42.0", + "hermit-abi 0.3.1", + "io-lifetimes 1.0.5", + "rustix 0.36.8", + "windows-sys 0.45.0", ] [[package]] @@ -1996,12 +2002,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.5" @@ -2010,9 +2010,9 @@ checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "ittapi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c4f6ff06169ce7048dac5150b1501c7e3716a929721aeb06b87e51a43e42f4" +checksum = "2e648c437172ce7d3ac35ca11a068755072054826fa455a916b43524fa4a62a7" dependencies = [ "anyhow", "ittapi-sys", @@ -2021,9 +2021,9 @@ dependencies = [ [[package]] name = "ittapi-sys" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e078cce01485f418bae3beb34dd604aaedf2065502853c7da17fbce8e64eda" +checksum = "a9b32a4d23f72548178dde54f3c12c6b6a08598e25575c0d0fa5bd861e0dc1a5" dependencies = [ "cc", ] @@ -2039,18 +2039,18 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] [[package]] name = "jwt-simple" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529a00f2d42d7dc349c994e65917c81bf53225831a65361f6c0454124c550f63" +checksum = "21a4c8e544a27e20e2fe4b82a93a9e823f01ebcc1e4e135e839db66df0e7dc54" dependencies = [ "anyhow", "binstring", @@ -2074,14 +2074,16 @@ dependencies = [ [[package]] name = "k256" -version = "0.11.6" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +checksum = "92a55e0ff3b72c262bcf041d9e97f1b84492b68f1c1a384de2323d3dc9403397" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", + "once_cell", "sha2 0.10.6", + "signature 2.0.0", ] [[package]] @@ -2263,9 +2265,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "matches" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "maybe-owned" @@ -2284,9 +2286,9 @@ dependencies = [ [[package]] name = "mediatype" -version = "0.19.11" +version = "0.19.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a280afd79e5a4c3e7f298c6139321aad0365690f24a57539e18590b5a606966" +checksum = "fea6e62614ab2fc0faa58bb15102a0382d368f896a9fa4776592589ab55c4de7" [[package]] name = "memchr" @@ -2300,7 +2302,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" dependencies = [ - "rustix 0.36.6", + "rustix 0.36.8", ] [[package]] @@ -2354,14 +2356,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -2409,16 +2411,37 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if", + "libc", +] + [[package]] name = "nom" -version = "7.1.2" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2460,9 +2483,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ "num-traits", "serde", @@ -2523,18 +2546,18 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "3e0072973714303aa6e3631c7e8e777970cf4bdd25dc4932e41031027b8bcc4e" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "0629cbd6b897944899b1f10496d9c4a7ac5878d45fd61bc22e9e79bfbbc29597" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2565,9 +2588,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "opaque-debug" @@ -2640,23 +2663,25 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "p256" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +checksum = "49c124b3cbce43bcbac68c58ec181d98ed6cc7e6d0aa7c3ba97b2563410b0e55" dependencies = [ "ecdsa", "elliptic-curve", + "primeorder", "sha2 0.10.6", ] [[package]] name = "p384" -version = "0.11.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" +checksum = "630a4a9b2618348ececfae61a4905f564b817063bf2d66cdfc2ced523fe1d2d4" dependencies = [ "ecdsa", "elliptic-curve", + "primeorder", "sha2 0.10.6", ] @@ -2678,7 +2703,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.5", + "parking_lot_core 0.9.7", ] [[package]] @@ -2697,15 +2722,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -2742,9 +2767,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", "indexmap", @@ -2857,15 +2882,23 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "primeorder" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b54f7131b3dba65a2f414cf5bd25b66d4682e4608610668eae785750ba4c5b2" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -2894,9 +2927,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -3016,9 +3049,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -3092,11 +3125,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.13" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ - "base64 0.13.1", + "base64 0.21.0", "bytes", "encoding_rs", "futures-core", @@ -3129,18 +3162,18 @@ dependencies = [ [[package]] name = "result-like" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b80fe0296795a96913be20558326b797a187bb3986ce84ed82dee0fb7414428" +checksum = "ccc7ce6435c33898517a30e85578cd204cbb696875efb93dec19a2d31294f810" dependencies = [ "result-like-derive", ] [[package]] name = "result-like-derive" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a29c8a4ac7839f1dcb8b899263b501e0d6932f210300c8a0d271323727b35c1" +checksum = "1fabf0a2e54f711c68c50d49f648a1a8a37adcb57353f518ac4df374f0788f42" dependencies = [ "pmutil", "proc-macro2", @@ -3175,7 +3208,7 @@ dependencies = [ "pkcs1", "pkcs8", "rand_core", - "signature", + "signature 1.6.4", "smallvec", "subtle", "zeroize", @@ -3262,7 +3295,7 @@ dependencies = [ "bitflags", "errno", "io-lifetimes 0.7.5", - "itoa 1.0.5", + "itoa", "libc", "linux-raw-sys 0.0.46", "once_cell", @@ -3271,16 +3304,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.6" +version = "0.36.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" dependencies = [ "bitflags", "errno", - "io-lifetimes 1.0.3", + "io-lifetimes 1.0.5", "libc", "linux-raw-sys 0.1.4", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -3444,7 +3477,7 @@ dependencies = [ "log", "memchr", "memoffset 0.6.5", - "nix", + "nix 0.24.3", "num-bigint", "num-complex", "num-integer", @@ -3498,9 +3531,9 @@ checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "rustyline" -version = "10.0.0" +version = "10.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1cd5ae51d3f7bf65d7969d579d502168ef578f289452bd8ccc91de28fda20e" +checksum = "c1e83c32c3f3c33b08496e0d1df9ea8c64d39adb8eb36a1ebb1440c690697aef" dependencies = [ "bitflags", "cfg-if", @@ -3510,7 +3543,7 @@ dependencies = [ "libc", "log", "memchr", - "nix", + "nix 0.25.1", "radix_trie", "scopeguard", "unicode-segmentation", @@ -3571,9 +3604,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -3584,9 +3617,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -3637,18 +3670,18 @@ checksum = "53192e38d5c88564b924dbe9b60865ecbb71b81d38c4e61c817cffd3e36ef696" dependencies = [ "form_urlencoded", "indexmap", - "itoa 1.0.5", + "itoa", "ryu", "serde", ] [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ - "itoa 1.0.5", + "itoa", "ryu", "serde", ] @@ -3660,7 +3693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.5", + "itoa", "ryu", "serde", ] @@ -3720,9 +3753,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -3737,6 +3770,16 @@ dependencies = [ "rand_core", ] +[[package]] +name = "signature" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +dependencies = [ + "digest 0.10.6", + "rand_core", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -3745,29 +3788,13 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] -[[package]] -name = "sled" -version = "0.34.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" -dependencies = [ - "crc32fast", - "crossbeam-epoch", - "crossbeam-utils", - "fs2", - "fxhash", - "libc", - "log", - "parking_lot 0.11.2", -] - [[package]] name = "slice-group-by" version = "0.3.0" @@ -3808,9 +3835,9 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" +checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" dependencies = [ "itertools", "nom", @@ -3854,7 +3881,7 @@ dependencies = [ "hkdf", "hmac", "indexmap", - "itoa 1.0.5", + "itoa", "libc", "log", "md-5", @@ -3994,9 +4021,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14" dependencies = [ "proc-macro2", "quote", @@ -4030,9 +4057,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "tempfile" @@ -4061,9 +4088,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -4096,10 +4123,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] @@ -4116,11 +4144,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" dependencies = [ - "itoa 1.0.5", + "itoa", "serde", "time-core", "time-macros", @@ -4134,9 +4162,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c" dependencies = [ "time-core", ] @@ -4167,15 +4195,15 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.24.1" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ "autocfg", "bytes", @@ -4204,9 +4232,9 @@ dependencies = [ [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -4225,9 +4253,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -4239,13 +4267,30 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -4428,9 +4473,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-casing" @@ -4455,9 +4500,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" @@ -4502,9 +4547,9 @@ checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" [[package]] name = "utoipa" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bdd08005ac3ff3aa380060bc30b2ff4a840c4405ea072deba2bad4891e404c0" +checksum = "a15f6da6a2b471134ca44b7d18e8a76d73035cf8b3ed24c4dd5ca6a63aa439c5" dependencies = [ "indexmap", "serde", @@ -4514,9 +4559,9 @@ dependencies = [ [[package]] name = "utoipa-gen" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6e78c81140a4b93470645091775e4048a57a29b6cc78add875ae60ffced17c" +checksum = "6f2e33027986a4707b3f5c37ed01b33d0e5a53da30204b52ff18f80600f1d0ec" dependencies = [ "lazy_static", "proc-macro-error", @@ -4545,9 +4590,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" dependencies = [ "getrandom", "serde", @@ -4654,9 +4699,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4664,9 +4709,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -4679,9 +4724,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -4691,9 +4736,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4701,9 +4746,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -4714,9 +4759,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wasm-encoder" @@ -4729,9 +4774,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.20.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05632e0a66a6ed8cca593c24223aabd6262f256c3693ad9822c315285f010614" +checksum = "68f7d56227d910901ce12dfd19acc40c12687994dfb3f57c90690f80be946ec5" dependencies = [ "leb128", ] @@ -4966,30 +5011,30 @@ dependencies = [ [[package]] name = "wast" -version = "50.0.0" +version = "54.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2cbb59d4ac799842791fe7e806fa5dbbf6b5554d538e51cc8e176db6ff0ae34" +checksum = "3d48d9d731d835f4f8dacbb8de7d47be068812cb9877f5c60d408858778d8d2a" dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.20.0", + "wasm-encoder 0.24.1", ] [[package]] name = "wat" -version = "1.0.52" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584aaf7a1ecf4d383bbe1a25eeab0cbb8ff96acc6796707ff65cde48f4632f15" +checksum = "d1db2e3ed05ea31243761439194bec3af6efbbaf87c4c8667fb879e4f23791a0" dependencies = [ - "wast 50.0.0", + "wast 54.0.1", ] [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -4997,9 +5042,9 @@ dependencies = [ [[package]] name = "which" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", @@ -5128,19 +5173,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc 0.42.1", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" @@ -5156,9 +5225,9 @@ checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" @@ -5174,9 +5243,9 @@ checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" @@ -5192,9 +5261,9 @@ checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" @@ -5210,15 +5279,15 @@ checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" @@ -5234,9 +5303,9 @@ checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" @@ -5389,9 +5458,9 @@ checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" [[package]] name = "zip" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080" +checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" dependencies = [ "byteorder", "crc32fast", diff --git a/Cargo.toml b/Cargo.toml index d813e3e43df6ff846f76b9520346acf000b5acb8..55a8dc7c9d76fc4fc85b9940155eab828598a3b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,19 +17,19 @@ authors = [] #authors = [ "Overpeek <overpeek.fin@gmail.com>" ] [workspace.dependencies] -anyhow = { version = "1.0.66", features = ["backtrace"] } +anyhow = { version = "1.0.69", features = ["backtrace"] } thiserror = "1.0.38" actix-web = "4.3.0" tracing = "0.1.37" tracing-subscriber = "0.3.16" dotenv = "0.15.0" -serde = { version = "1.0.147", features = ["derive"] } -reqwest = { version = "0.11.13", features = ["json"] } +serde = { version = "1.0.152", features = ["derive"] } +reqwest = { version = "0.11.14", features = ["json"] } #utoipa = "2.4.2" -utoipa = { version = "3.0.2", features = ["actix_extras", "uuid"] } +utoipa = { version = "3.0.3", features = ["actix_extras", "uuid"] } utoipa-swagger-ui = { version = "3.0.2", features = ["actix-web"] } -serde_json = "1.0.89" -clap = { version = "4.0.29", features = ["derive", "env"] } +serde_json = "1.0.93" +clap = { version = "4.1.6", features = ["derive", "env"] } # tokio-stream = "0.1.12" #[workspace.dependencies.paperclip] @@ -38,7 +38,7 @@ clap = { version = "4.0.29", features = ["derive", "env"] } #features = ["actix4", "v3", "uuid"] [workspace.dependencies.tokio] -version = "1.21.2" +version = "1.25.0" features = ["rt-multi-thread", "macros", "time", "signal"] [profile.release-wasm] diff --git a/aava.sql b/aava.sql index eacdffe8af907c4a5bd4f195ebe4c5f4b5187dd7..db33931f5550d76893b74756c5e7014339e0af94 100644 --- a/aava.sql +++ b/aava.sql @@ -14,7 +14,7 @@ CREATE TABLE users ( CREATE TABLE courses ( id UUID NOT NULL DEFAULT uuid_generate_v4() PRIMARY KEY, name VARCHAR(256) NOT NULL, - description VARCHAR(2048), + description TEXT, -- TODO: image points_max int NOT NULL DEFAULT 0, @@ -31,7 +31,7 @@ CREATE TABLE course_admins ( CREATE TABLE lessons ( id UUID NOT NULL DEFAULT uuid_generate_v4() PRIMARY KEY, name VARCHAR(256) NOT NULL, - description VARCHAR(2048), + description TEXT, -- TODO: image points_max int NOT NULL DEFAULT 0, @@ -43,7 +43,7 @@ CREATE TABLE lessons ( CREATE TABLE exercises ( id UUID NOT NULL DEFAULT uuid_generate_v4() PRIMARY KEY, name VARCHAR(256) NOT NULL, - description VARCHAR(2048), + description TEXT, -- TODO: image points_max int NOT NULL DEFAULT 0, @@ -51,10 +51,9 @@ CREATE TABLE exercises ( lesson UUID NOT NULL REFERENCES lessons(id), - code VARCHAR(65536) NOT NULL, - example_code VARCHAR(65536), - stdin VARCHAR(65536) NOT NULL, - stdout VARCHAR(65536) NOT NULL + code TEXT NOT NULL, + stdin TEXT NOT NULL, + stdout TEXT NOT NULL ); -- user specific(private) tables @@ -86,7 +85,7 @@ CREATE TABLE exercise_progress ( points int NOT NULL DEFAULT 0, attempts int NOT NULL DEFAULT 0, - code VARCHAR(65536) NOT NULL + code TEXT NOT NULL ); -- test data diff --git a/crates/back/Cargo.toml b/crates/back/Cargo.toml index af96a7069c8eecc975e532f250f95527e79c9e4a..823e74ce9c37e6c270b231d9513910491edfc428 100644 --- a/crates/back/Cargo.toml +++ b/crates/back/Cargo.toml @@ -8,7 +8,7 @@ authors.workspace = true [features] default = ["embedded_db", "postgres_db"] -embedded_db = ["bincode", "sled"] +embedded_db = ["dashmap"] postgres_db = ["sqlx/postgres"] [dependencies] @@ -45,7 +45,7 @@ serde_json.workspace = true # required by utoipa # data serialization serde.workspace = true -bincode = { version = "1.3.3", optional = true } +# bincode = { version = "1.3.3", optional = true } # runner interaction reqwest.workspace = true @@ -53,12 +53,13 @@ glue = { path = "../glue" } # database interaction sqlx = { version = "0.6.2", features = ["runtime-tokio-native-tls", "uuid"] } -sled = { version = "0.34.7", optional = true } -uuid = { version = "1.2.2", features = ["serde", "v4"] } +# sled = { version = "0.34.7", optional = true } +dashmap = { version = "5.4.0", optional = true } +uuid = { version = "1.3.0", features = ["serde", "v4"] } # auth argon2 = { version = "0.4.1", features = ["std"] } -jwt-simple = "0.11.2" +jwt-simple = "0.11.3" regex = "1.7.1" -once_cell = "1.17.0" +once_cell = "1.17.1" diff --git a/crates/back/src/api/auth.rs b/crates/back/src/api/auth.rs index 999433e045e446d85a7d2f1a74f0e832c419afce..a568e20c31e8c767d95fc5d7ca78bbc387768b8d 100644 --- a/crates/back/src/api/auth.rs +++ b/crates/back/src/api/auth.rs @@ -1,29 +1,27 @@ -use crate::db::{Database, DbCreateUser, DbVerifyUser}; +use crate::{ + create_jwt, + db::{Database, DbCreateUser, DbVerifyUser}, +}; use actix_web::{ dev::HttpServiceFactory, web::{self, Data, Json}, - HttpResponse, Responder, + HttpResponse, }; -use jwt_simple::prelude::*; -use once_cell::sync::Lazy; use regex::Regex; - use serde::{Deserialize, Serialize}; - use utoipa::ToSchema; -use uuid::Uuid; // pub fn api() -> impl HttpServiceFactory { - let _username_regex = UsernameRegex( + let username_regex = UsernameRegex( Regex::new(r#"^[a-zA-Z0-9\-_]{4,32}$"#).expect("The username regex is invalid"), ); web::scope("/auth") + .app_data(Data::new(username_regex)) .route("/login", web::post().to(login)) - .route("/register", web::post().to(create)) - .app_data(UsernameRegex) + .route("/register", web::post().to(register)) } // @@ -42,11 +40,11 @@ struct UsernameRegex(Regex); (status = CONFLICT, description = "Username is already taken"), ), )] -async fn create( +async fn register( Json(info): Json<UserCreate>, db: Data<Database>, username_regex: Data<UsernameRegex>, -) -> impl Responder { +) -> HttpResponse { let UserCreate { username, password } = info; if password.len() < 8 { @@ -68,15 +66,23 @@ async fn create( } }; + let jwt = match create_jwt(id) { + Ok(v) => v, + Err(err) => { + tracing::error!("JWT gen error: {err}"); + return HttpResponse::InternalServerError().finish(); /* .json("Internal Server Error"); */ + } + }; + HttpResponse::Created().json(UserInfo { username, privileged: false, - jwt: create_jwt(id), + jwt, }) } #[utoipa::path( - get, + post, path = "/api/auth/login", request_body = UserCreate, responses( @@ -84,7 +90,7 @@ async fn create( (status = UNAUTHORIZED, description = "Invalid username/password"), ), )] -async fn login(Json(info): Json<UserLogin>, db: Data<Database>) -> impl Responder { +async fn login(Json(info): Json<UserLogin>, db: Data<Database>) -> HttpResponse { let UserLogin { username, password } = info; let (id, privileged) = match db.verify_user(&username, &password).await { @@ -98,22 +104,21 @@ async fn login(Json(info): Json<UserLogin>, db: Data<Database>) -> impl Responde } }; + let jwt = match create_jwt(id) { + Ok(v) => v, + Err(err) => { + tracing::error!("JWT gen error: {err}"); + return HttpResponse::InternalServerError().finish(); /* .json("Internal Server Error"); */ + } + }; + HttpResponse::Ok().json(UserInfo { username, privileged, - jwt: create_jwt(id), + jwt, }) } -fn create_jwt(id: Uuid) -> String { - // lazy initialized JWT crypt key - // => JWT:s become invalid after a server restart - static JWT_KEYS: Lazy<HS512Key> = Lazy::new(HS512Key::generate); - - let claims = Claims::with_custom_claims(id, Duration::from_hours(2)); - JWT_KEYS.authenticate(claims).unwrap() -} - /// User creation information #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub(super) struct UserCreate { diff --git a/crates/back/src/api/course.rs b/crates/back/src/api/course.rs index e1ab0322fa558804e4666f332de48e2dc0ced435..cf07e39f89433a9f66395623afd0268e0c8cb0d7 100644 --- a/crates/back/src/api/course.rs +++ b/crates/back/src/api/course.rs @@ -1,12 +1,10 @@ -use super::lesson::Lesson; -use crate::{db::DbCourse, ierr, HttpResult}; +use crate::db::Database; use actix_web::{ dev::HttpServiceFactory, - web::{self, Data, Json, Path}, - Responder, + web::{self, Data, Path}, + HttpResponse, }; use serde::{Deserialize, Serialize}; -use sqlx::{FromRow, PgPool}; use utoipa::ToSchema; use uuid::Uuid; @@ -23,49 +21,16 @@ pub fn api() -> impl HttpServiceFactory { responses((status = 200, description = "List lessons in course", body = [Exercise])), params(("uuid", description = "UUID for the course")) )] -async fn lessons(course: Path<Uuid>, db: Data<PgPool>) -> HttpResult<impl Responder> { +async fn lessons(course: Path<Uuid>, db: Data<Database>) -> HttpResponse { // TODO: auth - #[derive(FromRow)] - struct Lessons { - id: Uuid, - name: String, - description: Option<String>, - points_max: i32, + match db.get_lessons(*course).await { + Ok(v) => HttpResponse::Ok().json(v), + Err(err) => { + tracing::error!("Database error: {err}"); + HttpResponse::InternalServerError().finish() + } } - - let query = sqlx::query_as( - r#"SELECT id, name, description, points_max - FROM lessons - WHERE course = $1"#, - ) - .bind(*course) - .fetch_all(&**db) - .await - .map_err(|err| { - tracing::error!("Database interaction error: {err}"); - ierr() - })?; - - let lessons = query - .into_iter() - .map( - |Lessons { - id, - name, - description, - points_max, - }| Lesson { - id, - name, - description, - points: 0, // TODO: - points_max: points_max as _, - }, - ) - .collect::<Vec<_>>(); - - Ok(Json(lessons)) } // @@ -93,25 +58,6 @@ pub struct Course { pub points_max: u32, } -impl From<DbCourse> for Course { - fn from( - DbCourse { - id, - name, - description, - points_max, - }: DbCourse, - ) -> Self { - Course { - id, - name, - description, - points: 0, // TODO: - points_max: points_max as _, - } - } -} - // #[cfg(test)] diff --git a/crates/back/src/api/courses.rs b/crates/back/src/api/courses.rs index 718d52deaa74ba05cd70a104fe4281b54fc5b678..b79b820a2fcb5d8f76f210bcd7c9145834e4c75c 100644 --- a/crates/back/src/api/courses.rs +++ b/crates/back/src/api/courses.rs @@ -1,9 +1,8 @@ -use super::course::Course; use crate::db::Database; use actix_web::{ dev::HttpServiceFactory, web::{self, Data}, - HttpResponse, Responder, + HttpResponse, }; // @@ -18,18 +17,16 @@ pub fn api() -> impl HttpServiceFactory { path = "/api/courses", responses((status = OK, description = "List of courses available", body = [Course])), )] -async fn get(db: Data<Database>) -> impl Responder { +async fn get(db: Data<Database>) -> HttpResponse { // TODO: auth - let courses = match db.get_courses().await { - Ok(v) => v, + match db.get_courses().await { + Ok(v) => HttpResponse::Ok().json(v), Err(err) => { tracing::error!("Database error: {err}"); - return HttpResponse::InternalServerError().finish(); /* .json("Internal Server Error"); */ + HttpResponse::InternalServerError().finish() } - }; - - HttpResponse::Ok().json(courses.into_iter().map(Course::from).collect::<Vec<_>>()) + } } // diff --git a/crates/back/src/api/exercise.rs b/crates/back/src/api/exercise.rs index bcbcaa99bcbd0e36ec6782e70eef8e55f21a3e0b..398e63e29382deae8354ca5c016100cf88581bef 100644 --- a/crates/back/src/api/exercise.rs +++ b/crates/back/src/api/exercise.rs @@ -1,13 +1,11 @@ -use crate::{ierr, HttpResult, Runner}; +use crate::{db::Database, Runner}; use actix_web::{ dev::HttpServiceFactory, - error::ErrorNotFound, web::{self, Data, Json, Path}, - Responder, + HttpResponse, }; use glue::{CodeRunArgs, CodeRunResult}; use serde::{Deserialize, Serialize}; -use sqlx::{FromRow, PgPool}; use utoipa::ToSchema; use uuid::Uuid; @@ -23,26 +21,24 @@ pub fn api() -> impl HttpServiceFactory { #[utoipa::path( get, path = "/api/exercise/{uuid}/code", - responses((status = 200, description = "Default code and stdin for an exercise", body = DefaultCode)), + responses((status = 200, description = "Default code and stdin for an exercise", body = ExerciseDetail)), + responses((status = 404, description = "Exercise not found")), params(("uuid", description = "UUID for the exercise")) )] -async fn code(exercise: Path<Uuid>, db: Data<PgPool>) -> HttpResult<impl Responder> { +async fn code(exercise: Path<Uuid>, db: Data<Database>) -> HttpResponse { // TODO: auth - let query: DefaultCode = sqlx::query_as( - r#"SELECT code, stdin, stdout - FROM exercises - WHERE id = $1"#, - ) - .bind(*exercise) - .fetch_one(&**db) - .await - .map_err(|err| { + let Ok(details) = db.get_exercise_details(*exercise).await.map_err(|err| { tracing::error!("Database interaction error: {err}"); - ierr() - })?; + }) else { + return HttpResponse::InternalServerError().finish(); + }; + + let Some(details) = details else { + return HttpResponse::NotFound().finish(); + }; - Ok(Json(query)) + HttpResponse::Ok().json(details) } /// Submit an answer for an exercise and get the result @@ -50,64 +46,68 @@ async fn code(exercise: Path<Uuid>, db: Data<PgPool>) -> HttpResult<impl Respond post, path = "/api/exercise/{uuid}/submit", request_body = CodeSubmit, - responses((status = 200, description = "Submit exercise", body = CodeSubmitResult)), - params(("uuid", description = "UUID for the exercise")) + responses( + (status = 200, description = "Submit exercise", body = CodeSubmitResult), + (status = 404, description = "Exercise not found"), + ), + params( + ("uuid" = Uuid, Path, description = "UUID for the exercise"), + ("code" = CodeSubmit, description = "Submitted code"), + ) )] pub async fn submit( exercise: Path<Uuid>, code: Json<CodeSubmit>, runner: Data<Runner>, - db: Data<PgPool>, -) -> HttpResult<impl Responder> { + db: Data<Database>, +) -> HttpResponse { // TODO: auth // TODO: rate limit // TODO: max attempts - // load the stdin for the exercise - let Some((stdin, expected_stdout, points_max)): Option<(String, String, i32)> = sqlx::query_as( - r#"SELECT stdin, stdout, points_max - FROM exercises - WHERE id = $1"#, - ) - .bind(*exercise) - .fetch_optional(&**db) - .await - .map_err(|err| { - tracing::error!("Database interaction error: {err}"); - ierr() - })? else { - return Err(ErrorNotFound("Exercise not found")); + let ExerciseVerifyData { + stdin, + stdout: expected_stdout, + max_points: points_max, + } = match db.get_exercise_verify(*exercise).await { + Ok(Some(v)) => v, + Ok(None) => { + return HttpResponse::NotFound().finish(); + } + Err(err) => { + tracing::error!("Database interaction error: {err}"); + return HttpResponse::InternalServerError().finish(); + } }; - let points_max = points_max.max(0) as u32; - // get the code run results from the Runner - let CodeRunResult { - stdout, - stderr, - exit_code, - } = reqwest::Client::builder() - .build() - .map_err(|err| { - tracing::error!("Failed to create a reqwest Client: {err}"); - ierr() - })? - .get(runner.0.clone()) + let runner_resp = reqwest::Client::new() + .get(runner.0.as_str()) .json(&CodeRunArgs { code: code.code.to_string(), stdin, }) .send() - .await - .map_err(|err| { - tracing::error!("Runner server responded with an unexpected error: {err}"); - ierr() - })? - .json::<CodeRunResult>() - .await - .map_err(|err| { - tracing::error!("Runner server responded with an invalid json: {err}"); - ierr() - })?; + .await; + + let runner_result = match runner_resp { + Ok(v) => v.json().await, + Err(err) => { + tracing::error!("Runner responded with an unknown error: {err}"); + return HttpResponse::InternalServerError().finish(); + } + }; + + let CodeRunResult { + stdout, + stderr, + exit_code, + } = match runner_result { + Ok(v) => v, + Err(err) => { + tracing::error!("Runner responded with an invalid json: {err}"); + return HttpResponse::InternalServerError().finish(); + } + }; // TODO: points let points = if !stderr.is_empty() { @@ -120,13 +120,13 @@ pub async fn submit( // TODO: update points - Ok(Json(CodeSubmitResult { + HttpResponse::Ok().json(CodeSubmitResult { stdout, stderr, exit_code, points, points_max, - })) + }) } // @@ -164,8 +164,8 @@ pub struct Exercise { pub attempts_max: u32, } -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, FromRow)] -pub struct DefaultCode { +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +pub struct ExerciseDetail { /// Initial code for the exercise #[schema(example = "print('x')")] pub code: String, @@ -210,6 +210,13 @@ pub struct CodeSubmitResult { pub points_max: u32, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ExerciseVerifyData { + pub stdin: String, + pub stdout: String, + pub max_points: u32, +} + // #[cfg(test)] diff --git a/crates/back/src/api/lesson.rs b/crates/back/src/api/lesson.rs index c3f3e230b7f433fc0249563ba8dc816f4b7f44f7..4e0647cab8fe2bdfac166b2a922367f7af608e89 100644 --- a/crates/back/src/api/lesson.rs +++ b/crates/back/src/api/lesson.rs @@ -1,12 +1,10 @@ -use super::exercise::Exercise; -use crate::{ierr, HttpResult}; +use crate::db::Database; use actix_web::{ dev::HttpServiceFactory, - web::{self, Data, Json, Path}, - Responder, + web::{self, Data, Path}, + HttpResponse, }; use serde::{Deserialize, Serialize}; -use sqlx::{FromRow, PgPool}; use utoipa::ToSchema; use uuid::Uuid; @@ -23,53 +21,16 @@ pub fn api() -> impl HttpServiceFactory { responses((status = 200, description = "List exercises in lesson", body = [Exercise])), params(("uuid", description = "UUID for the lesson")) )] -async fn exercises(lesson: Path<Uuid>, db: Data<PgPool>) -> HttpResult<impl Responder> { +async fn exercises(lesson: Path<Uuid>, db: Data<Database>) -> HttpResponse { // TODO: auth - #[derive(FromRow)] - struct Exercises { - id: Uuid, - name: String, - description: Option<String>, - points_max: i32, - attempts_max: i32, + match db.get_exercises(*lesson).await { + Ok(exercises) => HttpResponse::Ok().json(exercises), + Err(err) => { + tracing::error!("Database error: {err}"); + HttpResponse::InternalServerError().finish() + } } - - let query = sqlx::query_as( - r#"SELECT id, name, description, points_max, attempts_max - FROM exercises - WHERE lesson = $1"#, - ) - .bind(*lesson) - .fetch_all(&**db) - .await - .map_err(|err| { - tracing::error!("Database interaction error: {err}"); - ierr() - })?; - - let courses: Vec<_> = query - .into_iter() - .map( - |Exercises { - id, - name, - description, - points_max, - attempts_max, - }| Exercise { - id, - name, - description, - points: 0, // TODO: - points_max: points_max as _, - attempts: 0, // TODO: - attempts_max: attempts_max as _, - }, - ) - .collect(); - - Ok(Json(courses)) } // diff --git a/crates/back/src/api/mod.rs b/crates/back/src/api/mod.rs index a30c04779e4a9aa2774ad28b605fbdc7d5a4ba05..a5270044df025bf206fcfec3757507dcc8e4910e 100644 --- a/crates/back/src/api/mod.rs +++ b/crates/back/src/api/mod.rs @@ -3,18 +3,18 @@ use utoipa::OpenApi; // -mod auth; -mod course; -mod courses; -mod exercise; -mod lesson; +pub mod auth; +pub mod course; +pub mod courses; +pub mod exercise; +pub mod lesson; // #[derive(OpenApi)] #[openapi( paths( - auth::create, + auth::register, auth::login, courses::get, course::lessons, @@ -28,7 +28,7 @@ mod lesson; auth::UserInfo, course::Course, exercise::Exercise, - exercise::DefaultCode, + exercise::ExerciseDetail, exercise::CodeSubmit, exercise::CodeSubmitResult, lesson::Lesson, diff --git a/crates/back/src/cli.rs b/crates/back/src/cli.rs index 8c86371cddc578fed93fb9306c28d49a461e6b84..2996dd291288189a8f9859352094d7857ba20aeb 100644 --- a/crates/back/src/cli.rs +++ b/crates/back/src/cli.rs @@ -9,7 +9,7 @@ use std::{ // -#[derive(Debug, Clone, Parser)] +#[derive(Clone, Parser)] #[clap(name = "Aava Main Backend")] #[clap(author, version)] pub struct Args { @@ -28,9 +28,13 @@ pub struct Args { /// address for the Aava database /// /// examples: - /// - postgres=username:password@localhost - /// - embedded=./db/ - /// - none= + /// + #[cfg_attr( + feature = "postgres_db", + doc = " - postgres://username:password@localhost\n" + )] + #[cfg_attr(feature = "embedded_db", doc = " - embedded=./db/\n")] + /// - none #[clap(alias = "db", short, long, env, default_value_t)] pub database: DatabaseConfig, @@ -39,7 +43,7 @@ pub struct Args { pub test_data: bool, } -#[derive(Debug, Clone, Default)] +#[derive(Clone, Default)] pub enum DatabaseConfig { #[cfg(feature = "postgres_db")] Postgres(Url), @@ -51,27 +55,30 @@ pub enum DatabaseConfig { None, } +// + impl FromStr for DatabaseConfig { type Err = String; fn from_str(s: &str) -> Result<Self, Self::Err> { + // nodb if s == "none" { return Ok(Self::None); } - let (left, right) = s.split_once('=').ok_or_else(|| "missing '='".to_owned())?; + // embedded db + #[cfg(feature = "embedded_db")] + if let Some(("embedded", path)) = s.split_once('=') { + let path = PathBuf::from_str(path).map_err(|err| err.to_string())?; + return Ok(Self::Embedded(path)); + } - match left { - #[cfg(feature = "postgres_db")] - "postgres" => Ok(Self::Postgres( - Url::from_str(&format!("postgres://{right}")).map_err(|err| err.to_string())?, - )), - - #[cfg(feature = "embedded_db")] - "embedded" => Ok(Self::Embedded( - PathBuf::from_str(right).map_err(|err| err.to_string())?, - )), + // url db + let url = Url::from_str(s).map_err(|err| err.to_string())?; + match url.scheme() { + #[cfg(feature = "postgres_db")] + "postgres" => Ok(Self::Postgres(url)), other => Err(format!("unknown database type '{other}'")), } } @@ -93,41 +100,24 @@ impl Display for DatabaseConfig { } } -// impl ValueParserFactory for DatabaseConfig { -// type Parser = DatabaseConfigParser; -// -// fn value_parser() -> Self::Parser { -// DatabaseConfigParser -// } -// } -// -// #[derive(Debug, Clone, Copy)] -// pub struct DatabaseConfigParser; -// -// impl TypedValueParser for DatabaseConfigParser { -// type Value = DatabaseConfig; -// -// fn parse_ref( -// &self, -// cmd: &clap::Command, -// arg: Option<&clap::Arg>, -// value: &std::ffi::OsStr, -// ) -> Result<Self::Value, clap::Error> { -// match value {} -// value; -// todo!() -// } -// } - -// +impl Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Args") + .field("bind", &self.bind) + .field("frontend", &self.frontend) + .field("runner", &self.runner.as_str()) + .field("database", &self.database) + .field("test_data", &self.test_data) + .finish() + } +} -// impl Debug for Args { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// f.debug_struct("Args") -// .field("bind", &self.bind) -// .field("frontend", &self.frontend) -// .field("runner", &self.runner.as_str()) -// .field("database", &self.database.as_str()) -// .finish() -// } -// } +impl Debug for DatabaseConfig { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Postgres(arg0) => f.debug_tuple("Postgres").field(&arg0.as_str()).finish(), + Self::Embedded(arg0) => f.debug_tuple("Embedded").field(&arg0.as_os_str()).finish(), + Self::None => write!(f, "None"), + } + } +} diff --git a/crates/back/src/db/any.rs b/crates/back/src/db/any.rs index e328b01c8176baa9505603012a16364d0c07b492..92897825474a19ee479df0b88b88c2e32c052087 100644 --- a/crates/back/src/db/any.rs +++ b/crates/back/src/db/any.rs @@ -1,25 +1,33 @@ -use super::{postgres, sled, DbCourse, DbCreateUser, DbError, DbUser}; -use crate::{cli::DatabaseConfig, LazyAwait}; +use std::mem::transmute; + +use super::{embedded, postgres, DbCreateUser, DbError, DbResult, DbUser}; +use crate::{ + api::{ + course::Course, + exercise::{Exercise, ExerciseDetail, ExerciseVerifyData}, + lesson::Lesson, + }, + cli::DatabaseConfig, + LazyAwait, +}; +use actix_web_lab::__reexports::futures_util::future::join_all; use argon2::{ password_hash::{rand_core::OsRng, SaltString}, Argon2, PasswordHash, PasswordHasher, }; -use std::{future::Future, pin::Pin}; +use tokio::task::JoinHandle; use uuid::Uuid; // +// TODO: +// mysql/mariadb - SQL not compatible with postgreSQL +// surrealdb - pure rust db +// sled - in-memory db pub(super) enum AnyDatabase { - // TODO: - // mysql/mariadb - SQL not compatible with postgreSQL - // surrealdb - pure rust db - // sled - in-memory db - - // embedded db #[cfg(feature = "postgres_db")] - Sled(sled::Database), + Embedded(embedded::Database), - // postgre sql #[cfg(feature = "embedded_db")] Postgres(postgres::Database), @@ -29,24 +37,16 @@ pub(super) enum AnyDatabase { // -pub(super) type DbFut<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>; -pub(super) type GetCoursesFut<'a> = DbFut<'a, Result<Vec<DbCourse>, DbError>>; -pub(super) type CreateUserFut<'a> = DbFut<'a, Result<DbCreateUser, DbError>>; -pub(super) type GetUserFut<'a> = DbFut<'a, Result<Option<DbUser>, DbError>>; - -pub(super) trait DatabaseImpl { - // TODO: https://github.com/rust-lang/rust/issues/91611 - - fn get_courses(&self) -> GetCoursesFut; - - fn create_user<'a>( - &'a self, - uuid: Uuid, - name: &'a str, - passwd: PasswordHash<'a>, - ) -> CreateUserFut<'a>; - - fn get_user<'a>(&'a self, name: &'a str) -> GetUserFut<'a>; +macro_rules! gen_match { + ($self:ident, $($f:tt)*) => { + match $self { + #[cfg(feature = "postgres_db")] + AnyDatabase::Postgres(v) => v.$($f)*.await, + #[cfg(feature = "embedded_db")] + AnyDatabase::Embedded(v) => v.$($f)*.await, + AnyDatabase::None => Err(DbError::DatabaseNotConnected), + } + }; } // @@ -72,7 +72,7 @@ impl AnyDatabase { .unwrap_or(AnyDatabase::None) } - pub async fn new(db: &DatabaseConfig, test_data: bool) -> Result<AnyDatabase, DbError> { + pub async fn new(db: &DatabaseConfig, test_data: bool) -> DbResult<AnyDatabase> { let result = match db { #[cfg(feature = "postgres_db")] DatabaseConfig::Postgres(url) => { @@ -82,7 +82,7 @@ impl AnyDatabase { #[cfg(feature = "embedded_db")] DatabaseConfig::Embedded(path) => { tracing::info!("Creating an embedded db"); - AnyDatabase::Sled(sled::Database::new(path)?) + AnyDatabase::Embedded(embedded::Database::new(path)?) } DatabaseConfig::None => { tracing::warn!("No database configured"); @@ -91,87 +91,90 @@ impl AnyDatabase { }; if test_data { - for _ in 0..10 { + result.insert_test_data().await?; + } + + Ok(result) + } + + pub async fn insert_test_data(&self) -> DbResult<()> { + let mut futs: Vec<JoinHandle<DbResult<()>>> = vec![]; + + // TODO: tokio::scoped whenever it is added + // SAFETY: tokio::spawn needs to take a static ref + // because these JoinHandles could be ignored, but + // every single one of them is joined before returning + let sta: &'static Self = unsafe { transmute(self) }; + + // insert random users + for _ in 0..10 { + futs.push(tokio::spawn(async move { let name = Uuid::new_v4().to_string(); let passwd = Uuid::new_v4().to_string(); let salt = SaltString::generate(&mut OsRng); + // this is the slow part: let hash = Argon2::default().hash_password(passwd.as_bytes(), &salt)?; - let res = result.create_user(Uuid::new_v4(), &name, hash).await; + let res = sta.create_user(&name, hash).await; tracing::info!("Created a test user {name} with password {passwd} ({res:?})"); - } + + Ok(()) + })); } - Ok(result) - } -} + // insert random courses + // TODO: -/* impl Deref for AnyDatabase { - type Target = dyn DatabaseImpl; + // insert random lessons + // TODO: - fn deref(&self) -> &Self::Target { - match self { - #[cfg(feature = "postgres_db")] - AnyDatabase::Postgres(v) => v, - #[cfg(feature = "embedded_db")] - AnyDatabase::Sled(v) => v, - AnyDatabase::None => &(), + // insert random exercises + // TODO: + + // join handles + let results = join_all(futs).await; + for result in results { + result.unwrap_or_else(|err| { + // returning from `insert_test_data` is now UB + panic!("Failed to join a handle ({err})"); + })?; } + + Ok(()) } -} */ -impl DatabaseImpl for AnyDatabase { - fn get_courses(&self) -> GetCoursesFut { - match self { - #[cfg(feature = "postgres_db")] - AnyDatabase::Postgres(v) => v.get_courses(), - #[cfg(feature = "embedded_db")] - AnyDatabase::Sled(v) => v.get_courses(), - AnyDatabase::None => ().get_courses(), - } + pub async fn get_courses(&self) -> DbResult<Vec<Course>> { + gen_match!(self, get_courses()) } - fn create_user<'a>( - &'a self, - uuid: Uuid, - name: &'a str, - passwd: PasswordHash<'a>, - ) -> CreateUserFut<'a> { - match self { - #[cfg(feature = "postgres_db")] - AnyDatabase::Postgres(v) => v.create_user(uuid, name, passwd), - #[cfg(feature = "embedded_db")] - AnyDatabase::Sled(v) => v.create_user(uuid, name, passwd), - AnyDatabase::None => ().create_user(uuid, name, passwd), - } + pub async fn get_lessons(&self, course: Uuid) -> DbResult<Vec<Lesson>> { + gen_match!(self, get_lessons(course)) } - fn get_user<'a>(&'a self, name: &'a str) -> GetUserFut<'a> { - match self { - #[cfg(feature = "postgres_db")] - AnyDatabase::Postgres(v) => v.get_user(name), - #[cfg(feature = "embedded_db")] - AnyDatabase::Sled(v) => v.get_user(name), - AnyDatabase::None => ().get_user(name), - } + pub async fn get_exercises(&self, lesson: Uuid) -> DbResult<Vec<Exercise>> { + gen_match!(self, get_exercises(lesson)) + } + + pub async fn get_exercise_details(&self, exercise: Uuid) -> DbResult<Option<ExerciseDetail>> { + gen_match!(self, get_exercise_details(exercise)) } -} -impl DatabaseImpl for () { - fn get_courses(&self) -> GetCoursesFut { - Box::pin(async { Err(DbError::DatabaseNotConnected) }) + pub async fn get_exercise_verify( + &self, + exercise: Uuid, + ) -> DbResult<Option<ExerciseVerifyData>> { + gen_match!(self, get_exercise_verify(exercise)) } - fn create_user<'a>( + pub async fn create_user<'a>( &'a self, - _uuid: Uuid, - _name: &'a str, - _passwd: PasswordHash<'a>, - ) -> CreateUserFut<'a> { - Box::pin(async { Err(DbError::DatabaseNotConnected) }) + name: &'a str, + passwd: PasswordHash<'a>, + ) -> DbResult<DbCreateUser> { + gen_match!(self, create_user(name, passwd)) } - fn get_user<'a>(&'a self, _name: &'a str) -> GetUserFut<'a> { - Box::pin(async { Err(DbError::DatabaseNotConnected) }) + pub async fn get_user<'a>(&'a self, name: &'a str) -> DbResult<Option<DbUser>> { + gen_match!(self, get_user(name)) } } diff --git a/crates/back/src/db/embedded.rs b/crates/back/src/db/embedded.rs new file mode 100644 index 0000000000000000000000000000000000000000..af5fd91ab34b38512e0e69fcfda138c3dc48725b --- /dev/null +++ b/crates/back/src/db/embedded.rs @@ -0,0 +1,204 @@ +use super::{DbCreateUser, DbError, DbResult, DbUser}; +use crate::api::{ + course::Course, + exercise::{Exercise, ExerciseDetail, ExerciseVerifyData}, + lesson::Lesson, +}; +use argon2::PasswordHash; +use dashmap::{mapref::entry::Entry, DashMap}; +use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, + path::Path, +}; +use uuid::Uuid; + +// + +#[derive(Debug, Default)] +pub struct Database { + // hash : String |---> u64 + // lookup : u64 |---> Uuid + usernames: DashMap<u64, Uuid>, + + users: DashMap<Uuid, UsersRow>, + courses: DashMap<Uuid, CoursesRow>, + lessons: DashMap<Uuid, LessonsRow>, + exercises: DashMap<Uuid, ExercisesRow>, +} + +#[derive(Debug, Default)] +struct UsersRow { + username: String, + password: String, // argon2id + privileged: bool, +} + +#[derive(Debug, Default)] +struct CoursesRow { + name: String, + description: Option<String>, + + points_max: u32, + + _owner: Uuid, +} + +#[derive(Debug, Default)] +struct LessonsRow { + name: String, + description: Option<String>, + + points_max: u32, + + course: Uuid, +} + +#[derive(Debug, Default)] +struct ExercisesRow { + name: String, + description: Option<String>, + + points_max: u32, + attempts_max: u32, + + lesson: Uuid, + + code: String, + stdin: String, + stdout: String, +} + +// + +impl Database { + pub fn new(_path: &Path) -> Result<Self, DbError> { + // TODO: load + + Ok(Self::default()) + } +} + +impl Drop for Database { + fn drop(&mut self) { + // TODO: save + } +} + +impl Database { + pub async fn get_courses(&self) -> DbResult<Vec<Course>> { + Ok(self + .courses + .iter() + .map(|row| Course { + id: *row.key(), + name: row.name.clone(), // TODO: copy-on-write + description: row.description.clone(), + points: 0, // TODO: + points_max: row.points_max, + }) + .collect()) + } + + pub async fn get_lessons(&self, course: Uuid) -> DbResult<Vec<Lesson>> { + Ok(self + .lessons + .iter() + .filter(|row| row.course == course) + .map(|row| Lesson { + id: *row.key(), + name: row.name.clone(), // TODO: copy-on-write + description: row.description.clone(), + points: 0, // TODO: + points_max: row.points_max, + }) + .collect()) + } + + pub async fn get_exercises(&self, lesson: Uuid) -> DbResult<Vec<Exercise>> { + Ok(self + .exercises + .iter() + .filter(|row| row.lesson == lesson) + .map(|row| Exercise { + id: *row.key(), + name: row.name.clone(), // TODO: copy-on-write + description: row.description.clone(), + points: 0, // TODO: + points_max: row.points_max, + attempts: 0, // TODO: + attempts_max: row.attempts_max, + }) + .collect()) + } + + pub async fn get_exercise_details(&self, exercise: Uuid) -> DbResult<Option<ExerciseDetail>> { + Ok(self.exercises.get(&exercise).map(|row| ExerciseDetail { + code: row.code.clone(), // TODO: copy-on-write + stdin: row.stdin.clone(), + stdout: row.stdout.clone(), + })) + } + + pub async fn get_exercise_verify( + &self, + exercise: Uuid, + ) -> DbResult<Option<ExerciseVerifyData>> { + Ok(self.exercises.get(&exercise).map(|row| ExerciseVerifyData { + max_points: row.points_max, + stdin: row.stdin.clone(), + stdout: row.stdout.clone(), + })) + } + + pub async fn create_user<'a>( + &'a self, + name: &'a str, + passwd: PasswordHash<'a>, + ) -> DbResult<DbCreateUser> { + let res @ DbCreateUser::Created(uuid) = self.take_username(name).await else { + return Ok(DbCreateUser::Existed); + }; + + self.users.insert( + uuid, + UsersRow { + username: name.to_string(), + password: passwd.to_string(), + privileged: false, + }, + ); + + Ok(res) + } + + pub async fn get_user<'a>(&'a self, name: &'a str) -> DbResult<Option<DbUser>> { + let Some(uuid) = self.usernames.get(&Self::hash_username(name)).map(|row| *row.value()) else { + return Ok(None); + }; + + Ok(self.users.get(&uuid).map(|row| DbUser { + id: *row.key(), + name: row.username.clone(), + passwd: row.password.clone(), + privileged: row.privileged, + })) + } + + async fn take_username(&self, name: &str) -> DbCreateUser { + match self.usernames.entry(Self::hash_username(name)) { + Entry::Occupied(_) => DbCreateUser::Existed, + Entry::Vacant(entry) => { + let uuid = Uuid::new_v4(); + entry.insert(uuid); + DbCreateUser::Created(uuid) + } + } + } + + fn hash_username(name: &str) -> u64 { + let mut hasher = DefaultHasher::new(); + name.hash(&mut hasher); + hasher.finish() + } +} diff --git a/crates/back/src/db/mod.rs b/crates/back/src/db/mod.rs index b197880af4c707b9f4ff6900aa6cef1bb8ca1a77..1d9bb92670a59723255a1da8f3455460d91a24e0 100644 --- a/crates/back/src/db/mod.rs +++ b/crates/back/src/db/mod.rs @@ -1,5 +1,13 @@ -use self::any::{AnyDatabase, DatabaseImpl}; -use crate::{cli::DatabaseConfig, LazyAwait}; +use self::any::AnyDatabase; +use crate::{ + api::{ + course::Course, + exercise::{Exercise, ExerciseDetail, ExerciseVerifyData}, + lesson::Lesson, + }, + cli::DatabaseConfig, + LazyAwait, +}; use argon2::{ password_hash::{rand_core::OsRng, SaltString}, Argon2, PasswordHash, PasswordHasher, PasswordVerifier, @@ -12,10 +20,10 @@ use uuid::Uuid; // mod any; +#[cfg(feature = "embedded_db")] +mod embedded; #[cfg(feature = "postgres_db")] mod postgres; -#[cfg(feature = "embedded_db")] -mod sled; // @@ -23,15 +31,8 @@ pub struct Database { pool: LazyAwait<AnyDatabase>, } -#[derive(Debug, FromRow, Serialize, Deserialize)] -pub struct DbCourse { - pub id: Uuid, - pub name: String, - pub description: Option<String>, - pub points_max: i32, -} - -#[derive(Debug, FromRow, Serialize, Deserialize)] +#[derive(Debug, FromRow)] +#[cfg_attr(feature = "embedded_db", derive(Serialize, Deserialize))] pub struct DbUser { pub id: Uuid, pub name: String, @@ -39,13 +40,13 @@ pub struct DbUser { pub privileged: bool, // can create courses } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum DbCreateUser { Created(Uuid), Existed, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum DbVerifyUser { Valid((Uuid, bool)), Invalid, @@ -59,22 +60,15 @@ pub enum DbError { #[error("Database interaction error ({0})")] Database(#[from] sqlx::Error), - #[cfg(feature = "embedded_db")] + /* #[cfg(feature = "embedded_db")] #[error("Embedded database error ({0})")] - EDatabase(#[from] ::sled::Error), - - #[cfg(feature = "embedded_db")] - #[error("Embedded database error ({0})")] - EDatabaseTransaction(#[from] ::sled::transaction::TransactionError), - - #[cfg(feature = "embedded_db")] - #[error("Database data format error ({0})")] - DataFormat(#[from] bincode::Error), - + EDatabase(#[from] ::sled::Error), */ #[error("No connection to the database")] DatabaseNotConnected, } +pub type DbResult<T> = Result<T, DbError>; + // impl Database { @@ -84,29 +78,49 @@ impl Database { Self { pool } } - pub async fn get_courses(&self) -> Result<Vec<DbCourse>, DbError> { + pub async fn get_courses(&self) -> DbResult<Vec<Course>> { self.db().await.get_courses().await } - pub async fn create_user(&self, name: &str, passwd: &str) -> Result<DbCreateUser, DbError> { - let uuid = Uuid::new_v4(); + pub async fn get_lessons(&self, course: Uuid) -> DbResult<Vec<Lesson>> { + self.db().await.get_lessons(course).await + } + + pub async fn get_exercises(&self, lesson: Uuid) -> DbResult<Vec<Exercise>> { + self.db().await.get_exercises(lesson).await + } + + pub async fn get_exercise_details(&self, exercise: Uuid) -> DbResult<Option<ExerciseDetail>> { + self.db().await.get_exercise_details(exercise).await + } + + pub async fn get_exercise_verify( + &self, + exercise: Uuid, + ) -> DbResult<Option<ExerciseVerifyData>> { + self.db().await.get_exercise_verify(exercise).await + } + + pub async fn create_user(&self, name: &str, passwd: &str) -> DbResult<DbCreateUser> { let salt = SaltString::generate(&mut OsRng); let passwd = Argon2::default().hash_password(passwd.as_bytes(), &salt)?; - self.db().await.create_user(uuid, name, passwd).await + self.db().await.create_user(name, passwd).await } - pub async fn verify_user(&self, name: &str, _passwd: &str) -> Result<DbVerifyUser, DbError> { + pub async fn verify_user(&self, name: &str, passwd: &str) -> DbResult<DbVerifyUser> { let Some(user) = self.db().await.get_user(name).await? else { + tracing::trace!("Username not found"); // username not found return Ok(DbVerifyUser::Invalid); }; let hash = PasswordHash::new(&user.passwd)?; if Argon2::default() - .verify_password(user.passwd.as_bytes(), &hash) + .verify_password(passwd.as_bytes(), &hash) .is_err() { + tracing::trace!("Invalid password"); // invalid password return Ok(DbVerifyUser::Invalid); } diff --git a/crates/back/src/db/postgres.rs b/crates/back/src/db/postgres.rs index 393c1bcf6bca451d819f6ca6abac62bc14d88721..a00d642ac091b1312d53007b5ec5b0e0fbc11fc2 100644 --- a/crates/back/src/db/postgres.rs +++ b/crates/back/src/db/postgres.rs @@ -1,10 +1,11 @@ -use super::{ - any::{CreateUserFut, DatabaseImpl, GetCoursesFut, GetUserFut}, - DbCreateUser, DbError, +use super::{DbCreateUser, DbError, DbResult, DbUser}; +use crate::api::{ + course::Course, + exercise::{Exercise, ExerciseDetail, ExerciseVerifyData}, + lesson::Lesson, }; use argon2::PasswordHash; use sqlx::{postgres::PgPoolOptions, PgPool}; -use std::{future::Future, pin::Pin}; use uuid::Uuid; // @@ -29,60 +30,166 @@ impl Database { } } -impl DatabaseImpl for Database { - fn get_courses(&self) -> GetCoursesFut { - Box::pin(async move { - Ok(sqlx::query_as( - r#"SELECT id, name, description, points_max - FROM courses"#, - ) - .fetch_all(&self.pool) - .await?) - }) +impl Database { + pub async fn get_courses(&self) -> DbResult<Vec<Course>> { + Ok(sqlx::query_as( + r#"SELECT + (id, name, description, points_max) + FROM + courses"#, + ) + .fetch_all(&self.pool) + .await? + .into_iter() + .map( + |(id, name, description, points_max): (Uuid, String, Option<String>, i32)| Course { + id, + name, + description, + points: 0, // TODO: + points_max: points_max.max(0) as u32, + }, + ) + .collect()) + } + + pub async fn get_lessons(&self, course: Uuid) -> DbResult<Vec<Lesson>> { + Ok(sqlx::query_as( + r#"SELECT + (id, name, description, points_max) + FROM + lessons + WHERE + course = $1"#, + ) + .bind(course) + .fetch_all(&self.pool) + .await? + .into_iter() + .map( + |(id, name, description, points_max): (Uuid, String, Option<String>, i32)| Lesson { + id, + name, + description, + points: 0, // TODO: + points_max: points_max.max(0) as u32, + }, + ) + .collect()) + } + + pub async fn get_exercises(&self, lesson: Uuid) -> DbResult<Vec<Exercise>> { + Ok(sqlx::query_as( + r#"SELECT + (id, name, description, points_max, attempts_max) + FROM + exercises + WHERE + lesson = $1"#, + ) + .bind(lesson) + .fetch_all(&self.pool) + .await? + .into_iter() + .map( + |(id, name, description, points_max, attempts_max): ( + Uuid, + String, + Option<String>, + i32, + i32, + )| Exercise { + id, + name, + description, + points: 0, // TODO: + points_max: points_max.max(0) as u32, + attempts: 0, // TODO: + attempts_max: attempts_max.max(0) as u32, + }, + ) + .collect()) + } + + pub async fn get_exercise_details(&self, exercise: Uuid) -> DbResult<Option<ExerciseDetail>> { + Ok(sqlx::query_as( + r#"SELECT + (code, stdin, stdout) + FROM + exercises + WHERE + id = $1"#, + ) + .bind(exercise) + .fetch_optional(&self.pool) + .await? + .map( + |(code, stdin, stdout): (String, String, String)| ExerciseDetail { + code, + stdin, + stdout, + }, + )) } - fn create_user<'a>( + pub async fn get_exercise_verify( + &self, + exercise: Uuid, + ) -> DbResult<Option<ExerciseVerifyData>> { + Ok(sqlx::query_as( + r#"SELECT + (stdin, stdout, points_max) + FROM + exercises + WHERE + id = $1"#, + ) + .bind(exercise) + .fetch_optional(&self.pool) + .await? + .map( + |(stdin, stdout, max_points): (String, String, i32)| ExerciseVerifyData { + stdin, + stdout, + max_points: max_points.max(0) as _, + }, + )) + } + + pub async fn create_user<'a>( &'a self, - uuid: Uuid, name: &'a str, passwd: PasswordHash<'a>, - ) -> CreateUserFut<'a> { - Box::pin(async move { - let result: Option<(Uuid,)> = sqlx::query_as( - r#" - INSERT INTO - users (username, password) - VALUES - ($2, $3); - ON CONFLICT DO NOTHING - RETURNING id"#, - ) - .bind(uuid) - .bind(name) - .bind(passwd.to_string()) - .fetch_optional(&self.pool) - .await?; - - if let Some((uuid,)) = result { - Ok(DbCreateUser::Created(uuid)) - } else { - Ok(DbCreateUser::Existed) - } - }) as Pin<Box<dyn Future<Output = Result<DbCreateUser, DbError>> + Send>> + ) -> DbResult<DbCreateUser> { + Ok(sqlx::query_as( + r#"INSERT INTO + users (username, password) + VALUES + ($2, $3); + ON CONFLICT + DO NOTHING + RETURNING + id"#, + ) + .bind(name) + .bind(passwd.to_string()) + .fetch_optional(&self.pool) + .await? + .map(|(uuid,): (Uuid,)| DbCreateUser::Created(uuid)) + .unwrap_or(DbCreateUser::Existed)) } - fn get_user<'a>(&'a self, name: &'a str) -> GetUserFut<'a> { - Box::pin(async move { - let result = sqlx::query_as( - r#"SELECT (id, name, password, privileged) - FROM users - WHERE username = $1"#, - ) - .bind(name) - .fetch_optional(&self.pool) - .await?; - - Ok(result) - }) + pub async fn get_user<'a>(&'a self, name: &'a str) -> DbResult<Option<DbUser>> { + Ok(sqlx::query_as( + r#"SELECT + (id, name, password, privileged) + FROM + users + WHERE + username = $1"#, + ) + .bind(name) + .fetch_optional(&self.pool) + .await?) } } diff --git a/crates/back/src/db/sled.rs b/crates/back/src/db/sled.rs deleted file mode 100644 index 3d8c208a5e78c5ef3b9edbd22df4658da315dbec..0000000000000000000000000000000000000000 --- a/crates/back/src/db/sled.rs +++ /dev/null @@ -1,104 +0,0 @@ -use super::{ - any::{CreateUserFut, DatabaseImpl, GetCoursesFut, GetUserFut}, - DbCreateUser, DbError, DbUser, -}; -use argon2::PasswordHash; -use sled::{IVec, Tree}; -use std::path::Path; -use uuid::Uuid; - -// - -pub struct Database { - /* db: Db, */ - users: Tree, - courses: Tree, - /* course_admins: Tree, - lessons: Tree, - exercises: Tree, - - course_progress: Tree, - lesson_progress: Tree, - exercise_progress: Tree, */ -} - -// - -impl Database { - pub fn new(path: &Path) -> Result<Self, DbError> { - let db = sled::open(path)?; - - let users = db.open_tree("users")?; - let courses = db.open_tree("courses")?; - /* let course_admins = db.open_tree("course_admins")?; - let lessons = db.open_tree("lessons")?; - let exercises = db.open_tree("exercises")?; - let course_progress = db.open_tree("course_progress")?; - let lesson_progress = db.open_tree("lesson_progress")?; - let exercise_progress = db.open_tree("exercise_progress")?; */ - - Ok(Self { - /* db, */ - users, - courses, - /* course_admins, - lessons, - exercises, - - course_progress, - lesson_progress, - exercise_progress, */ - }) - } -} - -impl DatabaseImpl for Database { - fn get_courses(&self) -> GetCoursesFut { - Box::pin(async move { - self.courses - .iter() - .map(|row: Result<(IVec, IVec), sled::Error>| { - row.map_err(DbError::from) - .and_then(|(_, val)| Ok(bincode::deserialize(&val[..])?)) - }) - .collect::<Result<Vec<_>, _>>() - }) - } - - fn create_user<'a>( - &'a self, - uuid: Uuid, - name: &'a str, - passwd: PasswordHash<'a>, - ) -> CreateUserFut<'a> { - Box::pin(async move { - let userdata = bincode::serialize(&DbUser { - id: uuid, - name: name.to_owned(), - passwd: passwd.to_string(), - privileged: false, - })?; - - Ok(self.users.transaction(move |t| { - if t.get(uuid.as_bytes()).is_ok() { - return Ok(DbCreateUser::Existed); - } - - t.insert(uuid.as_bytes(), &userdata[..])?; - Ok(DbCreateUser::Created(uuid)) - })?) - }) - } - - fn get_user<'a>(&'a self, name: &'a str) -> GetUserFut<'a> { - Box::pin(async move { - // TODO: name -> uuid table - Ok(self - .users - .iter() - .flat_map(|row| row.ok()) - .flat_map(|(_, row)| bincode::deserialize(&row[..]).ok()) - .find(|i: &DbUser| i.name == name)) - }) - } -} diff --git a/crates/back/src/util/jwt.rs b/crates/back/src/util/jwt.rs new file mode 100644 index 0000000000000000000000000000000000000000..41be9c6de74db2b805a4d789f11e679c93dff22e --- /dev/null +++ b/crates/back/src/util/jwt.rs @@ -0,0 +1,29 @@ +use jwt_simple::prelude::*; +use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +// + +pub fn create_jwt(id: Uuid) -> anyhow::Result<String> { + let claims = Claims::with_custom_claims(JwtData { id }, Duration::from_hours(2)); + JWT_KEYS.authenticate(claims) +} + +pub fn verify_jwt(jwt: &str) -> Option<Uuid> { + JWT_KEYS + .verify_token::<JwtData>(jwt, None) + .ok() + .map(|claims| claims.custom.id) +} + +// + +// lazy initialized JWT crypt key +// => JWT:s become invalid after a server restart +static JWT_KEYS: Lazy<HS512Key> = Lazy::new(HS512Key::generate); + +#[derive(Serialize, Deserialize)] +struct JwtData { + id: Uuid, +} diff --git a/crates/back/src/util/mod.rs b/crates/back/src/util/mod.rs index 27c524ec3ad03d998264c4cf5dab81fb0fd9316b..2e79d6e40cc1f090be184d2b94193ff1ffcd0199 100644 --- a/crates/back/src/util/mod.rs +++ b/crates/back/src/util/mod.rs @@ -1,5 +1,6 @@ pub use actix_web::Result as HttpResult; pub use iter_list::*; +pub use jwt::*; pub use lazy_await::*; // @@ -10,6 +11,7 @@ use reqwest::Url; // mod iter_list; +mod jwt; mod lazy_await; //