🎉WebSocket refactored!

This commit is contained in:
Shiroyasha 2024-10-28 03:19:33 +03:00
parent 7a4f3dc7a5
commit 4c0871e26c
Signed by: shiroyashik
GPG key ID: E4953D3940D7860A
30 changed files with 650 additions and 587 deletions

323
Cargo.lock generated
View file

@ -60,9 +60,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.89"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
[[package]]
name = "arbitrary"
@ -75,13 +75,13 @@ dependencies = [
[[package]]
name = "async-trait"
version = "0.1.82"
version = "0.1.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
]
[[package]]
@ -103,9 +103,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.3.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "axum"
@ -139,7 +139,7 @@ dependencies = [
"sync_wrapper 1.0.1",
"tokio",
"tokio-tungstenite",
"tower 0.5.1",
"tower",
"tower-layer",
"tower-service",
"tracing",
@ -174,7 +174,7 @@ checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
]
[[package]]
@ -233,9 +233,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.7.1"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
[[package]]
name = "bzip2"
@ -260,9 +260,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.1.19"
version = "1.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800"
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
dependencies = [
"jobserver",
"libc",
@ -529,7 +529,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
]
[[package]]
@ -571,7 +571,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
]
[[package]]
@ -594,9 +594,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "encoding_rs"
version = "0.8.34"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
@ -635,9 +635,9 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "flate2"
version = "1.0.33"
version = "1.0.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253"
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
dependencies = [
"crc32fast",
"miniz_oxide 0.8.0",
@ -675,9 +675,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
@ -685,33 +685,33 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-io"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-sink"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-io",
@ -762,7 +762,7 @@ dependencies = [
"futures-core",
"futures-sink",
"http",
"indexmap 2.5.0",
"indexmap 2.6.0",
"slab",
"tokio",
"tokio-util",
@ -781,6 +781,12 @@ version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hashbrown"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
[[package]]
name = "heck"
version = "0.4.1"
@ -862,9 +868,9 @@ dependencies = [
[[package]]
name = "httparse"
version = "1.9.4"
version = "1.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
[[package]]
name = "httpdate"
@ -874,9 +880,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.4.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
dependencies = [
"bytes",
"futures-channel",
@ -928,9 +934,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba"
checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b"
dependencies = [
"bytes",
"futures-channel",
@ -941,7 +947,6 @@ dependencies = [
"pin-project-lite",
"socket2",
"tokio",
"tower 0.4.13",
"tower-service",
"tracing",
]
@ -997,12 +1002,12 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
"equivalent",
"hashbrown 0.14.5",
"hashbrown 0.15.0",
"serde",
]
@ -1017,9 +1022,9 @@ dependencies = [
[[package]]
name = "ipnet"
version = "2.10.0"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4"
checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
[[package]]
name = "is_ci"
@ -1044,9 +1049,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.70"
version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
dependencies = [
"wasm-bindgen",
]
@ -1059,9 +1064,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.158"
version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
[[package]]
name = "libredox"
@ -1244,15 +1249,15 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.19.0"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "openssl"
version = "0.10.66"
version = "0.10.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
dependencies = [
"bitflags 2.6.0",
"cfg-if",
@ -1271,7 +1276,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
]
[[package]]
@ -1282,9 +1287,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.103"
version = "0.9.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
dependencies = [
"cc",
"libc",
@ -1352,31 +1357,11 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
]
[[package]]
name = "pin-project-lite"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
[[package]]
name = "pin-utils"
@ -1386,9 +1371,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "powerfmt"
@ -1431,9 +1416,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.86"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-ident",
]
@ -1479,9 +1464,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.4"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
dependencies = [
"bitflags 2.6.0",
]
@ -1499,14 +1484,14 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.6"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.7",
"regex-syntax 0.8.4",
"regex-automata 0.4.8",
"regex-syntax 0.8.5",
]
[[package]]
@ -1520,13 +1505,13 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.4",
"regex-syntax 0.8.5",
]
[[package]]
@ -1537,15 +1522,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.4"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "reqwest"
version = "0.12.7"
version = "0.12.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b"
dependencies = [
"base64",
"bytes",
@ -1617,9 +1602,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.37"
version = "0.38.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a"
dependencies = [
"bitflags 2.6.0",
"errno",
@ -1630,9 +1615,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.13"
version = "0.23.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993"
dependencies = [
"once_cell",
"rustls-pki-types",
@ -1643,19 +1628,18 @@ dependencies = [
[[package]]
name = "rustls-pemfile"
version = "2.1.3"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"base64",
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.8.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
[[package]]
name = "rustls-webpki"
@ -1670,9 +1654,9 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.17"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
[[package]]
name = "ryu"
@ -1691,9 +1675,9 @@ dependencies = [
[[package]]
name = "schannel"
version = "0.1.24"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b"
checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1"
dependencies = [
"windows-sys 0.59.0",
]
@ -1716,7 +1700,7 @@ dependencies = [
"dashmap",
"dotenvy",
"hex",
"indexmap 2.5.0",
"indexmap 2.6.0",
"lazy_static",
"rand",
"reqwest",
@ -1752,9 +1736,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
version = "2.11.1"
version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6"
dependencies = [
"core-foundation-sys",
"libc",
@ -1768,22 +1752,22 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.210"
version = "1.0.213"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
version = "1.0.213"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
]
[[package]]
@ -1797,9 +1781,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.128"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
dependencies = [
"itoa",
"memchr",
@ -1819,9 +1803,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "0.6.7"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [
"serde",
]
@ -1957,9 +1941,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.77"
version = "2.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
dependencies = [
"proc-macro2",
"quote",
@ -2004,9 +1988,9 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.12.0"
version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
dependencies = [
"cfg-if",
"fastrand",
@ -2032,22 +2016,22 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
[[package]]
name = "thiserror"
version = "1.0.64"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.64"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
]
[[package]]
@ -2108,9 +2092,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.40.0"
version = "1.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
dependencies = [
"backtrace",
"bytes",
@ -2132,7 +2116,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
]
[[package]]
@ -2213,32 +2197,17 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.21"
version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [
"indexmap 2.5.0",
"indexmap 2.6.0",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "tower"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"pin-project",
"pin-project-lite",
"tokio",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower"
version = "0.5.1"
@ -2315,7 +2284,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
]
[[package]]
@ -2400,9 +2369,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-bidi"
version = "0.3.15"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
[[package]]
name = "unicode-ident"
@ -2412,9 +2381,9 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "unicode-normalization"
version = "0.1.23"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
dependencies = [
"tinyvec",
]
@ -2444,9 +2413,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "uuid"
version = "1.10.0"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
dependencies = [
"serde",
]
@ -2496,9 +2465,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
dependencies = [
"cfg-if",
"once_cell",
@ -2507,24 +2476,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.43"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed"
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
dependencies = [
"cfg-if",
"js-sys",
@ -2534,9 +2503,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -2544,28 +2513,28 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
[[package]]
name = "web-sys"
version = "0.3.70"
version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -2737,9 +2706,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.6.18"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
dependencies = [
"memchr",
]
@ -2762,7 +2731,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
]
[[package]]
@ -2782,7 +2751,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
"syn 2.0.85",
]
[[package]]
@ -2801,7 +2770,7 @@ dependencies = [
"displaydoc",
"flate2",
"hmac",
"indexmap 2.5.0",
"indexmap 2.6.0",
"lzma-rs",
"memchr",
"pbkdf2",

View file

@ -7,40 +7,40 @@ publish = false
[dependencies]
# Logging
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "chrono"] }
tracing-appender = "0.2.3"
tracing-panic = "0.1.2"
tracing = "0.1.40"
tracing-subscriber = { version = "0.3", features = ["env-filter", "chrono"] }
tracing-appender = "0.2"
tracing-panic = "0.1"
tracing = "0.1"
# Errors handelers
anyhow = "1.0.83"
thiserror = "1.0.64"
chrono = { version = "0.4.38", features = ["now", "serde"] }
serde = { version = "1.0.201", features = ["derive"] }
serde_json = "1.0.117"
toml = "0.8.19"
anyhow = "1.0"
thiserror = "1.0"
chrono = { version = "0.4", features = ["now", "serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.8"
# Other
dashmap = { version = "6.0.1", features = ["serde"] }
hex = "0.4.3"
uuid = { version = "1.8.0", features = ["serde"] }
base64 = "0.22.1"
reqwest = { version = "0.12.7", features = ["blocking", "json"] }
dotenvy = "0.15.7"
semver = "1.0.23"
walkdir = "2.5.0"
dashmap = { version = "6.0", features = ["serde"] }
hex = "0.4"
uuid = { version = "1.11", features = ["serde"] }
base64 = "0.22"
reqwest = { version = "0.12", features = ["blocking", "json"] }
dotenvy = "0.15"
semver = "1.0"
walkdir = "2.5"
# Crypto
ring = "0.17.8"
rand = "0.8.5"
ring = "0.17"
rand = "0.8"
# Web framework
axum = { version = "0.7.7", features = ["ws", "macros", "http2"] }
tower-http = { version = "0.6.1", features = ["trace"] }
tokio = { version = "1.37.0", features = ["full"] }
indexmap = { version = "2.5.0", features = ["serde"] }
zip = "2.2.0"
lazy_static = "1.5.0"
axum = { version = "0.7", features = ["ws", "macros", "http2"] }
tower-http = { version = "0.6", features = ["trace"] }
tokio = { version = "1.37", features = ["full"] }
indexmap = { version = "2.6", features = ["serde"] }
zip = "2.2"
lazy_static = "1.5"
[dev-dependencies]
cross = "0.2.5"

View file

@ -1,6 +1,6 @@
## Chef
# FROM clux/muslrust:stable AS chef
FROM rust:1.81.0-alpine3.20 AS chef
FROM rust:1.82.0-alpine3.20 AS chef
USER root
RUN apk add --no-cache musl-dev libressl-dev
RUN cargo install cargo-chef

View file

@ -4,6 +4,7 @@
# The Sculptor
[![Push Dev](https://github.com/shiroyashik/sculptor/actions/workflows/dev-release.yml/badge.svg?branch=dev)](https://github.com/shiroyashik/sculptor/actions/workflows/dev-release.yml)
![maintenance-status](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg)
Unofficial backend for the Minecraft mod [Figura](https://github.com/FiguraMC/Figura).
@ -19,11 +20,11 @@ I'm keeping the public server running at the moment!
You can use it if running your own Sculptor instance is difficult for you.
To connect, simply change **IP Server** in Figura settings to the address below:
To connect, simply change **Figura Cloud IP** in Figura settings to the address below:
> figura.shsr.ru
Authentication is enabled on the server via: Mojang and [Ely.By](https://ely.by/)
Authentication is enabled on the server via: Mojang(Microsoft) and [Ely.By](https://ely.by/)
For reasons beyond my control, the server is not available in some countries.
@ -73,10 +74,15 @@ cargo run --release
```
## Contributing
![Ask me anything!](https://img.shields.io/badge/Ask%20me-anything-1abc9c.svg)
on
[![Telegram](https://badgen.net/static/icon/telegram?icon=telegram&color=cyan&label)](https://t.me/shiroyashik)
or
![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)
If you have ideas for new features, have found a bug, or want to suggest improvements,
please create an [issue](https://github.com/shiroyashik/sculptor/issues)
or contact me directly via Discord/Telegram (@shiroyashik).
or contact me directly via Discord/Telegram (**@shiroyashik**).
If you are a Rust developer, you can modify the code yourself and request a Pull Request:

View file

@ -3,12 +3,13 @@
# The Sculptor
[![Push Dev](https://github.com/shiroyashik/sculptor/actions/workflows/dev-release.yml/badge.svg?branch=dev)](https://github.com/shiroyashik/sculptor/actions/workflows/dev-release.yml)
![maintenance-status](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg)
Неофициальный бэкенд для Minecraft мода [Figura](https://github.com/FiguraMC/Figura).
Это полноценная замена официальной версии. Реализован весь функционал который вы можете использовать во время игры.
А также отличительной особенностью является возможность игры с сторонними провайдерерами аутентификации (таких как [Ely.By](https://ely.by/))
А также отличительной особенностью является возможность игры с сторонними провайдерерами аутентификации (такими как [Ely.By](https://ely.by/))
## Публичный сервер
@ -18,13 +19,13 @@
Вы можете использовать его если запуск собственного сервера затруднителен для вас.
Для подключения достаточно сменить **Сервер IP** в настройках Figura на адрес ниже:
Для подключения достаточно сменить **IP сервера Figura** в настройках Figura на адрес ниже:
> figura.shsr.ru
На сервере включена аутентификация через: Mojang и [Ely.By](https://ely.by/)
На сервере включена аутентификация через: Mojang(Microsoft) и [Ely.By](https://ely.by/)
По неконтролируемым мною причинам, сервер не доступен в некоторых странах
По неконтролируемым мною причинам, сервер не доступен в некоторых странах.
## Запуск
@ -71,15 +72,20 @@ cargo run --release
```
## Вклад в развитие
![Спроси меня о чём угодно!](https://img.shields.io/badge/Ask%20me-anything-1abc9c.svg)
в
[![Telegram](https://badgen.net/static/icon/telegram?icon=telegram&color=cyan&label)](https://t.me/shiroyashik)
или
![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)
Если у вас есть идем, нашли баг или хотите предложить улучшения
создавайте [issue](https://github.com/shiroyashik/sculptor/issues)
или свяжитесь со мной напрямую через Discord/Telegram (@shiroyashik).
или свяжитесь со мной напрямую через Discord/Telegram (**@shiroyashik**).
Если вы Rust разработчик, буду рад вашим Pull Request'ам:
1. Форкните репу
2. Создайте новую репу для вашего гения
2. Создайте новую ветку
3. Создайте PR!
Буду рад любой вашей помощи! ❤

View file

@ -21,10 +21,10 @@ async fn versions() -> ApiResult<Json<Value>> {
let mut directories = Vec::new();
let mut entries = fs::read_dir(dir_path).await.map_err(|err| internal_and_log(err))?;
let mut entries = fs::read_dir(dir_path).await.map_err(internal_and_log)?;
while let Some(entry) = entries.next_entry().await.map_err(|err| internal_and_log(err))? {
if entry.metadata().await.map_err(|err| internal_and_log(err))?.is_dir() {
while let Some(entry) = entries.next_entry().await.map_err(internal_and_log)? {
if entry.metadata().await.map_err(internal_and_log)?.is_dir() {
if let Some(name) = entry.file_name().to_str() {
let name = name.to_string();
if !name.starts_with('.') {
@ -38,7 +38,7 @@ async fn versions() -> ApiResult<Json<Value>> {
}
async fn hashes(Path(version): Path<String>) -> ApiResult<Json<IndexMap<String, Value>>> {
let map = index_assets(&version).await.map_err(|err| internal_and_log(err))?;
let map = index_assets(&version).await.map_err(internal_and_log)?;
Ok(Json(map))
}
@ -49,7 +49,7 @@ async fn download(Path((version, path)): Path<(String, String)>) -> ApiResult<Ve
return Err(ApiError::NotFound)
};
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).await.map_err(|err| internal_and_log(err))?;
file.read_to_end(&mut buffer).await.map_err(internal_and_log)?;
Ok(buffer)
}
@ -65,13 +65,11 @@ async fn index_assets(version: &str) -> anyhow::Result<IndexMap<String, Value>>
Err(_) => continue
};
let path: String;
if cfg!(windows) {
path = entry.path().strip_prefix(version_path.clone())?.to_string_lossy().to_string().replace("\\", "/");
let path: String = if cfg!(windows) {
entry.path().strip_prefix(version_path.clone())?.to_string_lossy().to_string().replace("\\", "/")
} else {
path = entry.path().strip_prefix(version_path.clone())?.to_string_lossy().to_string();
}
entry.path().strip_prefix(version_path.clone())?.to_string_lossy().to_string()
};
map.insert(path, Value::from(hex::encode(digest(&SHA256, &data).as_ref())));
}

View file

@ -5,4 +5,4 @@ pub mod profile;
pub mod info;
pub mod assets;
pub use websocket::handler as ws;
pub use websocket::{initial as ws, SessionMessage};

View file

@ -14,7 +14,7 @@ use crate::{
auth::Token, utils::{calculate_file_sha256, format_uuid},
ApiError, ApiResult, AppState, AVATARS_VAR
};
use super::types::S2CMessage;
use super::websocket::S2CMessage;
pub async fn user_info(
Path(uuid): Path<Uuid>,
@ -85,7 +85,7 @@ pub async fn download_avatar(Path(uuid): Path<Uuid>) -> ApiResult<Vec<u8>> {
return Err(ApiError::NotFound)
};
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).await.map_err(|err| internal_and_log(err))?;
file.read_to_end(&mut buffer).await.map_err(internal_and_log)?;
Ok(buffer)
}
@ -103,15 +103,15 @@ pub async fn upload_avatar(
user_info.username
);
let avatar_file = format!("{}/{}.moon", *AVATARS_VAR, user_info.uuid);
let mut file = BufWriter::new(fs::File::create(&avatar_file).await.map_err(|err| internal_and_log(err))?);
io::copy(&mut request_data.as_ref(), &mut file).await.map_err(|err| internal_and_log(err))?;
let mut file = BufWriter::new(fs::File::create(&avatar_file).await.map_err(internal_and_log)?);
io::copy(&mut request_data.as_ref(), &mut file).await.map_err(internal_and_log)?;
}
Ok("ok".to_string())
}
pub async fn equip_avatar(Token(token): Token, State(state): State<AppState>) -> ApiResult<&'static str> {
debug!("[API] S2C : Equip");
let uuid = state.user_manager.get(&token).ok_or_else(|| ApiError::Unauthorized)?.uuid;
let uuid = state.user_manager.get(&token).ok_or(ApiError::Unauthorized)?.uuid;
send_event(&state, &uuid).await;
Ok("ok")
}
@ -124,7 +124,7 @@ pub async fn delete_avatar(Token(token): Token, State(state): State<AppState>) -
user_info.username
);
let avatar_file = format!("{}/{}.moon", *AVATARS_VAR, user_info.uuid);
fs::remove_file(avatar_file).await.map_err(|err| internal_and_log(err))?;
fs::remove_file(avatar_file).await.map_err(internal_and_log)?;
send_event(&state, &user_info.uuid).await;
}
Ok("ok".to_string())
@ -132,16 +132,16 @@ pub async fn delete_avatar(Token(token): Token, State(state): State<AppState>) -
pub async fn send_event(state: &AppState, uuid: &Uuid) {
// To user subscribers
if let Some(broadcast) = state.broadcasts.get(&uuid) {
if broadcast.send(S2CMessage::Event(*uuid).to_vec()).is_err() {
if let Some(broadcast) = state.subscribes.get(uuid) {
if broadcast.send(S2CMessage::Event(*uuid).into()).is_err() {
debug!("[WebSocket] Failed to send Event! There is no one to send. UUID: {uuid}")
};
} else {
debug!("[WebSocket] Failed to send Event! Can't find UUID: {uuid}")
};
// To user
if let Some(session) = state.session.get(&uuid) {
if session.send(S2CMessage::Event(*uuid).to_vec()).await.is_err() {
if let Some(session) = state.session.get(uuid) {
if session.send(super::SessionMessage::Ping(S2CMessage::Event(*uuid).into())).await.is_err() {
debug!("[WebSocket] Failed to send Event! WS doesn't connected? UUID: {uuid}")
};
} else {

View file

@ -1,8 +1 @@
mod c2s;
mod errors;
mod s2c;
pub mod auth;
pub use c2s::C2SMessage;
pub use errors::MessageLoadError;
pub use s2c::S2CMessage;

View file

@ -1,234 +0,0 @@
use std::sync::Arc;
use axum::{
extract::{
ws::{Message, WebSocket},
State, WebSocketUpgrade,
},
response::Response,
};
use dashmap::DashMap;
use tracing::{debug, error, info, trace, warn};
use tokio::sync::{
broadcast::{self, Receiver},
mpsc, Notify,
};
use uuid::Uuid;
use crate::AppState;
use super::types::{C2SMessage, S2CMessage};
pub async fn handler(ws: WebSocketUpgrade, State(state): State<AppState>) -> Response {
ws.on_upgrade(|socket| handle_socket(socket, state))
}
#[derive(Debug, Clone)]
struct WSUser {
username: String,
uuid: Uuid,
}
trait ExtWSUser {
fn name(&self) -> String;
}
impl ExtWSUser for Option<WSUser> {
fn name(&self) -> String {
if let Some(user) = self {
format!(" ({})", user.username)
} else {
String::new()
}
}
}
async fn handle_socket(mut socket: WebSocket, state: AppState) {
debug!("[WebSocket] New unknown connection!");
let mut owner: Option<WSUser> = None; // Information about user
let cutoff: DashMap<Uuid, Arc<Notify>> = DashMap::new(); // Отключение подписки
let (mtx, mut mrx) = mpsc::channel(64); // multiple tx and single receive
let mut bctx: Option<broadcast::Sender<Vec<u8>>> = None; // broadcast tx send
loop {
tokio::select! {
// Main loop what receving messages from WebSocket
Some(msg) = socket.recv() => {
trace!("[WebSocket{}] Raw: {msg:?}", owner.name());
let mut msg = if let Ok(msg) = msg {
if let Message::Close(_) = msg {
info!("[WebSocket{}] Connection successfully closed!", owner.name());
break;
}
msg
} else {
debug!("[WebSocket{}] Receive error! Connection terminated!", owner.name());
break;
};
// Checking ban list
if let Some(ref user) = owner {
if state.user_manager.is_banned(&user.uuid) {
warn!("[WebSocket] Detected banned user with active WebSocket! Sending close with Banned code.");
let _ = socket.send(Message::Binary(S2CMessage::Toast(2, "You're banned!", None).to_vec())).await; // option слищком жирный Some("Reason: Lorum Ipsum interсно сколько влезет~~~ 0w0.")
tokio::time::sleep(std::time::Duration::from_secs(6)).await;
debug!("{:?}", socket.send(Message::Close(Some(axum::extract::ws::CloseFrame { code: 4001, reason: "You're banned!".into() }))).await);
continue;
}
}
// Next is the code for processing msg
let msg_vec = msg.clone().into_data();
let msg_array = msg_vec.as_slice();
if msg_array.len() == 0 { tracing::debug!("[WebSocket{}] Deprecated len 0 msg", owner.name()); continue; };
let newmsg = match C2SMessage::try_from(msg_array) {
Ok(data) => data,
Err(e) => {
error!("[WebSocket{}] This message is not from Figura! {}", owner.name(), e.to_string());
debug!("[WebSocket{}] Broken data: {}", owner.name(), hex::encode(msg_vec));
continue;
// break;
},
};
debug!("[WebSocket{}] MSG: {:?}, HEX: {}", owner.name(), newmsg, hex::encode(newmsg.to_vec()));
match newmsg {
C2SMessage::Token(token) => {
trace!("[WebSocket{}] C2S : Token", owner.name());
let token = String::from_utf8(token.to_vec()).unwrap();
match state.user_manager.get(&token) { // The principle is simple: if there is no token in authenticated, then it's "dirty hacker" :D
Some(t) => {
//username = t.username.clone();
owner = Some(WSUser { username: t.username.clone(), uuid: t.uuid });
state.session.insert(t.uuid, mtx.clone());
msg = Message::Binary(S2CMessage::Auth.to_vec());
match state.broadcasts.get(&t.uuid) {
Some(tx) => {
bctx = Some(tx.to_owned());
},
None => {
let (tx, _rx) = broadcast::channel(64);
state.broadcasts.insert(t.uuid, tx.clone());
bctx = Some(tx.to_owned());
},
};
},
None => {
warn!("[WebSocket] Authentication error! Sending close with Re-auth code.");
debug!("[WebSocket] Tried to log in with {token}"); // Tried to log in with token: {token}
debug!("{:?}", socket.send(Message::Close(Some(axum::extract::ws::CloseFrame { code: 4000, reason: "Re-auth".into() }))).await);
continue;
},
};
},
C2SMessage::Ping(_, _, _) => {
trace!("[WebSocket{}] C2S : Ping", owner.name());
let data = into_s2c_ping(msg_vec, owner.clone().unwrap().uuid);
match bctx.clone().unwrap().send(data) {
Ok(_) => (),
Err(_) => debug!("[WebSocket{}] Failed to send Ping! Maybe there's no one to send", owner.name()),
};
continue;
},
// Subscribing
C2SMessage::Sub(uuid) => { // TODO: Eliminate the possibility of using SUB without authentication
trace!("[WebSocket{}] C2S : Sub", owner.name());
// Ignoring self Sub
if uuid == owner.clone().unwrap().uuid {
continue;
};
let rx = match state.broadcasts.get(&uuid) { // Get sender
Some(rx) => rx.to_owned().subscribe(), // Subscribe on sender to get receiver
None => {
warn!("[WebSocket{}] Attention! The required UUID for subscription was not found!", owner.name());
let (tx, rx) = broadcast::channel(64); // Pre creating broadcast for future
state.broadcasts.insert(uuid, tx); // Inserting into dashmap
rx
},
};
let shutdown = Arc::new(Notify::new()); // Creating new shutdown <Notify>
tokio::spawn(subscribe(mtx.clone(), rx, shutdown.clone())); // <For send pings to >
cutoff.insert(uuid, shutdown);
continue;
},
// Unsubscribing
C2SMessage::Unsub(uuid) => {
trace!("[WebSocket{}] C2S : Unsub", owner.name());
// Ignoring self Unsub
if uuid == owner.clone().unwrap().uuid {
continue;
};
let shutdown = cutoff.remove(&uuid).unwrap().1; // Getting <Notify> from list // FIXME: UNWRAP PANIC! NONE VALUE
shutdown.notify_one(); // Shutdown <subscribe> function
continue;
},
}
// Sending message
debug!("[WebSocket{}] Answering: {msg:?}", owner.name());
if socket.send(msg).await.is_err() {
warn!("[WebSocket{}] Send error! Connection terminated!", owner.name());
break;
}
}
msg = mrx.recv() => {
match socket.send(Message::Binary(msg.clone().unwrap())).await {
Ok(_) => {
debug!("[WebSocketSubscribe{}] Answering: {}", owner.name(), hex::encode(msg.unwrap()));
}
Err(_) => {
warn!("[WebSocketSubscriber{}] Send error! Connection terminated!", owner.name());
break;
}
}
}
}
}
// Closing connection
if let Some(u) = owner {
debug!("[WebSocket ({})] Removing session data", u.username);
state.session.remove(&u.uuid); // FIXME: Temporary solution
// state.broadcasts.remove(&u.uuid); // NOTE: Create broadcasts manager ??
state.user_manager.remove(&u.uuid);
} else {
debug!("[WebSocket] Nothing to remove");
}
}
async fn subscribe(
socket: mpsc::Sender<Vec<u8>>,
mut rx: Receiver<Vec<u8>>,
shutdown: Arc<Notify>,
) {
loop {
tokio::select! {
_ = shutdown.notified() => {
debug!("SUB successfully closed!");
return;
}
msg = rx.recv() => {
let msg = msg.ok();
if let Some(msg) = msg {
if socket.send(msg.clone()).await.is_err() {
debug!("Forced shutdown SUB! Client died?");
return;
};
} else {
debug!("Forced shutdown SUB! Source died?");
return;
}
}
}
}
}
fn into_s2c_ping(buf: Vec<u8>, uuid: Uuid) -> Vec<u8> {
use std::iter::once;
once(1)
.chain(uuid.into_bytes().iter().copied())
.chain(buf.as_slice()[1..].iter().copied())
.collect()
}

View file

@ -0,0 +1,212 @@
use anyhow::bail;
use axum::extract::{ws::{Message, WebSocket}, State};
use dashmap::DashMap;
use tokio::sync::{broadcast, mpsc};
use crate::{auth::Userinfo, AppState};
use super::{processor::*, AuthModeError, S2CMessage, C2SMessage, WSSession, SessionMessage, RADError};
pub async fn initial(
ws: axum::extract::WebSocketUpgrade,
State(state): State<AppState>
) -> axum::response::Response {
ws.on_upgrade(|socket| handle_socket(socket, state))
}
async fn handle_socket(mut ws: WebSocket, state: AppState) {
// Trying authenticate & get user data or dropping connection
match authenticate(&mut ws, &state).await {
Ok(user) => {
// Creating session & creating/getting channels
let mut session = {
let sub_workers_aborthandles = DashMap::new();
// Channel for receiving messages from internal functions.
let (own_tx, own_rx) = mpsc::channel(32);
state.session.insert(user.uuid, own_tx.clone());
// Channel for sending messages to subscribers
let subs_tx = match state.subscribes.get(&user.uuid) {
Some(tx) => tx.clone(),
None => {
tracing::debug!("[Subscribes] Can't find own subs channel for {}, creating new...", user.uuid);
let (subs_tx, _) = broadcast::channel(32);
state.subscribes.insert(user.uuid, subs_tx.clone());
subs_tx
},
};
WSSession { user: user.clone(), own_tx, own_rx, subs_tx, sub_workers_aborthandles }
};
// Starting main worker
match main_worker(&mut session, &mut ws, &state).await {
Ok(_) => (),
Err(kind) => tracing::error!("[WebSocket] Main worker halted due to {}.", kind),
}
for (_, handle) in session.sub_workers_aborthandles {
handle.abort();
}
// Removing session data
state.session.remove(&user.uuid);
state.user_manager.remove(&user.uuid);
},
Err(kind) => {
tracing::info!("[WebSocket] Can't authenticate: {}", kind);
}
}
// Closing connection
if let Err(kind) = ws.close().await { tracing::trace!("[WebSocket] Closing fault: {}", kind) }
}
async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppState) -> anyhow::Result<()> {
tracing::debug!("WebSocket control for {} is transferred to the main worker", session.user.username);
loop {
tokio::select! {
external_msg = ws.recv_and_decode() => {
// Getting a value or halt the worker without an error
let external_msg = match external_msg {
Ok(m) => m,
Err(kind) => {
match kind {
RADError::Close(_) => return Ok(()),
RADError::StreamClosed => return Ok(()),
_ => return Err(kind.into())
}
},
};
// Processing message
match external_msg {
C2SMessage::Token(_) => bail!("authentication passed, but the client sent the Token again"),
C2SMessage::Ping(func_id, echo, data) => {
let s2c_ping: Vec<u8> = S2CMessage::Ping(session.user.uuid, func_id, echo, data).into();
// Echo check
if echo {
ws.send(Message::Binary(s2c_ping.clone())).await?
}
// Sending to others
let _ = session.subs_tx.send(s2c_ping);
},
C2SMessage::Sub(uuid) => {
tracing::debug!("[WebSocket] {} subscribes to {}", session.user.username, uuid);
// Doesn't allow to subscribe to yourself
if session.user.uuid != uuid {
// Creates a channel to send pings to a subscriber if it can't find an existing one
let rx = match state.subscribes.get(&uuid) {
Some(tx) => tx.subscribe(),
None => {
let (tx, rx) = broadcast::channel(32);
state.subscribes.insert(uuid, tx);
rx
},
};
let handle = tokio::spawn(sub_worker(session.own_tx.clone(), rx)).abort_handle();
session.sub_workers_aborthandles.insert(uuid, handle);
}
},
C2SMessage::Unsub(uuid) => {
tracing::debug!("[WebSocket] {} unsubscribes from {}", session.user.username, uuid);
match session.sub_workers_aborthandles.get(&uuid) {
Some(handle) => handle.abort(),
None => tracing::warn!("[WebSocket] {} was not subscribed.", session.user.username),
};
},
}
},
internal_msg = session.own_rx.recv() => {
let internal_msg = internal_msg.ok_or(anyhow::anyhow!("Unexpected error! Session channel broken!"))?;
match internal_msg {
SessionMessage::Ping(msg) => {
ws.send(Message::Binary(msg)).await?
},
SessionMessage::Banned => {
let _ = ban_action(ws).await
.inspect_err(
|kind| tracing::warn!("[WebSocket] Didn't get the ban message due to {}", kind)
);
bail!("{} banned!", session.user.username)
},
}
}
}
}
}
async fn sub_worker(tx_main: mpsc::Sender<SessionMessage>, mut rx: broadcast::Receiver<Vec<u8>>) {
loop {
let msg = match rx.recv().await {
Ok(m) => m,
Err(kind) => {
tracing::error!("[Subscribes_Worker] Broadcast error! {}", kind);
return;
},
};
match tx_main.send(SessionMessage::Ping(msg)).await {
Ok(_) => (),
Err(kind) => {
tracing::error!("[Subscribes_Worker] Session error! {}", kind);
return;
},
}
}
}
async fn authenticate(socket: &mut WebSocket, state: &AppState) -> Result<Userinfo, AuthModeError> {
match socket.recv_and_decode().await {
Ok(msg) => {
match msg {
C2SMessage::Token(token) => {
let token = String::from_utf8(token.to_vec()).map_err(|_| AuthModeError::ConvertError)?;
match state.user_manager.get(&token) {
Some(user) => {
if socket.send(Message::Binary(S2CMessage::Auth.into())).await.is_err() {
Err(AuthModeError::SendError)
} else if !user.banned {
Ok(user.clone())
} else {
let _ = ban_action(socket).await
.inspect_err(
|kind| tracing::warn!("[WebSocket] Didn't get the ban message due to {}", kind)
);
Err(AuthModeError::Banned(user.username.clone()))
}
},
None => {
if socket.send(
Message::Close(Some(axum::extract::ws::CloseFrame { code: 4000, reason: "Re-auth".into() }))
).await.is_err() {
Err(AuthModeError::SendError)
} else {
Err(AuthModeError::AuthenticationFailure)
}
},
}
},
_ => {
Err(AuthModeError::UnauthorizedAction)
}
}
},
Err(err) => {
Err(AuthModeError::RecvError(err))
},
}
}
async fn ban_action(ws: &mut WebSocket) -> anyhow::Result<()> {
ws.send(Message::Binary(S2CMessage::Toast(2, "You're banned!".to_string(), None).into())).await?;
tokio::time::sleep(std::time::Duration::from_secs(6)).await;
ws.send(Message::Close(Some(axum::extract::ws::CloseFrame { code: 4001, reason: "You're banned!".into() }))).await?;
Ok(())
}

View file

@ -0,0 +1,8 @@
// mod websocket;
mod handler;
mod processor;
mod types;
// pub use websocket::*;
pub use handler::initial;
pub use types::*;

View file

@ -0,0 +1,32 @@
use axum::extract::ws::{Message, WebSocket};
use super::{C2SMessage, RADError};
pub trait RecvAndDecode {
async fn recv_and_decode(&mut self) -> Result<C2SMessage, RADError>;
}
impl RecvAndDecode for WebSocket {
async fn recv_and_decode(&mut self) -> Result<C2SMessage, RADError> {
if let Some(msg) = self.recv().await {
match msg {
Ok(msg) => {
match msg {
Message::Close(frame) => Err(RADError::Close(frame.map(|f| format!("code: {}, reason: {}", f.code, f.reason)))),
_ => {
match C2SMessage::try_from(msg.clone().into_data().as_slice()) {
Ok(decoded) => Ok(decoded),
Err(e) => {
Err(RADError::DecodeError(e, hex::encode(msg.into_data())))
},
}
}
}
},
Err(e) => Err(RADError::WebSocketError(e)),
}
} else {
Err(RADError::StreamClosed)
}
}
}

View file

@ -5,27 +5,27 @@ use std::convert::{TryFrom, TryInto};
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum C2SMessage<'a> {
Token(&'a [u8]) = 0,
Ping(u32, bool, &'a [u8]) = 1,
pub enum C2SMessage {
Token(Vec<u8>) = 0,
Ping(u32, bool, Vec<u8>) = 1,
Sub(Uuid) = 2, // owo
Unsub(Uuid) = 3,
}
// 6 - 6
impl<'a> TryFrom<&'a [u8]> for C2SMessage<'a> {
impl TryFrom<&[u8]> for C2SMessage {
type Error = MessageLoadError;
fn try_from(buf: &'a [u8]) -> Result<Self, <Self as TryFrom<&'a [u8]>>::Error> {
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
if buf.is_empty() {
Err(MessageLoadError::BadLength("C2SMessage", 1, false, 0))
} else {
match buf[0] {
0 => Ok(C2SMessage::Token(&buf[1..])),
0 => Ok(C2SMessage::Token(buf[1..].to_vec())),
1 => {
if buf.len() >= 6 {
Ok(C2SMessage::Ping(
u32::from_be_bytes((&buf[1..5]).try_into().unwrap()),
buf[5] != 0,
&buf[6..],
buf[6..].to_vec(),
))
} else {
Err(MessageLoadError::BadLength(
@ -73,10 +73,10 @@ impl<'a> TryFrom<&'a [u8]> for C2SMessage<'a> {
}
}
}
impl<'a> From<C2SMessage<'a>> for Box<[u8]> {
fn from(val: C2SMessage<'a>) -> Self {
impl From<C2SMessage> for Vec<u8> {
fn from(val: C2SMessage) -> Self {
use std::iter;
let a: Box<[u8]> = match val {
let a: Vec<u8> = match val {
C2SMessage::Token(t) => iter::once(0).chain(t.iter().copied()).collect(),
C2SMessage::Ping(p, s, d) => iter::once(1)
.chain(p.to_be_bytes())
@ -90,11 +90,11 @@ impl<'a> From<C2SMessage<'a>> for Box<[u8]> {
}
}
impl<'a> C2SMessage<'a> {
pub fn to_array(&self) -> Box<[u8]> {
<C2SMessage as Into<Box<[u8]>>>::into(self.clone())
}
pub fn to_vec(&self) -> Vec<u8> {
self.to_array().to_vec()
}
}
// impl<'a> C2SMessage<'a> {
// pub fn to_array(&self) -> Box<[u8]> {
// <C2SMessage as Into<Box<[u8]>>>::into(self.clone())
// }
// pub fn to_vec(&self) -> Vec<u8> {
// self.to_array().to_vec()
// }
// }

View file

@ -1,6 +1,8 @@
use std::fmt::*;
use std::ops::RangeInclusive;
use thiserror::Error;
#[derive(Debug)]
pub enum MessageLoadError {
BadEnum(&'static str, RangeInclusive<usize>, usize),
@ -23,6 +25,35 @@ impl Display for MessageLoadError {
}
}
}
#[derive(Error, Debug)]
pub enum RADError {
#[error("message decode error due: {0}, invalid data: {1}")]
DecodeError(MessageLoadError, String),
#[error("close, frame: {0:?}")]
Close(Option<String>),
#[error(transparent)]
WebSocketError(#[from] axum::Error),
#[error("stream closed")]
StreamClosed,
}
#[derive(Error, Debug)]
pub enum AuthModeError {
#[error("token recieve error due {0}")]
RecvError(RADError),
#[error("action attempt without authentication")]
UnauthorizedAction,
#[error("convert error, bytes into string")]
ConvertError,
#[error("can't send, websocket broken")]
SendError,
#[error("authentication failure, sending re-auth...")]
AuthenticationFailure,
#[error("{0} banned")]
Banned(String),
}
#[cfg(test)]
#[test]
fn message_load_error_display() {

View file

@ -0,0 +1,9 @@
mod c2s;
mod s2c;
mod errors;
mod session;
pub use session::*;
pub use errors::*;
pub use c2s::*;
pub use s2c::*;

View file

@ -5,17 +5,19 @@ use uuid::Uuid;
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum S2CMessage<'a> {
pub enum S2CMessage {
Auth = 0,
Ping(Uuid, u32, bool, &'a [u8]) = 1,
Ping(Uuid, u32, bool, Vec<u8>) = 1,
Event(Uuid) = 2, // Updates avatar for other players
Toast(u8, &'a str, Option<&'a str>) = 3,
Chat(&'a str) = 4,
Toast(u8, String, Option<String>) = 3,
Chat(String) = 4,
Notice(u8) = 5,
}
impl<'a> TryFrom<&'a [u8]> for S2CMessage<'a> {
impl TryFrom<&[u8]> for S2CMessage {
type Error = MessageLoadError;
fn try_from(buf: &'a [u8]) -> Result<Self, <Self as TryFrom<&'a [u8]>>::Error> {
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
if buf.is_empty() {
Err(MessageLoadError::BadLength("S2CMessage", 1, false, 0))
} else {
@ -35,7 +37,7 @@ impl<'a> TryFrom<&'a [u8]> for S2CMessage<'a> {
Uuid::from_bytes((&buf[1..17]).try_into().unwrap()),
u32::from_be_bytes((&buf[17..21]).try_into().unwrap()),
buf[21] != 0,
&buf[22..],
buf[22..].to_vec(),
))
} else {
Err(BadLength("S2CMessage::Ping", 22, false, buf.len()))
@ -56,12 +58,13 @@ impl<'a> TryFrom<&'a [u8]> for S2CMessage<'a> {
}
}
}
impl<'a> From<S2CMessage<'a>> for Box<[u8]> {
fn from(val: S2CMessage<'a>) -> Self {
impl From<S2CMessage> for Vec<u8> {
fn from(val: S2CMessage) -> Self {
use std::iter::once;
use S2CMessage::*;
match val {
Auth => Box::new([0]),
Auth => vec![0],
Ping(u, i, s, d) => once(1)
.chain(u.into_bytes().iter().copied())
.chain(i.to_be_bytes().iter().copied())
@ -74,20 +77,20 @@ impl<'a> From<S2CMessage<'a>> for Box<[u8]> {
.chain(h.as_bytes().iter().copied())
.chain(
d.into_iter()
.flat_map(|s| once(0).chain(s.as_bytes().iter().copied())),
.flat_map(|s| once(0).chain(s.as_bytes().iter().copied()).collect::<Vec<_>>()), // FIXME: Try find other solution
)
.collect(),
Chat(c) => once(4).chain(c.as_bytes().iter().copied()).collect(),
Notice(t) => Box::new([5, t]),
Notice(t) => vec![5, t],
}
}
}
impl<'a> S2CMessage<'a> {
pub fn to_array(&self) -> Box<[u8]> {
<S2CMessage as Into<Box<[u8]>>>::into(self.clone())
}
pub fn to_vec(&self) -> Vec<u8> {
self.to_array().to_vec()
}
}
// impl<'a> S2CMessage<'a> {
// pub fn to_array(&self) -> Box<[u8]> {
// <S2CMessage as Into<Box<[u8]>>>::into(self.clone())
// }
// pub fn to_vec(&self) -> Vec<u8> {
// self.to_array().to_vec()
// }
// }

View file

@ -0,0 +1,15 @@
use dashmap::DashMap;
use tokio::{sync::{broadcast, mpsc}, task::AbortHandle};
pub struct WSSession {
pub user: crate::auth::Userinfo,
pub own_tx: mpsc::Sender<SessionMessage>,
pub own_rx: mpsc::Receiver<SessionMessage>,
pub subs_tx: broadcast::Sender<Vec<u8>>,
pub sub_workers_aborthandles: DashMap<uuid::Uuid, AbortHandle>,
}
pub enum SessionMessage {
Ping(Vec<u8>),
Banned,
}

View file

@ -28,7 +28,7 @@ pub(super) async fn raw(
Some(uuid) => {
// for only one
let tx = state.session.get(&uuid).ok_or_else(|| { warn!("unknown uuid"); crate::ApiError::NotFound })?;
tx.value().send(payload).await.map_err(|err| internal_and_log(err))?;
tx.value().send(crate::api::figura::SessionMessage::Ping(payload)).await.map_err(internal_and_log)?;
Ok("ok")
},
None => {
@ -53,8 +53,8 @@ pub(super) async fn sub_raw(
match query.uuid {
Some(uuid) => {
// for only one
let tx = state.broadcasts.get(&uuid).ok_or_else(|| { warn!("unknown uuid"); crate::ApiError::NotFound })?;
tx.value().send(payload).map_err(|err| internal_and_log(err))?;
let tx = state.subscribes.get(&uuid).ok_or_else(|| { warn!("unknown uuid"); crate::ApiError::NotFound })?;
tx.value().send(payload).map_err(internal_and_log)?;
Ok("ok")
},
None => {

View file

@ -29,7 +29,8 @@ pub(super) async fn ban(
info!("Trying ban user: {uuid}");
state.user_manager.ban(&Userinfo { uuid: uuid, banned: true, ..Default::default() });
if let Some(tx) = state.session.get(&uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
state.user_manager.ban(&Userinfo { uuid, banned: true, ..Default::default() });
Ok("ok")
}

View file

@ -83,8 +83,8 @@ async fn fetch_json(
trace!("{res:?}");
match res.status().as_u16() {
200 => {
let json = serde_json::from_str::<serde_json::Value>(&res.text().await?).with_context(|| format!("Cant deserialize"))?;
let uuid = get_id_json(&json).with_context(|| format!("Cant get UUID"))?;
let json = serde_json::from_str::<serde_json::Value>(&res.text().await?).with_context(|| "Cant deserialize".to_string())?;
let uuid = get_id_json(&json).with_context(|| "Cant get UUID".to_string())?;
Ok((uuid, auth_provider.clone()))
}
_ => Err(FetchError::WrongResponse(res.status().as_u16(), res.text().await)),
@ -131,7 +131,7 @@ pub async fn has_joined(
// Choosing what error return
// Returns if some internals errors occured
if errors.len() != 0 {
if !errors.is_empty() {
error!("Something wrong with your authentification providers!\nMisses: {misses:?}\nErrors: {errors:?}");
Err(anyhow::anyhow!("{:?}", errors))
@ -203,7 +203,7 @@ impl UManager {
pub fn insert_user(&self, uuid: Uuid, userinfo: Userinfo) {
// self.registered.insert(uuid, userinfo)
let usercopy = userinfo.clone();
self.registered.entry(uuid.clone())
self.registered.entry(uuid)
.and_modify(|exist| {
if !userinfo.username.is_empty() { exist.username = userinfo.username };
if !userinfo.auth_provider.is_empty() { exist.auth_provider = userinfo.auth_provider };

View file

@ -51,11 +51,7 @@ impl Default for AuthProvider {
impl AuthProvider {
pub fn is_empty(&self) -> bool {
if self.name == "Unknown".to_string() {
true
} else {
false
}
self.name == "Unknown"
}
}

View file

@ -1,22 +1,22 @@
// Environment
pub const LOGGER_ENV: &'static str = "RUST_LOG";
pub const CONFIG_ENV: &'static str = "RUST_CONFIG";
pub const LOGS_ENV: &'static str = "LOGS_FOLDER";
pub const ASSETS_ENV: &'static str = "ASSETS_FOLDER";
pub const AVATARS_ENV: &'static str = "AVATARS_FOLDER";
pub const LOGGER_ENV: &str = "RUST_LOG";
pub const CONFIG_ENV: &str = "RUST_CONFIG";
pub const LOGS_ENV: &str = "LOGS_FOLDER";
pub const ASSETS_ENV: &str = "ASSETS_FOLDER";
pub const AVATARS_ENV: &str = "AVATARS_FOLDER";
// Instance info
pub const SCULPTOR_VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub const REPOSITORY: &'static str = "shiroyashik/sculptor";
pub const SCULPTOR_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const REPOSITORY: &str = "shiroyashik/sculptor";
// reqwest parameters
pub const USER_AGENT: &'static str = "reqwest";
pub const USER_AGENT: &str = "reqwest";
pub const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
// Figura update checker
pub const FIGURA_RELEASES_URL: &'static str = "https://api.github.com/repos/figuramc/figura/releases";
pub const FIGURA_DEFAULT_VERSION: &'static str = "0.1.4";
pub const FIGURA_RELEASES_URL: &str = "https://api.github.com/repos/figuramc/figura/releases";
pub const FIGURA_DEFAULT_VERSION: &str = "0.1.4";
// Figura Assets
pub const FIGURA_ASSETS_ZIP_URL: &'static str = "https://github.com/FiguraMC/Assets/archive/refs/heads/main.zip";
pub const FIGURA_ASSETS_COMMIT_URL: &'static str = "https://api.github.com/repos/FiguraMC/Assets/commits/main";
pub const FIGURA_ASSETS_ZIP_URL: &str = "https://github.com/FiguraMC/Assets/archive/refs/heads/main.zip";
pub const FIGURA_ASSETS_COMMIT_URL: &str = "https://api.github.com/repos/FiguraMC/Assets/commits/main";

View file

@ -1,3 +1,4 @@
#![allow(clippy::module_inception)]
use anyhow::Result;
use axum::{
extract::DefaultBodyLimit, routing::{delete, get, post, put}, Router
@ -6,9 +7,8 @@ use dashmap::DashMap;
use tracing_panic::panic_hook;
use tracing_subscriber::{fmt::{self, time::ChronoLocal}, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
use std::{path::PathBuf, sync::Arc, env::var};
use tokio::{fs, sync::{broadcast, mpsc, RwLock}, time::Instant};
use tokio::{fs, sync::RwLock, time::Instant};
use tower_http::trace::TraceLayer;
use uuid::Uuid;
use lazy_static::lazy_static;
// Consts
@ -31,28 +31,12 @@ use auth::{UManager, check_auth};
// Config
mod state;
use state::Config;
use state::{Config, AppState};
// Utils
mod utils;
use utils::*;
#[derive(Debug, Clone)]
pub struct AppState {
/// Uptime
uptime: Instant,
/// User manager
user_manager: Arc<UManager>,
/// Send into WebSocket
session: Arc<DashMap<Uuid, mpsc::Sender<Vec<u8>>>>,
/// Ping broadcasts for WebSocket connections
broadcasts: Arc<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>,
/// Current configuration
config: Arc<RwLock<state::Config>>,
/// Caching Figura Versions
figura_versions: Arc<RwLock<Option<FiguraVersions>>>,
}
lazy_static! {
pub static ref LOGGER_VAR: String = {
var(LOGGER_ENV).unwrap_or(String::from("info"))
@ -77,7 +61,7 @@ async fn main() -> Result<()> {
let _ = dotenvy::dotenv();
// 2. Set up logging
let file_appender = tracing_appender::rolling::never(&*LOGS_VAR, get_log_file(&*LOGS_VAR));
let file_appender = tracing_appender::rolling::never(&*LOGS_VAR, get_log_file(&LOGS_VAR));
let timer = ChronoLocal::new(String::from("%Y-%m-%dT%H:%M:%S%.3f%:z"));
let file_layer = fmt::layer()
@ -147,7 +131,7 @@ async fn app() -> Result<bool> {
// Config
let config = Arc::new(RwLock::new(Config::parse(CONFIG_VAR.clone().into())));
let listen = config.read().await.listen.clone();
let limit = get_limit_as_bytes(config.read().await.limitations.max_avatar_size.clone() as usize);
let limit = get_limit_as_bytes(config.read().await.limitations.max_avatar_size as usize);
if config.read().await.assets_updater_enabled {
// Force update assets if folder or hash file doesn't exists.
@ -179,15 +163,17 @@ async fn app() -> Result<bool> {
uptime: Instant::now(),
user_manager: Arc::new(UManager::new()),
session: Arc::new(DashMap::new()),
broadcasts: Arc::new(DashMap::new()),
subscribes: Arc::new(DashMap::new()),
figura_versions: Arc::new(RwLock::new(None)),
config,
};
// FIXME: FIXME: FIXME: ПЕРЕДЕЛАЙ ЭТО! НЕМЕДЛЕННО! ЕБУЧИЙ ПОЗОР :<
// Automatic update of configuration while the server is running
let config_update = Arc::clone(&state.config);
let user_manager = Arc::clone(&state.user_manager);
update_advanced_users(&config_update.read().await.advanced_users.clone(), &user_manager);
let umanager = Arc::clone(&state.user_manager);
let session = Arc::clone(&state.session);
update_advanced_users(&config_update.read().await.advanced_users.clone(), &umanager, &session).await;
tokio::spawn(async move {
loop {
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
@ -197,14 +183,15 @@ async fn app() -> Result<bool> {
if new_config != *config {
tracing::info!("Server configuration modification detected!");
*config = new_config;
update_advanced_users(&config.advanced_users.clone(), &user_manager);
update_advanced_users(&config.advanced_users.clone(), &umanager, &session).await;
}
}
});
if state.config.read().await.mc_folder.exists() {
tokio::spawn(update_bans_from_minecraft(
state.config.read().await.mc_folder.clone(),
Arc::clone(&state.user_manager)
Arc::clone(&state.user_manager),
Arc::clone(&state.session)
));
}

View file

@ -62,11 +62,11 @@ pub struct BannedPlayer {
pub name: String,
}
impl Into<Userinfo> for BannedPlayer {
fn into(self) -> Userinfo {
impl From<BannedPlayer> for Userinfo {
fn from(val: BannedPlayer) -> Self {
Userinfo {
uuid: self.uuid,
username: self.name,
uuid: val.uuid,
username: val.name,
banned: true,
..Default::default()
}

View file

@ -2,3 +2,4 @@ mod config;
mod state;
pub use config::*;
pub use state::*;

View file

@ -0,0 +1,23 @@
use std::sync::Arc;
use dashmap::DashMap;
use tokio::{sync::*, time::Instant};
use uuid::Uuid;
use crate::{api::figura::SessionMessage, auth::UManager, FiguraVersions};
#[derive(Debug, Clone)]
pub struct AppState {
/// Uptime
pub uptime: Instant,
/// User manager
pub user_manager: Arc<UManager>,
/// Send into WebSocket
pub session: Arc<DashMap<Uuid, mpsc::Sender<SessionMessage>>>,
/// Send messages for subscribers
pub subscribes: Arc<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>,
/// Current configuration
pub config: Arc<RwLock<super::Config>>,
/// Caching Figura Versions
pub figura_versions: Arc<RwLock<Option<FiguraVersions>>>,
}

View file

@ -30,14 +30,18 @@ pub fn _generate_hex_string(length: usize) -> String {
hex::encode(random_bytes)
}
pub fn update_advanced_users(value: &std::collections::HashMap<Uuid, AdvancedUsers>, umanager: &UManager) {
pub async fn update_advanced_users(
value: &std::collections::HashMap<Uuid, AdvancedUsers>,
umanager: &UManager,
sessions: &dashmap::DashMap<Uuid, tokio::sync::mpsc::Sender<crate::api::figura::SessionMessage>>
) {
let users: Vec<(Uuid, Userinfo)> = value
.iter()
.map( |(uuid, userdata)| {
(
uuid.clone(),
*uuid,
Userinfo {
uuid: uuid.clone(),
uuid: *uuid,
username: userdata.username.clone(),
banned: userdata.banned,
..Default::default()
@ -48,12 +52,17 @@ pub fn update_advanced_users(value: &std::collections::HashMap<Uuid, AdvancedUse
for (uuid, userinfo) in users {
umanager.insert_user(uuid, userinfo.clone());
if userinfo.banned {
umanager.ban(&userinfo)
umanager.ban(&userinfo);
if let Some(tx) = sessions.get(&uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
}
}
}
pub async fn update_bans_from_minecraft(folder: PathBuf, umanager: std::sync::Arc<UManager>) {
pub async fn update_bans_from_minecraft(
folder: PathBuf,
umanager: std::sync::Arc<UManager>,
sessions: std::sync::Arc<dashmap::DashMap<Uuid, tokio::sync::mpsc::Sender<crate::api::figura::SessionMessage>>>
) {
let path = folder.join("banned-players.json");
let mut file = tokio::fs::File::open(path.clone()).await.expect("Access denied or banned-players.json doesn't exists!");
let mut data = String::new();
@ -70,6 +79,7 @@ pub async fn update_bans_from_minecraft(folder: PathBuf, umanager: std::sync::Ar
for player in &old_bans {
umanager.ban(&player.clone().into());
if let Some(tx) = sessions.get(&player.uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
}
// old_bans
@ -97,6 +107,7 @@ pub async fn update_bans_from_minecraft(folder: PathBuf, umanager: std::sync::Ar
if !ban.is_empty() {
for player in ban {
umanager.ban(&player.clone().into());
if let Some(tx) = sessions.get(&player.uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
}
} else { ban_names = String::from("-")};
info!("List of changes:\n Banned: {ban_names}\n Unbanned: {unban_names}");

View file

@ -65,12 +65,10 @@ pub async fn get_figura_versions() -> anyhow::Result<FiguraVersions> {
if tag_ver > prerelease_ver {
prerelease_ver = tag_ver
}
} else {
if tag_ver > release_ver {
} else if tag_ver > release_ver {
release_ver = tag_ver
}
}
}
if release_ver > prerelease_ver {
prerelease_ver = release_ver.clone();
}
@ -115,14 +113,12 @@ pub async fn is_assets_outdated(last_sha: &str) -> anyhow::Result<bool> {
if contents.lines().count() != 1 {
// Lines count in file abnormal
Ok(true)
} else {
if contents == last_sha {
} else if contents == last_sha {
Ok(false)
} else {
// SHA in file mismatches with provided SHA
Ok(true)
}
}
},
Err(err) => if err.kind() == tokio::io::ErrorKind::NotFound {
// Can't find file

View file

@ -1,7 +1,7 @@
mod utils;
mod auxiliary;
mod check_updates;
mod motd;
pub use utils::*;
pub use auxiliary::*;
pub use motd::*;
pub use check_updates::*;