Compare commits
23 Commits
410ca2eeef
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
c4f3c129f1
|
|||
|
64b2f04f8f
|
|||
|
940b595e3a
|
|||
|
46a886a6bd
|
|||
|
3791a8b04e
|
|||
|
9ba7ac5c70
|
|||
|
c3c74bc47c
|
|||
|
8f3de30cd1
|
|||
|
b2ab524b7e
|
|||
|
97f3e4c139
|
|||
|
6aa0ab473d
|
|||
|
5125aed0da
|
|||
|
0bbb8b3245
|
|||
|
5ccac4f3d4
|
|||
|
37afa2c044
|
|||
|
887a228cf2
|
|||
|
3f840ea284
|
|||
|
fe5235a3ca
|
|||
|
7bae17c525
|
|||
|
35ac5453d9
|
|||
|
17032a1940
|
|||
|
408530db00
|
|||
|
5136e0bdc3
|
254
Cargo.lock
generated
254
Cargo.lock
generated
@ -174,6 +174,42 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-process"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
|
||||||
|
dependencies = [
|
||||||
|
"async-channel 2.5.0",
|
||||||
|
"async-io",
|
||||||
|
"async-lock",
|
||||||
|
"async-signal",
|
||||||
|
"async-task",
|
||||||
|
"blocking",
|
||||||
|
"cfg-if",
|
||||||
|
"event-listener 5.4.1",
|
||||||
|
"futures-lite",
|
||||||
|
"rustix",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-signal"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
|
||||||
|
dependencies = [
|
||||||
|
"async-io",
|
||||||
|
"async-lock",
|
||||||
|
"atomic-waker",
|
||||||
|
"cfg-if",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"rustix",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"slab",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-std"
|
name = "async-std"
|
||||||
version = "1.13.2"
|
version = "1.13.2"
|
||||||
@ -214,9 +250,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
@ -226,15 +262,15 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.11.1"
|
version = "2.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
|
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.12.0"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
|
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
@ -298,9 +334,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.20.2"
|
version = "3.20.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
@ -319,9 +355,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.63"
|
version = "1.2.64"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
|
checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
"jobserver",
|
"jobserver",
|
||||||
@ -337,9 +373,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.44"
|
version = "0.4.45"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@ -518,15 +554,12 @@ name = "deranged"
|
|||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
||||||
dependencies = [
|
|
||||||
"powerfmt",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diesel"
|
name = "diesel"
|
||||||
version = "2.3.9"
|
version = "2.3.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9940fb8467a0a06312218ed384185cb8536aa10d8ec017d0ce7fad2c1bd882d5"
|
checksum = "29fe29a87fb84c631ffb3ba21798c4b1f3a964701ba78f0dce4bf8668562ec88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"diesel_derives",
|
"diesel_derives",
|
||||||
@ -561,6 +594,17 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diesel_migrations"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d0f4a98124ba6d4ca75da535f65984badec16a003b6e2f94a01e31a79490b8"
|
||||||
|
dependencies = [
|
||||||
|
"diesel",
|
||||||
|
"migrations_internals",
|
||||||
|
"migrations_macros",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diesel_table_macro_syntax"
|
name = "diesel_table_macro_syntax"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@ -585,9 +629,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -596,14 +640,17 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dockermcmgr"
|
name = "dockermcmgr"
|
||||||
version = "0.1.0"
|
version = "0.0.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-process",
|
||||||
"async-std",
|
"async-std",
|
||||||
"bollard",
|
"bollard",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel-enum",
|
"diesel-enum",
|
||||||
|
"diesel_migrations",
|
||||||
|
"futures-util",
|
||||||
"json",
|
"json",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@ -894,9 +941,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.4.0"
|
version = "1.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
|
checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"itoa",
|
"itoa",
|
||||||
@ -948,9 +995,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.9.0"
|
version = "1.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
|
checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -1201,13 +1248,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.98"
|
version = "0.3.102"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
|
checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"once_cell",
|
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1277,9 +1323,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.29"
|
version = "0.4.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"value-bag",
|
"value-bag",
|
||||||
]
|
]
|
||||||
@ -1295,9 +1341,30 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.8.0"
|
version = "2.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "migrations_internals"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36c791ecdf977c99f45f23280405d7723727470f6689a5e6dbf513ac547ae10d"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"toml",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "migrations_macros"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36fc5ac76be324cfd2d3f2cf0fdf5d5d3c4f14ed8aaebadb09e304ba42282703"
|
||||||
|
dependencies = [
|
||||||
|
"migrations_internals",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
@ -1311,9 +1378,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.2.0"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
|
checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
@ -1529,9 +1596,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.12.3"
|
version = "1.12.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -1552,9 +1619,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.10"
|
version = "0.8.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsqlite-vfs"
|
name = "rsqlite-vfs"
|
||||||
@ -1644,9 +1711,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.149"
|
version = "1.0.150"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -1666,6 +1733,15 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@ -1730,15 +1806,15 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.15.1"
|
version = "1.15.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.6.3"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
@ -1823,12 +1899,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.47"
|
version = "0.3.49"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa",
|
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"num-conv",
|
"num-conv",
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
@ -1839,15 +1914,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-core"
|
name = "time-core"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros"
|
name = "time-macros"
|
||||||
version = "0.2.27"
|
version = "0.2.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
|
checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-conv",
|
"num-conv",
|
||||||
"time-core",
|
"time-core",
|
||||||
@ -1865,9 +1940,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.52.1"
|
version = "1.52.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
|
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
@ -1903,6 +1978,37 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.9.12+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_parser",
|
||||||
|
"winnow 0.7.15",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.7.5+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.1.2+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||||
|
dependencies = [
|
||||||
|
"winnow 1.0.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -2011,9 +2117,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasip2"
|
name = "wasip2"
|
||||||
version = "1.0.3+wasi-0.2.9"
|
version = "1.0.4+wasi-0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
|
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wit-bindgen 0.57.1",
|
"wit-bindgen 0.57.1",
|
||||||
]
|
]
|
||||||
@ -2029,9 +2135,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.121"
|
version = "0.2.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790"
|
checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -2042,9 +2148,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-futures"
|
name = "wasm-bindgen-futures"
|
||||||
version = "0.4.71"
|
version = "0.4.75"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8"
|
checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@ -2052,9 +2158,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.121"
|
version = "0.2.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578"
|
checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@ -2062,9 +2168,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.121"
|
version = "0.2.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2"
|
checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -2075,9 +2181,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.121"
|
version = "0.2.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441"
|
checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -2206,6 +2312,18 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.51.0"
|
version = "0.51.0"
|
||||||
@ -2308,9 +2426,9 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.8.2"
|
version = "0.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
|
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
"yoke-derive",
|
"yoke-derive",
|
||||||
@ -2352,9 +2470,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "zeroize"
|
||||||
version = "1.8.2"
|
version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerotrie"
|
name = "zerotrie"
|
||||||
|
|||||||
11
Cargo.toml
11
Cargo.toml
@ -1,20 +1,27 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dockermcmgr"
|
name = "dockermcmgr"
|
||||||
version = "0.1.0"
|
version = "0.0.9"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "dmm"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-process = "2.5.0"
|
||||||
async-std = "1.13.2"
|
async-std = "1.13.2"
|
||||||
bollard = "0.21.0"
|
bollard = "0.21.0"
|
||||||
chrono = "0.4.44"
|
chrono = "0.4.44"
|
||||||
clap = "4.6.1"
|
clap = "4.6.1"
|
||||||
diesel = { version = "2.3.9", features = ["sqlite", "r2d2", "chrono"] }
|
diesel = { version = "2.3.9", features = ["sqlite", "r2d2", "chrono"] }
|
||||||
diesel-enum = "0.2.1"
|
diesel-enum = "0.2.1"
|
||||||
|
diesel_migrations = "2.3.2"
|
||||||
|
futures-util = "0.3.32"
|
||||||
json = "0.12.4"
|
json = "0.12.4"
|
||||||
num-derive = "0.4.2"
|
num-derive = "0.4.2"
|
||||||
num-traits = "0.2.19"
|
num-traits = "0.2.19"
|
||||||
regex = "1.12.3"
|
regex = "1.12.3"
|
||||||
serde = "1.0.228"
|
serde = "1.0.228"
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
tokio = { version = "1.52.1", features = ["net", "rt", "macros", "signal", "fs"] }
|
tokio = { version = "1.52.1", features = ["net", "rt", "macros", "signal", "fs", "rt-multi-thread"] }
|
||||||
zip = "8.6.0"
|
zip = "8.6.0"
|
||||||
|
|||||||
95
flake.lock
generated
Normal file
95
flake.lock
generated
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1767039857,
|
||||||
|
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1780930886,
|
||||||
|
"narHash": "sha256-rppURzHviaQN131F+nLiLdGfcb0uCd9gGP0E5+iw9MI=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "8c3cede7ddc26bd659d2d383b5610efbd2c7a16e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1770107345,
|
||||||
|
"narHash": "sha256-tbS0Ebx2PiA1FRW8mt8oejR0qMXmziJmPaU1d4kYY9g=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "4533d9293756b63904b7238acb84ac8fe4c8c2c4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"systems": "systems",
|
||||||
|
"treefmt-nix": "treefmt-nix"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"treefmt-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1780220602,
|
||||||
|
"narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"rev": "db947814a175b7ca6ded66e21383d938df01c227",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
78
flake.nix
Normal file
78
flake.nix
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
systems.url = "github:nix-systems/default";
|
||||||
|
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||||
|
flake-compat = {
|
||||||
|
url = "github:NixOS/flake-compat";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
nixpkgs,
|
||||||
|
...
|
||||||
|
}@inputs:
|
||||||
|
with builtins;
|
||||||
|
with nixpkgs.lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
doubles = import inputs.systems;
|
||||||
|
forAllSystems = genAttrs doubles;
|
||||||
|
|
||||||
|
pkgs_gen =
|
||||||
|
{ ... }@args:
|
||||||
|
import nixpkgs args;
|
||||||
|
|
||||||
|
dmm_gen =
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
tools ? [ ],
|
||||||
|
specialArgs ? { },
|
||||||
|
}:
|
||||||
|
pkgs.rustPlatform.buildRustPackage (
|
||||||
|
let
|
||||||
|
src = cleanSource ./.;
|
||||||
|
in
|
||||||
|
final:
|
||||||
|
{
|
||||||
|
pname = "dmm";
|
||||||
|
version = "0.0.9";
|
||||||
|
|
||||||
|
inherit src;
|
||||||
|
cargoLock.lockFile = src + "/Cargo.lock";
|
||||||
|
nativeBuildInputs = tools;
|
||||||
|
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
sqlite
|
||||||
|
lrzip
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// specialArgs
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = forAllSystems (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = pkgs_gen { inherit system; };
|
||||||
|
dmm = dmm_gen { inherit pkgs; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit dmm;
|
||||||
|
default = dmm;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
formatter = forAllSystems (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = pkgs_gen { inherit system; };
|
||||||
|
in
|
||||||
|
inputs.treefmt-nix.lib.mkWrapper pkgs {
|
||||||
|
programs.nixfmt.enable = true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,10 +1,10 @@
|
|||||||
CREATE TABLE "servers" (
|
CREATE TABLE "servers" (
|
||||||
"name" varchar(255) UNIQUE NOT NULL PRIMARY KEY,
|
"name" varchar(255) UNIQUE NOT NULL PRIMARY KEY,
|
||||||
"last_login" timestamp,
|
"last_login" timestamp,
|
||||||
"container_id" varchar(30),
|
|
||||||
"status" INT2 NOT NULL DEFAULT 0,
|
"status" INT2 NOT NULL DEFAULT 0,
|
||||||
"is_default" bool NOT NULL DEFAULT false,
|
"is_default" bool NOT NULL DEFAULT false,
|
||||||
"redirect_ip" varchar(50)
|
"redirect_ip" varchar(50),
|
||||||
|
"simple_redirect" bool NOT NULL DEFAULT false
|
||||||
);
|
);
|
||||||
|
|
||||||
insert into servers (name, last_login, container_id, status, is_default, redirect_ip) values ('potato', datetime(), null, 1, false, 'play.hypixel.net:25565');
|
insert into servers (name, last_login, status, is_default, redirect_ip, simple_redirect) values ('potato', datetime(), 4, false, 'play.hypixel.net:25565', true);
|
||||||
|
|||||||
@ -10,7 +10,7 @@ pkgs.mkShell {
|
|||||||
|
|
||||||
];
|
];
|
||||||
PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig";
|
PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig";
|
||||||
|
COMPRESSION_COMMAND = "${pkgs.lrzip}/bin/lrzip";
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
[ ! -f .env ] || export $(grep -v '^#' .env | xargs)
|
[ ! -f .env ] || export $(grep -v '^#' .env | xargs)
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
|
use chrono::Utc;
|
||||||
use clap::{ArgMatches, Command, arg};
|
use clap::{ArgMatches, Command, arg};
|
||||||
use crate::cli::env_settings;
|
use crate::cli::env_settings;
|
||||||
use crate::status::ServerStatus;
|
use crate::status::ServerStatus;
|
||||||
use crate::utils::{ validate_name, extract_archive, move_all_elements};
|
use crate::utils::{ validate_name, extract_archive, move_all_elements};
|
||||||
|
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use crate::{DbPool, models};
|
use crate::{DbPool, models}; use crate::schema;
|
||||||
use crate::schema;
|
|
||||||
|
|
||||||
use tokio::{fs::{File, create_dir}, io::AsyncWriteExt};
|
use tokio::{fs::{File, create_dir}, io::AsyncWriteExt};
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
pub fn create_subcommand() -> Command {
|
pub fn create_subcommand() -> Command {
|
||||||
let mut arguments = vec![
|
let mut arguments = vec![
|
||||||
@ -27,7 +27,7 @@ pub fn create_subcommand() -> Command {
|
|||||||
|
|
||||||
|
|
||||||
pub async fn subcommand(arg: &ArgMatches, pool: DbPool) -> Result<(), String>{
|
pub async fn subcommand(arg: &ArgMatches, pool: DbPool) -> Result<(), String>{
|
||||||
// use schema::servers::dsl::*;
|
use schema::servers::dsl::*;
|
||||||
let config = crate::Config::load();
|
let config = crate::Config::load();
|
||||||
let server_name = arg.get_one::<String>("NAME").unwrap();
|
let server_name = arg.get_one::<String>("NAME").unwrap();
|
||||||
|
|
||||||
@ -57,14 +57,13 @@ pub async fn subcommand(arg: &ArgMatches, pool: DbPool) -> Result<(), String>{
|
|||||||
std::fs::remove_dir_all(path.to_string() + "tmp").map_err(|e| format!("failed to remove the tmp dir, reason : {}", e))?;
|
std::fs::remove_dir_all(path.to_string() + "tmp").map_err(|e| format!("failed to remove the tmp dir, reason : {}", e))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let _new_server = models::CreateServer {
|
let new_server = models::CreateServer {
|
||||||
name: server_name,
|
name: server_name,
|
||||||
last_login: Some(SystemTime::now()),
|
last_login: Some(Utc::now().naive_utc()),
|
||||||
container_id: None,
|
|
||||||
status: ServerStatus::Stopped,
|
status: ServerStatus::Stopped,
|
||||||
redirect_ip: None
|
redirect_ip: None
|
||||||
};
|
};
|
||||||
|
|
||||||
// insert_into(schema::servers::dsl::servers).values(&new_server).execute(conn).map_err(|e| format!("Failed to insert in db, error : {:?}", e))?;
|
diesel::insert_into(servers).values(&new_server).execute(conn).map_err(|e| format!("Failed to insert in db, error : {:?}", e))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
47
src/cli/archive.rs
Normal file
47
src/cli/archive.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use bollard::Docker;
|
||||||
|
use clap::Arg;
|
||||||
|
use clap::{Command, ArgMatches};
|
||||||
|
use diesel::{SelectableHelper};
|
||||||
|
use diesel::result::Error::NotFound;
|
||||||
|
|
||||||
|
use crate::status::ServerStatus;
|
||||||
|
use crate::{DbPool, schema, models};
|
||||||
|
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
use crate::srvmgr::{self, archive_server};
|
||||||
|
|
||||||
|
pub fn create_subcommand() -> Command {
|
||||||
|
Command::new("archive")
|
||||||
|
.about("archive a server")
|
||||||
|
.arg(Arg::new("NAME").required(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn subcommand(arguments: &ArgMatches, pool: &DbPool, docker: &Docker) -> Result<(), String> {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
|
||||||
|
let server_name = arguments.get_one::<String>("NAME").unwrap();
|
||||||
|
let conn = &mut pool.get().unwrap();
|
||||||
|
|
||||||
|
let server = match servers.select(models::Servers::as_select()).filter(name.eq(server_name)).first(conn) {
|
||||||
|
Ok(server) => {server},
|
||||||
|
Err(NotFound) => { return Err("server not found".to_string()); },
|
||||||
|
Err(e) => { return Err(format!("Failed to fetch server from db : {}", e)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
if server.status != ServerStatus::Running && server.status != ServerStatus::Stopped {
|
||||||
|
return Err(format!("invalid current server state : {}", server.status));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if server.status == ServerStatus::Running {
|
||||||
|
println!("Stopping container");
|
||||||
|
srvmgr::stop_server(&docker, pool, server_name, false).await?;
|
||||||
|
println!("container stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
archive_server(&server, pool).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
32
src/cli/disable.rs
Normal file
32
src/cli/disable.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use bollard::Docker;
|
||||||
|
use clap::Arg;
|
||||||
|
use clap::{Command, ArgMatches};
|
||||||
|
|
||||||
|
use crate::DbPool;
|
||||||
|
|
||||||
|
use crate::srvmgr;
|
||||||
|
use crate::status::ServerStatus;
|
||||||
|
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
use crate::schema;
|
||||||
|
|
||||||
|
pub fn create_subcommand() -> Command {
|
||||||
|
Command::new("disable")
|
||||||
|
.about("disable")
|
||||||
|
.arg(Arg::new("NAME").required(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn subcommand(arguments: &ArgMatches, pool: &DbPool, docker: &Docker) -> Result<(), String> {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
let server_name = arguments.get_one::<String>("NAME").unwrap();
|
||||||
|
println!("Stopping container");
|
||||||
|
srvmgr::stop_server(&docker, pool, server_name, true).await?;
|
||||||
|
println!("container stopped");
|
||||||
|
|
||||||
|
let conn = &mut pool.get().unwrap();
|
||||||
|
|
||||||
|
diesel::update(servers).filter(name.eq(server_name)).set(status.eq(ServerStatus::Disabled)).execute(conn).map_err(|e| format!("Failed to disable the server : {}", e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -40,7 +40,6 @@ pub async fn subcommand(arguments: &ArgMatches, pool: DbPool) -> Result<(), Stri
|
|||||||
let prev_env = fs::read_to_string(path.to_string() + "/env").map_err(|e| format!("failed to read previous env file : {}", e))?;
|
let prev_env = fs::read_to_string(path.to_string() + "/env").map_err(|e| format!("failed to read previous env file : {}", e))?;
|
||||||
|
|
||||||
let new_env = env_settings::update_env(arguments, prev_env);
|
let new_env = env_settings::update_env(arguments, prev_env);
|
||||||
println!("\n\nnew env : {}", new_env);
|
|
||||||
|
|
||||||
|
|
||||||
let mut env_file = fs::OpenOptions::new().write(true).truncate(true).open(path + "/env").map_err(|e| format!("failed to reopen env file : {}" ,e))?;
|
let mut env_file = fs::OpenOptions::new().write(true).truncate(true).open(path + "/env").map_err(|e| format!("failed to reopen env file : {}" ,e))?;
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use clap::{ Arg, ArgMatches, parser::ValueSource};
|
use clap::{ Arg, ArgMatches, parser::ValueSource};
|
||||||
|
|
||||||
pub fn get_args() -> [Arg; 21] {
|
pub fn get_args() -> [Arg; 22] {
|
||||||
[
|
[
|
||||||
Arg::new("VERSION").short('v').long("version").help("Minecraft version of the server").default_value("LATEST"),
|
Arg::new("VERSION").short('v').long("version").help("Minecraft version of the server").default_value("LATEST"),
|
||||||
Arg::new("MEMORY").short('m').long("memory").help("How much ram does this server have").default_value("4G"),
|
Arg::new("MEMORY").short('m').long("memory").help("How much ram does this server have").default_value("4G"),
|
||||||
@ -26,12 +26,13 @@ pub fn get_args() -> [Arg; 21] {
|
|||||||
Arg::new("SPAWN_ANIMALS").long("spawn-animals").help("spawn animals").default_value("true"),
|
Arg::new("SPAWN_ANIMALS").long("spawn-animals").help("spawn animals").default_value("true"),
|
||||||
Arg::new("SPAWN_MONSTERS").long("spawn-monsters").help("spawn monsters").default_value("true"),
|
Arg::new("SPAWN_MONSTERS").long("spawn-monsters").help("spawn monsters").default_value("true"),
|
||||||
Arg::new("SPAWN_NPCS").long("spawn-npcs").help("spawn npcs").default_value("true"),
|
Arg::new("SPAWN_NPCS").long("spawn-npcs").help("spawn npcs").default_value("true"),
|
||||||
|
Arg::new("IMAGE_TAG").long("image-tag").help("set the tag of the docker image").default_value("latest")
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn args_to_settings(arguments: &ArgMatches, default: bool) -> Vec<(String, String)> {
|
pub fn args_to_settings(arguments: &ArgMatches, default: bool) -> Vec<(String, String)> {
|
||||||
let default_handle = vec!["VERSION", "MEMORY", "MAX_PLAYERS", "SPAWN_PROTECTION", "ONLINE_MODE", "TYPE", "MOTD", "DIFFICULTY", "OPS", "MODE", "ALLOW_FLIGHT",
|
let default_handle = vec!["VERSION", "MEMORY", "MAX_PLAYERS", "SPAWN_PROTECTION", "ONLINE_MODE", "TYPE", "MOTD", "DIFFICULTY", "OPS", "MODE", "ALLOW_FLIGHT",
|
||||||
"ALLOW_NETHER", "HARDCORE", "MAX_TICK_TIME", "PAUSE_WHEN_EMPTY_SECONDS", "PVP", "SPAWN_ANIMALS", "SPAWN_MONSTERS", "SPAWN_NPCS"];
|
"ALLOW_NETHER", "HARDCORE", "MAX_TICK_TIME", "PAUSE_WHEN_EMPTY_SECONDS", "PVP", "SPAWN_ANIMALS", "SPAWN_MONSTERS", "SPAWN_NPCS", "IMAGE_TAG"];
|
||||||
|
|
||||||
let mut res = vec![
|
let mut res = vec![
|
||||||
("EULA".to_string(), "TRUE".to_string()),
|
("EULA".to_string(), "TRUE".to_string()),
|
||||||
@ -96,5 +97,3 @@ pub fn update_env(arguments: &ArgMatches, prev_env: String) -> String{
|
|||||||
settings_to_env(merged)
|
settings_to_env(merged)
|
||||||
|
|
||||||
}
|
}
|
||||||
// START COMMAND :
|
|
||||||
// docker run --rm --env-file ./env -u 1000:100 -it --volume $PWD/server:/data itzg/minecraft-server
|
|
||||||
|
|||||||
@ -2,6 +2,11 @@ pub mod add;
|
|||||||
pub mod edit;
|
pub mod edit;
|
||||||
pub mod remove;
|
pub mod remove;
|
||||||
pub mod ls;
|
pub mod ls;
|
||||||
|
pub mod start;
|
||||||
|
pub mod stop;
|
||||||
|
pub mod set_state;
|
||||||
|
pub mod archive;
|
||||||
|
pub mod disable;
|
||||||
|
|
||||||
pub mod env_settings;
|
pub mod env_settings;
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
|
use bollard::Docker;
|
||||||
use clap::{Arg, ArgMatches, Command};
|
use clap::{Arg, ArgMatches, Command};
|
||||||
|
|
||||||
use crate::{Config, DbPool};
|
use crate::{Config, DbPool, srvmgr};
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
@ -12,15 +13,16 @@ pub fn create_subcommand() -> Command {
|
|||||||
.arg(Arg::new("NAME").required(true))
|
.arg(Arg::new("NAME").required(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn subcommand(arguments: &ArgMatches, pool: DbPool) -> Result<(), String> {
|
pub async fn subcommand(arguments: &ArgMatches, pool: &DbPool, docker: &Docker) -> Result<(), String> {
|
||||||
use crate::schema::servers::dsl::*;
|
use crate::schema::servers::dsl::*;
|
||||||
let server_name = arguments.get_one::<String>("NAME").unwrap();
|
let server_name = arguments.get_one::<String>("NAME").unwrap();
|
||||||
|
|
||||||
let config = Config::load();
|
let config = Config::load();
|
||||||
|
|
||||||
let conn = &mut pool.get().unwrap();
|
let conn = &mut pool.get().unwrap();
|
||||||
|
|
||||||
//todo stop the server if it's started
|
println!("stopping container if it exists");
|
||||||
|
srvmgr::stop_server(docker, pool, server_name, true).await?;
|
||||||
|
|
||||||
let deleted = diesel::delete(servers.filter(name.eq(server_name))).execute(conn).map_err(|_| "failed to delete from db".to_string())?;
|
let deleted = diesel::delete(servers.filter(name.eq(server_name))).execute(conn).map_err(|_| "failed to delete from db".to_string())?;
|
||||||
if deleted != 1 {
|
if deleted != 1 {
|
||||||
|
|||||||
33
src/cli/set_state.rs
Normal file
33
src/cli/set_state.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use clap::Arg;
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use clap::Command;
|
||||||
|
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use crate::DbPool;
|
||||||
|
use crate::schema;
|
||||||
|
use crate::status::ServerStatus;
|
||||||
|
|
||||||
|
pub fn create_subcommand() -> Command {
|
||||||
|
Command::new("setState")
|
||||||
|
.about("force the known state of a server (will not change the actual state)")
|
||||||
|
.arg(Arg::new("NAME").required(true))
|
||||||
|
.arg(Arg::new("STATE").required(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn subcommand(arguments: &ArgMatches, pool: DbPool) -> Result<(), String> {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
|
||||||
|
|
||||||
|
let server_name = arguments.get_one::<String>("NAME").unwrap();
|
||||||
|
let wanted_state = match arguments.get_one::<String>("STATE").unwrap().as_str() {
|
||||||
|
"stopped" => {ServerStatus::Stopped},
|
||||||
|
"running" => {ServerStatus::Running},
|
||||||
|
_ => { return Err("invalid state, must be `stoppped` or `running`".to_string()) }
|
||||||
|
};
|
||||||
|
|
||||||
|
let conn = &mut pool.get().unwrap();
|
||||||
|
|
||||||
|
diesel::update(servers).filter(name.eq(server_name)).set(status.eq(wanted_state)).execute(conn).map_err(|e| format!("Failed to set the server to the wanted state : {}", e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
26
src/cli/start.rs
Normal file
26
src/cli/start.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use bollard::Docker;
|
||||||
|
use chrono::Utc;
|
||||||
|
use clap::Arg;
|
||||||
|
use clap::{Command, ArgMatches};
|
||||||
|
|
||||||
|
use crate::{ DbPool, schema };
|
||||||
|
|
||||||
|
use crate::srvmgr;
|
||||||
|
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
pub fn create_subcommand() -> Command {
|
||||||
|
Command::new("start")
|
||||||
|
.about("manually start a server (and set last login datetime to now)")
|
||||||
|
.arg(Arg::new("NAME").required(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn subcommand(arguments: &ArgMatches, pool: &DbPool, docker: &Docker) -> Result<(), String> {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
let server_name = arguments.get_one::<String>("NAME").unwrap();
|
||||||
|
srvmgr::start_server(&docker, pool, server_name).await?;
|
||||||
|
|
||||||
|
diesel::update(servers).filter(name.eq(server_name)).set(last_login.eq(Utc::now().naive_utc())).execute(&mut pool.get().unwrap()).map_err(|e| format!("Failed to set the last login date: {}", e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
22
src/cli/stop.rs
Normal file
22
src/cli/stop.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use bollard::Docker;
|
||||||
|
use clap::Arg;
|
||||||
|
use clap::{Command, ArgMatches};
|
||||||
|
|
||||||
|
use crate::DbPool;
|
||||||
|
|
||||||
|
use crate::srvmgr;
|
||||||
|
|
||||||
|
pub fn create_subcommand() -> Command {
|
||||||
|
Command::new("stop")
|
||||||
|
.about("manually stop a server")
|
||||||
|
.arg(Arg::new("NAME").required(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn subcommand(arguments: &ArgMatches, pool: &DbPool, docker: &Docker) -> Result<(), String> {
|
||||||
|
let server_name = arguments.get_one::<String>("NAME").unwrap();
|
||||||
|
println!("Stopping container");
|
||||||
|
srvmgr::stop_server(&docker, pool, server_name, false).await?;
|
||||||
|
println!("container stopped");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -63,23 +63,18 @@ pub async fn extract_archive(archive_path: String, output: String) -> Result<(),
|
|||||||
|
|
||||||
|
|
||||||
pub fn move_all_elements(src: &Path, dst: &Path) -> io::Result<()> {
|
pub fn move_all_elements(src: &Path, dst: &Path) -> io::Result<()> {
|
||||||
// 1. Ensure the destination directory exists; if not, create it
|
|
||||||
if !dst.exists() {
|
if !dst.exists() {
|
||||||
fs::create_dir_all(dst)?;
|
fs::create_dir_all(dst)?;
|
||||||
println!("Created destination directory: {:?}", dst);
|
println!("Created destination directory: {:?}", dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Read the contents of the source directory
|
|
||||||
for entry in fs::read_dir(src)? {
|
for entry in fs::read_dir(src)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let file_name = entry.file_name();
|
let file_name = entry.file_name();
|
||||||
|
|
||||||
// 3. Construct the new path (destination + filename)
|
|
||||||
let old_path = entry.path();
|
let old_path = entry.path();
|
||||||
let new_path = dst.join(file_name);
|
let new_path = dst.join(file_name);
|
||||||
|
|
||||||
// 4. Perform the move
|
|
||||||
// fs::rename works for both files and directories
|
|
||||||
fs::rename(&old_path, &new_path)?;
|
fs::rename(&old_path, &new_path)?;
|
||||||
println!("Moved: {:?} -> {:?}", old_path, new_path);
|
println!("Moved: {:?} -> {:?}", old_path, new_path);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub config_path: String
|
pub config_path: String,
|
||||||
|
pub docker_image: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn load() -> Self {
|
pub fn load() -> Self {
|
||||||
Self {
|
Self {
|
||||||
config_path: "/home/tom/desktop/rust/yo_mama/servers".to_string() // that's my program I do what I want
|
config_path: env::var("DMM_CONFIG_PATH").unwrap_or_else(|_| "/raid/crit/mc_servers".to_string()),
|
||||||
|
docker_image: "itzg/minecraft-server".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
mod start;
|
|
||||||
pub use start::start_server;
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
pub async fn start_server() -> Result<String, String> {
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
use bollard::Docker;
|
|
||||||
|
|
||||||
use std::sync::LazyLock;
|
|
||||||
|
|
||||||
pub static mut DOCKER: LazyLock<Docker> = LazyLock::new(|| Docker::connect_with_local_defaults().expect("Failed to connect to the docker socket") );
|
|
||||||
|
|
||||||
40
src/main.rs
40
src/main.rs
@ -6,7 +6,7 @@
|
|||||||
/* By: tomoron <tomoron@student.42angouleme.fr> +#+ +:+ +#+ */
|
/* By: tomoron <tomoron@student.42angouleme.fr> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2026/05/29 21:22:17 by tomoron #+# #+# */
|
/* Created: 2026/05/29 21:22:17 by tomoron #+# #+# */
|
||||||
/* Updated: 2026/06/06 19:39:47 by tomoron ### ########.fr */
|
/* Updated: 2026/06/17 17:21:44 by tomoron ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ pub mod models;
|
|||||||
|
|
||||||
pub mod status;
|
pub mod status;
|
||||||
|
|
||||||
pub mod docker;
|
pub mod srvmgr;
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
use cli::*;
|
use cli::*;
|
||||||
@ -28,20 +28,27 @@ use cli::*;
|
|||||||
mod config;
|
mod config;
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
|
|
||||||
|
use clap::Command;
|
||||||
|
|
||||||
pub type DbPool = Pool<ConnectionManager<SqliteConnection>>;
|
pub type DbPool = Pool<ConnectionManager<SqliteConnection>>;
|
||||||
|
|
||||||
|
use bollard::Docker;
|
||||||
|
|
||||||
|
mod migrations;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
fn get_connection_pool() -> DbPool {
|
fn get_connection_pool() -> DbPool {
|
||||||
let database_url = "sqlite:///".to_string() + &Config::load().config_path + "/db.sqlite";
|
let database_url = "sqlite://".to_string() + &Config::load().config_path + "/db.sqlite";
|
||||||
|
|
||||||
let manager = ConnectionManager::<SqliteConnection>::new(&database_url);
|
let manager = ConnectionManager::<SqliteConnection>::new(&database_url);
|
||||||
Pool::builder()
|
Pool::builder()
|
||||||
.build(manager)
|
.build(manager)
|
||||||
.unwrap_or_else(|_| panic!("Error creating pool for {}", database_url))
|
.unwrap_or_else(|e| panic!("Error creating pool for {}\nerror: {:?}", database_url, e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
use clap::Command;
|
|
||||||
|
|
||||||
fn cli() -> Command {
|
fn cli() -> Command {
|
||||||
Command::new("dmm")
|
Command::new("dmm")
|
||||||
.about("Docker Minecraft server Manager cli")
|
.about("Docker Minecraft server Manager cli")
|
||||||
@ -56,19 +63,34 @@ fn cli() -> Command {
|
|||||||
.subcommand(remove::create_subcommand())
|
.subcommand(remove::create_subcommand())
|
||||||
.subcommand(edit::create_subcommand())
|
.subcommand(edit::create_subcommand())
|
||||||
.subcommand(ls::create_subcommand())
|
.subcommand(ls::create_subcommand())
|
||||||
|
.subcommand(set_state::create_subcommand())
|
||||||
|
.subcommand(start::create_subcommand())
|
||||||
|
.subcommand(stop::create_subcommand())
|
||||||
|
.subcommand(archive::create_subcommand())
|
||||||
|
.subcommand(disable::create_subcommand())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "multi_thread")]
|
||||||
async fn main() -> Result<(), String> {
|
async fn main() -> Result<(), String> {
|
||||||
let arg = cli().get_matches();
|
let arg = cli().get_matches();
|
||||||
let pool = get_connection_pool();
|
let pool = get_connection_pool();
|
||||||
|
|
||||||
|
migrations::run_migrations(&pool).map_err(|e| format!("Failed to run migrations : {}", e))?;
|
||||||
|
|
||||||
|
let docker = Docker::connect_with_defaults().expect("Failed to connect to local docker");
|
||||||
|
|
||||||
match arg.subcommand() {
|
match arg.subcommand() {
|
||||||
Some(("serverMode", _)) => { mc_socket_listen(pool).await.map_err(|_| "socket error".to_string())?; },
|
Some(("serverMode", _)) => { mc_socket_listen(pool, docker).await.map_err(|_| "socket error".to_string())?; },
|
||||||
Some(("add", sub_matches)) => { add::subcommand(sub_matches, pool).await?; },
|
Some(("add", sub_matches)) => { add::subcommand(sub_matches, pool).await?; },
|
||||||
Some(("remove", sub_matches)) => { remove::subcommand(sub_matches, pool).await?; },
|
Some(("remove", sub_matches)) => { remove::subcommand(sub_matches, &pool, &docker).await?; },
|
||||||
Some(("ls", sub_matches)) => { ls::subcommand(sub_matches, pool).await?; },
|
Some(("ls", sub_matches)) => { ls::subcommand(sub_matches, pool).await?; },
|
||||||
Some(("edit", sub_matches)) => { edit::subcommand(sub_matches, pool).await?; },
|
Some(("edit", sub_matches)) => { edit::subcommand(sub_matches, pool).await?; },
|
||||||
|
Some(("setState", sub_matches)) => { set_state::subcommand(sub_matches, pool).await? },
|
||||||
|
|
||||||
|
Some(("start", sub_matches)) => { start::subcommand(sub_matches, &pool, &docker).await?; },
|
||||||
|
Some(("stop", sub_matches)) => { stop::subcommand(sub_matches, &pool, &docker).await?; },
|
||||||
|
Some(("archive", sub_matches)) => { archive::subcommand(sub_matches, &pool, &docker).await? },
|
||||||
|
Some(("disable", sub_matches)) => { disable::subcommand(sub_matches, &pool, &docker).await? },
|
||||||
_ => {panic!("subcommand not implemented")}
|
_ => {panic!("subcommand not implemented")}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
15
src/migrations.rs
Normal file
15
src/migrations.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||||
|
|
||||||
|
use crate::DbPool;
|
||||||
|
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("./migrations");
|
||||||
|
|
||||||
|
|
||||||
|
pub fn run_migrations(pool: &DbPool) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||||
|
|
||||||
|
let connection = &mut pool.get().unwrap();
|
||||||
|
connection.run_pending_migrations(MIGRATIONS)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -2,17 +2,22 @@ use crate::minecraft::client::client::Client;
|
|||||||
|
|
||||||
use crate::minecraft::varint::varint_read;
|
use crate::minecraft::varint::varint_read;
|
||||||
|
|
||||||
|
use std::cmp::min;
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub async fn in_read(&mut self) -> Result<u8, String> {
|
pub async fn in_read(&mut self) -> Result<u8, String> {
|
||||||
loop
|
loop
|
||||||
{
|
{
|
||||||
match self.in_stream.try_read(&mut self.read_buf) {
|
match self.in_stream.try_read(&mut self.read_buf) {
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
if let Some(out_stream) = &self.out_stream {
|
if let Some(out_stream) = &mut self.out_stream {
|
||||||
let _ = out_stream.writable().await;
|
|
||||||
let _ = out_stream.try_write(&self.read_buf[..n]);
|
out_stream.write_all(&self.read_buf[..n]).await.map_err(|e| format!("Failed to write to server stream :{}", e))?;
|
||||||
} else {
|
} else {
|
||||||
self.buffer_append((&self.read_buf[..n]).to_vec()).await?;
|
self.buffer_append((&self.read_buf[..n]).to_vec()).await?;
|
||||||
}
|
}
|
||||||
@ -23,8 +28,8 @@ impl Client {
|
|||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||||
return Ok(1);
|
return Ok(1);
|
||||||
}
|
}
|
||||||
Err(_e) => {
|
Err(e) => {
|
||||||
return Err("read failed".to_string())
|
return Err(format!("in read failed: {}", e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,11 +48,9 @@ impl Client {
|
|||||||
return Ok(1);
|
return Ok(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
match self.out_stream.as_mut().unwrap().try_read(&mut self.read_buf) {
|
match self.out_stream.as_mut().unwrap().try_read(&mut self.read_buf) {
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
let _ = self.in_stream.writable().await;
|
self.in_stream.write_all(&self.read_buf[..n]).await.map_err(|e| format!("Failed to write to client stream :{}", e))?;
|
||||||
let _ = self.in_stream.try_write(&self.read_buf[..n]);
|
|
||||||
if n == 0 { Ok(0) } else { Ok(1) }
|
if n == 0 { Ok(0) } else { Ok(1) }
|
||||||
}
|
}
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||||
@ -60,11 +63,11 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn buffer_append(&mut self, data: Vec<u8>) -> Result<(),String> {
|
pub async fn buffer_append(&mut self, data: Vec<u8>) -> Result<(),String> {
|
||||||
if data.len() == 0 {
|
if data.len() == 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if self.buffer.len() + data.len() > 65536 {
|
if self.buffer.len() + data.len() > 65536 {
|
||||||
return Err("buffer full".to_string());
|
return Err("buffer full".to_string());
|
||||||
@ -72,20 +75,35 @@ impl Client {
|
|||||||
|
|
||||||
let _ = self.buffer.extend(data);
|
let _ = self.buffer.extend(data);
|
||||||
|
|
||||||
while self.buffer.len() != 0 && self.buffer.len() - 1 >= self.buffer[0] as usize {
|
|
||||||
|
loop {
|
||||||
|
if self.buffer.len() == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let varint_buffer_len = min(self.buffer.len(), 5);
|
||||||
|
|
||||||
|
let mut varint_buffer: VecDeque<u8> = self.buffer[..varint_buffer_len].to_vec().into();
|
||||||
|
|
||||||
|
let expected_len = varint_read(&mut varint_buffer)?;
|
||||||
|
let varint_len = varint_buffer_len - varint_buffer.len();
|
||||||
|
|
||||||
|
|
||||||
|
if self.buffer.len() - varint_len < expected_len as usize {
|
||||||
|
break ;
|
||||||
|
}
|
||||||
|
|
||||||
if self.buffer[0] == 0 {
|
if self.buffer[0] == 0 {
|
||||||
return Err("invalid packet".to_string());
|
return Err("invalid packet".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.handle_packet(self.buffer[varint_len..=(expected_len as usize)].to_vec().into()).await?;
|
||||||
let len = varint_read(&mut self.buffer.clone().into())? as usize;
|
self.buffer.drain(..(expected_len as usize + varint_len));
|
||||||
self.handle_packet(self.buffer[1..=len].to_vec().into()).await?;
|
|
||||||
self.buffer.drain(..len + 1);
|
|
||||||
|
|
||||||
if let Some(out_stream) = &self.out_stream {
|
if let Some(out_stream) = &self.out_stream {
|
||||||
if self.buffer.len() != 0 {
|
if self.buffer.len() != 0 {
|
||||||
let _ = out_stream.writable().await;
|
let _ = out_stream.writable().await.map_err(|e| format!("out stream writable call failed : {}", e))?;
|
||||||
let _ = out_stream.try_write(self.buffer.as_slice());
|
let _ = out_stream.try_write(self.buffer.as_slice()).map_err(|e| format!("Failed to send remaining buffer to out stream : {}", e))?;
|
||||||
self.buffer.drain(..);
|
self.buffer.drain(..);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,16 +6,29 @@
|
|||||||
/* By: tomoron <tomoron@student.42angouleme.fr> +#+ +:+ +#+ */
|
/* By: tomoron <tomoron@student.42angouleme.fr> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2026/05/07 17:23:09 by tomoron #+# #+# */
|
/* Created: 2026/05/07 17:23:09 by tomoron #+# #+# */
|
||||||
/* Updated: 2026/06/05 12:36:46 by tomoron ### ########.fr */
|
/* Updated: 2026/06/13 17:43:46 by tomoron ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
use bollard::Docker;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use crate::minecraft::handshake::Handshake;
|
use crate::minecraft::handshake::Handshake;
|
||||||
|
use crate::status::ServerStatus;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::models::Servers;
|
use crate::models::Servers;
|
||||||
|
|
||||||
|
use crate::DbPool;
|
||||||
|
|
||||||
|
use crate::schema;
|
||||||
|
use crate::models;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
pub in_stream: TcpStream,
|
pub in_stream: TcpStream,
|
||||||
|
|
||||||
@ -28,19 +41,12 @@ pub struct Client {
|
|||||||
|
|
||||||
pub server: Option<Result<Servers,diesel::result::Error>>,
|
pub server: Option<Result<Servers,diesel::result::Error>>,
|
||||||
|
|
||||||
pub db_pool: DbPool
|
pub db_pool: DbPool,
|
||||||
|
pub docker: Docker
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::DbPool;
|
|
||||||
|
|
||||||
use crate::schema;
|
|
||||||
use crate::models;
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use regex::Regex;
|
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn create(stream: TcpStream, pool: DbPool) -> Self {
|
pub fn create(stream: TcpStream, pool: DbPool, docker: Docker) -> Self {
|
||||||
Self {
|
Self {
|
||||||
in_stream: stream,
|
in_stream: stream,
|
||||||
|
|
||||||
@ -53,13 +59,14 @@ impl Client {
|
|||||||
|
|
||||||
server: None,
|
server: None,
|
||||||
|
|
||||||
db_pool: pool
|
db_pool: pool,
|
||||||
|
|
||||||
|
docker: docker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn get_server(&self) -> Result<Servers, diesel::result::Error> {
|
pub async fn get_server(&self) -> Result<Servers, diesel::result::Error> {
|
||||||
println!("get server name from domain : {}", self.handshake.as_ref().unwrap().server_address);
|
|
||||||
if let None = self.handshake {
|
if let None = self.handshake {
|
||||||
panic!("get server called without a handshake");
|
panic!("get server called without a handshake");
|
||||||
}
|
}
|
||||||
@ -67,7 +74,7 @@ impl Client {
|
|||||||
|
|
||||||
let conn = &mut self.db_pool.get().unwrap();
|
let conn = &mut self.db_pool.get().unwrap();
|
||||||
|
|
||||||
let reg = Regex::new(r"^(?:([a-zA-Z-_]*)\.)?mc\.tmoron\.fr$").unwrap();
|
let reg = Regex::new(r"^(?:([0-9a-zA-Z\-_]*)\.)?mc\.tmoron\.fr").unwrap();
|
||||||
let mut found: Option<String> = None;
|
let mut found: Option<String> = None;
|
||||||
if let Some(capture) = reg.captures(&self.handshake.as_ref().unwrap().server_address) {
|
if let Some(capture) = reg.captures(&self.handshake.as_ref().unwrap().server_address) {
|
||||||
if let Some(captured_str) = capture.get(1) {
|
if let Some(captured_str) = capture.get(1) {
|
||||||
@ -76,15 +83,49 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = found {
|
if let Some(name) = found {
|
||||||
println!("found server name : {}", name);
|
|
||||||
schema::servers::table.filter(schema::servers::name.eq(name)).select(models::Servers::as_select()).first(conn)
|
schema::servers::table.filter(schema::servers::name.eq(name)).select(models::Servers::as_select()).first(conn)
|
||||||
} else {
|
} else {
|
||||||
schema::servers::table.filter(schema::servers::is_default.eq(true)).select(models::Servers::as_select()).first(conn)
|
schema::servers::table.filter(schema::servers::is_default.eq(true)).select(models::Servers::as_select()).first(conn)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn update_last_login(&mut self) {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
|
||||||
|
let server = match self.server.as_ref().unwrap() {
|
||||||
|
Ok(server) => {server},
|
||||||
|
Err(_) => { return; },
|
||||||
|
};
|
||||||
|
|
||||||
|
let conn = &mut self.db_pool.get().unwrap();
|
||||||
|
|
||||||
|
if self.handshake.as_ref().unwrap().intent == 2 {
|
||||||
|
match diesel::update(servers).filter(name.eq(&server.name)).set(last_login.eq(Utc::now().naive_utc())).execute(conn) {
|
||||||
|
Err(e) => { eprintln!("Failed to set last login time : {}",e); },
|
||||||
|
Ok(_) => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn check_online(&mut self) {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
let server = match self.server.as_ref().unwrap() {
|
||||||
|
Ok(server) => { server },
|
||||||
|
Err(_) => { return; },
|
||||||
|
};
|
||||||
|
|
||||||
|
let conn = &mut self.db_pool.get().unwrap();
|
||||||
|
|
||||||
|
match self.docker.inspect_container(&format!("minecraft-{}", server.name), None).await {
|
||||||
|
Ok(_) => { return; },
|
||||||
|
Err(_) => {
|
||||||
|
let _ = diesel::update(servers).filter(name.eq(&server.name)).set(status.eq(ServerStatus::Stopped)).execute(conn);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Client {
|
impl fmt::Display for Client {
|
||||||
|
|||||||
@ -1,16 +1,44 @@
|
|||||||
use crate::minecraft::client::client::Client;
|
use crate::{srvmgr::start_server, minecraft::client::client::Client};
|
||||||
|
|
||||||
|
use diesel::result::Error::NotFound;
|
||||||
use json::object;
|
use json::object;
|
||||||
use crate::minecraft::varint::varint_write;
|
use crate::minecraft::varint::varint_write;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub async fn login_intent_handle(&mut self, mut _packet: &mut VecDeque<u8>, _packet_id: i32) -> Result<(),String> {
|
pub async fn login_intent_handle(&mut self, mut _packet: &mut VecDeque<u8>, _packet_id: i32) -> Result<(),String> {
|
||||||
|
match self.login_start_server().await {
|
||||||
|
Ok(_) => {
|
||||||
|
self.login_send_disconnect(&"Start request successful, the server will start soon".to_string(), &"green".to_string()).await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.login_send_disconnect(&e, &"red".to_string()).await?;
|
||||||
|
return Err(e);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn login_start_server(&mut self) -> Result<(), String> {
|
||||||
|
let server = match self.server.as_ref().unwrap().as_ref() {
|
||||||
|
Ok(server) => { server },
|
||||||
|
Err(NotFound) => { return Err("This server was not found".to_string()); },
|
||||||
|
Err(e) => { return Err(format!("db fetch error : {}", e)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
start_server(&self.docker, &self.db_pool, &server.name).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn login_send_disconnect(&mut self, message: &String, color: &String) -> Result<(), String> {
|
||||||
let reason_json = object! {
|
let reason_json = object! {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"text": "login to start not implemented yet",
|
"text": *message.clone(),
|
||||||
"color": "red"
|
"color": *color.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut response: Vec<u8> = vec![];
|
let mut response: Vec<u8> = vec![];
|
||||||
let response_json = json::stringify(reason_json);
|
let response_json = json::stringify(reason_json);
|
||||||
let mut response_json_len = varint_write(response_json.len() as i32);
|
let mut response_json_len = varint_write(response_json.len() as i32);
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use std::collections::VecDeque;
|
|||||||
|
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub async fn handle_packet(&mut self, mut packet: VecDeque<u8>) -> Result<(), String> {
|
pub async fn handle_packet(&mut self, mut packet: VecDeque<u8>) -> Result<(), String> {
|
||||||
let packet_id = varint_read(&mut packet)?;
|
let packet_id = varint_read(&mut packet)?;
|
||||||
@ -18,6 +19,9 @@ impl Client {
|
|||||||
}
|
}
|
||||||
self.handshake = Some(Handshake::from_packet(&mut packet)?);
|
self.handshake = Some(Handshake::from_packet(&mut packet)?);
|
||||||
self.server = Some(self.get_server().await);
|
self.server = Some(self.get_server().await);
|
||||||
|
|
||||||
|
self.update_last_login().await;
|
||||||
|
|
||||||
if let Some(Ok(server)) = &self.server {
|
if let Some(Ok(server)) = &self.server {
|
||||||
if server.status == ServerStatus::Running {
|
if server.status == ServerStatus::Running {
|
||||||
return self.proxy_start().await;
|
return self.proxy_start().await;
|
||||||
@ -56,17 +60,34 @@ impl Client {
|
|||||||
|
|
||||||
async fn proxy_start(&mut self) -> Result<(),String> {
|
async fn proxy_start(&mut self) -> Result<(),String> {
|
||||||
println!("[{}] starting proxy mode", self);
|
println!("[{}] starting proxy mode", self);
|
||||||
// let stream = TcpStream::connect(&self.server.as_ref().unwrap().redirect_ip.as_ref().unwrap()).await
|
let server = &self.server.as_ref().unwrap().as_ref().unwrap();
|
||||||
let stream = TcpStream::connect("play.hypixel.net").await
|
let redirect_ip = server.redirect_ip.as_ref().unwrap();
|
||||||
.map_err(|_| "failed to connect to remote host".to_string())?;
|
let split_ip = redirect_ip.split_once(":");
|
||||||
|
let domain : String = split_ip.unwrap_or_else(|| (redirect_ip, "")).0.to_string();
|
||||||
|
let port : u16 = split_ip.unwrap_or_else(|| ("", "25565")).1.parse().map_err(|e| format!("Failed to parse redirect_ip : {}", e))?;
|
||||||
|
|
||||||
|
let stream = match TcpStream::connect(redirect_ip).await {
|
||||||
|
Ok(stream) => {stream},
|
||||||
|
Err(e) => {
|
||||||
|
self.check_online().await;
|
||||||
|
return Err(format!("failed to connect to remote host : {}", e));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
let mut new_handshake = self.handshake.as_ref().unwrap().clone();
|
let mut new_handshake = self.handshake.as_ref().unwrap().clone();
|
||||||
|
|
||||||
new_handshake.server_address = "play.hypixel.net".to_string();
|
if server.simple_redirect {
|
||||||
new_handshake.server_port = 25565;
|
new_handshake.server_address = domain;
|
||||||
|
} else {
|
||||||
|
new_handshake.server_address = self.handshake.as_ref().unwrap().server_address.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
new_handshake.server_port = port;
|
||||||
|
|
||||||
let handshake_packet = new_handshake.to_packet();
|
let handshake_packet = new_handshake.to_packet();
|
||||||
let mut packet = Vec::new();
|
let mut packet = Vec::new();
|
||||||
packet.extend(varint_write(handshake_packet.len() as i32));
|
packet.extend(varint_write((handshake_packet.len() + 1) as i32));
|
||||||
|
packet.push(0x00);
|
||||||
packet.extend(handshake_packet);
|
packet.extend(handshake_packet);
|
||||||
|
|
||||||
self.out_stream = Some(stream);
|
self.out_stream = Some(stream);
|
||||||
@ -74,7 +95,8 @@ impl Client {
|
|||||||
let _ = self.out_stream.as_ref().unwrap().writable().await;
|
let _ = self.out_stream.as_ref().unwrap().writable().await;
|
||||||
let _ = self.out_stream.as_ref().unwrap().try_write(packet.as_slice());
|
let _ = self.out_stream.as_ref().unwrap().try_write(packet.as_slice());
|
||||||
|
|
||||||
println!("[{}] sent handshake to server", self);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -31,14 +31,12 @@ impl Client {
|
|||||||
|
|
||||||
response.append(&mut response_json_len);
|
response.append(&mut response_json_len);
|
||||||
response.append(&mut response_json.as_bytes().into());
|
response.append(&mut response_json.as_bytes().into());
|
||||||
println!("[{}] sent status response to client", self);
|
|
||||||
|
|
||||||
self.send_packet(0x0, response).await;
|
self.send_packet(0x0, response).await;
|
||||||
} else if packet_id == 1 {
|
} else if packet_id == 1 {
|
||||||
let mut response = vec![];
|
let mut response = vec![];
|
||||||
response.extend(packet.drain(..));
|
response.extend(packet.drain(..));
|
||||||
self.send_packet(0x1, response).await;
|
self.send_packet(0x1, response).await;
|
||||||
println!("[{}] sent ping response to client", self);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use std::collections::VecDeque;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use crate::minecraft::varint::{varint_read, varint_write};
|
use crate::minecraft::varint::{varint_read, varint_write};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Handshake {
|
pub struct Handshake {
|
||||||
pub protocol_version: i32,
|
pub protocol_version: i32,
|
||||||
pub server_address: String,
|
pub server_address: String,
|
||||||
@ -33,7 +33,6 @@ impl Handshake {
|
|||||||
|
|
||||||
pub fn to_packet(&self) -> Vec<u8> {
|
pub fn to_packet(&self) -> Vec<u8> {
|
||||||
let mut packet = Vec::new();
|
let mut packet = Vec::new();
|
||||||
packet.extend(varint_write(0));
|
|
||||||
packet.extend(varint_write(self.protocol_version));
|
packet.extend(varint_write(self.protocol_version));
|
||||||
let address_bytes = self.server_address.as_bytes();
|
let address_bytes = self.server_address.as_bytes();
|
||||||
packet.extend(varint_write(address_bytes.len() as i32));
|
packet.extend(varint_write(address_bytes.len() as i32));
|
||||||
|
|||||||
89
src/minecraft/manager_loop.rs
Normal file
89
src/minecraft/manager_loop.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use bollard::Docker;
|
||||||
|
use chrono::{TimeDelta, Utc};
|
||||||
|
|
||||||
|
use crate::{ DbPool, models::{self, Servers}, schema, srvmgr::{self, archive_server}, status::ServerStatus };
|
||||||
|
|
||||||
|
use async_std::task;
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
|
||||||
|
async fn server_init(pool: &DbPool, _docker: &Docker) -> Result<(), String> {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
let conn = &mut pool.get().unwrap();
|
||||||
|
|
||||||
|
println!("init start");
|
||||||
|
|
||||||
|
let _ = diesel::update(servers).filter(
|
||||||
|
simple_redirect.eq(false)
|
||||||
|
.and(status.eq(ServerStatus::Stopping)
|
||||||
|
.or(status.eq(ServerStatus::Starting))
|
||||||
|
))
|
||||||
|
.set(status.eq(ServerStatus::Stopped)).execute(conn);
|
||||||
|
println!("init done");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn server_manager_loop(pool: DbPool, docker: Docker)
|
||||||
|
{
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
|
||||||
|
let conn = &mut pool.get().unwrap();
|
||||||
|
|
||||||
|
match server_init(&pool, &docker).await {
|
||||||
|
Err(e) => { eprintln!("init failed , ignoring because idgaf : {}", e); },
|
||||||
|
Ok(_) => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let a_servers = match servers.select(models::Servers::as_select()).filter(simple_redirect.eq(false)).load(conn) {
|
||||||
|
Ok( srv ) => { srv },
|
||||||
|
Err(error) => { eprintln!("Failed to retreive servers, ignoring : {}", error); vec![] },
|
||||||
|
};
|
||||||
|
|
||||||
|
for srv in a_servers {
|
||||||
|
let time_diff = Utc::now().naive_utc() - srv.last_login.unwrap();
|
||||||
|
should_stop(&docker, &pool, &time_diff, &srv).await;
|
||||||
|
should_archive(&docker, &pool, &time_diff, &srv).await;
|
||||||
|
check_still_online(&docker, &pool, &srv).await;
|
||||||
|
}
|
||||||
|
task::sleep(Duration::from_secs(30)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn should_stop(docker: &Docker, pool: &DbPool, diff: &TimeDelta, srv: &Servers) {
|
||||||
|
if srv.status != ServerStatus::Running { return ; }
|
||||||
|
|
||||||
|
if diff.num_days() >= 7 {
|
||||||
|
println!("last login time exceeded, stopping {}", srv.name);
|
||||||
|
let _ = srvmgr::stop_server(docker, pool, &srv.name, false).await;
|
||||||
|
println!("{} stopped", srv.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn should_archive(_docker: &Docker, pool: &DbPool, diff: &TimeDelta, srv: &Servers) {
|
||||||
|
if srv.status != ServerStatus::Stopped { return ; }
|
||||||
|
|
||||||
|
if diff.num_days() >= 30 {
|
||||||
|
println!("last login time exceedd, archiving {}", srv.name);
|
||||||
|
let _ = archive_server(srv, pool);
|
||||||
|
println!("{} archived", srv.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_still_online(docker: &Docker, pool: &DbPool, srv: &Servers) {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
|
||||||
|
if srv.status != ServerStatus::Running { return ; }
|
||||||
|
|
||||||
|
match docker.inspect_container(&format!("minecraft-{}", srv.name), None).await {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(_) => {
|
||||||
|
println!("Failed to find minecraft-{} container, setting it as Stopped", srv.name);
|
||||||
|
let conn = &mut pool.get().unwrap();
|
||||||
|
let _ = diesel::update(servers).filter(name.eq(&srv.name)).set(status.eq(ServerStatus::Stopped)).execute(conn);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,3 +5,6 @@ mod client;
|
|||||||
mod socket;
|
mod socket;
|
||||||
|
|
||||||
pub use socket::mc_socket_listen;
|
pub use socket::mc_socket_listen;
|
||||||
|
|
||||||
|
mod manager_loop;
|
||||||
|
pub use manager_loop::server_manager_loop;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use crate::minecraft::client::client::Client;
|
use crate::minecraft::{client::client::Client, server_manager_loop};
|
||||||
|
use bollard::Docker;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
use crate::DbPool;
|
use crate::DbPool;
|
||||||
@ -7,13 +8,26 @@ use tokio::net::TcpListener;
|
|||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
pub async fn mc_socket_listen(pool: DbPool) -> io::Result<()> {
|
|
||||||
|
pub async fn mc_socket_listen(pool: DbPool, docker: Docker) -> io::Result<()> {
|
||||||
let mc_listener = TcpListener::bind("0.0.0.0:25565").await?;
|
let mc_listener = TcpListener::bind("0.0.0.0:25565").await?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let cloned_pool = pool.clone();
|
||||||
|
let cloned_docker = docker.clone();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
server_manager_loop(cloned_pool, cloned_docker).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Ok((socket, _)) = mc_listener.accept().await {
|
if let Ok((socket, _)) = mc_listener.accept().await {
|
||||||
let cloned = pool.clone();
|
let cloned_pool = pool.clone();
|
||||||
|
let cloned_docker = docker.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Err(e) = process_mc_socket(socket, cloned).await {
|
if let Err(e) = process_mc_socket(socket, cloned_pool, cloned_docker).await {
|
||||||
eprintln!("mc error: {:?}", e);
|
eprintln!("mc error: {:?}", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -21,10 +35,10 @@ pub async fn mc_socket_listen(pool: DbPool) -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn process_mc_socket(stream: TcpStream, pool: DbPool) -> Result<(), String> {
|
async fn process_mc_socket(stream: TcpStream, pool: DbPool, docker: Docker) -> Result<(), String> {
|
||||||
println!("new client {:?}", stream);
|
println!("new client {:?}", stream);
|
||||||
|
|
||||||
let mut client = Client::create(stream, pool);
|
let mut client = Client::create(stream, pool, docker);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
|
|||||||
@ -1,11 +1,8 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};
|
use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use crate::status::ServerStatus;
|
use crate::status::ServerStatus;
|
||||||
|
|
||||||
use crate::schema;
|
use crate::schema;
|
||||||
@ -16,18 +13,17 @@ use crate::schema;
|
|||||||
pub struct Servers {
|
pub struct Servers {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub last_login: Option<NaiveDateTime>,
|
pub last_login: Option<NaiveDateTime>,
|
||||||
pub container_id: Option<String>,
|
|
||||||
pub status: ServerStatus,
|
pub status: ServerStatus,
|
||||||
pub redirect_ip: Option<String>
|
pub redirect_ip: Option<String>,
|
||||||
|
pub simple_redirect: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Deserialize, Insertable)]
|
#[derive(Insertable)]
|
||||||
#[diesel(table_name = schema::servers)]
|
#[diesel(table_name = schema::servers)]
|
||||||
pub struct CreateServer<'a> {
|
pub struct CreateServer<'a> {
|
||||||
pub name: &'a str,
|
pub name: &'a str,
|
||||||
pub last_login: Option<SystemTime>,
|
pub last_login: Option<NaiveDateTime>,
|
||||||
pub container_id: Option<&'a str>,
|
|
||||||
pub status: ServerStatus,
|
pub status: ServerStatus,
|
||||||
pub redirect_ip: Option<&'a str>
|
pub redirect_ip: Option<&'a str>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,9 +4,9 @@ diesel::table! {
|
|||||||
servers (name) {
|
servers (name) {
|
||||||
name -> Text,
|
name -> Text,
|
||||||
last_login -> Nullable<Timestamp>,
|
last_login -> Nullable<Timestamp>,
|
||||||
container_id -> Nullable<Text>,
|
|
||||||
status -> SmallInt,
|
status -> SmallInt,
|
||||||
is_default -> Bool,
|
is_default -> Bool,
|
||||||
redirect_ip -> Nullable<Text>,
|
redirect_ip -> Nullable<Text>,
|
||||||
|
simple_redirect -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
src/srvmgr/archive.rs
Normal file
36
src/srvmgr/archive.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
use crate::{ models::Servers, DbPool, schema, status::ServerStatus};
|
||||||
|
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
pub async fn archive_server(server: &Servers, pool: &DbPool) -> Result<(), String> {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
let conn = &mut pool.get().unwrap();
|
||||||
|
|
||||||
|
let config = crate::Config::load();
|
||||||
|
|
||||||
|
diesel::update(servers).filter(name.eq(&server.name)).set(status.eq(ServerStatus::Archiving)).execute(conn).map_err(|e| format!("Failed to set the server as archiving :{}", e))?;
|
||||||
|
|
||||||
|
println!("Starting compression (this might take a while)");
|
||||||
|
|
||||||
|
let cmd = format!("cd {} && tar cf /dev/stdout {} | lrzip - -Q -o {}.tar.lrz", config.config_path, server.name, server.name);
|
||||||
|
|
||||||
|
let _ = async_process::Command::new("sh").arg("-c").arg(cmd).output().await.map_err(|e| format!("compression failed : {}", e))?;
|
||||||
|
|
||||||
|
std::fs::remove_dir_all(config.config_path.to_owned() + "/" + &server.name).map_err(|e| format!("Failed to delete server directory: {}", e))?;
|
||||||
|
|
||||||
|
diesel::update(servers).filter(name.eq(&server.name)).set(status.eq(ServerStatus::Archived)).execute(conn).map_err(|e| format!("Failed to set the server as achived :{}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn unarchive_server(server: &Servers) -> Result<(), String> {
|
||||||
|
let config = crate::Config::load();
|
||||||
|
|
||||||
|
println!("Starting decompression (this might take a while)");
|
||||||
|
|
||||||
|
let cmd = format!("cd {} && cat {}.tar.lrz | lrzip -d - | tar x", config.config_path, server.name);
|
||||||
|
|
||||||
|
let _ = async_process::Command::new("sh").arg("-c").arg(cmd).output().await.map_err(|e| format!("decompression failed : {}", e))?;
|
||||||
|
|
||||||
|
std::fs::remove_file(config.config_path + "/" + &server.name + ".tar.lrz").map_err(|e| format!("Failed to delete the archive of the server : {}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
10
src/srvmgr/mod.rs
Normal file
10
src/srvmgr/mod.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
mod start;
|
||||||
|
pub use start::start_server;
|
||||||
|
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
|
mod stop;
|
||||||
|
pub use stop::stop_server;
|
||||||
|
|
||||||
|
mod archive;
|
||||||
|
pub use archive::archive_server;
|
||||||
96
src/srvmgr/start.rs
Normal file
96
src/srvmgr/start.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
use bollard::{Docker, plugin::{ContainerCreateBody, HostConfig, Mount, MountType}, query_parameters::CreateContainerOptionsBuilder};
|
||||||
|
use diesel::{prelude::*, result::Error::NotFound};
|
||||||
|
|
||||||
|
use crate::{models, schema, srvmgr::{archive::unarchive_server, utils::get_image_tag}, status::ServerStatus};
|
||||||
|
|
||||||
|
use crate::{DbPool, srvmgr::utils::{self, get_container_ip}};
|
||||||
|
|
||||||
|
async fn start_docker_container(docker: &Docker, name: &String) -> Result<String, String> {
|
||||||
|
|
||||||
|
let container_name = format!("minecraft-{}", name);
|
||||||
|
|
||||||
|
if let Some(_) = utils::get_container(&docker, &container_name).await {
|
||||||
|
return Err("Container already exists".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
let config_path = crate::Config::load().config_path;
|
||||||
|
|
||||||
|
let env_file = std::fs::read_to_string(config_path.clone() + "/" + name + "/env").map_err(|e| format!("Failed to read env file {}", e))?;
|
||||||
|
|
||||||
|
let env = env_file.split("\n").filter_map(|x| if x.len() == 0 { None } else { Some(x.to_string()) } ).collect();
|
||||||
|
|
||||||
|
let image_tag = match get_image_tag(&env).await {
|
||||||
|
Some(tag) => {tag},
|
||||||
|
None => { return Err("env missing IMAGE_TAG".to_string()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
utils::check_image(&docker, &image_tag).await?;
|
||||||
|
|
||||||
|
let mut mount = Mount::default();
|
||||||
|
mount.typ = Some(MountType::BIND);
|
||||||
|
mount.target = Some("/data".to_string());
|
||||||
|
mount.source = Some(config_path + "/" + name + "/server");
|
||||||
|
mount.read_only = Some(false);
|
||||||
|
|
||||||
|
let mut host_config = HostConfig::default();
|
||||||
|
host_config.mounts = Some(vec![mount]);
|
||||||
|
host_config.auto_remove = Some(true);
|
||||||
|
|
||||||
|
let config = crate::Config::load();
|
||||||
|
|
||||||
|
let mut create_config = ContainerCreateBody::default();
|
||||||
|
create_config.tty = Some(true);
|
||||||
|
create_config.open_stdin = Some(true);
|
||||||
|
create_config.user = Some("1000:100".to_string());
|
||||||
|
create_config.stop_timeout = Some(60);
|
||||||
|
create_config.host_config = Some(host_config);
|
||||||
|
create_config.image = Some(config.docker_image + ":" + &image_tag);
|
||||||
|
create_config.env = Some(env);
|
||||||
|
|
||||||
|
let options = CreateContainerOptionsBuilder::default()
|
||||||
|
.name(&container_name)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
docker.create_container(Some(options), create_config).await.map_err(|e| format!("failed to create the container : {}", e))?;
|
||||||
|
println!("created container");
|
||||||
|
|
||||||
|
println!("starting container");
|
||||||
|
docker.start_container(&container_name, None).await.map_err(|e| format!("Failed to start the container : {}", e))?;
|
||||||
|
Ok(get_container_ip(docker, &container_name).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn start_server(docker: &Docker, pool: &DbPool, server_name: &String) -> Result<(), String> {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
|
||||||
|
let conn = &mut pool.get().unwrap();
|
||||||
|
|
||||||
|
let server = match servers.select(models::Servers::as_select()).filter(name.eq(server_name)).first(conn) {
|
||||||
|
Err(NotFound) => { return Err("This server does not exist".to_string()); },
|
||||||
|
Err(e) => { return Err(format!("Failed to get server from db : {}", e)) },
|
||||||
|
Ok(server) => { server }
|
||||||
|
};
|
||||||
|
|
||||||
|
if server.status == ServerStatus::Starting {
|
||||||
|
return Err("Server is starting, please wait".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if server.status == ServerStatus::Disabled {
|
||||||
|
return Err("This server is disabled".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if server.status != ServerStatus::Stopped && server.status != ServerStatus::Archived {
|
||||||
|
return Err(format!("Invalid server state ({}), refusing to start", server.status));
|
||||||
|
}
|
||||||
|
|
||||||
|
diesel::update(servers).filter(name.eq(server_name)).set(status.eq(ServerStatus::Starting)).execute(conn).map_err(|e| format!("Failed to set server to starting : {}", e))?;
|
||||||
|
|
||||||
|
if server.status == ServerStatus::Archived {
|
||||||
|
unarchive_server(&server).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ip = start_docker_container(docker, server_name).await?;
|
||||||
|
|
||||||
|
diesel::update(servers).filter(name.eq(server_name)).set((status.eq(ServerStatus::Running), redirect_ip.eq(ip.to_string() + ":25565"))).execute(conn).map_err(|e| format!("Failed to set server to starting : {}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
45
src/srvmgr/stop.rs
Normal file
45
src/srvmgr/stop.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use bollard::{Docker, query_parameters::StopContainerOptionsBuilder};
|
||||||
|
use diesel::result::Error::NotFound;
|
||||||
|
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
use crate::{ DbPool, models, schema, status::ServerStatus };
|
||||||
|
|
||||||
|
|
||||||
|
async fn stop_container(docker: &Docker, name: &String) -> Result<(), String> {
|
||||||
|
let container_name = format!("minecraft-{}", name);
|
||||||
|
|
||||||
|
let options = StopContainerOptionsBuilder::new()
|
||||||
|
.t(60)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
docker.stop_container(&container_name, Some(options)).await.map_err(|e| format!("Failed to stop the container : {}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn stop_server(docker: &Docker, pool: &DbPool, server_name: &String, ignore_stopped: bool) -> Result<(), String> {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
|
||||||
|
let conn = &mut pool.get().unwrap();
|
||||||
|
|
||||||
|
let server = match servers.select(models::Servers::as_select()).filter(name.eq(server_name)).first(conn) {
|
||||||
|
Err(NotFound) => { return Err("This server does not exist".to_string()); },
|
||||||
|
Err(e) => { return Err(format!("Failed to get server from db : {}", e)) },
|
||||||
|
Ok(server) => { server }
|
||||||
|
};
|
||||||
|
|
||||||
|
if ignore_stopped && (server.status == ServerStatus::Stopped || server.status == ServerStatus::Archived) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if server.status != ServerStatus::Running {
|
||||||
|
return Err(format!("Invalid server state ({}), refusing to stop the server", server.status));
|
||||||
|
}
|
||||||
|
|
||||||
|
diesel::update(servers).filter(name.eq(server_name)).set(status.eq(ServerStatus::Stopping)).execute(conn).map_err(|e| format!("Failed to set the server state to stopping : {}", e))?;
|
||||||
|
|
||||||
|
stop_container(docker, server_name).await?;
|
||||||
|
|
||||||
|
diesel::update(servers).filter(name.eq(server_name)).set((redirect_ip.eq(None::<String>), status.eq(ServerStatus::Stopped))).execute(conn).map_err(|e| format!("Failed to set the server state to stopped : {}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
101
src/srvmgr/utils.rs
Normal file
101
src/srvmgr/utils.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use bollard::{
|
||||||
|
Docker,
|
||||||
|
plugin::{ContainerInspectResponse},
|
||||||
|
query_parameters::{CreateImageOptionsBuilder, ListImagesOptionsBuilder}
|
||||||
|
};
|
||||||
|
|
||||||
|
use futures_util::stream::StreamExt;
|
||||||
|
|
||||||
|
pub async fn check_image(docker: &Docker, tag: &String) -> Result<(), String> {
|
||||||
|
let mut filters = HashMap::new();
|
||||||
|
let config = crate::Config::load();
|
||||||
|
let container_name = config.docker_image + ":" + tag;
|
||||||
|
filters.insert("reference", vec![&container_name]);
|
||||||
|
let options = ListImagesOptionsBuilder::default()
|
||||||
|
.all(true)
|
||||||
|
.filters(&filters)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
let images = docker.list_images(Some(options)).await.map_err(|e| format!("failed to check if image exists, error : {:?}", e))?;
|
||||||
|
if images.len() >= 1 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let options = CreateImageOptionsBuilder::default()
|
||||||
|
.from_image(&container_name)
|
||||||
|
.tag(tag)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
println!("pulling {} from docker hub ... (this might take a while and I'm too lazy to do an Interface to show the progress, if you want progress info, pull it yourself on with `docker pull {}`)", container_name, container_name);
|
||||||
|
|
||||||
|
let mut pull_result = docker.create_image(Some(options), None, None);
|
||||||
|
|
||||||
|
while let Some(update) = pull_result.next().await {
|
||||||
|
if let Err(e) = update {
|
||||||
|
return Err(format!("Failed to pull the image from docker hub, error : {:?}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("pulled successfully");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_container(docker: &Docker, name: &String) -> Option<ContainerInspectResponse> {
|
||||||
|
println!("get {}", name);
|
||||||
|
|
||||||
|
let result = docker.inspect_container(name, None).await.map_err(|e| format!("failed to retreive container : {:?}", e));
|
||||||
|
match result {
|
||||||
|
Err(_) => { None },
|
||||||
|
Ok(container) => { Some(container) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_container_ip(docker: &Docker, name: &String) -> Result<String, String>{
|
||||||
|
let mut retries = 20;
|
||||||
|
|
||||||
|
while retries > 0 {
|
||||||
|
let container = get_container(docker, name).await;
|
||||||
|
|
||||||
|
if let Some(container) = container && let Some(network) = container.network_settings.unwrap().networks.unwrap().get("bridge") {
|
||||||
|
if let Some(ip) = &network.ip_address {
|
||||||
|
return Ok(ip.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async_std::task::sleep(std::time::Duration::from_millis(500)).await;
|
||||||
|
retries -= 1;
|
||||||
|
}
|
||||||
|
Ok("can't get container ip".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_image_tag(env: &Vec<String>) -> Option<String> {
|
||||||
|
for var in env {
|
||||||
|
match var.split_once("=") {
|
||||||
|
Some(split) => { if split.0 == "IMAGE_TAG" { return Some(split.1.to_string()); }},
|
||||||
|
None => { },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -16,7 +16,9 @@ pub enum ServerStatus {
|
|||||||
Stopping = 2,
|
Stopping = 2,
|
||||||
Starting = 3,
|
Starting = 3,
|
||||||
Running = 4,
|
Running = 4,
|
||||||
Unknown = 5,
|
Archiving = 5,
|
||||||
|
Disabled = 6,
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromSql<SmallInt, Sqlite> for ServerStatus{
|
impl FromSql<SmallInt, Sqlite> for ServerStatus{
|
||||||
@ -32,7 +34,7 @@ impl FromSql<SmallInt, Sqlite> for ServerStatus{
|
|||||||
impl ToSql<SmallInt, Sqlite> for ServerStatus {
|
impl ToSql<SmallInt, Sqlite> for ServerStatus {
|
||||||
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> diesel::serialize::Result {
|
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> diesel::serialize::Result {
|
||||||
let value: i16 = self.clone() as i16;
|
let value: i16 = self.clone() as i16;
|
||||||
out.set_value(value.to_le_bytes().to_vec());
|
out.set_value(value as i32);
|
||||||
Ok(diesel::serialize::IsNull::No)
|
Ok(diesel::serialize::IsNull::No)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,7 +43,9 @@ impl ToSql<SmallInt, Sqlite> for ServerStatus {
|
|||||||
impl fmt::Display for ServerStatus {
|
impl fmt::Display for ServerStatus {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let str_status = match self {
|
let str_status = match self {
|
||||||
|
ServerStatus::Disabled => "disabled",
|
||||||
ServerStatus::Archived => "archived",
|
ServerStatus::Archived => "archived",
|
||||||
|
ServerStatus::Archiving => "archiving",
|
||||||
ServerStatus::Stopped => "stopped",
|
ServerStatus::Stopped => "stopped",
|
||||||
ServerStatus::Stopping => "stopping",
|
ServerStatus::Stopping => "stopping",
|
||||||
ServerStatus::Starting => "starting",
|
ServerStatus::Starting => "starting",
|
||||||
|
|||||||
21
src/tests/minecraft/handshake.rs
Normal file
21
src/tests/minecraft/handshake.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use crate::minecraft::handshake::Handshake;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_handshake_to_packat_from_packet() {
|
||||||
|
let expected_handshake = Handshake {
|
||||||
|
protocol_version: 775,
|
||||||
|
server_address: "tmoron.fr".to_string(),
|
||||||
|
server_port: 25565,
|
||||||
|
intent: 1
|
||||||
|
};
|
||||||
|
let buffer: Vec<u8> = vec![
|
||||||
|
0x87, 0x06, // varint 775
|
||||||
|
0x09, 0x74, 0x6d, 0x6f, 0x72, 0x6f, 0x6e, 0x2e, 0x66, 0x72, // 9 chars, "tmoron.fr"
|
||||||
|
0x63, 0xdd, // port 25565 (unsigned short)
|
||||||
|
0x01 // varint intent 1
|
||||||
|
];
|
||||||
|
|
||||||
|
let parsed = Handshake::from_packet(&mut buffer.clone().into()).unwrap();
|
||||||
|
assert_eq!(parsed, expected_handshake);
|
||||||
|
assert_eq!(expected_handshake.to_packet(), buffer);
|
||||||
|
}
|
||||||
2
src/tests/minecraft/mod.rs
Normal file
2
src/tests/minecraft/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod handshake;
|
||||||
|
pub mod varint;
|
||||||
61
src/tests/minecraft/varint.rs
Normal file
61
src/tests/minecraft/varint.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use crate::minecraft::varint::{ varint_read, varint_write };
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_varint_no_interfere_other_values() {
|
||||||
|
let mut buf: VecDeque<u8> = vec![123, 0x28, 0x21].into();
|
||||||
|
let _ = varint_read(&mut buf);
|
||||||
|
|
||||||
|
assert_eq!(buf, vec![0x28, 0x21]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_varint_right_answer() {
|
||||||
|
let mut buf: VecDeque<u8> = vec![0xa8, 0x60].into();
|
||||||
|
let res = varint_read(&mut buf);
|
||||||
|
|
||||||
|
assert_eq!(buf.len(), 0);
|
||||||
|
assert_eq!(res, Ok(12328));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_varint_incomplete() {
|
||||||
|
let mut buf: VecDeque<u8> = vec![0xa8].into();
|
||||||
|
let res = varint_read(&mut buf);
|
||||||
|
|
||||||
|
assert_eq!(buf.len(), 0);
|
||||||
|
assert_eq!(res.is_err(), true);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_varint_limits() {
|
||||||
|
let mut buf: VecDeque<u8> = vec![0x0].into();
|
||||||
|
let res_zero = varint_read(&mut buf);
|
||||||
|
|
||||||
|
let mut buf: VecDeque<u8> = vec![0xff, 0xff, 0xff, 0xff, 0x07].into();
|
||||||
|
let res_max_int = varint_read(&mut buf);
|
||||||
|
|
||||||
|
let mut buf: VecDeque<u8> = vec![0x80, 0x80, 0x80, 0x80, 0x8].into();
|
||||||
|
let res_min_int = varint_read(&mut buf);
|
||||||
|
|
||||||
|
assert_eq!(buf.len(), 0);
|
||||||
|
assert_eq!(res_zero, Ok(0));
|
||||||
|
assert_eq!(res_max_int, Ok(2147483647));
|
||||||
|
assert_eq!(res_min_int, Ok(-2147483648));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_varint_write_normal() {
|
||||||
|
let res = varint_write(82149);
|
||||||
|
assert_eq!(res, vec![0xe5, 0x81, 0x05]);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_varint_write_limits() {
|
||||||
|
let res_zero = varint_write(0);
|
||||||
|
let res_max_int = varint_write(2147483647);
|
||||||
|
let res_min_int = varint_write(-2147483648);
|
||||||
|
|
||||||
|
assert_eq!(res_zero, vec![0x00]);
|
||||||
|
assert_eq!(res_max_int, vec![0xff, 0xff, 0xff, 0xff, 0x07]);
|
||||||
|
assert_eq!(res_min_int, vec![0x80, 0x80, 0x80, 0x80, 0x8])
|
||||||
|
}
|
||||||
1
src/tests/mod.rs
Normal file
1
src/tests/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod minecraft;
|
||||||
Reference in New Issue
Block a user