diff --git a/.env b/.env new file mode 100644 index 0000000..e69de29 diff --git a/Cargo.lock b/Cargo.lock index aa67b9e..c3c0177 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,17 @@ dependencies = [ "regex", ] +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -21,6 +32,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "api" version = "0.1.0" @@ -32,6 +52,7 @@ dependencies = [ "redis", "serde", "serde_json", + "sqlx", "tokio", "tower-http", ] @@ -163,6 +184,21 @@ dependencies = [ "syn", ] +[[package]] +name = "async_once" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82" + +[[package]] +name = "atoi" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" +dependencies = [ + "num-traits", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -171,9 +207,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744864363a200a5e724a7e61bc8c11b6628cf2e3ec519c8a1a48e609a8156b40" +checksum = "08b108ad2665fa3f6e6a517c3d80ec3e77d224c47d605167aefaa5d7ef97fa48" dependencies = [ "async-trait", "axum-core", @@ -199,7 +235,7 @@ dependencies = [ "sha-1", "sync_wrapper", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.17.2", "tower", "tower-http", "tower-layer", @@ -286,6 +322,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "num-integer", + "num-traits", + "winapi", +] + [[package]] name = "codegen" version = "0.1.0" @@ -300,6 +348,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "combine" version = "4.6.6" @@ -339,6 +397,31 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.14" @@ -358,6 +441,50 @@ dependencies = [ "typenum", ] +[[package]] +name = "cxx" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "darling" version = "0.14.2" @@ -401,8 +528,41 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dotenvy" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0" + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + [[package]] name = "encoding_rs" version = "0.8.31" @@ -412,6 +572,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fast_chemail" version = "0.9.6" @@ -502,6 +668,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.11.2", +] + [[package]] name = "futures-io" version = "0.3.25" @@ -618,6 +795,18 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" +dependencies = [ + "hashbrown", +] [[package]] name = "headers" @@ -644,6 +833,15 @@ dependencies = [ "http", ] +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -653,6 +851,30 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "http" version = "0.2.8" @@ -730,6 +952,30 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -790,6 +1036,15 @@ version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.4" @@ -817,6 +1072,15 @@ version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + [[package]] name = "lock_api" version = "0.4.9" @@ -838,9 +1102,18 @@ dependencies = [ [[package]] name = "matchit" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfc802da7b1cf80aefffa0c7b2f77247c8b32206cc83c270b61264f5b360a80" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] [[package]] name = "memchr" @@ -854,6 +1127,12 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "mio" version = "0.8.5" @@ -902,6 +1181,26 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -972,6 +1271,17 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.5", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -979,7 +1289,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.4", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", ] [[package]] @@ -995,6 +1319,12 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "paste" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" + [[package]] name = "percent-encoding" version = "2.2.0" @@ -1125,7 +1455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" dependencies = [ "log", - "parking_lot", + "parking_lot 0.12.1", "scheduled-thread-pool", ] @@ -1189,6 +1519,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + [[package]] name = "regex" version = "1.7.0" @@ -1289,7 +1630,7 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "977a7519bff143a44f842fd07e80ad1329295bd71686457f18e496736f4bf9bf" dependencies = [ - "parking_lot", + "parking_lot 0.12.1", ] [[package]] @@ -1298,6 +1639,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + [[package]] name = "security-framework" version = "2.7.0" @@ -1323,18 +1670,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.148" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc" +checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" dependencies = [ "serde_derive", ] [[package]] -name = "serde_derive" -version = "1.0.148" +name = "serde-aux" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c" +checksum = "c599b3fd89a75e0c18d6d2be693ddb12cccaf771db4ff9e39097104808a014c0" +dependencies = [ + "chrono", + "serde", + "serde_json", +] + +[[package]] +name = "serde_derive" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" dependencies = [ "proc-macro2", "quote", @@ -1401,6 +1759,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -1441,18 +1810,136 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" +[[package]] +name = "sqlformat" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9249290c05928352f71c077cc44a464d880c63f26f7534728cca008e135c0428" +dependencies = [ + "sqlx-core", + "sqlx-macros", +] + +[[package]] +name = "sqlx-core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105" +dependencies = [ + "ahash", + "atoi", + "base64", + "bitflags", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "dirs", + "dotenvy", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-util", + "hashlink", + "hex", + "hkdf", + "hmac", + "indexmap", + "itoa", + "libc", + "log", + "md-5", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "rand", + "serde", + "serde_json", + "sha1", + "sha2", + "smallvec", + "sqlformat", + "sqlx-rt", + "stringprep", + "thiserror", + "tokio-stream", + "url", + "whoami", +] + +[[package]] +name = "sqlx-macros" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9" +dependencies = [ + "dotenvy", + "either", + "heck", + "once_cell", + "proc-macro2", + "quote", + "sha2", + "sqlx-core", + "sqlx-rt", + "syn", + "url", +] + +[[package]] +name = "sqlx-rt" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396" +dependencies = [ + "native-tls", + "once_cell", + "tokio", + "tokio-native-tls", +] + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.104" @@ -1474,8 +1961,13 @@ checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" name = "tasks" version = "0.1.0" dependencies = [ + "async_once", + "dotenvy", + "lazy_static", "once_cell", "redis", + "sqlx", + "tokio", ] [[package]] @@ -1508,6 +2000,15 @@ dependencies = [ "unic-segment", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.37" @@ -1554,9 +2055,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" dependencies = [ "autocfg", "bytes", @@ -1564,12 +2065,12 @@ dependencies = [ "memchr", "mio", "num_cpus", - "parking_lot", + "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.42.0", ] [[package]] @@ -1593,18 +2094,41 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-tungstenite" version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.17.3", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" dependencies = [ "futures-util", "log", "native-tls", "tokio", "tokio-native-tls", - "tungstenite", + "tungstenite 0.18.0", ] [[package]] @@ -1649,9 +2173,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ "bitflags", "bytes", @@ -1717,7 +2241,6 @@ dependencies = [ "http", "httparse", "log", - "native-tls", "rand", "sha-1", "thiserror", @@ -1725,6 +2248,26 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +dependencies = [ + "base64", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "native-tls", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.15.0" @@ -1808,6 +2351,24 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "url" version = "2.3.1" @@ -1944,17 +2505,32 @@ dependencies = [ name = "websocket" version = "0.1.0" dependencies = [ + "async_once", + "axum", "futures", "futures-util", "lazy_static", "redis", "serde", + "serde-aux", "serde_json", + "sqlx", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.18.0", "url", ] +[[package]] +name = "whoami" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" +dependencies = [ + "bumpalo", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/docker-compose.live.yaml b/docker-compose.live.yaml index 9f6c423..8826cf6 100644 --- a/docker-compose.live.yaml +++ b/docker-compose.live.yaml @@ -1,4 +1,5 @@ -# Temporary deployment stuff +# Temporary deployment stuff. +# Passwords in this file SHOULD BE CHANGED. version: '3.7' services: @@ -9,104 +10,34 @@ services: restart: always ports: - 6379:6379 - + + tsdb: + image: timescale/timescaledb:latest-pg14 + environment: + POSTGRES_PASSWORD: saerro321 + POSTGRES_USER: saerrouser + POSTGRES_DB: data + ports: + - 5432 + api: image: ghcr.io/genudine/saerro/api:latest pull_policy: always ports: - 8000:80 links: - - redis + - tsdb restart: always environment: - - REDIS_HOST=redis - - ROCKET_ADDRESS=0.0.0.0 - - ws_pc: + DATABASE_ADDR: postgres://saerrouser:saerro321@tsdb:$TSDB_PORT/data + + ws: image: ghcr.io/genudine/saerro/websocket:latest pull_policy: always restart: always environment: - REDIS_ADDR: redis://redis:6379 + DATABASE_ADDR: postgres://saerrouser:saerro321@tsdb:$TSDB_PORT/data WS_ADDR: wss://push.nanite-systems.net/streaming?environment=ps2&service-id=s:saegd - WORLDS: 1,10,13,17,19,40 - PAIR: pc - ROLE: primary + WORLDS: all links: - - redis - - ws_pc_backup: - image: ghcr.io/genudine/saerro/websocket:latest - pull_policy: always - restart: always - environment: - REDIS_ADDR: redis://redis:6379 - WS_ADDR: wss://push.planetside2.com/streaming?environment=ps2&service-id=s:saegd - WORLDS: 1,10,13,17,19,40 - PAIR: pc - ROLE: backup - links: - - redis - - ws_ps4us: - image: ghcr.io/genudine/saerro/websocket:latest - pull_policy: always - restart: always - environment: - REDIS_ADDR: redis://redis:6379 - WS_ADDR: wss://push.nanite-systems.net/streaming?environment=ps2ps4us&service-id=s:saegd - WORLDS: 1000 - PAIR: ps4us - ROLE: primary - links: - - redis - - ws_ps4us_backup: - image: ghcr.io/genudine/saerro/websocket:latest - pull_policy: always - restart: always - environment: - REDIS_ADDR: redis://redis:6379 - WS_ADDR: wss://push.planetside2.com/streaming?environment=ps2ps4us&service-id=s:saegd - WORLDS: 1000 - PAIR: ps4us - ROLE: backup - links: - - redis - - - ws_ps4eu: - image: ghcr.io/genudine/saerro/websocket:latest - pull_policy: always - restart: always - environment: - REDIS_ADDR: redis://redis:6379 - WS_ADDR: wss://push.nanite-systems.net/streaming?environment=ps2ps4eu&service-id=s:saegd - WORLDS: 2000 - PAIR: ps4eu - ROLE: primary - links: - - redis - - ws_ps4eu_backup: - image: ghcr.io/genudine/saerro/websocket:latest - pull_policy: always - restart: always - environment: - REDIS_ADDR: redis://redis:6379 - WS_ADDR: wss://push.planetside2.com/streaming?environment=ps2ps4eu&service-id=s:saegd - WORLDS: 2000 - PAIR: ps4eu - ROLE: backup - links: - - redis - - task_prune: - image: ghcr.io/genudine/saerro/tasks:latest - command: /app prune - pull_policy: always - restart: "no" - environment: - REDIS_ADDR: redis://redis:6379 - links: - - redis \ No newline at end of file + - tsdb diff --git a/docker-compose.yaml b/docker-compose.yaml index 5f35a05..61cd819 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -5,6 +5,14 @@ services: image: redis command: redis-server --save "" --appendonly no container_name: redis - restart: always ports: - "6379:6379" + + tsdb: + image: timescale/timescaledb:latest-pg14 + environment: + POSTGRES_PASSWORD: saerro321 + POSTGRES_USER: saerrouser + POSTGRES_DB: data + ports: + - 5432:5432 diff --git a/services/api/Cargo.toml b/services/api/Cargo.toml index b84b8c4..adf67cc 100644 --- a/services/api/Cargo.toml +++ b/services/api/Cargo.toml @@ -8,10 +8,11 @@ edition = "2021" [dependencies] redis = { version = "0.22.1", features = ["aio", "r2d2", "tokio-comp"] } serde_json = "1.0.89" -serde = "1.0.148" +serde = "1.0.149" async-graphql = { version = "5.0.2" } async-graphql-axum = "5.0.2" -axum = "0.6.0" -tokio = { version = "1.22.0" } -tower-http = { version = "0.3.4", features = ["cors"] } +axum = "0.6.1" +sqlx = { version = "0.6.2", features = [ "runtime-tokio-native-tls" , "postgres" ] } +tokio = { version = "1.23.0", features = [ "full" ] } +tower-http = { version = "0.3.5", features = ["cors"] } lazy_static = "1.4.0" \ No newline at end of file diff --git a/services/api/src/main.rs b/services/api/src/main.rs index 625b4d2..7662a81 100644 --- a/services/api/src/main.rs +++ b/services/api/src/main.rs @@ -6,8 +6,7 @@ mod vehicles; mod world; use async_graphql::{ - http::{playground_source, GraphQLPlaygroundConfig}, - EmptyMutation, EmptySubscription, Request, Response, Schema, + http::GraphiQLSource, EmptyMutation, EmptySubscription, Request, Response, Schema, }; use axum::{ extract::Query, @@ -42,13 +41,19 @@ async fn graphql_handler_get( query: Query, ) -> axum::response::Response { if query.query == "" { - return Redirect::to("/graphql/playground").into_response(); + return Redirect::to("/graphiql").into_response(); } Json(schema.execute(query.0).await).into_response() } -async fn graphql_playground() -> impl IntoResponse { - Html(playground_source(GraphQLPlaygroundConfig::new("/graphql"))) + +async fn graphiql() -> impl IntoResponse { + Html( + GraphiQLSource::build() + .endpoint("/graphql") + .title("Saerro Listening Post") + .finish(), + ) } #[tokio::main] @@ -76,7 +81,7 @@ async fn main() { "/graphql", post(graphql_handler_post).get(graphql_handler_get), ) - .route("/graphql/playground", get(graphql_playground)) + .route("/graphql/playground", get(graphiql)) .fallback(handle_404) .layer(Extension(redis)) .layer(Extension(schema)) diff --git a/services/tasks/Cargo.toml b/services/tasks/Cargo.toml index 369e529..feb9a2d 100644 --- a/services/tasks/Cargo.toml +++ b/services/tasks/Cargo.toml @@ -6,5 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -redis = { version = "0.22.1", default_features = false, features = [] } -once_cell = "1.16.0" \ No newline at end of file +redis = { version = "0.22.1", features = ["aio", "r2d2", "tokio-comp"] } +once_cell = "1.16.0" +tokio = { version = "1.23.0", features = ["full"] } +sqlx = { version = "0.6.2", features = [ "runtime-tokio-native-tls" , "postgres" ] } +lazy_static = "1.4.0" +async_once = "0.2.6" +dotenvy = "0.15.6" \ No newline at end of file diff --git a/services/tasks/src/main.rs b/services/tasks/src/main.rs index 0006e3c..42dd8ad 100644 --- a/services/tasks/src/main.rs +++ b/services/tasks/src/main.rs @@ -1,14 +1,29 @@ +use async_once::AsyncOnce; +use dotenvy::dotenv; +use lazy_static::lazy_static; +use migrations::cmd_migrate; use once_cell::sync::Lazy; use redis::Commands; +use sqlx::query; use std::env::args; use std::ops::Sub; use std::time::{Duration, SystemTime}; +mod migrations; + pub static REDIS_CLIENT: Lazy = Lazy::new(|| { redis::Client::open(std::env::var("REDIS_ADDR").unwrap_or("redis://localhost:6379".to_string())) .unwrap() }); +lazy_static! { + pub static ref PG: AsyncOnce = AsyncOnce::new(async { + let db_url = std::env::var("DATABASE_URL") + .unwrap_or("postgres://saerrouser:saerro321@localhost:5432/data".to_string()); + sqlx::PgPool::connect(&db_url).await.unwrap() + }); +} + fn cmd_prune() { println!("Pruning old data..."); let mut con = REDIS_CLIENT.get_connection().unwrap(); @@ -46,14 +61,19 @@ fn cmd_help() { println!("Commands:"); println!(" help - Show this help message"); println!(" prune - Remove stale data from Redis"); + println!(" migrate - Reset and create database tables"); } -fn main() { +#[tokio::main] +async fn main() { + dotenv().ok(); + let command = args().nth(1).unwrap_or("help".to_string()); match command.as_str() { "help" => cmd_help(), "prune" => cmd_prune(), + "migrate" => cmd_migrate().await, _ => { println!("Unknown command: {}", command); cmd_help(); diff --git a/services/tasks/src/migrations.rs b/services/tasks/src/migrations.rs new file mode 100644 index 0000000..de6a9a2 --- /dev/null +++ b/services/tasks/src/migrations.rs @@ -0,0 +1,137 @@ +use crate::PG; +use sqlx::query; + +pub async fn cmd_migrate() { + println!("Migrating database..."); + + tokio::join!(migrate_players(), migrate_classes(), migrate_vehicles(),); +} + +async fn migrate_players() { + let pool = PG.get().await; + + println!("-> Migrating players"); + + println!("PLAYERS => DROP TABLE IF EXISTS players"); + query("DROP TABLE IF EXISTS players") + .execute(pool) + .await + .unwrap(); + + println!("PLAYERS => CREATE TABLE players"); + query( + "CREATE TABLE players ( + time TIMESTAMPTZ NOT NULL, + character_id TEXT NOT NULL, + world_id INT NOT NULL, + faction_id INT NOT NULL, + zone_id INT NOT NULL);", + ) + .execute(pool) + .await + .unwrap(); + + println!("PLAYERS => create_hypertable"); + query( + "SELECT create_hypertable('players', 'time', + chunk_time_interval => INTERVAL '1 minute', if_not_exists => TRUE);", + ) + .execute(pool) + .await + .unwrap(); + + println!("PLAYERS => add_retention_policy"); + query("SELECT add_retention_policy('players', INTERVAL '15 minutes');") + .execute(pool) + .await + .unwrap(); + + println!("PLAYERS => done!"); +} + +async fn migrate_classes() { + let pool = PG.get().await; + + println!("-> Migrating classes"); + + println!("CLASSES => DROP TABLE IF EXISTS classes"); + query("DROP TABLE IF EXISTS classes") + .execute(pool) + .await + .unwrap(); + + println!("CLASSES => CREATE TABLE classes"); + query( + "CREATE TABLE classes ( + time TIMESTAMPTZ NOT NULL, + character_id TEXT NOT NULL, + world_id INT NOT NULL, + faction_id INT NOT NULL, + zone_id INT NOT NULL, + class_id TEXT NOT NULL);", + ) + .execute(pool) + .await + .unwrap(); + + println!("CLASSES => create_hypertable"); + query( + "SELECT create_hypertable('classes', 'time', + chunk_time_interval => INTERVAL '1 minute', if_not_exists => TRUE);", + ) + .execute(pool) + .await + .unwrap(); + + println!("CLASSES => add_retention_policy"); + query("SELECT add_retention_policy('classes', INTERVAL '15 minutes');") + .execute(pool) + .await + .unwrap(); + + println!("CLASSES => done!"); +} + +async fn migrate_vehicles() { + let pool = PG.get().await; + + println!("-> Migrating vehicles"); + + println!("VEHICLES => DROP TABLE IF EXISTS vehicles"); + query("DROP TABLE IF EXISTS vehicles") + .execute(pool) + .await + .unwrap(); + + println!("VEHICLES => CREATE TABLE vehicles"); + query( + "CREATE TABLE vehicles ( + time TIMESTAMPTZ NOT NULL, + character_id TEXT NOT NULL, + world_id INT NOT NULL, + faction_id INT NOT NULL, + zone_id INT NOT NULL, + vehicle_id TEXT NOT NULL);", + ) + .execute(pool) + .await + .unwrap(); + + println!("VEHICLES => create_hypertable"); + query( + "SELECT create_hypertable('vehicles', 'time', + chunk_time_interval => INTERVAL '1 minute', if_not_exists => TRUE);", + ) + .execute(pool) + .await + .unwrap(); + + println!("VEHICLES => add_retention_policy"); + + query("SELECT add_retention_policy('vehicles', INTERVAL '15 minutes');") + .execute(pool) + .await + .unwrap(); + + println!("VEHICLES => done!"); +} diff --git a/services/websocket/Cargo.toml b/services/websocket/Cargo.toml index 84ac9f2..61b545f 100644 --- a/services/websocket/Cargo.toml +++ b/services/websocket/Cargo.toml @@ -8,10 +8,14 @@ edition = "2021" [dependencies] redis = { version = "0.22.1", default_features = false, features = ["r2d2"] } lazy_static = "1.4.0" -tokio-tungstenite = { version = "0.17.2", features=["native-tls"] } +tokio-tungstenite = { version = "0.18.0", features=["native-tls"] } +serde = { version = "1.0.149", features = ["derive"] } serde_json = "1.0.89" -serde = { version = "1.0.148", features = ["derive"] } -tokio = { version = "1.22.0" } +tokio = { version = "1.23.0", features = ["full"] } +sqlx = { version = "0.6.2", features = [ "runtime-tokio-native-tls" , "postgres" ] } url = "2.3.1" futures-util = "0.3.25" futures = "0.3.25" +async_once = "0.2.6" +serde-aux = "4.1.2" +axum = "0.6.1" \ No newline at end of file diff --git a/services/websocket/src/main.rs b/services/websocket/src/main.rs index 1ce17a0..3da9d18 100644 --- a/services/websocket/src/main.rs +++ b/services/websocket/src/main.rs @@ -1,24 +1,27 @@ +use async_once::AsyncOnce; +use axum::{routing::get, Router}; use futures::{pin_mut, FutureExt}; use futures_util::StreamExt; use lazy_static::lazy_static; -use redis::Commands; use serde::Deserialize; +use serde_aux::prelude::*; use serde_json::json; -use std::{env, time::SystemTime}; +use sqlx::{postgres::PgPoolOptions, query}; +use std::{env, net::SocketAddr, time::Duration}; +use tokio::task::JoinSet; use tokio_tungstenite::{connect_async, tungstenite::Message}; mod translators; lazy_static! { - static ref REDIS_CLIENT: redis::Client = redis::Client::open(format!( - "redis://{}:{}", - std::env::var("REDIS_HOST").unwrap_or("localhost".to_string()), - std::env::var("REDIS_PORT").unwrap_or("6379".to_string()), - )) - .unwrap(); static ref PAIR: String = env::var("PAIR").unwrap_or_default(); static ref ROLE: String = env::var("ROLE").unwrap_or("primary".to_string()); static ref WS_ADDR: String = env::var("WS_ADDR").unwrap_or_default(); + static ref PG: AsyncOnce = AsyncOnce::new(async { + let db_url = std::env::var("DATABASE_URL") + .unwrap_or("postgres://saerrouser:saerro321@localhost:5432/data".to_string()); + PgPoolOptions::new().connect(&db_url).await.unwrap() + }); } async fn send_init(tx: futures::channel::mpsc::UnboundedSender) { @@ -42,54 +45,69 @@ async fn send_init(tx: futures::channel::mpsc::UnboundedSender) { tx.unbounded_send(Message::text(setup_msg.to_string())) .unwrap(); - println!("Sent setup message"); + println!("[ws] Sent setup message"); } +#[derive(Clone)] struct PopEvent { - world_id: String, - team_id: String, + world_id: i32, + team_id: i32, character_id: String, - timestamp: u64, + zone_id: i32, } struct VehicleEvent { - world_id: String, + world_id: i32, vehicle_id: String, character_id: String, - timestamp: u64, + zone_id: i32, + team_id: i32, } struct ClassEvent { - world_id: String, + world_id: i32, character_id: String, loadout_id: String, - timestamp: u64, + zone_id: i32, + team_id: i32, } +// async fn track_pop(pop_event: PopEvent) { +// track_pop_db(pop_event.clone()).await; +// track_pop_redis(pop_event).await; +// } + async fn track_pop(pop_event: PopEvent) { - let mut con = REDIS_CLIENT.get_connection().unwrap(); + // println!("[ws/track_pop]"); + let pool = PG.get().await; let PopEvent { world_id, team_id, character_id, - timestamp, + zone_id, } = pop_event; - let key = format!("wp:{}/{}", world_id, team_id); - let _: () = con.zadd(key, character_id.clone(), timestamp).unwrap(); - let key = format!("wp:{}", world_id); - let _: () = con.zadd(key, character_id, timestamp).unwrap(); + query("INSERT INTO players (time, character_id, world_id, faction_id, zone_id) VALUES (now(), $1, $2, $3, $4);") + .bind(character_id) + .bind(world_id) + .bind(team_id) + .bind(zone_id) + .execute(pool) + .await + .unwrap(); } async fn track_vehicle(vehicle_event: VehicleEvent) { - let mut con = REDIS_CLIENT.get_connection().unwrap(); + // println!("[ws/track_vehicle]"); + let pool = PG.get().await; let VehicleEvent { world_id, vehicle_id, - timestamp, + zone_id, character_id, + team_id, } = vehicle_event; let vehicle_name = translators::vehicle_to_name(vehicle_id.as_str()); @@ -98,18 +116,27 @@ async fn track_vehicle(vehicle_event: VehicleEvent) { return; } - let key = format!("v:{}/{}", world_id, vehicle_name); - let _: () = con.zadd(key, character_id, timestamp).unwrap(); + query("INSERT INTO vehicles (time, character_id, world_id, faction_id, zone_id, vehicle_id) VALUES (now(), $1, $2, $3, $4, $5);") + .bind(character_id) + .bind(world_id) + .bind(team_id) + .bind(zone_id) + .bind(vehicle_name) + .execute(pool) + .await + .unwrap(); } async fn track_class(class_event: ClassEvent) { - let mut con = REDIS_CLIENT.get_connection().unwrap(); + // println!("[ws/track_class]"); + let pool = PG.get().await; let ClassEvent { world_id, character_id, loadout_id, - timestamp, + zone_id, + team_id, } = class_event; let class_name = translators::loadout_to_class(loadout_id.as_str()); @@ -118,108 +145,107 @@ async fn track_class(class_event: ClassEvent) { return; } - let key = format!("c:{}/{}", world_id, class_name); - let _: () = con.zadd(key, character_id, timestamp).unwrap(); + query( + "INSERT INTO classes ( + time, + character_id, + world_id, + faction_id, + zone_id, + class_id + ) VALUES (now(), $1, $2, $3, $4, $5);", + ) + .bind(character_id) + .bind(world_id) + .bind(team_id) + .bind(zone_id) + .bind(class_name) + .execute(pool) + .await + .unwrap(); } -fn should_process_event() -> bool { - let mut con = REDIS_CLIENT.get_connection().unwrap(); - let role: String = ROLE.parse().unwrap(); - - let heartbeat_key = format!("heartbeat:{}:{}", PAIR.to_string(), role); - let _: () = con.set_ex(heartbeat_key, "1", 60).unwrap(); - - if role == "primary" { - return false; +async fn process_event(event: &Event) { + let mut set = JoinSet::new(); + // println!("[ws/process_event] EVENT: {:?}", event); + if event.character_id != "0" { + // General population tracking + set.spawn(track_pop(PopEvent { + world_id: event.world_id.clone(), + team_id: event.team_id.clone(), + character_id: event.character_id.clone(), + zone_id: event.zone_id.clone(), + })); } - let primary_heartbeat_key = format!("heartbeat:{}:primary", PAIR.to_string()); - match con.get(primary_heartbeat_key) { - Ok(1) => true, - _ => false, - } -} - -fn process_event(event: &Event) { - if should_process_event() { - return; - } - - let timestamp = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(); - - // General population tracking - track_pop(PopEvent { - world_id: event.world_id.clone(), - team_id: event.team_id.clone(), - character_id: event.character_id.clone(), - timestamp, - }) - .now_or_never(); - if event.event_name == "VehicleDestroy" { - track_vehicle(VehicleEvent { + set.spawn(track_vehicle(VehicleEvent { world_id: event.world_id.clone(), vehicle_id: event.vehicle_id.clone(), character_id: event.character_id.clone(), - timestamp, - }) - .now_or_never(); + zone_id: event.zone_id.clone(), + team_id: event.team_id.clone(), + })); } if event.event_name == "Death" { - track_class(ClassEvent { + set.spawn(track_class(ClassEvent { world_id: event.world_id.clone(), character_id: event.character_id.clone(), loadout_id: event.loadout_id.clone(), - timestamp, - }) - .now_or_never(); + zone_id: event.zone_id.clone(), + team_id: event.team_id.clone(), + })); } if event.attacker_character_id != "" - && (event.attacker_team_id != "" || event.attacker_team_id != "0") + && event.attacker_character_id != "0" + && (event.attacker_team_id != 0 || event.attacker_team_id != 0) { - track_pop(PopEvent { + set.spawn(track_pop(PopEvent { world_id: event.world_id.clone(), team_id: event.attacker_team_id.clone(), character_id: event.attacker_character_id.clone(), - timestamp, - }) - .now_or_never(); + zone_id: event.zone_id.clone(), + })); if event.event_name == "VehicleDestroy" { - track_vehicle(VehicleEvent { + set.spawn(track_vehicle(VehicleEvent { world_id: event.world_id.clone(), vehicle_id: event.attacker_vehicle_id.clone(), character_id: event.attacker_character_id.clone(), - timestamp, - }) - .now_or_never(); + zone_id: event.zone_id.clone(), + team_id: event.attacker_team_id.clone(), + })); } if event.event_name == "Death" { - track_class(ClassEvent { + set.spawn(track_class(ClassEvent { world_id: event.world_id.clone(), character_id: event.attacker_character_id.clone(), loadout_id: event.attacker_loadout_id.clone(), - timestamp, - }) - .now_or_never(); + zone_id: event.zone_id.clone(), + team_id: event.attacker_team_id.clone(), + })); } } + + while let Some(_) = set.join_next().await {} } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug, Clone, Default)] struct Event { event_name: String, - world_id: String, + #[serde(deserialize_with = "deserialize_number_from_string")] + world_id: i32, character_id: String, attacker_character_id: String, - attacker_team_id: String, - team_id: String, + #[serde(deserialize_with = "deserialize_number_from_string")] + attacker_team_id: i32, + #[serde(deserialize_with = "deserialize_number_from_string")] + team_id: i32, + #[serde(deserialize_with = "deserialize_number_from_string")] + zone_id: i32, // Class Tracking #[serde(default)] @@ -239,23 +265,22 @@ struct Payload { payload: Event, } -// /// Send a longer heartbeat in case this is PS4EU and gets like one event per hour -// async fn heartbeat() { -// let mut interval = tokio::time::interval(Duration::from_secs(150)); -// loop { -// interval.tick().await; -// let mut con = REDIS_CLIENT.get_connection().unwrap(); -// let role: String = ROLE.parse().unwrap(); -// let heartbeat_key = format!("heartbeat:{}:{}", PAIR.to_string(), role); -// let response: Option = con.get(heartbeat_key.clone()).unwrap(); -// match response { -// None => { -// let _: () = con.set_ex(heartbeat_key, "1", 300).unwrap(); -// } -// _ => (), -// } -// } -// } +async fn healthz() { + let app = Router::new().route("/healthz", get(|| async { "ok" })); + + let port: u16 = std::env::var("PORT") + .unwrap_or("8999".to_string()) + .parse() + .unwrap(); + let addr = SocketAddr::from(([0, 0, 0, 0], port)); + + println!("[healthz] Listening on http://{}", addr); + + axum::Server::bind(&addr) + .serve(app.into_make_service()) + .await + .unwrap(); +} #[tokio::main] async fn main() { @@ -273,42 +298,27 @@ async fn main() { let fused_writer = rx.map(Ok).forward(write).fuse(); let fused_reader = read .for_each(|msg| async move { - // println!("Processing event: {:?}", msg); - let body = &msg.unwrap().to_string(); let data: Payload = serde_json::from_str(body).unwrap_or(Payload { - payload: Event { - event_name: "".to_string(), - world_id: "".to_string(), - character_id: "".to_string(), - attacker_character_id: "".to_string(), - attacker_team_id: "".to_string(), - team_id: "".to_string(), - attacker_loadout_id: "".to_string(), - loadout_id: "".to_string(), - vehicle_id: "".to_string(), - attacker_vehicle_id: "".to_string(), - }, + payload: Event::default(), }); if data.payload.event_name == "" { return; } - process_event(&data.payload); + process_event(&data.payload).await; }) .fuse(); pin_mut!(fused_writer, fused_reader); let init = tokio::spawn(send_init(tx.clone())); - + let mut healthz = tokio::spawn(healthz()).fuse(); futures::select! { _ = fused_reader => {} _ = fused_writer => {} + _ = healthz => {} } - - // tokio::spawn(heartbeat()); - init.await.unwrap(); }