mirror of
https://github.com/shiroyashik/sculptor.git
synced 2025-12-06 04:51:13 +03:00
Configuration auto-update fixed and hex replaced with faster_hex
This commit is contained in:
parent
4c0871e26c
commit
2f8ab4cfa8
14 changed files with 589 additions and 210 deletions
579
Cargo.lock
generated
579
Cargo.lock
generated
|
|
@ -60,15 +60,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.91"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.3.2"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
|
@ -81,7 +81,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -174,7 +174,7 @@ checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -260,9 +260,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.31"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
|
||||
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
|
|
@ -388,9 +388,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
|||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
|
||||
checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
|
@ -444,7 +444,7 @@ dependencies = [
|
|||
"shell-escape",
|
||||
"shell-words",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
"toml 0.5.11",
|
||||
"which",
|
||||
"winapi",
|
||||
|
|
@ -523,13 +523,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.3.2"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -571,7 +571,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -628,16 +628,38 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.1.1"
|
||||
name = "faster-hex"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||
checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73"
|
||||
dependencies = [
|
||||
"heapless",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"libredox",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.34"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
|
||||
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide 0.8.0",
|
||||
|
|
@ -673,6 +695,15 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
|
|
@ -769,6 +800,15 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
|
|
@ -783,9 +823,19 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
||||
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||
dependencies = [
|
||||
"hash32",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
|
|
@ -808,12 +858,6 @@ version = "0.3.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
|
|
@ -934,9 +978,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b"
|
||||
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
|
|
@ -975,13 +1019,142 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
name = "icu_collections"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_locid_transform_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"write16",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_provider_macros",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_macros"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1007,10 +1180,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.0",
|
||||
"hashbrown 0.15.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"inotify-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify-sys"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
|
|
@ -1020,6 +1213,15 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.10.1"
|
||||
|
|
@ -1056,6 +1258,26 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
|
||||
dependencies = [
|
||||
"kqueue-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue-sys"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
|
|
@ -1064,9 +1286,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.161"
|
||||
version = "0.2.162"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
|
|
@ -1076,6 +1298,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
|||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1084,6 +1307,12 @@ version = "0.4.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
|
|
@ -1169,6 +1398,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
|||
dependencies = [
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
|
@ -1213,6 +1443,34 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "7.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"filetime",
|
||||
"fsevent-sys",
|
||||
"inotify",
|
||||
"kqueue",
|
||||
"libc",
|
||||
"log",
|
||||
"mio",
|
||||
"notify-types",
|
||||
"walkdir",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify-types"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
|
|
@ -1276,7 +1534,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1479,7 +1737,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
|||
dependencies = [
|
||||
"getrandom",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1490,7 +1748,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.8",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
|
|
@ -1505,9 +1763,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.8"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
|
@ -1528,9 +1786,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
|||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.8"
|
||||
version = "0.12.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b"
|
||||
checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
|
|
@ -1602,9 +1860,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.38"
|
||||
version = "0.38.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a"
|
||||
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"errno",
|
||||
|
|
@ -1615,9 +1873,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.15"
|
||||
version = "0.23.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993"
|
||||
checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"rustls-pki-types",
|
||||
|
|
@ -1699,16 +1957,17 @@ dependencies = [
|
|||
"cross",
|
||||
"dashmap",
|
||||
"dotenvy",
|
||||
"hex",
|
||||
"faster-hex",
|
||||
"indexmap 2.6.0",
|
||||
"lazy_static",
|
||||
"notify",
|
||||
"rand",
|
||||
"reqwest",
|
||||
"ring",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"thiserror 2.0.3",
|
||||
"tokio",
|
||||
"toml 0.8.19",
|
||||
"tower-http",
|
||||
|
|
@ -1736,9 +1995,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.12.0"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6"
|
||||
checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
|
|
@ -1752,22 +2011,22 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.213"
|
||||
version = "1.0.215"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.213"
|
||||
version = "1.0.215"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1906,6 +2165,12 @@ version = "0.9.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
|
@ -1941,9 +2206,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.85"
|
||||
version = "2.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -1965,6 +2230,17 @@ dependencies = [
|
|||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.6.1"
|
||||
|
|
@ -1988,9 +2264,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.13.0"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
|
||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
|
|
@ -2016,22 +2292,42 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.65"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.65"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2076,25 +2372,20 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
name = "tinystr"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.41.0"
|
||||
version = "1.41.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
|
||||
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
|
@ -2116,7 +2407,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2271,7 +2562,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
"time",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
|
@ -2284,7 +2575,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2357,7 +2648,7 @@ dependencies = [
|
|||
"log",
|
||||
"rand",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
|
|
@ -2367,27 +2658,12 @@ version = "1.17.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
|
|
@ -2396,9 +2672,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
|||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.2"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||
checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
|
|
@ -2411,6 +2687,18 @@ version = "0.7.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.11.0"
|
||||
|
|
@ -2485,7 +2773,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
|
@ -2519,7 +2807,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
|
@ -2713,6 +3001,42 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
|
|
@ -2731,7 +3055,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2751,7 +3096,29 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2776,7 +3143,7 @@ dependencies = [
|
|||
"pbkdf2",
|
||||
"rand",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
"time",
|
||||
"zeroize",
|
||||
"zopfli",
|
||||
|
|
|
|||
13
Cargo.toml
13
Cargo.toml
|
|
@ -14,7 +14,7 @@ tracing = "0.1"
|
|||
|
||||
# Errors handelers
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
thiserror = "2.0"
|
||||
chrono = { version = "0.4", features = ["now", "serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
|
@ -22,13 +22,17 @@ toml = "0.8"
|
|||
|
||||
# Other
|
||||
dashmap = { version = "6.0", features = ["serde"] }
|
||||
hex = "0.4"
|
||||
faster-hex = "0.10"
|
||||
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"
|
||||
indexmap = { version = "2.6", features = ["serde"] }
|
||||
zip = "2.2"
|
||||
lazy_static = "1.5"
|
||||
notify = "7.0"
|
||||
|
||||
# Crypto
|
||||
ring = "0.17"
|
||||
|
|
@ -37,10 +41,7 @@ rand = "0.8"
|
|||
# Web framework
|
||||
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"
|
||||
tokio = { version = "1.41", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
cross = "0.2.5"
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ customText = """
|
|||
[limitations]
|
||||
maxAvatarSize = 100 # KB
|
||||
maxAvatars = 10 # It doesn't look like Figura has any actions implemented with this?
|
||||
# P.S. And it doesn't look like the current API allows anything like that... Unless you store them directly in one file...
|
||||
# P.S. And it doesn't look like the current API allows anything like that...
|
||||
|
||||
[advancedUsers.66004548-4de5-49de-bade-9c3933d8eb97]
|
||||
username = "Shiroyashik"
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ async fn index_assets(version: &str) -> anyhow::Result<IndexMap<String, Value>>
|
|||
entry.path().strip_prefix(version_path.clone())?.to_string_lossy().to_string()
|
||||
};
|
||||
|
||||
map.insert(path, Value::from(hex::encode(digest(&SHA256, &data).as_ref())));
|
||||
map.insert(path, Value::from(faster_hex::hex_string(digest(&SHA256, &data).as_ref())));
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ async fn id(
|
|||
State(state): State<AppState>,
|
||||
) -> String {
|
||||
let server_id =
|
||||
hex::encode(&digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, &rand()).as_ref()[0..20]);
|
||||
faster_hex::hex_string(&digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, &rand()).as_ref()[0..20]);
|
||||
let state = state.user_manager;
|
||||
state.pending_insert(server_id.clone(), query.username);
|
||||
server_id
|
||||
|
|
@ -32,11 +32,11 @@ async fn verify(
|
|||
State(state): State<AppState>,
|
||||
) -> Response {
|
||||
let server_id = query.id.clone();
|
||||
let username = state.user_manager.pending_remove(&server_id).unwrap().1; // TODO: Add error check
|
||||
let nickname = state.user_manager.pending_remove(&server_id).unwrap().1; // TODO: Add error check
|
||||
let userinfo = match has_joined(
|
||||
state.config.read().await.auth_providers.clone(),
|
||||
&server_id,
|
||||
&username
|
||||
&nickname
|
||||
).await {
|
||||
Ok(d) => d,
|
||||
Err(_e) => {
|
||||
|
|
@ -47,12 +47,12 @@ async fn verify(
|
|||
if let Some((uuid, auth_provider)) = userinfo {
|
||||
let umanager = state.user_manager;
|
||||
if umanager.is_banned(&uuid) {
|
||||
info!("[Authentication] {username} tried to log in, but was banned");
|
||||
info!("[Authentication] {nickname} tried to log in, but was banned");
|
||||
return (StatusCode::BAD_REQUEST, "You're banned!".to_string()).into_response();
|
||||
}
|
||||
info!("[Authentication] {username} logged in using {}", auth_provider.name);
|
||||
info!("[Authentication] {nickname} logged in using {}", auth_provider.name);
|
||||
let userinfo = Userinfo {
|
||||
username,
|
||||
nickname,
|
||||
uuid,
|
||||
token: Some(server_id.clone()),
|
||||
auth_provider,
|
||||
|
|
@ -70,7 +70,7 @@ async fn verify(
|
|||
}
|
||||
(StatusCode::OK, server_id.to_string()).into_response()
|
||||
} else {
|
||||
info!("[Authentication] failed to verify {username}");
|
||||
info!("[Authentication] failed to verify {nickname}");
|
||||
(StatusCode::BAD_REQUEST, "failed to verify".to_string()).into_response()
|
||||
}
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ pub async fn upload_avatar(
|
|||
tracing::info!(
|
||||
"{} ({}) trying to upload an avatar",
|
||||
user_info.uuid,
|
||||
user_info.username
|
||||
user_info.nickname
|
||||
);
|
||||
let avatar_file = format!("{}/{}.moon", *AVATARS_VAR, user_info.uuid);
|
||||
let mut file = BufWriter::new(fs::File::create(&avatar_file).await.map_err(internal_and_log)?);
|
||||
|
|
@ -121,7 +121,7 @@ pub async fn delete_avatar(Token(token): Token, State(state): State<AppState>) -
|
|||
tracing::info!(
|
||||
"{} ({}) is trying to delete the avatar",
|
||||
user_info.uuid,
|
||||
user_info.username
|
||||
user_info.nickname
|
||||
);
|
||||
let avatar_file = format!("{}/{}.moon", *AVATARS_VAR, user_info.uuid);
|
||||
fs::remove_file(avatar_file).await.map_err(internal_and_log)?;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ async fn handle_socket(mut ws: WebSocket, state: AppState) {
|
|||
}
|
||||
|
||||
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);
|
||||
tracing::debug!("WebSocket control for {} is transferred to the main worker", session.user.nickname);
|
||||
loop {
|
||||
tokio::select! {
|
||||
external_msg = ws.recv_and_decode() => {
|
||||
|
|
@ -96,7 +96,7 @@ async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppSta
|
|||
let _ = session.subs_tx.send(s2c_ping);
|
||||
},
|
||||
C2SMessage::Sub(uuid) => {
|
||||
tracing::debug!("[WebSocket] {} subscribes to {}", session.user.username, uuid);
|
||||
tracing::debug!("[WebSocket] {} subscribes to {}", session.user.nickname, uuid);
|
||||
|
||||
// Doesn't allow to subscribe to yourself
|
||||
if session.user.uuid != uuid {
|
||||
|
|
@ -114,11 +114,11 @@ async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppSta
|
|||
}
|
||||
},
|
||||
C2SMessage::Unsub(uuid) => {
|
||||
tracing::debug!("[WebSocket] {} unsubscribes from {}", session.user.username, uuid);
|
||||
tracing::debug!("[WebSocket] {} unsubscribes from {}", session.user.nickname, uuid);
|
||||
|
||||
match session.sub_workers_aborthandles.get(&uuid) {
|
||||
Some(handle) => handle.abort(),
|
||||
None => tracing::warn!("[WebSocket] {} was not subscribed.", session.user.username),
|
||||
None => tracing::warn!("[WebSocket] {} was not subscribed.", session.user.nickname),
|
||||
};
|
||||
},
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppSta
|
|||
.inspect_err(
|
||||
|kind| tracing::warn!("[WebSocket] Didn't get the ban message due to {}", kind)
|
||||
);
|
||||
bail!("{} banned!", session.user.username)
|
||||
bail!("{} banned!", session.user.nickname)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -178,7 +178,7 @@ async fn authenticate(socket: &mut WebSocket, state: &AppState) -> Result<Userin
|
|||
.inspect_err(
|
||||
|kind| tracing::warn!("[WebSocket] Didn't get the ban message due to {}", kind)
|
||||
);
|
||||
Err(AuthModeError::Banned(user.username.clone()))
|
||||
Err(AuthModeError::Banned(user.nickname.clone()))
|
||||
}
|
||||
},
|
||||
None => {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ impl RecvAndDecode for WebSocket {
|
|||
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(RADError::DecodeError(e, faster_hex::hex_string(&msg.into_data())))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ pub(super) async fn raw(
|
|||
) -> ApiResult<&'static str> {
|
||||
trace!(body = body);
|
||||
state.config.read().await.clone().verify_token(&token)?;
|
||||
let payload = hex::decode(body).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
|
||||
let mut payload = vec![0; body.len() / 2];
|
||||
faster_hex::hex_decode(body.as_bytes(), &mut payload).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
|
||||
debug!("{:?}", payload);
|
||||
|
||||
match query.uuid {
|
||||
|
|
@ -47,7 +48,8 @@ pub(super) async fn sub_raw(
|
|||
) -> ApiResult<&'static str> {
|
||||
trace!(body = body);
|
||||
state.config.read().await.clone().verify_token(&token)?;
|
||||
let payload = hex::decode(body).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
|
||||
let mut payload = vec![0; body.len() / 2];
|
||||
faster_hex::hex_decode(body.as_bytes(), &mut payload).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
|
||||
debug!("{:?}", payload);
|
||||
|
||||
match query.uuid {
|
||||
|
|
|
|||
|
|
@ -19,11 +19,15 @@ pub struct Token(pub String);
|
|||
|
||||
impl Token {
|
||||
pub async fn check_auth(self, state: &AppState) -> ApiResult<()> {
|
||||
if state.user_manager.is_authenticated(&self.0) {
|
||||
if let Some(user) = state.user_manager.get(&self.0) {
|
||||
if !user.banned {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ApiError::Unauthorized)
|
||||
}
|
||||
} else {
|
||||
Err(ApiError::Unauthorized)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +195,7 @@ impl UManager {
|
|||
warn!("Rejected attempt to create a second session for the same user!");
|
||||
return Err(())
|
||||
}
|
||||
debug!("`{}` already have token in registered profile (old token already removed from 'authenticated')", userinfo.username);
|
||||
debug!("`{}` already have token in registered profile (old token already removed from 'authenticated')", userinfo.nickname);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +209,7 @@ impl UManager {
|
|||
let usercopy = userinfo.clone();
|
||||
self.registered.entry(uuid)
|
||||
.and_modify(|exist| {
|
||||
if !userinfo.username.is_empty() { exist.username = userinfo.username };
|
||||
if !userinfo.nickname.is_empty() { exist.nickname = userinfo.nickname };
|
||||
if !userinfo.auth_provider.is_empty() { exist.auth_provider = userinfo.auth_provider };
|
||||
if userinfo.rank != Userinfo::default().rank { exist.rank = userinfo.rank };
|
||||
if userinfo.token.is_some() { exist.token = userinfo.token };
|
||||
|
|
@ -236,7 +240,7 @@ impl UManager {
|
|||
user.banned = false;
|
||||
};
|
||||
}
|
||||
pub fn is_authenticated(&self, token: &String) -> bool {
|
||||
pub fn _is_authenticated(&self, token: &String) -> bool {
|
||||
self.authenticated.contains_key(token)
|
||||
}
|
||||
pub fn _is_registered(&self, uuid: &Uuid) -> bool {
|
||||
|
|
|
|||
|
|
@ -6,21 +6,20 @@ use uuid::Uuid;
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Userinfo {
|
||||
pub uuid: Uuid,
|
||||
pub username: String,
|
||||
pub nickname: String,
|
||||
pub rank: String,
|
||||
pub last_used: String,
|
||||
pub auth_provider: AuthProvider,
|
||||
pub token: Option<String>,
|
||||
pub version: String,
|
||||
pub banned: bool
|
||||
|
||||
}
|
||||
|
||||
impl Default for Userinfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
uuid: Default::default(),
|
||||
username: Default::default(),
|
||||
nickname: Default::default(),
|
||||
rank: "default".to_string(),
|
||||
last_used: Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
|
||||
auth_provider: Default::default(),
|
||||
|
|
|
|||
29
src/main.rs
29
src/main.rs
|
|
@ -93,7 +93,6 @@ async fn main() -> Result<()> {
|
|||
|
||||
// 3. Display info about current instance and check updates
|
||||
tracing::info!("The Sculptor v{SCULPTOR_VERSION} ({REPOSITORY})");
|
||||
// let _ = check_updates(REPOSITORY, SCULPTOR_VERSION).await; // Currently, there is no need to do anything with the result of the function
|
||||
|
||||
match get_latest_version(REPOSITORY).await {
|
||||
Ok(latest_version) => {
|
||||
|
|
@ -168,25 +167,13 @@ async fn app() -> Result<bool> {
|
|||
config,
|
||||
};
|
||||
|
||||
// FIXME: FIXME: FIXME: ПЕРЕДЕЛАЙ ЭТО! НЕМЕДЛЕННО! ЕБУЧИЙ ПОЗОР :<
|
||||
// Automatic update of configuration while the server is running
|
||||
let config_update = Arc::clone(&state.config);
|
||||
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;
|
||||
let new_config = Config::parse(CONFIG_VAR.clone().into());
|
||||
let mut config = config_update.write().await;
|
||||
|
||||
if new_config != *config {
|
||||
tracing::info!("Server configuration modification detected!");
|
||||
*config = new_config;
|
||||
update_advanced_users(&config.advanced_users.clone(), &umanager, &session).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Automatic update of configuration/ban list while the server is running
|
||||
tokio::spawn(update_advanced_users(
|
||||
CONFIG_VAR.clone().into(),
|
||||
Arc::clone(&state.user_manager),
|
||||
Arc::clone(&state.session),
|
||||
Arc::clone(&state.config)
|
||||
));
|
||||
if state.config.read().await.mc_folder.exists() {
|
||||
tokio::spawn(update_bans_from_minecraft(
|
||||
state.config.read().await.mc_folder.clone(),
|
||||
|
|
@ -242,11 +229,9 @@ async fn shutdown_signal() {
|
|||
let terminate = std::future::pending::<()>();
|
||||
tokio::select! {
|
||||
() = ctrl_c => {
|
||||
println!();
|
||||
tracing::info!("Ctrl+C signal received");
|
||||
},
|
||||
() = terminate => {
|
||||
println!();
|
||||
tracing::info!("Terminate signal received");
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ impl From<BannedPlayer> for Userinfo {
|
|||
fn from(val: BannedPlayer) -> Self {
|
||||
Userinfo {
|
||||
uuid: val.uuid,
|
||||
username: val.name,
|
||||
nickname: val.name,
|
||||
banned: true,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
use std::{fs::File, io::Read, path::{Path, PathBuf}};
|
||||
use std::{fs::File, io::Read, path::{Path, PathBuf}, sync::Arc};
|
||||
|
||||
use notify::{Event, Watcher};
|
||||
use tokio::{io::AsyncReadExt, sync::RwLock};
|
||||
use base64::prelude::*;
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use rand::{thread_rng, Rng};
|
||||
use ring::digest::{self, digest};
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tracing::{error, info};
|
||||
use uuid::Uuid;
|
||||
use chrono::prelude::*;
|
||||
|
||||
use crate::{auth::{UManager, Userinfo}, state::{AdvancedUsers, BannedPlayer}};
|
||||
use crate::{auth::Userinfo, state::{BannedPlayer, Config}, UManager};
|
||||
|
||||
// Core functions
|
||||
pub fn rand() -> [u8; 50] {
|
||||
let mut rng = thread_rng();
|
||||
let distr = rand::distributions::Uniform::new_inclusive(0, 255);
|
||||
|
|
@ -20,29 +19,40 @@ pub fn rand() -> [u8; 50] {
|
|||
}
|
||||
nums
|
||||
}
|
||||
// End of Core functions
|
||||
|
||||
pub fn _generate_hex_string(length: usize) -> String {
|
||||
// FIXME: Variable doesn't using!
|
||||
let rng = thread_rng();
|
||||
let random_bytes: Vec<u8> = rng.sample_iter(&Alphanumeric).take(length / 2).collect();
|
||||
|
||||
hex::encode(random_bytes)
|
||||
}
|
||||
|
||||
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>>
|
||||
path: PathBuf,
|
||||
umanager: Arc<UManager>,
|
||||
sessions: Arc<dashmap::DashMap<Uuid, tokio::sync::mpsc::Sender<crate::api::figura::SessionMessage>>>,
|
||||
config: Arc<RwLock<Config>>,
|
||||
) {
|
||||
let users: Vec<(Uuid, Userinfo)> = value
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel::<notify::Result<Event>>(1);
|
||||
tx.send(Ok(notify::Event::default())).await.unwrap();
|
||||
let mut watcher = notify::RecommendedWatcher::new(
|
||||
move |res| {
|
||||
tx.blocking_send(res).unwrap();
|
||||
},
|
||||
notify::Config::default(),
|
||||
).unwrap();
|
||||
watcher.watch(&path, notify::RecursiveMode::NonRecursive).unwrap();
|
||||
|
||||
let mut first_time = true;
|
||||
while rx.recv().await.is_some() {
|
||||
let new_config = Config::parse(path.clone());
|
||||
let mut config = config.write().await;
|
||||
|
||||
if new_config != *config || first_time {
|
||||
if !first_time { tracing::info!("Server configuration modification detected!") }
|
||||
first_time = false;
|
||||
*config = new_config;
|
||||
let users: Vec<(Uuid, Userinfo)> = config.advanced_users
|
||||
.iter()
|
||||
.map( |(uuid, userdata)| {
|
||||
(
|
||||
*uuid,
|
||||
Userinfo {
|
||||
uuid: *uuid,
|
||||
username: userdata.username.clone(),
|
||||
nickname: userdata.username.clone(),
|
||||
banned: userdata.banned,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -54,14 +64,18 @@ pub async fn update_advanced_users(
|
|||
if userinfo.banned {
|
||||
umanager.ban(&userinfo);
|
||||
if let Some(tx) = sessions.get(&uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
|
||||
} else {
|
||||
umanager.unban(&uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>>>
|
||||
umanager: Arc<UManager>,
|
||||
sessions: 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!");
|
||||
|
|
@ -74,7 +88,7 @@ pub async fn update_bans_from_minecraft(
|
|||
|
||||
if !old_bans.is_empty() {
|
||||
let names: Vec<String> = old_bans.iter().map(|user| user.name.clone()).collect();
|
||||
info!("Banned players: {}", names.join(", "));
|
||||
tracing::info!("Banned players: {}", names.join(", "));
|
||||
}
|
||||
|
||||
for player in &old_bans {
|
||||
|
|
@ -82,19 +96,27 @@ pub async fn update_bans_from_minecraft(
|
|||
if let Some(tx) = sessions.get(&player.uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
|
||||
}
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel::<notify::Result<Event>>(1);
|
||||
let mut watcher = notify::RecommendedWatcher::new(
|
||||
move |res| {
|
||||
tx.blocking_send(res).unwrap();
|
||||
},
|
||||
notify::Config::default(),
|
||||
).unwrap();
|
||||
watcher.watch(&path, notify::RecursiveMode::NonRecursive).unwrap();
|
||||
|
||||
// old_bans
|
||||
loop {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
||||
while rx.recv().await.is_some() {
|
||||
let mut file = tokio::fs::File::open(path.clone()).await.expect("Access denied or file doesn't exists!");
|
||||
let mut data = String::new();
|
||||
file.read_to_string(&mut data).await.expect("cant read banned-players.json");
|
||||
let new_bans: Vec<BannedPlayer> = if let Ok(res) = serde_json::from_str(&data) { res } else {
|
||||
error!("Error occured while parsing a banned-players.json");
|
||||
tracing::error!("Error occured while parsing a banned-players.json");
|
||||
continue;
|
||||
};
|
||||
|
||||
if new_bans != old_bans {
|
||||
info!("Minecraft ban list modification detected!");
|
||||
tracing::info!("Minecraft ban list modification detected!");
|
||||
let unban: Vec<&BannedPlayer> = old_bans.iter().filter(|user| !new_bans.contains(user)).collect();
|
||||
let mut unban_names = unban.iter().map(|user| user.name.clone()).collect::<Vec<String>>().join(", ");
|
||||
if !unban.is_empty() {
|
||||
|
|
@ -110,14 +132,13 @@ pub async fn update_bans_from_minecraft(
|
|||
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}");
|
||||
tracing::info!("List of changes:\n Banned: {ban_names}\n Unbanned: {unban_names}");
|
||||
// Write new to old for next iteration
|
||||
old_bans = new_bans;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn format_uuid(uuid: &Uuid) -> String {
|
||||
// let uuid = Uuid::parse_str(&uuid)?; TODO: Вероятно format_uuid стоит убрать
|
||||
// .map_err(|_| tide::Error::from_str(StatusCode::InternalServerError, "Failed to parse UUID"))?;
|
||||
|
|
@ -138,7 +159,7 @@ pub fn calculate_file_sha256(file_path: &str) -> Result<String, std::io::Error>
|
|||
let hash = binding.as_ref();
|
||||
|
||||
// Convert the hash to a hexadecimal string
|
||||
let hex_hash = hex::encode(hash);
|
||||
let hex_hash = faster_hex::hex_string(hash);
|
||||
|
||||
Ok(hex_hash)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue