mirror of
https://github.com/shiroyashik/sculptor.git
synced 2025-12-06 04:51:13 +03:00
Assets seeker more effective, directories changed, added assets auto check/update.
This commit is contained in:
parent
7a37ce8a6b
commit
325a73da5a
11 changed files with 550 additions and 60 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -3,9 +3,11 @@
|
||||||
/avatars
|
/avatars
|
||||||
/logs
|
/logs
|
||||||
/assets
|
/assets
|
||||||
|
/data
|
||||||
output.log
|
output.log
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
Config.toml
|
Config.toml
|
||||||
|
config.toml
|
||||||
.env
|
.env
|
||||||
perf.data*
|
perf.data*
|
||||||
banned-players.json
|
banned-players.json
|
||||||
312
Cargo.lock
generated
312
Cargo.lock
generated
|
|
@ -17,6 +17,23 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler2"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
|
|
@ -43,9 +60,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.88"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356"
|
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
|
|
@ -162,7 +188,7 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"miniz_oxide",
|
"miniz_oxide 0.7.4",
|
||||||
"object",
|
"object",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
]
|
]
|
||||||
|
|
@ -219,11 +245,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "bzip2"
|
||||||
version = "1.1.18"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476"
|
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bzip2-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2-sys"
|
||||||
|
version = "0.1.11+1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -254,6 +303,16 @@ dependencies = [
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"inout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "3.2.25"
|
version = "3.2.25"
|
||||||
|
|
@ -312,6 +371,12 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d"
|
checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
|
@ -337,6 +402,30 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc"
|
||||||
|
version = "3.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
|
||||||
|
dependencies = [
|
||||||
|
"crc-catalog",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc-catalog"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cross"
|
name = "cross"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
|
@ -424,6 +513,12 @@ version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deflate64"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
|
|
@ -433,6 +528,17 @@ dependencies = [
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.77",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
|
|
@ -441,6 +547,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -463,6 +570,17 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "displaydoc"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.77",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dotenvy"
|
name = "dotenvy"
|
||||||
version = "0.15.7"
|
version = "0.15.7"
|
||||||
|
|
@ -522,6 +640,16 @@ version = "2.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide 0.8.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
|
@ -559,6 +687,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -567,6 +696,12 @@ version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.30"
|
version = "0.3.30"
|
||||||
|
|
@ -586,8 +721,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
|
|
@ -678,6 +815,15 @@ version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.9"
|
version = "0.5.9"
|
||||||
|
|
@ -809,9 +955,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.60"
|
version = "0.1.61"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_system_properties",
|
"android_system_properties",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
|
|
@ -867,6 +1013,15 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.10.0"
|
version = "2.10.0"
|
||||||
|
|
@ -885,6 +1040,15 @@ version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.70"
|
version = "0.3.70"
|
||||||
|
|
@ -932,12 +1096,28 @@ dependencies = [
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lockfree-object-pool"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.22"
|
version = "0.4.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lzma-rs"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"crc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchers"
|
name = "matchers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -974,6 +1154,15 @@ dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||||
|
dependencies = [
|
||||||
|
"adler2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
|
@ -1154,6 +1343,16 @@ dependencies = [
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbkdf2"
|
||||||
|
version = "0.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"hmac",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
|
|
@ -1358,6 +1557,7 @@ dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2",
|
||||||
|
|
@ -1540,6 +1740,7 @@ dependencies = [
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"uuid",
|
"uuid",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1690,6 +1891,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-adler32"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
|
@ -2012,9 +2219,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.22.20"
|
version = "0.22.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
|
checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.5.0",
|
"indexmap 2.5.0",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
@ -2556,3 +2763,88 @@ name = "zeroize"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize_derive"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.77",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zip"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"arbitrary",
|
||||||
|
"bzip2",
|
||||||
|
"constant_time_eq",
|
||||||
|
"crc32fast",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"deflate64",
|
||||||
|
"displaydoc",
|
||||||
|
"flate2",
|
||||||
|
"hmac",
|
||||||
|
"indexmap 2.5.0",
|
||||||
|
"lzma-rs",
|
||||||
|
"memchr",
|
||||||
|
"pbkdf2",
|
||||||
|
"rand",
|
||||||
|
"sha1",
|
||||||
|
"thiserror",
|
||||||
|
"time",
|
||||||
|
"zeroize",
|
||||||
|
"zopfli",
|
||||||
|
"zstd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zopfli"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"crc32fast",
|
||||||
|
"lockfree-object-pool",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-safe",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-safe"
|
||||||
|
version = "7.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-sys"
|
||||||
|
version = "2.0.13+zstd.1.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ dashmap = { version = "6.0.1", features = ["serde"] }
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
uuid = { version = "1.8.0", features = ["serde"] }
|
uuid = { version = "1.8.0", features = ["serde"] }
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
reqwest = { version = "0.12.6", features = ["json"] }
|
reqwest = { version = "0.12.7", features = ["blocking", "json"] }
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
semver = "1.0.23"
|
semver = "1.0.23"
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
|
|
@ -39,6 +39,7 @@ axum = { version = "0.7.5", features = ["ws", "macros", "http2"] }
|
||||||
tower-http = { version = "0.5.2", features = ["trace"] }
|
tower-http = { version = "0.5.2", features = ["trace"] }
|
||||||
tokio = { version = "1.37.0", features = ["full"] }
|
tokio = { version = "1.37.0", features = ["full"] }
|
||||||
indexmap = { version = "2.5.0", features = ["serde"] }
|
indexmap = { version = "2.5.0", features = ["serde"] }
|
||||||
|
zip = "2.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
cross = "0.2.5"
|
cross = "0.2.5"
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ listen = "0.0.0.0:6665"
|
||||||
# { name = "ElyBy", url = "http://minecraft.ely.by/session/hasJoined" },
|
# { name = "ElyBy", url = "http://minecraft.ely.by/session/hasJoined" },
|
||||||
# ]
|
# ]
|
||||||
|
|
||||||
|
# FIXME: in dev
|
||||||
|
assetsUpdaterEnabled = true
|
||||||
|
|
||||||
## Message of The Day
|
## Message of The Day
|
||||||
## It will be displayed to every player in the Figura menu who is connected to your server
|
## It will be displayed to every player in the Figura menu who is connected to your server
|
||||||
[motd]
|
[motd]
|
||||||
|
|
|
||||||
|
|
@ -3,36 +3,47 @@ use std::path::PathBuf;
|
||||||
use axum::{extract::Path, routing::get, Json, Router};
|
use axum::{extract::Path, routing::get, Json, Router};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use ring::digest::{digest, SHA256};
|
use ring::digest::{digest, SHA256};
|
||||||
use serde_json::{json, Value};
|
use serde_json::Value;
|
||||||
use tokio::{fs, io::AsyncReadExt as _};
|
use tokio::{fs, io::AsyncReadExt as _};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::{api::errors::internal_and_log, ApiError, ApiResult, AppState};
|
use crate::{api::errors::internal_and_log, ApiError, ApiResult, AppState, ASSETS_ENV};
|
||||||
|
|
||||||
pub fn router() -> Router<AppState> {
|
pub fn router() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", get(versions))
|
.route("/", get(versions))
|
||||||
.route("/v1", get(v1))
|
.route("/:version", get(hashes))
|
||||||
.route("/v2", get(v2))
|
.route("/:version/*key", get(download))
|
||||||
.route("/*path", get(download))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn versions() -> Json<Value> {
|
async fn versions() -> ApiResult<Json<Value>> {
|
||||||
Json(json!(["v1", "v2"]))
|
let dir_path = PathBuf::from(&std::env::var(ASSETS_ENV).unwrap());
|
||||||
|
|
||||||
|
let mut directories = Vec::new();
|
||||||
|
|
||||||
|
let mut entries = fs::read_dir(dir_path).await.map_err(|err| internal_and_log(err))?;
|
||||||
|
|
||||||
|
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() {
|
||||||
|
if let Some(name) = entry.file_name().to_str() {
|
||||||
|
let name = name.to_string();
|
||||||
|
if !name.starts_with('.') {
|
||||||
|
directories.push(Value::String(name.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Json(serde_json::Value::Array(directories)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn v1() -> ApiResult<Json<IndexMap<String, Value>>> {
|
async fn hashes(Path(version): Path<String>) -> ApiResult<Json<IndexMap<String, Value>>> {
|
||||||
let map = index_assets("v1").await.map_err(|err| internal_and_log(err))?;
|
let map = index_assets(&version).await.map_err(|err| internal_and_log(err))?;
|
||||||
Ok(Json(map))
|
Ok(Json(map))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn v2() -> ApiResult<Json<IndexMap<String, Value>>> {
|
async fn download(Path((version, path)): Path<(String, String)>) -> ApiResult<Vec<u8>> {
|
||||||
let map = index_assets("v2").await.map_err(|err| internal_and_log(err))?;
|
let mut file = if let Ok(file) = fs::File::open(format!("{}/{version}/{path}", std::env::var(ASSETS_ENV).unwrap())).await {
|
||||||
Ok(Json(map))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn download(Path(path): Path<String>) -> ApiResult<Vec<u8>> {
|
|
||||||
let mut file = if let Ok(file) = fs::File::open(format!("assets/{path}")).await {
|
|
||||||
file
|
file
|
||||||
} else {
|
} else {
|
||||||
return Err(ApiError::NotFound)
|
return Err(ApiError::NotFound)
|
||||||
|
|
@ -46,7 +57,7 @@ async fn download(Path(path): Path<String>) -> ApiResult<Vec<u8>> {
|
||||||
|
|
||||||
async fn index_assets(version: &str) -> anyhow::Result<IndexMap<String, Value>> {
|
async fn index_assets(version: &str) -> anyhow::Result<IndexMap<String, Value>> {
|
||||||
let mut map = IndexMap::new();
|
let mut map = IndexMap::new();
|
||||||
let version_path = PathBuf::from("assets/").join(version);
|
let version_path = PathBuf::from(std::env::var(ASSETS_ENV).unwrap()).join(version);
|
||||||
|
|
||||||
for entry in WalkDir::new(version_path.clone()).into_iter().filter_map(|e| e.ok()) {
|
for entry in WalkDir::new(version_path.clone()).into_iter().filter_map(|e| e.ok()) {
|
||||||
let data = match fs::read(entry.path()).await {
|
let data = match fs::read(entry.path()).await {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::env::var;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
body::Bytes, extract::{Path, State}, Json
|
body::Bytes, extract::{Path, State}, Json
|
||||||
};
|
};
|
||||||
|
|
@ -12,7 +14,7 @@ use uuid::Uuid;
|
||||||
use crate::{
|
use crate::{
|
||||||
api::errors::internal_and_log,
|
api::errors::internal_and_log,
|
||||||
auth::Token, utils::{calculate_file_sha256, format_uuid},
|
auth::Token, utils::{calculate_file_sha256, format_uuid},
|
||||||
ApiError, ApiResult, AppState
|
ApiError, ApiResult, AppState, AVATARS_ENV
|
||||||
};
|
};
|
||||||
use super::types::S2CMessage;
|
use super::types::S2CMessage;
|
||||||
|
|
||||||
|
|
@ -24,7 +26,7 @@ pub async fn user_info(
|
||||||
|
|
||||||
let formatted_uuid = format_uuid(&uuid);
|
let formatted_uuid = format_uuid(&uuid);
|
||||||
|
|
||||||
let avatar_file = format!("avatars/{}.moon", formatted_uuid);
|
let avatar_file = format!("{}/{}.moon", var(AVATARS_ENV).unwrap(), formatted_uuid);
|
||||||
|
|
||||||
let userinfo = if let Some(info) = state.user_manager.get_by_uuid(&uuid) { info } else {
|
let userinfo = if let Some(info) = state.user_manager.get_by_uuid(&uuid) { info } else {
|
||||||
return Err(ApiError::BadRequest) // NOTE: Not Found (404) shows badge
|
return Err(ApiError::BadRequest) // NOTE: Not Found (404) shows badge
|
||||||
|
|
@ -79,7 +81,7 @@ pub async fn user_info(
|
||||||
pub async fn download_avatar(Path(uuid): Path<Uuid>) -> ApiResult<Vec<u8>> {
|
pub async fn download_avatar(Path(uuid): Path<Uuid>) -> ApiResult<Vec<u8>> {
|
||||||
let uuid = format_uuid(&uuid);
|
let uuid = format_uuid(&uuid);
|
||||||
tracing::info!("Requesting an avatar: {}", uuid);
|
tracing::info!("Requesting an avatar: {}", uuid);
|
||||||
let mut file = if let Ok(file) = fs::File::open(format!("avatars/{}.moon", uuid)).await {
|
let mut file = if let Ok(file) = fs::File::open(format!("{}/{}.moon", var(AVATARS_ENV).unwrap(), uuid)).await {
|
||||||
file
|
file
|
||||||
} else {
|
} else {
|
||||||
return Err(ApiError::NotFound)
|
return Err(ApiError::NotFound)
|
||||||
|
|
@ -102,7 +104,7 @@ pub async fn upload_avatar(
|
||||||
user_info.uuid,
|
user_info.uuid,
|
||||||
user_info.username
|
user_info.username
|
||||||
);
|
);
|
||||||
let avatar_file = format!("avatars/{}.moon", user_info.uuid);
|
let avatar_file = format!("{}/{}.moon", var(AVATARS_ENV).unwrap(), user_info.uuid);
|
||||||
let mut file = BufWriter::new(fs::File::create(&avatar_file).await.map_err(|err| internal_and_log(err))?);
|
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))?;
|
io::copy(&mut request_data.as_ref(), &mut file).await.map_err(|err| internal_and_log(err))?;
|
||||||
}
|
}
|
||||||
|
|
@ -123,11 +125,10 @@ pub async fn delete_avatar(Token(token): Token, State(state): State<AppState>) -
|
||||||
user_info.uuid,
|
user_info.uuid,
|
||||||
user_info.username
|
user_info.username
|
||||||
);
|
);
|
||||||
let avatar_file = format!("avatars/{}.moon", user_info.uuid);
|
let avatar_file = format!("{}/{}.moon", var(AVATARS_ENV).unwrap(), user_info.uuid);
|
||||||
fs::remove_file(avatar_file).await.map_err(|err| internal_and_log(err))?;
|
fs::remove_file(avatar_file).await.map_err(|err| internal_and_log(err))?;
|
||||||
send_event(&state, &user_info.uuid).await;
|
send_event(&state, &user_info.uuid).await;
|
||||||
}
|
}
|
||||||
// let avatar_file = format!("avatars/{}.moon",user_info.uuid);
|
|
||||||
Ok("ok".to_string())
|
Ok("ok".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
use std::env::var;
|
||||||
|
|
||||||
use axum::{body::Bytes, extract::{Path, State}};
|
use axum::{body::Bytes, extract::{Path, State}};
|
||||||
use tokio::{fs, io::{self, BufWriter}};
|
use tokio::{fs, io::{self, BufWriter}};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{api::figura::profile::send_event, auth::Token, ApiResult, AppState};
|
use crate::{api::figura::profile::send_event, auth::Token, ApiResult, AppState, AVATARS_ENV};
|
||||||
|
|
||||||
pub async fn upload_avatar(
|
pub async fn upload_avatar(
|
||||||
Path(uuid): Path<Uuid>,
|
Path(uuid): Path<Uuid>,
|
||||||
|
|
@ -20,7 +22,7 @@ pub async fn upload_avatar(
|
||||||
uuid,
|
uuid,
|
||||||
);
|
);
|
||||||
|
|
||||||
let avatar_file = format!("avatars/{}.moon", &uuid);
|
let avatar_file = format!("{}/{}.moon", var(AVATARS_ENV).unwrap(), &uuid);
|
||||||
let mut file = BufWriter::new(fs::File::create(&avatar_file).await.unwrap());
|
let mut file = BufWriter::new(fs::File::create(&avatar_file).await.unwrap());
|
||||||
io::copy(&mut request_data.as_ref(), &mut file).await.unwrap();
|
io::copy(&mut request_data.as_ref(), &mut file).await.unwrap();
|
||||||
send_event(&state, &uuid).await;
|
send_event(&state, &uuid).await;
|
||||||
|
|
@ -40,7 +42,7 @@ pub async fn delete_avatar(
|
||||||
uuid,
|
uuid,
|
||||||
);
|
);
|
||||||
|
|
||||||
let avatar_file = format!("avatars/{}.moon", &uuid);
|
let avatar_file = format!("{}/{}.moon", var(AVATARS_ENV).unwrap(), &uuid);
|
||||||
match fs::remove_file(avatar_file).await {
|
match fs::remove_file(avatar_file).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,22 @@
|
||||||
|
// Environment
|
||||||
pub const LOGGER_ENV: &'static str = "RUST_LOG";
|
pub const LOGGER_ENV: &'static str = "RUST_LOG";
|
||||||
pub const CONFIG_ENV: &'static str = "RUST_CONFIG";
|
pub const CONFIG_ENV: &'static str = "RUST_CONFIG";
|
||||||
pub const LOGS_ENV: &'static str = "LOGS_FOLDER";
|
pub const LOGS_ENV: &'static str = "LOGS_FOLDER";
|
||||||
|
pub const ASSETS_ENV: &'static str = "ASSETS_FOLDER";
|
||||||
|
pub const AVATARS_ENV: &'static str = "AVATARS_FOLDER";
|
||||||
|
|
||||||
|
// Sculptor update checker
|
||||||
pub const SCULPTOR_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
pub const SCULPTOR_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
pub const REPOSITORY: &'static str = "shiroyashik/sculptor";
|
pub const REPOSITORY: &'static str = "shiroyashik/sculptor";
|
||||||
|
|
||||||
|
// reqwest parameters
|
||||||
pub const USER_AGENT: &'static str = "reqwest";
|
pub const USER_AGENT: &'static str = "reqwest";
|
||||||
pub const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
|
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_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_DEFAULT_VERSION: &'static 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";
|
||||||
71
src/main.rs
71
src/main.rs
|
|
@ -5,10 +5,9 @@ use axum::{
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use tracing_panic::panic_hook;
|
use tracing_panic::panic_hook;
|
||||||
use tracing_subscriber::{fmt::{self, time::ChronoLocal}, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
use tracing_subscriber::{fmt::{self, time::ChronoLocal}, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{path::PathBuf, sync::Arc, env::{set_var, var}};
|
||||||
use tokio::{fs, sync::{broadcast, mpsc, RwLock}, time::Instant};
|
use tokio::{fs, sync::{broadcast, mpsc, RwLock}, time::Instant};
|
||||||
use tower_http::trace::TraceLayer;
|
use tower_http::trace::TraceLayer;
|
||||||
use tracing::info;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
// Consts
|
// Consts
|
||||||
|
|
@ -35,7 +34,7 @@ use state::Config;
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
mod utils;
|
mod utils;
|
||||||
use utils::{check_updates, get_log_file, update_advanced_users, update_bans_from_minecraft, FiguraVersions};
|
use utils::{check_updates, download_assets, get_commit_sha, get_log_file, get_path_to_assets_hash, is_assets_outdated, remove_assets, update_advanced_users, update_bans_from_minecraft, write_sha_to_file, FiguraVersions};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
|
|
@ -49,19 +48,25 @@ pub struct AppState {
|
||||||
broadcasts: Arc<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>,
|
broadcasts: Arc<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>,
|
||||||
/// Current configuration
|
/// Current configuration
|
||||||
config: Arc<RwLock<state::Config>>,
|
config: Arc<RwLock<state::Config>>,
|
||||||
/// Figura Versions
|
/// Caching Figura Versions
|
||||||
figura_versions: Arc<RwLock<Option<FiguraVersions>>>,
|
figura_versions: Arc<RwLock<Option<FiguraVersions>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_default_environment() {
|
||||||
|
if var(LOGGER_ENV).is_err() { set_var(LOGGER_ENV, "info") };
|
||||||
|
if var(CONFIG_ENV).is_err() { set_var(CONFIG_ENV, "Config.toml") };
|
||||||
|
if var(LOGS_ENV).is_err() { set_var(LOGS_ENV, "logs") };
|
||||||
|
if var(ASSETS_ENV).is_err() { set_var(ASSETS_ENV, "data/assets") };
|
||||||
|
if var(AVATARS_ENV).is_err() { set_var(ASSETS_ENV, "data/avatars") };
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let _ = dotenvy::dotenv();
|
let _ = dotenvy::dotenv();
|
||||||
|
apply_default_environment();
|
||||||
// "trace,axum=info,tower_http=info,tokio=info,tungstenite=info,tokio_tungstenite=info",
|
// "trace,axum=info,tower_http=info,tokio=info,tungstenite=info,tokio_tungstenite=info",
|
||||||
let logger_env = std::env::var(LOGGER_ENV).unwrap_or_else(|_| "info".into());
|
|
||||||
let config_file = std::env::var(CONFIG_ENV).unwrap_or_else(|_| "Config.toml".into());
|
|
||||||
let logs_folder = std::env::var(LOGS_ENV).unwrap_or_else(|_| "logs".into());
|
|
||||||
|
|
||||||
let file_appender = tracing_appender::rolling::never(&logs_folder, get_log_file(&logs_folder));
|
let file_appender = tracing_appender::rolling::never(&var(LOGS_ENV).unwrap(), get_log_file(&var(LOGS_ENV).unwrap()));
|
||||||
let timer = ChronoLocal::new(String::from("%Y-%m-%dT%H:%M:%S%.3f%:z"));
|
let timer = ChronoLocal::new(String::from("%Y-%m-%dT%H:%M:%S%.3f%:z"));
|
||||||
|
|
||||||
let file_layer = fmt::layer()
|
let file_layer = fmt::layer()
|
||||||
|
|
@ -79,7 +84,7 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
// Combine the layers and set the global subscriber
|
// Combine the layers and set the global subscriber
|
||||||
tracing_subscriber::registry()
|
tracing_subscriber::registry()
|
||||||
.with(EnvFilter::from(logger_env))
|
.with(EnvFilter::from(var(LOGGER_ENV).unwrap()))
|
||||||
.with(file_layer)
|
.with(file_layer)
|
||||||
.with(terminal_layer)
|
.with(terminal_layer)
|
||||||
.init();
|
.init();
|
||||||
|
|
@ -91,20 +96,46 @@ async fn main() -> Result<()> {
|
||||||
prev_hook(panic_info);
|
prev_hook(panic_info);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
info!("The Sculptor v{}{}", SCULPTOR_VERSION, check_updates(REPOSITORY, &SCULPTOR_VERSION).await?);
|
tracing::info!("The Sculptor v{}{}", SCULPTOR_VERSION, check_updates(REPOSITORY, &SCULPTOR_VERSION).await?);
|
||||||
|
|
||||||
|
// Preparing for launch
|
||||||
{
|
{
|
||||||
let path = PathBuf::from("avatars");
|
let path = PathBuf::from(var(AVATARS_ENV).unwrap());
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
fs::create_dir(path).await.expect("Can't create avatars folder!");
|
fs::create_dir_all(path).await.expect("Can't create avatars folder!");
|
||||||
info!("Created avatars directory");
|
tracing::info!("Created avatars directory");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
let config = Arc::new(RwLock::new(Config::parse(config_file.clone().into())));
|
let config = Arc::new(RwLock::new(Config::parse(var(CONFIG_ENV).unwrap().into())));
|
||||||
let listen = config.read().await.listen.clone();
|
let listen = config.read().await.listen.clone();
|
||||||
|
|
||||||
|
if config.read().await.assets_updater_enabled {
|
||||||
|
// Force update assets if folder or hash file doesn't exists.
|
||||||
|
if !(PathBuf::from(var(ASSETS_ENV).unwrap()).is_dir() && get_path_to_assets_hash().is_file()) {
|
||||||
|
tracing::debug!("Removing broken assets...");
|
||||||
|
remove_assets().await
|
||||||
|
}
|
||||||
|
match get_commit_sha(FIGURA_ASSETS_COMMIT_URL).await {
|
||||||
|
Ok(sha) => {
|
||||||
|
if is_assets_outdated(&sha).await.unwrap_or_else(|e| {tracing::error!("Can't check assets state due: {:?}", e); false}) {
|
||||||
|
remove_assets().await;
|
||||||
|
match tokio::task::spawn_blocking(|| { download_assets() }).await.unwrap() {
|
||||||
|
Err(e) => tracing::error!("Assets outdated! Can't download new version due: {:?}", e),
|
||||||
|
Ok(_) => {
|
||||||
|
match write_sha_to_file(&sha).await {
|
||||||
|
Ok(_) => tracing::info!("Assets successfully updated!"),
|
||||||
|
Err(e) => tracing::error!("Assets successfully updated! Can't create assets hash file due: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else { tracing::info!("Assets are up to date!") }
|
||||||
|
},
|
||||||
|
Err(e) => tracing::error!("Can't get assets last commit! Assets update check aborted due {:?}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// State
|
// State
|
||||||
let state = AppState {
|
let state = AppState {
|
||||||
uptime: Instant::now(),
|
uptime: Instant::now(),
|
||||||
|
|
@ -122,11 +153,11 @@ async fn main() -> Result<()> {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
||||||
let new_config = Config::parse(config_file.clone().into());
|
let new_config = Config::parse(var(CONFIG_ENV).unwrap().into());
|
||||||
let mut config = config_update.write().await;
|
let mut config = config_update.write().await;
|
||||||
|
|
||||||
if new_config != *config {
|
if new_config != *config {
|
||||||
info!("Server configuration modification detected!");
|
tracing::info!("Server configuration modification detected!");
|
||||||
*config = new_config;
|
*config = new_config;
|
||||||
update_advanced_users(&config.advanced_users.clone(), &user_manager);
|
update_advanced_users(&config.advanced_users.clone(), &user_manager);
|
||||||
}
|
}
|
||||||
|
|
@ -161,11 +192,11 @@ async fn main() -> Result<()> {
|
||||||
.route("/health", get(|| async { "ok" }));
|
.route("/health", get(|| async { "ok" }));
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind(listen).await?;
|
let listener = tokio::net::TcpListener::bind(listen).await?;
|
||||||
info!("Listening on {}", listener.local_addr()?);
|
tracing::info!("Listening on {}", listener.local_addr()?);
|
||||||
axum::serve(listener, app)
|
axum::serve(listener, app)
|
||||||
.with_graceful_shutdown(shutdown_signal())
|
.with_graceful_shutdown(shutdown_signal())
|
||||||
.await?;
|
.await?;
|
||||||
info!("Serve stopped. Closing...");
|
tracing::info!("Serve stopped. Closing...");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,11 +218,11 @@ async fn shutdown_signal() {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
() = ctrl_c => {
|
() = ctrl_c => {
|
||||||
println!();
|
println!();
|
||||||
info!("Ctrl+C signal received");
|
tracing::info!("Ctrl+C signal received");
|
||||||
},
|
},
|
||||||
() = terminate => {
|
() = terminate => {
|
||||||
println!();
|
println!();
|
||||||
info!("Terminate signal received");
|
tracing::info!("Terminate signal received");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use crate::auth::{default_authproviders, AuthProviders, Userinfo};
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub listen: String,
|
pub listen: String,
|
||||||
pub token: Option<String>,
|
pub token: Option<String>,
|
||||||
|
pub assets_updater_enabled: bool, // FIXME: IN DEV BRANCH ONLY
|
||||||
pub motd: CMotd,
|
pub motd: CMotd,
|
||||||
#[serde(default = "default_authproviders")]
|
#[serde(default = "default_authproviders")]
|
||||||
pub auth_providers: AuthProviders,
|
pub auth_providers: AuthProviders,
|
||||||
|
|
@ -78,7 +79,7 @@ impl Config {
|
||||||
let mut data = String::new();
|
let mut data = String::new();
|
||||||
file.read_to_string(&mut data).unwrap();
|
file.read_to_string(&mut data).unwrap();
|
||||||
|
|
||||||
toml::from_str(&data).unwrap()
|
toml::from_str(&data).unwrap_or_else(|err| {tracing::error!("{err:#?}"); panic!("Panic occured! See log messages!")})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_token(&self, suspicious: &str) -> crate::ApiResult<()> {
|
pub fn verify_token(&self, suspicious: &str) -> crate::ApiResult<()> {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
|
use std::{env::{self, var}, path::{self, PathBuf}};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::{fs::{self, File}, io::{AsyncReadExt as _, AsyncWriteExt as _}};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{FIGURA_RELEASES_URL, TIMEOUT, USER_AGENT};
|
use crate::{ASSETS_ENV, FIGURA_ASSETS_ZIP_URL, FIGURA_RELEASES_URL, TIMEOUT, USER_AGENT};
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct Tag {
|
struct Tag {
|
||||||
|
|
@ -104,3 +107,136 @@ pub struct FiguraVersions {
|
||||||
pub release: String,
|
pub release: String,
|
||||||
pub prerelease: String
|
pub prerelease: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assets
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct Commit {
|
||||||
|
sha: String
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_path_to_assets_hash() -> PathBuf {
|
||||||
|
path::PathBuf::from(&env::var(ASSETS_ENV).unwrap()).join("..").join("assets_last_commit")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_commit_sha(url: &str) -> anyhow::Result<String> {
|
||||||
|
let client = Client::builder().timeout(TIMEOUT).user_agent(USER_AGENT).build().unwrap();
|
||||||
|
let response: reqwest::Response = client.get(url).send().await?;
|
||||||
|
let commit: Commit = response.json().await?;
|
||||||
|
Ok(commit.sha)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn is_assets_outdated(last_sha: &str) -> anyhow::Result<bool> {
|
||||||
|
let path = get_path_to_assets_hash();
|
||||||
|
|
||||||
|
match File::open(path.clone()).await {
|
||||||
|
Ok(mut file) => {
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents).await?;
|
||||||
|
if contents.lines().count() != 1 {
|
||||||
|
// Lines count in file abnormal
|
||||||
|
Ok(true)
|
||||||
|
} 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
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("{:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn download_assets() -> anyhow::Result<()> {
|
||||||
|
use std::{fs::{File, self}, io::Write as _};
|
||||||
|
|
||||||
|
let assets_folder = var(ASSETS_ENV).unwrap();
|
||||||
|
|
||||||
|
// Path to save the downloaded ZIP file
|
||||||
|
let zip_file_path = path::PathBuf::from(&assets_folder).join("..").join("assets.zip");
|
||||||
|
|
||||||
|
// Download the ZIP file
|
||||||
|
|
||||||
|
let response = reqwest::blocking::get(FIGURA_ASSETS_ZIP_URL)?;
|
||||||
|
let bytes = response.bytes()?;
|
||||||
|
|
||||||
|
// Save the downloaded ZIP file to disk
|
||||||
|
let mut file = File::create(&zip_file_path)?;
|
||||||
|
file.write_all(&bytes)?;
|
||||||
|
|
||||||
|
// Open the downloaded ZIP file
|
||||||
|
let file = File::open(&zip_file_path)?;
|
||||||
|
|
||||||
|
let mut archive = zip::ZipArchive::new(file)?;
|
||||||
|
let mut extraction_info = String::from("Extraction complete! More info:\n");
|
||||||
|
let mut first_folder = String::new();
|
||||||
|
|
||||||
|
for i in 0..archive.len() {
|
||||||
|
let mut file = archive.by_index(i)?;
|
||||||
|
let zipoutpath = match file.enclosed_name() {
|
||||||
|
Some(path) => path,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Folder name spoofing
|
||||||
|
if i == 0 {
|
||||||
|
if file.is_dir() {
|
||||||
|
first_folder = zipoutpath.to_str().ok_or_else(|| anyhow::anyhow!("0 index doesn't contains path!"))?.to_string();
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("0 index is not a folder!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut outpath = path::PathBuf::from(&assets_folder);
|
||||||
|
outpath.push(zipoutpath.strip_prefix(first_folder.clone())?);
|
||||||
|
// Spoof end
|
||||||
|
|
||||||
|
{
|
||||||
|
let comment = file.comment();
|
||||||
|
if !comment.is_empty() {
|
||||||
|
extraction_info.push_str(&format!("File {i} comment: {comment}\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if file.is_dir() {
|
||||||
|
extraction_info.push_str(&format!("Dir {} extracted to \"{}\"\n", i, outpath.display()));
|
||||||
|
fs::create_dir_all(&outpath)?;
|
||||||
|
} else {
|
||||||
|
extraction_info.push_str(&format!(
|
||||||
|
"File {} extracted to \"{}\" ({} bytes)\n",
|
||||||
|
i,
|
||||||
|
outpath.display(),
|
||||||
|
file.size()
|
||||||
|
));
|
||||||
|
if let Some(p) = outpath.parent() {
|
||||||
|
if !p.exists() {
|
||||||
|
fs::create_dir_all(p)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut outfile = fs::File::create(&outpath)?;
|
||||||
|
std::io::copy(&mut file, &mut outfile)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extraction_info.pop(); // Removes \n from end
|
||||||
|
tracing::debug!("{extraction_info}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write_sha_to_file(sha: &str) -> anyhow::Result<()> {
|
||||||
|
let path = get_path_to_assets_hash();
|
||||||
|
|
||||||
|
let mut file = File::create(path).await?;
|
||||||
|
file.write_all(sha.as_bytes()).await?;
|
||||||
|
file.flush().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_assets() {
|
||||||
|
fs::remove_dir_all(&var(ASSETS_ENV).unwrap()).await.unwrap_or_else(|err| tracing::debug!("Assets dir remove failed due {err:?}"));
|
||||||
|
fs::remove_file(get_path_to_assets_hash()).await.unwrap_or_else(|err| tracing::debug!("Assets hash file remove failed due {err:?}"));
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue