Compare commits

...

11 Commits

Author SHA1 Message Date
c4f3c129f1 change default config path 2026-06-23 22:54:58 +02:00
64b2f04f8f add 'disabled' state, fix handshake proxy send 2026-06-17 18:23:24 +02:00
940b595e3a bump version to 0.0.8, enable tests on nix build 2026-06-14 16:37:10 +02:00
46a886a6bd add tests 2026-06-14 16:33:23 +02:00
3791a8b04e fix url server name retreival 2026-06-13 17:44:34 +02:00
9ba7ac5c70 reduce logging 2026-06-12 19:06:58 +02:00
c3c74bc47c bump version to 0.0.7 2026-06-12 19:02:32 +02:00
8f3de30cd1 server unarchive when starting 2026-06-12 19:02:01 +02:00
b2ab524b7e rename dockermgr to srvmgr 2026-06-12 18:32:52 +02:00
97f3e4c139 archive server 2026-06-12 18:27:55 +02:00
6aa0ab473d keep already started containers, multi threaded async 2026-06-12 12:50:25 +02:00
32 changed files with 385 additions and 163 deletions

168
Cargo.lock generated
View File

@ -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",
@ -596,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",
@ -607,8 +640,9 @@ dependencies = [
[[package]] [[package]]
name = "dockermcmgr" name = "dockermcmgr"
version = "0.0.6" version = "0.0.9"
dependencies = [ dependencies = [
"async-process",
"async-std", "async-std",
"bollard", "bollard",
"chrono", "chrono",
@ -907,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",
@ -961,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",
@ -1214,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",
] ]
@ -1290,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",
] ]
@ -1308,9 +1341,9 @@ 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]] [[package]]
name = "migrations_internals" name = "migrations_internals"
@ -1345,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",
@ -1563,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",
@ -1586,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"
@ -1678,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",
@ -1773,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",
@ -1866,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",
@ -1882,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",
@ -1908,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",
@ -2085,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",
] ]
@ -2103,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",
@ -2116,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",
@ -2126,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",
@ -2136,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",
@ -2149,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",
] ]
@ -2394,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",
@ -2438,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"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "dockermcmgr" name = "dockermcmgr"
version = "0.0.6" version = "0.0.9"
edition = "2024" edition = "2024"
[[bin]] [[bin]]
@ -8,6 +8,7 @@ name = "dmm"
path = "src/main.rs" 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"
@ -22,5 +23,5 @@ 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"

View File

@ -38,18 +38,18 @@
final: final:
{ {
pname = "dmm"; pname = "dmm";
version = "0.0.6"; version = "0.0.9";
inherit src; inherit src;
cargoLock.lockFile = src + "/Cargo.lock"; cargoLock.lockFile = src + "/Cargo.lock";
nativeBuildInputs = tools; nativeBuildInputs = tools;
doCheck = false;
buildInputs = buildInputs = with pkgs; [
with pkgs; [ sqlite ]; sqlite
lrzip
];
} }
// specialArgs // specialArgs
); );
in in

View File

@ -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)

47
src/cli/archive.rs Normal file
View 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
View 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(())
}

View File

@ -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))?;

View File

@ -5,6 +5,8 @@ pub mod ls;
pub mod start; pub mod start;
pub mod stop; pub mod stop;
pub mod set_state; pub mod set_state;
pub mod archive;
pub mod disable;
pub mod env_settings; pub mod env_settings;

View File

@ -1,7 +1,7 @@
use bollard::Docker; use bollard::Docker;
use clap::{Arg, ArgMatches, Command}; use clap::{Arg, ArgMatches, Command};
use crate::{Config, DbPool, dockermgr}; use crate::{Config, DbPool, srvmgr};
use std::fs; use std::fs;
@ -21,7 +21,8 @@ pub async fn subcommand(arguments: &ArgMatches, pool: &DbPool, docker: &Docker)
let conn = &mut pool.get().unwrap(); let conn = &mut pool.get().unwrap();
let _ = dockermgr::stop_server(docker, pool, server_name).await; 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 {

View File

@ -5,7 +5,7 @@ use clap::{Command, ArgMatches};
use crate::{ DbPool, schema }; use crate::{ DbPool, schema };
use crate::dockermgr; use crate::srvmgr;
use diesel::prelude::*; use diesel::prelude::*;
@ -18,7 +18,7 @@ pub fn create_subcommand() -> Command {
pub async fn subcommand(arguments: &ArgMatches, pool: &DbPool, docker: &Docker) -> Result<(), String> { pub async fn subcommand(arguments: &ArgMatches, pool: &DbPool, docker: &Docker) -> Result<(), String> {
use schema::servers::dsl::*; use schema::servers::dsl::*;
let server_name = arguments.get_one::<String>("NAME").unwrap(); let server_name = arguments.get_one::<String>("NAME").unwrap();
dockermgr::start_server(&docker, pool, server_name).await?; 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))?; 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))?;

View File

@ -4,7 +4,7 @@ use clap::{Command, ArgMatches};
use crate::DbPool; use crate::DbPool;
use crate::dockermgr; use crate::srvmgr;
pub fn create_subcommand() -> Command { pub fn create_subcommand() -> Command {
Command::new("stop") Command::new("stop")
@ -15,7 +15,7 @@ pub fn create_subcommand() -> Command {
pub async fn subcommand(arguments: &ArgMatches, pool: &DbPool, docker: &Docker) -> Result<(), String> { pub async fn subcommand(arguments: &ArgMatches, pool: &DbPool, docker: &Docker) -> Result<(), String> {
let server_name = arguments.get_one::<String>("NAME").unwrap(); let server_name = arguments.get_one::<String>("NAME").unwrap();
println!("Stopping container"); println!("Stopping container");
dockermgr::stop_server(&docker, pool, server_name).await?; srvmgr::stop_server(&docker, pool, server_name, false).await?;
println!("container stopped"); println!("container stopped");
Ok(()) Ok(())

View File

@ -8,7 +8,7 @@ pub struct Config {
impl Config { impl Config {
pub fn load() -> Self { pub fn load() -> Self {
Self { Self {
config_path: env::var("DMM_CONFIG_PATH").unwrap_or_else(|_| "/raid/mc_servers".to_string()), config_path: env::var("DMM_CONFIG_PATH").unwrap_or_else(|_| "/raid/crit/mc_servers".to_string()),
docker_image: "itzg/minecraft-server".to_string(), docker_image: "itzg/minecraft-server".to_string(),
} }
} }

View File

@ -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/11 23:41:49 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 dockermgr; pub mod srvmgr;
mod cli; mod cli;
use cli::*; use cli::*;
@ -36,6 +36,9 @@ use bollard::Docker;
mod migrations; 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";
@ -63,9 +66,11 @@ fn cli() -> Command {
.subcommand(set_state::create_subcommand()) .subcommand(set_state::create_subcommand())
.subcommand(start::create_subcommand()) .subcommand(start::create_subcommand())
.subcommand(stop::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();
@ -80,10 +85,12 @@ async fn main() -> Result<(), String> {
Some(("remove", sub_matches)) => { remove::subcommand(sub_matches, &pool, &docker).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(("setState", sub_matches)) => { set_state::subcommand(sub_matches, pool).await? },
Some(("start", sub_matches)) => { start::subcommand(sub_matches, &pool, &docker).await?; }, Some(("start", sub_matches)) => { start::subcommand(sub_matches, &pool, &docker).await?; },
Some(("stop", sub_matches)) => { stop::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(())

View File

@ -8,10 +8,6 @@ pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("./migrations");
pub fn run_migrations(pool: &DbPool) -> Result<(), Box<dyn Error + Send + Sync + 'static>> { pub fn run_migrations(pool: &DbPool) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
// This will run the necessary migrations.
//
// See the documentation for `MigrationHarness` for
// all available methods.
let connection = &mut pool.get().unwrap(); let connection = &mut pool.get().unwrap();
connection.run_pending_migrations(MIGRATIONS)?; connection.run_pending_migrations(MIGRATIONS)?;

View File

@ -48,7 +48,6 @@ 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) => {
self.in_stream.write_all(&self.read_buf[..n]).await.map_err(|e| format!("Failed to write to client stream :{}", e))?; self.in_stream.write_all(&self.read_buf[..n]).await.map_err(|e| format!("Failed to write to client stream :{}", e))?;
@ -70,9 +69,6 @@ impl Client {
return Ok(()); return Ok(());
} }
println!("[{}] adding {} bytes to the buffer",self, data.len());
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());
} }
@ -92,14 +88,11 @@ impl Client {
let expected_len = varint_read(&mut varint_buffer)?; let expected_len = varint_read(&mut varint_buffer)?;
let varint_len = varint_buffer_len - varint_buffer.len(); let varint_len = varint_buffer_len - varint_buffer.len();
println!("current len : {}, expected_len : {}", self.buffer.len() -varint_len, expected_len);
if self.buffer.len() - varint_len < expected_len as usize { if self.buffer.len() - varint_len < expected_len as usize {
break ; break ;
} }
println!("buffer : {:?}", self.buffer);
if self.buffer[0] == 0 { if self.buffer[0] == 0 {
return Err("invalid packet".to_string()); return Err("invalid packet".to_string());
} }

View File

@ -6,7 +6,7 @@
/* 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/11 17:25:09 by tomoron ### ########.fr */ /* Updated: 2026/06/13 17:43:46 by tomoron ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -67,7 +67,6 @@ impl Client {
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");
} }
@ -75,22 +74,18 @@ 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) {
println!("capture : {:?}", capture);
if let Some(captured_str) = capture.get(1) { if let Some(captured_str) = capture.get(1) {
println!("found valid capture : {}", captured_str.as_str());
found = Some(captured_str.as_str().to_string()); found = Some(captured_str.as_str().to_string());
} }
} }
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)
} }
} }

View File

@ -1,13 +1,10 @@
use crate::minecraft::client::client::Client; use crate::{srvmgr::start_server, minecraft::client::client::Client};
use diesel::result::Error::NotFound; 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;
use crate::{ dockermgr };
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> {
@ -30,7 +27,8 @@ impl Client {
Err(e) => { return Err(format!("db fetch error : {}", e)); } Err(e) => { return Err(format!("db fetch error : {}", e)); }
}; };
dockermgr::start_server(&self.docker, &self.db_pool, &server.name).await?; start_server(&self.docker, &self.db_pool, &server.name).await?;
Ok(()) Ok(())
} }

View File

@ -10,7 +10,6 @@ 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> {
println!("packet len : {}", packet.len());
let packet_id = varint_read(&mut packet)?; let packet_id = varint_read(&mut packet)?;
@ -87,16 +86,14 @@ impl Client {
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);
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);
println!("proxy mode started, buffer len : {}", self.buffer.len());
Ok(()) Ok(())
} }

View File

@ -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(())
} }

View File

@ -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));

View File

@ -1,44 +1,27 @@
use bollard::{Docker, query_parameters::{ListContainersOptionsBuilder, StopContainerOptionsBuilder}}; use bollard::Docker;
use chrono::{TimeDelta, Utc}; use chrono::{TimeDelta, Utc};
use crate::{ DbPool, dockermgr, models::{self, Servers}, schema, status::ServerStatus }; use crate::{ DbPool, models::{self, Servers}, schema, srvmgr::{self, archive_server}, status::ServerStatus };
use async_std::task; use async_std::task;
use std::{collections::HashMap, time::Duration}; use std::time::Duration;
use diesel::prelude::*; use diesel::prelude::*;
async fn server_init(pool: &DbPool, docker: &Docker) -> Result<(), String> { async fn server_init(pool: &DbPool, _docker: &Docker) -> Result<(), String> {
use schema::servers::dsl::*; use schema::servers::dsl::*;
let conn = &mut pool.get().unwrap(); let conn = &mut pool.get().unwrap();
println!("init start"); println!("init start");
let _ = diesel::update(servers).filter(simple_redirect.eq(false)).set(status.eq(ServerStatus::Stopped)).execute(conn); let _ = diesel::update(servers).filter(
simple_redirect.eq(false)
let mut filters = HashMap::new(); .and(status.eq(ServerStatus::Stopping)
filters.insert("name", vec!["minecraft-"]); .or(status.eq(ServerStatus::Starting))
let container_list_options = ListContainersOptionsBuilder::new() ))
.all(true) .set(status.eq(ServerStatus::Stopped)).execute(conn);
.filters(&filters)
.build();
let containers = docker.list_containers(Some(container_list_options)).await.map_err(|e| format!("Failed get current containers : {}", e))?;
if containers.len() != 0 {
println!("stopping {} stray container(s)", containers.len())
}
for container in containers {
let container_name = &container.names.unwrap()[0][1..];
println!("stopping {}", container_name);
let stop_options = StopContainerOptionsBuilder::new()
.t(60)
.build();
docker.stop_container(container_name, Some(stop_options)).await.map_err(|e| format!("Failed to stop container {}, : {}", container_name, e))?;
println!("{} stopped", container_name);
}
println!("init done"); println!("init done");
Ok(()) Ok(())
} }
@ -63,6 +46,7 @@ pub async fn server_manager_loop(pool: DbPool, docker: Docker)
for srv in a_servers { for srv in a_servers {
let time_diff = Utc::now().naive_utc() - srv.last_login.unwrap(); let time_diff = Utc::now().naive_utc() - srv.last_login.unwrap();
should_stop(&docker, &pool, &time_diff, &srv).await; should_stop(&docker, &pool, &time_diff, &srv).await;
should_archive(&docker, &pool, &time_diff, &srv).await;
check_still_online(&docker, &pool, &srv).await; check_still_online(&docker, &pool, &srv).await;
} }
task::sleep(Duration::from_secs(30)).await; task::sleep(Duration::from_secs(30)).await;
@ -74,11 +58,21 @@ async fn should_stop(docker: &Docker, pool: &DbPool, diff: &TimeDelta, srv: &Ser
if diff.num_days() >= 7 { if diff.num_days() >= 7 {
println!("last login time exceeded, stopping {}", srv.name); println!("last login time exceeded, stopping {}", srv.name);
let _ = dockermgr::stop_server(docker, pool, &srv.name).await; let _ = srvmgr::stop_server(docker, pool, &srv.name, false).await;
println!("{} stopped", srv.name); 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) { async fn check_still_online(docker: &Docker, pool: &DbPool, srv: &Servers) {
use schema::servers::dsl::*; use schema::servers::dsl::*;

36
src/srvmgr/archive.rs Normal file
View 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(())
}

View File

@ -5,3 +5,6 @@ pub mod utils;
mod stop; mod stop;
pub use stop::stop_server; pub use stop::stop_server;
mod archive;
pub use archive::archive_server;

View File

@ -1,9 +1,9 @@
use bollard::{Docker, plugin::{ContainerCreateBody, HostConfig, Mount, MountType}, query_parameters::CreateContainerOptionsBuilder}; use bollard::{Docker, plugin::{ContainerCreateBody, HostConfig, Mount, MountType}, query_parameters::CreateContainerOptionsBuilder};
use diesel::{prelude::*, result::Error::NotFound}; use diesel::{prelude::*, result::Error::NotFound};
use crate::{dockermgr::utils::get_image_tag, models, schema, status::ServerStatus}; use crate::{models, schema, srvmgr::{archive::unarchive_server, utils::get_image_tag}, status::ServerStatus};
use crate::{DbPool, dockermgr::utils::{self, get_container_ip}}; use crate::{DbPool, srvmgr::utils::{self, get_container_ip}};
async fn start_docker_container(docker: &Docker, name: &String) -> Result<String, String> { async fn start_docker_container(docker: &Docker, name: &String) -> Result<String, String> {
@ -75,12 +75,20 @@ pub async fn start_server(docker: &Docker, pool: &DbPool, server_name: &String)
return Err("Server is starting, please wait".to_string()); return Err("Server is starting, please wait".to_string());
} }
if server.status != ServerStatus::Stopped { if server.status == ServerStatus::Disabled {
return Err(format!("Invalid server state({}), refusing to start", server.status)); 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))?; 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?; 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))?; 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))?;

View File

@ -17,7 +17,7 @@ async fn stop_container(docker: &Docker, name: &String) -> Result<(), String> {
Ok(()) Ok(())
} }
pub async fn stop_server(docker: &Docker, pool: &DbPool, server_name: &String) -> Result<(), String> { pub async fn stop_server(docker: &Docker, pool: &DbPool, server_name: &String, ignore_stopped: bool) -> Result<(), String> {
use schema::servers::dsl::*; use schema::servers::dsl::*;
let conn = &mut pool.get().unwrap(); let conn = &mut pool.get().unwrap();
@ -28,6 +28,10 @@ pub async fn stop_server(docker: &Docker, pool: &DbPool, server_name: &String) -
Ok(server) => { server } Ok(server) => { server }
}; };
if ignore_stopped && (server.status == ServerStatus::Stopped || server.status == ServerStatus::Archived) {
return Ok(());
}
if server.status != ServerStatus::Running { if server.status != ServerStatus::Running {
return Err(format!("Invalid server state ({}), refusing to stop the server", server.status)); return Err(format!("Invalid server state ({}), refusing to stop the server", server.status));
} }

View File

@ -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{
@ -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",

View 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);
}

View File

@ -0,0 +1,2 @@
pub mod handshake;
pub mod varint;

View 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
View File

@ -0,0 +1 @@
pub mod minecraft;

View File

@ -1,9 +0,0 @@
docker run -v "$(pwd)/servers/potato:/data" \
-e EULA=TRUE \
-u 1000:100 \
--rm \
-e TYPE=VANILLA\
-e VERSION="1.21.6" \
-e DIFFICULTY="2" \
-p 8080:8080 \
-it itzg/minecraft-server:latest