From 73e1ccdcba475babc985c5987e74eef0bc3e3b8d Mon Sep 17 00:00:00 2001 From: Marco Kirchner Date: Fri, 5 Jan 2024 20:57:12 +0100 Subject: [PATCH] initial commit --- .dockerignore | 3 + .github/workflows/docker-build-push.yml | 56 + .gitignore | 4 + Cargo.lock | 2076 +++++++++++++++++++++++ Cargo.toml | 23 + Dockerfile | 9 + LICENSE | 21 + MONITOR_TYPES.md | 221 +++ README.md | 93 + icon.svg | 14 + logo.svg | 182 ++ src/config.rs | 38 + src/kuma/client.rs | 430 +++++ src/kuma/mod.rs | 5 + src/kuma/models.rs | 749 ++++++++ src/main.rs | 20 + src/sync.rs | 219 +++ src/util.rs | 134 ++ 18 files changed, 4297 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker-build-push.yml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 MONITOR_TYPES.md create mode 100644 README.md create mode 100644 icon.svg create mode 100644 logo.svg create mode 100644 src/config.rs create mode 100644 src/kuma/client.rs create mode 100644 src/kuma/mod.rs create mode 100644 src/kuma/models.rs create mode 100644 src/main.rs create mode 100644 src/sync.rs create mode 100644 src/util.rs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..bc6f572 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +target +config.toml +docker.sock \ No newline at end of file diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml new file mode 100644 index 0000000..4bfacb7 --- /dev/null +++ b/.github/workflows/docker-build-push.yml @@ -0,0 +1,56 @@ +name: Build and Push Docker Image + +on: + push: + branches: + - master + tags: + - '*' + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + + - run: git fetch --prune --unshallow --tag + + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: ghcr.io/BigBoot/AutoKuma + tags: | + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + id: docker_build + uses: docker/build-push-action@v5 + with: + context: . + file: Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Image digest + run: echo ${{ steps.docker_build.outputs.digest }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..04bc0ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +config.toml +docker.sock +.vscode \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b7a6421 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2076 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "autokuma" +version = "0.0.0" +dependencies = [ + "bollard", + "confique", + "cute_custom_default", + "futures-util", + "itertools", + "rust_socketio", + "serde", + "serde-inline-default", + "serde_alias", + "serde_json", + "serde_merge", + "serde_with", + "strum", + "tokio", + "toml 0.8.8", +] + +[[package]] +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "getrandom", + "instant", + "rand", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bollard" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03db470b3c0213c47e978da93200259a1eb4dae2e5512cba9955e2b540a6fc6" +dependencies = [ + "base64", + "bollard-stubs", + "bytes", + "futures-core", + "futures-util", + "hex", + "http", + "hyper", + "hyperlocal", + "log", + "pin-project-lite", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "serde_urlencoded", + "thiserror", + "tokio", + "tokio-util", + "url", + "winapi", +] + +[[package]] +name = "bollard-stubs" +version = "1.43.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58071e8fd9ec1e930efd28e3a90c1251015872a2ce49f81f36421b86466932e" +dependencies = [ + "serde", + "serde_repr", + "serde_with", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.48.5", +] + +[[package]] +name = "confique" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c37945ed2efccb10339a12eea282a5af9ebac77720d088723b2bbbdc44eca964" +dependencies = [ + "confique-macro", + "serde", + "toml 0.5.11", +] + +[[package]] +name = "confique-macro" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3821efdaaab3c5297054a90201cc3afa2061fc6ba2bc5d2fa558b850a7dabefe" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cute_custom_default" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed431abf442833fd62ad7cc527a3833d969155c803f0dcf7f8a68db8adddc4c5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.47", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "hyperlocal" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" +dependencies = [ + "futures-util", + "hex", + "hyper", + "pin-project", + "tokio", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "reqwest" +version = "0.11.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + +[[package]] +name = "rust_engineio" +version = "0.4.4" +source = "git+https://github.com/1c3t3a/rust-socketio.git?rev=9ccf67b#9ccf67b6197664a946f152c6fd3c9f45490ce3f6" +dependencies = [ + "adler32", + "async-stream", + "async-trait", + "base64", + "bytes", + "futures-util", + "http", + "native-tls", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-tungstenite", + "tungstenite", + "url", +] + +[[package]] +name = "rust_socketio" +version = "0.4.4" +source = "git+https://github.com/1c3t3a/rust-socketio.git?rev=9ccf67b#9ccf67b6197664a946f152c6fd3c9f45490ce3f6" +dependencies = [ + "adler32", + "async-stream", + "backoff", + "base64", + "bytes", + "futures-util", + "log", + "native-tls", + "rand", + "rust_engineio", + "serde", + "serde_json", + "thiserror", + "tokio", + "url", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-inline-default" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa824cde50b5f01ff28a955114d8152a07cd62d81f53459dad0f2610136be844" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_alias" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2be4d78e3674912678d7c86b8c507fa72ebff66a8dd3359dc54ec97164b68474" +dependencies = [ + "convert_case", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_derive" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_merge" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "606e91878516232ac3b16c12e063d4468d762f16d77e7aef14a1f2326c5f409b" +dependencies = [ + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "serde_repr" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.1.0", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.47", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +dependencies = [ + "futures-util", + "log", + "native-tls", + "tokio", + "tokio-native-tls", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.1.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "native-tls", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.47", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "wasm-streams" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..17072b2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "autokuma" +version = "0.0.0" +edition = "2021" + +[dependencies] +bollard = { version = "0.15.0" } +cute_custom_default = { version = "2.1.0" } +confique = { version = "0.2.5", default-features = false, features = ["toml"] } +futures-util = { version = "0.3.30" } +itertools = { version = "0.12.0" } +rust_socketio = { git = "https://github.com/1c3t3a/rust-socketio.git", rev = "9ccf67b", features = [ + "async", +] } +serde = { version = "1.0.194", features = ["derive"] } +serde_alias = { version = "0.0.2" } +serde_json = { version = "1.0.111" } +serde_merge = { version = "0.1.3" } +serde_with = { version = "3.4.0" } +serde-inline-default = { version = "0.1.1" } +strum = { version = "0.25.0", features = ["derive"] } +tokio = { version = "1.35.1", features = ["full"] } +toml = { version = "0.8.8" } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fd9f2c4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM rust:1.75 as builder +WORKDIR /usr/src/autokuma +COPY . . +RUN cargo install --path . + +FROM debian:bookworm-slim +RUN apt-get update && apt-get install -y libssl3 && rm -rf /var/lib/apt/lists/* +COPY --from=builder /usr/local/cargo/bin/autokuma /usr/local/bin/autokuma +CMD ["autokuma"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..12d5ef3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Marco Kirchner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/MONITOR_TYPES.md b/MONITOR_TYPES.md new file mode 100644 index 0000000..ab28e06 --- /dev/null +++ b/MONITOR_TYPES.md @@ -0,0 +1,221 @@ +## `ping` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `hostname` | localhost | +| `packet_size` | 56 | +| `accepted_statuscodes` | 200-299 | + +## `mqtt` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `hostname` | localhost | +| `port` | 0 | +| `mqtt_username` | null | +| `mqtt_password` | null | +| `mqtt_topic` | "" | +| `mqtt_check_type` | null | +| `mqtt_success_message` | null | + +## `redis` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `database_connection_string` | null | +| `accepted_statuscodes` | 200-299 | + +## `push` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `push_url` | null | +| `accepted_statuscodes` | 200-299 | + +## `mysql` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `database_connection_string` | null | +| `radius_password` | "" | +| `accepted_statuscodes` | 200-299 | + +## `docker` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `docker_container` | "" | +| `docker_host` | "" | +| `accepted_statuscodes` | 200-299 | + +## `tailscale-ping` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `accepted_statuscodes` | 200-299 | + +## `radius` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `hostname` | localhost | +| `port` | 0 | +| `radius_username` | "" | +| `radius_password` | "" | +| `radius_secret` | "" | +| `radius_called_station_id` | "" | +| `radius_calling_station_id` | "" | +| `accepted_statuscodes` | 200-299 | + +## `kafka-producer` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `kafka_producer_brokers` | null | +| `kafka_producer_topic` | null | +| `kafka_producer_message` | null | +| `kafka_producer_ssl` | null | +| `kafka_producer_allow_auto_topic_creation` | null | +| `accepted_statuscodes` | 200-299 | + +## `gamedig` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `game` | "" | +| `hostname` | localhost | +| `port` | 0 | +| `gamedig_given_port_only` | null | +| `accepted_statuscodes` | 200-299 | + +## `real-browser` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `url` | https://example.com | +| `remote_browsers_toggle` | null | +| `remote_browser` | null | +| `accepted_statuscodes` | 200-299 | + +## `sqlserver` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `database_connection_string` | null | +| `accepted_statuscodes` | 200-299 | + +## `group` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `accepted_statuscodes` | 200-299 | + +## `http` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `url` | https://example.com | +| `timeout` | 48 | +| `method` | GET | +| `accepted_statuscodes` | 200-299 | + +## `grpc-keyword` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `keyword` | "" | +| `invert_keyword` | null | +| `grpc_url` | "" | +| `maxredirects` | 10 | +| `grpc_enable_tls` | null | +| `grpc_service_name` | "" | +| `grpc_method` | "" | +| `grpc_protobuf` | null | +| `grpc_body` | null | +| `grpc_metadata` | null | +| `accepted_statuscodes` | 200-299 | + +## `mongodb` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `database_connection_string` | null | +| `accepted_statuscodes` | 200-299 | + +## `keyword` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `url` | https://example.com | +| `timeout` | 48 | +| `method` | GET | +| `keyword` | "" | +| `invert_keyword` | null | +| `accepted_statuscodes` | 200-299 | + +## `json-query` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `url` | https://example.com | +| `timeout` | 48 | +| `json_path` | null | +| `expected_Example value` | null + + | +| `accepted_statuscodes` | 200-299 | + +## `steam` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `hostname` | localhost | +| `port` | 0 | +| `accepted_statuscodes` | 200-299 | + +## `dns` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `hostname` | localhost | +| `dns_resolve_server` | 1.1.1.1 | +| `port` | 0 | +| `dns_resolve_type` | A | +| `accepted_statuscodes` | 200-299 | + +## `port` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `hostname` | localhost | +| `port` | 0 | +| `accepted_statuscodes` | 200-299 | + +## `postgres` +| Property | Example Value | +|----------|---------------| +| `name` | Example | +| `interval` | 60 | +| `database_connection_string` | null | +| `accepted_statuscodes` | 200-299 | \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae211f5 --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +
+ +
+ + +# AutoKuma + +AutoKuma is a utility that automates the creation of Uptime Kuma monitors based on Docker container labels. With AutoKuma, you can eliminate the need for manual monitor creation in the Uptime Kuma UI. + +## 🔧 How to Install + +The AutoKuma Docker container is available on [GitHub Container Registry (GHCR)](https://ghcr.io/username/autokuma). To install, simply pull the container using: + +```bash +docker pull ghcr.io/bigboot/autokuma:master +``` + +## Example Docker Compose + +Here's an example `docker-compose.yml`: + +```yaml +version: '3' + +services: + autokuma: + image: ghcr.io/bigboot/autokuma:master + environment: + - AUTOKUMA__KUMA__URL=http://localhost:3001 + - AUTOKUMA__KUMA__USERNAME= + - AUTOKUMA__KUMA__PASSWORD= + - AUTOKUMA__KUMA__MFA_TOKEN= + - AUTOKUMA__KUMA__HEADERS="=, =, ..." + - AUTOKUMA__KUMA__TAG_NAME=AutoKuma + - AUTOKUMA__KUMA__TAG_COLOR=#42C0FB + - AUTOKUMA__DOCKER__SOCKET=/var/run/docker.sock + - AUTOKUMA__DOCKER__LABEL_PREFIX=kuma + labels: + - "kuma.example.http.name=Example" + - "kuma.example.http.url=https://example.com" +``` + +## Configuration + +AutoKuma can be configured using the following environment variables: + +| Variable | Description | +| ----------------------------------- | ------------------------------------------------ | +| `AUTOKUMA__KUMA__URL` | The url AutoKuma should use to connect to Uptime Kuma | +| `AUTOKUMA__KUMA__USERNAME` | The username for logging into Uptime Kuma (required unless auth is disabled) | +| `AUTOKUMA__KUMA__PASSWORD` | The password for logging into Uptime Kuma (required unless auth is disabled) | +| `AUTOKUMA__KUMA__MFA_TOKEN` | The MFA token for logging into Uptime Kuma (required if MFA is enabled) | +| `AUTOKUMA__KUMA__HEADERS` | List of HTTP headers used when connecting to Uptime Kuma | +| `AUTOKUMA__KUMA__TAG_NAME` | The name of the AutoKuma tag, used to track managed containers | +| `AUTOKUMA__KUMA__TAG_COLOR` | The color of the AutoKuma tag | +| `AUTOKUMA__DOCKER__SOCKET` | Path to the Docker socket | +| `AUTOKUMA__DOCKER__LABEL_PREFIX` | Prefix used when scanning for container labels | + + + +## Usage + +AutoKuma interprets Docker container labels with the following format: + +```plaintext +...: +``` + +- ``: Default is `kuma` unless changed using the `DOCKER__LABEL_PREFIX` env variable. +- ``: A unique identifier for the monitor (ensure it's unique between all monitors). +- ``: The type of the monitor as configured in Uptime Kuma. +- ``: The key of the value to be set. +- ``: The value for the option. + +Labels are grouped by `` into a single monitor. For example, to create a simple HTTP monitor, use the following labels: + +### Example Labels + +```plaintext +kuma.example.http.name: "Example" +kuma.example.http.url: "https://example.com" +``` + +Take a look at [all available monitor types](MONITOR_TYPES.md) and the corresponding settings. + + +## Contributing + +Contributions to AutoKuma are welcome! Feel free to open issues, submit pull requests, or provide feedback. + +## License + +AutoKuma is released under the [MIT License](LICENSE). \ No newline at end of file diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..285cc47 --- /dev/null +++ b/icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/logo.svg b/logo.svg new file mode 100644 index 0000000..81ad368 --- /dev/null +++ b/logo.svg @@ -0,0 +1,182 @@ + +image/svg+xml \ No newline at end of file diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..d8d7122 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,38 @@ +use confique::env::parse::list_by_comma; + +#[derive(confique::Config)] +pub struct KumaConfig { + #[config(env = "AUTOKUMA__KUMA__URL")] + pub url: String, + #[config(env = "AUTOKUMA__KUMA__USERNAME")] + pub username: Option, + #[config(env = "AUTOKUMA__KUMA__PASSWORD")] + pub password: Option, + #[config(env = "AUTOKUMA__KUMA__MFA_TOKEN")] + pub mfa_token: Option, + #[config(env = "AUTOKUMA__KUMA__HEADER", default = [], parse_env = list_by_comma)] + pub headers: Vec, + #[config(env = "AUTOKUMA__KUMA__TAG_NAME", default = "AutoKuma")] + pub tag_name: String, + #[config(env = "AUTOKUMA__KUMA__TAG_COLOR", default = "#42C0FB")] + pub tag_color: String, +} + +#[derive(confique::Config)] +pub struct DockerConfig { + #[config( + env = "AUTOKUMA__DOCKER__SOCKET_PATH", + default = "/var/run/docker.sock" + )] + pub socket_path: String, + #[config(env = "AUTOKUMA__DOCKER__LABEL_PREFOX", default = "kuma")] + pub label_prefix: String, +} + +#[derive(confique::Config)] +pub struct Config { + #[config(nested)] + pub kuma: KumaConfig, + #[config(nested)] + pub docker: DockerConfig, +} diff --git a/src/kuma/client.rs b/src/kuma/client.rs new file mode 100644 index 0000000..aa71638 --- /dev/null +++ b/src/kuma/client.rs @@ -0,0 +1,430 @@ +use super::{Event, Monitor, MonitorList, MonitorType, Tag, TagList}; +use crate::config::Config; +use crate::util::ResultLogger; +use futures_util::FutureExt; +use itertools::Itertools; +use rust_socketio::Payload; +use rust_socketio::{ + asynchronous::{Client as SocketIO, ClientBuilder}, + Event as SocketIOEvent, +}; +use serde::de::DeserializeOwned; +use serde_json::{json, Value}; +use std::collections::HashMap; +use std::mem; +use std::str::FromStr; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::{mpsc, Mutex}; + +type EventArgs = (Event, Value); +type Sender = mpsc::Sender; +type Err = (); +type Result = std::result::Result; + +struct Worker { + config: Arc, + socket_io: Arc>>, + event_sender: Arc, + tags: Arc>, + monitors: Arc>, + is_ready: Arc>, +} + +impl Worker { + fn new(config: Arc, event_sender: Sender) -> Self { + Worker { + config: config, + socket_io: Arc::new(Mutex::new(None)), + event_sender: (Arc::new(event_sender)), + tags: Default::default(), + monitors: Default::default(), + is_ready: Arc::new(Mutex::new(false)), + } + } + + async fn on_monitor_list(&self, monitor_list: MonitorList) { + *self.monitors.lock().await = monitor_list; + + let tags = self.get_tags().await; + *self.tags.lock().await = tags; + *self.is_ready.lock().await = true; + } + + async fn on_connect(&self) { + if let (Some(username), Some(password)) = + (&self.config.kuma.username, &self.config.kuma.password) + { + self.login(username, password, self.config.kuma.mfa_token.clone()) + .await; + } + } + + async fn on_event(&self, event: Event, payload: Value) { + match event { + Event::MonitorList => { + self.on_monitor_list(serde_json::from_value(payload).unwrap()) + .await + } + Event::Connect => self.on_connect().await, + _ => {} + } + } + + fn verify_response( + response: Vec, + result_ptr: impl AsRef, + ) -> Result { + json!(response) + .pointer(&format!("/0/0{}", result_ptr.as_ref())) + .and_then(|value| serde_json::from_value(value.to_owned()).ok()) + .ok_or_else(|| ()) + } + + async fn get_tags(&self) -> TagList { + self.call("getTags", vec![], "/tags", Duration::from_secs(2)) + .await + .unwrap_or_default() + } + + async fn call( + &self, + method: impl Into, + args: A, + result_ptr: impl Into, + timeout: Duration, + ) -> Result + where + A: IntoIterator + Send + Clone, + T: DeserializeOwned + Send + 'static, + { + let method = method.into(); + let result_ptr: String = result_ptr.into(); + + for i in 0..5 { + let method = method.clone(); + let args = args.clone(); + let result_ptr = result_ptr.clone(); + let (tx, mut rx) = tokio::sync::mpsc::channel::>(1); + + if let Some(socket_io) = &*self.socket_io.lock().await { + let result = socket_io + .emit_with_ack( + method, + Payload::Text(args.into_iter().collect_vec()), + timeout, + move |message: Payload, _: SocketIO| { + let tx = tx.clone(); + let result_ptr = result_ptr.clone(); + async move { + match message { + Payload::Text(response) => { + let _ = tx + .send(Self::verify_response(response, result_ptr)) + .await; + } + _ => {} + } + } + .boxed() + }, + ) + .await; + + match result { + Ok(_) => match tokio::time::timeout(Duration::from_secs(3), rx.recv()).await { + Ok(Some(Ok(value))) => return Ok(value), + _ => { + println!("Error during send"); + } + }, + Err(e) => { + println!("{}", e.to_string()); + } + }; + } + + tokio::time::sleep(Duration::from_millis(200 * i)).await; + + println!("Reconnecting..."); + self.connect().await; + } + + Err(()) + } + + pub async fn login( + &self, + username: impl AsRef, + password: impl AsRef, + token: Option, + ) -> bool { + self.call( + "login", + vec![serde_json::to_value(HashMap::from([ + ("username", json!(username.as_ref())), + ("password", json!(password.as_ref())), + ("token", json!(token)), + ])) + .unwrap()], + "/ok", + Duration::from_secs(2), + ) + .await + .unwrap_or_else(|_| false) + } + + pub async fn add_tag(&self, tag: Tag) -> Tag { + self.call( + "addTag", + vec![serde_json::to_value(tag.clone()).unwrap()], + "/tag", + Duration::from_secs(2), + ) + .await + .unwrap_or_else(|_| tag) + } + + pub async fn add_monitor_tag(&self, monitor_id: i32, tag_id: i32, value: Option) { + let _: bool = self + .call( + "addMonitorTag", + vec![ + json!(tag_id), + json!(monitor_id), + json!(value.unwrap_or_default()), + ], + "/ok", + Duration::from_secs(2), + ) + .await + .unwrap_or_default(); + } + + pub async fn delete_monitor(&self, monitor_id: i32) { + let _: bool = self + .call( + "deleteMonitor", + vec![json!(monitor_id)], + "/ok", + Duration::from_secs(2), + ) + .await + .unwrap_or_default(); + } + + async fn resolve_group(&self, monitor: &mut Monitor) -> bool { + if let Some(group_name) = &monitor.common().group { + if let Some(Some(group_id)) = self + .monitors + .lock() + .await + .iter() + .find(|x| { + x.1.monitor_type() == MonitorType::Group + && x.1.common().tags.iter().any(|tag| { + tag.name == "AutoKuma" + && tag + .value + .as_ref() + .is_some_and(|tag_value| tag_value == group_name) + }) + }) + .map(|x| x.1.common().id) + { + monitor.common_mut().parent = Some(group_id); + } else { + return false; + } + } + return true; + } + + async fn update_monitor_tags(&self, monitor_id: i32, tags: &Vec) { + for tag in tags { + if let Some(tag_id) = tag.id { + self.add_monitor_tag(monitor_id, tag_id, tag.value.clone()) + .await; + } + } + } + + pub async fn add_monitor(&self, mut monitor: Monitor) -> Monitor { + let mut tags = vec![]; + mem::swap(&mut tags, &mut monitor.common_mut().tags); + + if !self.resolve_group(&mut monitor).await { + return monitor; + } + + let id: i32 = match self + .call( + "add", + vec![serde_json::to_value(&monitor).unwrap()], + "/monitorID", + Duration::from_secs(2), + ) + .await + { + Ok(id) => id, + Err(_) => return monitor, + }; + + self.update_monitor_tags(id, &tags).await; + + monitor.common_mut().id = Some(id); + monitor.common_mut().tags = tags; + + self.monitors + .lock() + .await + .insert(id.to_string(), monitor.clone()); + + monitor + } + + pub async fn edit_monitor(&self, mut monitor: Monitor) -> Monitor { + if !self.resolve_group(&mut monitor).await { + return monitor; + } + + // TODO: Tags + let mut tags = vec![]; + mem::swap(&mut tags, &mut monitor.common_mut().tags); + + let _: i32 = self + .call( + "editMonitor", + vec![serde_json::to_value(&monitor).unwrap()], + "/monitorID", + Duration::from_secs(2), + ) + .await + .unwrap_or_default(); + + monitor.common_mut().tags = tags; + + monitor + } + + pub async fn connect(&self) { + *self.is_ready.lock().await = false; + *self.socket_io.lock().await = None; + + let callback_tx = self.event_sender.clone(); + let mut builder = ClientBuilder::new(self.config.kuma.url.clone()); + + for (key, value) in self + .config + .kuma + .headers + .iter() + .filter_map(|header| header.split_once("=")) + { + builder = builder.opening_header(key, value); + } + + let client = builder + .on_any(move |event, payload, _| { + let callback_tx = callback_tx.clone(); + async move { + match (event, payload) { + (SocketIOEvent::Message, Payload::Text(params)) => { + if let Ok(e) = Event::from_str( + ¶ms[0] + .as_str() + .on_error_log(|| "Error while deserializing Event...") + .unwrap_or(""), + ) { + callback_tx + .send((e, json!(null))) + .await + .on_error_log(|_| "Error while sending Message event") + .unwrap(); + } + } + (event, Payload::Text(params)) => { + if let Ok(e) = Event::from_str(&String::from(event)) { + callback_tx + .send((e, params.into_iter().next().unwrap())) + .await + .on_error_log(|_| "Error while sending event") + .unwrap(); + } + } + _ => {} + } + } + .boxed() + }) + .connect() + .await + .on_error_log(|_| "Error during connect") + .ok(); + + self.event_sender + .send((Event::Connect, json!(null))) + .await + .ok(); + + *self.socket_io.lock().await = client; + + for i in 0..10 { + if *self.is_ready.lock().await { + println!("Connected!"); + return; + } + println!("Waiting for Kuma to get ready..."); + tokio::time::sleep(Duration::from_millis(200 * i)).await; + } + + println!("Timeout while waiting for Kuma to get ready..."); + } +} + +pub struct Client { + worker: Arc, +} + +impl Client { + pub async fn connect(config: Arc) -> Client { + let (tx, mut rx) = mpsc::channel::(100); + + let worker = Arc::new(Worker::new(config, tx)); + + let worker_ref = worker.clone(); + tokio::spawn(async move { + while let Some((event, payload)) = rx.recv().await { + worker_ref.on_event(event, payload).await; + } + }); + + worker.connect().await; + + Self { worker } + } + + pub async fn monitors(&self) -> MonitorList { + self.worker.monitors.lock().await.clone() + } + + pub async fn tags(&self) -> TagList { + self.worker.tags.lock().await.clone() + } + + pub async fn add_tag(&self, tag: Tag) -> Tag { + self.worker.add_tag(tag).await + } + + pub async fn add_monitor(&self, monitor: Monitor) -> Monitor { + self.worker.add_monitor(monitor).await + } + + pub async fn edit_monitor(&self, monitor: Monitor) -> Monitor { + self.worker.edit_monitor(monitor).await + } + + pub async fn delete_monitor(&self, monitor_id: i32) { + self.worker.delete_monitor(monitor_id).await + } +} diff --git a/src/kuma/mod.rs b/src/kuma/mod.rs new file mode 100644 index 0000000..4a1c2d9 --- /dev/null +++ b/src/kuma/mod.rs @@ -0,0 +1,5 @@ +mod client; +mod models; + +pub use client::*; +pub use models::*; diff --git a/src/kuma/models.rs b/src/kuma/models.rs new file mode 100644 index 0000000..49c6dc4 --- /dev/null +++ b/src/kuma/models.rs @@ -0,0 +1,749 @@ +use crate::util::{DeserializeBoolLenient, DeserializeNumberLenient}; +use serde::{Deserialize, Serialize}; +use serde_alias::serde_alias; +use serde_inline_default::serde_inline_default; +use serde_with::{serde_as, skip_serializing_none}; +use std::collections::HashMap; +use strum::EnumString; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub enum DnsResolverType { + A, + AAAA, + CAA, + CNAME, + MX, + NS, + PTR, + SOA, + SRV, + TXT, +} + +#[derive(Debug, EnumString)] +#[strum(serialize_all = "camelCase")] +pub enum Event { + Connect, + Disconnect, + MonitorList, + NotificationList, + ProxyList, + StatusPageList, + HeartbeatList, + ImportantHeartbeatList, + AvgPing, + Uptime, + Heartbeat, + Info, + CertInfo, + DockerHostList, + AutoLogin, + InitServerTimezone, + MaintenanceList, + ApiKeyList, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub enum MonitorType { + #[serde(rename = "group")] + Group, + #[serde(rename = "http")] + Http, + #[serde(rename = "port")] + Port, + #[serde(rename = "ping")] + Ping, + #[serde(rename = "keyword")] + Keyword, + #[serde(rename = "json-query")] + JsonQuery, + #[serde(rename = "grpc-keyword")] + GrpcKeyword, + #[serde(rename = "dns")] + Dns, + #[serde(rename = "docker")] + Docker, + #[serde(rename = "real-browser")] + RealBrowser, + #[serde(rename = "push")] + Push, + #[serde(rename = "steam")] + Steam, + #[serde(rename = "gamedig")] + GameDig, + #[serde(rename = "mqtt")] + Mqtt, + #[serde(rename = "kafka-producer")] + KafkaProducer, + #[serde(rename = "sqlserver")] + SqlServer, + #[serde(rename = "postgres")] + Postgres, + #[serde(rename = "mysql")] + Mysql, + #[serde(rename = "mongodb")] + Mongodb, + #[serde(rename = "radius")] + Radius, + #[serde(rename = "redis")] + Redis, + #[serde(rename = "tailscale-ping")] + TailscalePing, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq)] +pub enum KafkaProducerSaslOption { + #[serde(rename = "None")] + None, + #[serde(rename = "plain")] + Plain { + #[serde(rename = "username")] + username: Option, + #[serde(rename = "password")] + password: Option, + }, + #[serde(rename = "scram-sha-256")] + ScramSha256 { + #[serde(rename = "username")] + username: Option, + #[serde(rename = "password")] + password: Option, + }, + #[serde(rename = "scram-sha-512")] + ScramSha512 { + #[serde(rename = "username")] + username: Option, + #[serde(rename = "password")] + password: Option, + }, + #[serde(rename = "aws")] + AWS { + #[serde(rename = "authorizationIdentity")] + #[serde(alias = "authorization_identity")] + authorization_identity: Option, + #[serde(rename = "accessKeyId")] + #[serde(alias = "name")] + access_key_id: Option, + #[serde(rename = "secretAccessKey")] + #[serde(alias = "name")] + secret_access_key: Option, + #[serde(alias = "name")] + #[serde(rename = "sessionToken")] + session_token: Option, + }, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub enum HttpMethod { + GET, + POST, + PUT, + PATCH, + DELETE, + HEAD, + OPTIONS, +} + +#[serde_inline_default] +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorCommon { + #[serde(rename = "id")] + #[serde_as(as = "Option")] + pub id: Option, + #[serde(rename = "name")] + pub name: String, + #[serde(rename = "interval")] + #[serde_inline_default(Some(60))] + #[serde_as(as = "Option")] + pub interval: Option, + #[serde(rename = "active")] + #[serde_inline_default(None)] + #[serde_as(as = "Option")] + pub active: Option, + #[serde(rename = "maxretries")] + #[serde_as(as = "Option")] + pub max_retries: Option, + #[serde(rename = "retryInterval")] + #[serde_inline_default(Some(60))] + #[serde_as(as = "Option")] + pub retry_interval: Option, + #[serde(rename = "upsideDown")] + #[serde_as(as = "Option")] + pub upside_down: Option, + #[serde(rename = "parent")] + #[serde_as(as = "Option")] + pub parent: Option, + #[serde(rename = "group")] + #[serde(skip_serializing)] + pub group: Option, + #[serde(rename = "tags")] + #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] + pub tags: Vec, + #[serde(rename = "notificationIDList")] + pub notification_id_list: Option>, + #[serde(rename = "accepted_statuscodes")] + #[serde_inline_default(vec!["200-299".to_owned()])] + pub accepted_statuscodes: Vec, +} + +#[serde_inline_default] +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorHttp { + #[serde(rename = "url")] + pub url: String, + #[serde(rename = "timeout")] + #[serde_inline_default(Some(48))] + #[serde_as(as = "Option")] + pub timeout: Option, + #[serde(rename = "resendInterval")] + #[serde_as(as = "Option")] + pub resend_interval: Option, + #[serde(rename = "expiryNotification")] + #[serde_as(as = "Option")] + pub expiry_notification: Option, + #[serde(rename = "ignoreTls")] + #[serde_as(as = "Option")] + pub ignore_tls: Option, + #[serde(rename = "maxredirects")] + #[serde_as(as = "Option")] + pub max_redirects: Option, + #[serde(rename = "proxyId")] + pub proxy_id: Option, + #[serde(rename = "method")] + #[serde_inline_default(Some(HttpMethod::GET))] + pub method: Option, + #[serde(rename = "httpBodyEncoding")] + pub http_body_encoding: Option, + #[serde(rename = "body")] + pub body: Option, + #[serde(rename = "headers")] + pub headers: Option, + #[serde(rename = "authMethod")] + pub auth_method: Option, + #[serde(rename = "tlsCert")] + pub tls_cert: Option, + #[serde(rename = "tlsKey")] + pub tls_key: Option, + #[serde(rename = "tlsCa")] + pub tls_ca: Option, + #[serde(rename = "oauth_auth_method")] + pub oauth_auth_method: Option, + #[serde(rename = "oauth_client_id")] + pub oauth_client_id: Option, + #[serde(rename = "oauth_token_url")] + pub oauth_token_url: Option, + #[serde(rename = "oauth_client_secret")] + pub oauth_client_secret: Option, + #[serde(rename = "oauth_scopes")] + pub oauth_scopes: Option, + #[serde(rename = "basic_auth_user")] + pub basic_auth_user: Option, + #[serde(rename = "basic_auth_pass")] + pub basic_auth_pass: Option, + #[serde(rename = "authDomain")] + pub auth_domain: Option, + #[serde(rename = "authWorkstation")] + pub auth_workstation: Option, +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorPort { + #[serde(rename = "hostname")] + hostname: String, + #[serde(rename = "port")] + port: String, +} + +#[serde_inline_default] +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorPing { + #[serde(rename = "hostname")] + hostname: String, + #[serde(rename = "packetSize")] + #[serde_inline_default(56)] + packet_size: i32, +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorKeyword { + #[serde(rename = "keyword")] + keyword: String, + #[serde(rename = "invertKeyword")] + #[serde_as(as = "Option")] + invert_keyword: Option, +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorJsonQuery { + #[serde(rename = "jsonPath")] + json_path: Option, + #[serde(rename = "expectedValue")] + expected_value: Option, +} + +#[serde_inline_default] +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorGrpcKeyword { + #[serde_as(as = "Option")] + invert_keyword: Option, + #[serde(rename = "grpcUrl")] + grpc_url: String, + #[serde(rename = "maxredirects")] + #[serde_inline_default(10)] + max_redirects: i32, + #[serde(rename = "grpcEnableTls")] + #[serde_as(as = "Option")] + grpc_enable_tls: Option, + #[serde(rename = "grpcServiceName")] + grpc_service_name: String, + #[serde(rename = "grpcMethod")] + grpc_method: String, + #[serde(rename = "grpcProtobuf")] + grpc_protobuf: Option, + #[serde(rename = "grpcBody")] + grpc_body: Option, + #[serde(rename = "grpcMetadata")] + grpc_metadata: Option, +} + +#[serde_inline_default] +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorDns { + #[serde(rename = "hostname")] + hostname: String, + #[serde(rename = "dns_resolve_server")] + #[serde_inline_default("1.1.1.1".to_owned())] + dns_resolve_server: String, + #[serde(rename = "port")] + port: String, + #[serde(rename = "dns_resolve_type")] + #[serde_inline_default(DnsResolverType::A)] + dns_resolve_type: DnsResolverType, +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorDocker { + #[serde(rename = "docker_container")] + docker_container: String, + #[serde(rename = "docker_host")] + docker_host: String, +} + +#[skip_serializing_none] +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorRealBrowser { + #[serde(rename = "url")] + url: String, + #[serde(rename = "remoteBrowsersToggle")] + #[serde_as(as = "Option")] + remote_browsers_toggle: Option, + #[serde(rename = "remote_browser")] + remote_browser: Option, +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorPush { + #[serde(rename = "pushURL")] + push_url: Option, +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorSteam { + #[serde(rename = "hostname")] + hostname: String, + #[serde(rename = "port")] + port: String, +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorGameDig { + #[serde(rename = "game")] + game: String, + #[serde(rename = "hostname")] + hostname: String, + #[serde(rename = "port")] + port: String, + #[serde(rename = "gamedigGivenPortOnly")] + #[serde_as(as = "Option")] + gamedig_given_port_only: Option, +} + +#[serde_inline_default] +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorMqtt { + #[serde(rename = "hostname")] + hostname: String, + #[serde(rename = "port")] + port: String, + #[serde(rename = "mqttUsername")] + mqtt_username: Option, + #[serde(rename = "mqttPassword")] + mqtt_password: Option, + #[serde(rename = "mqttTopic")] + mqtt_topic: String, + #[serde(rename = "mqttCheckType")] + mqtt_check_type: Option, + #[serde(rename = "mqttSuccessMessage")] + mqtt_success_message: Option, +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorKafkaProducer { + #[serde(rename = "kafkaProducerBrokers")] + kafka_producer_brokers: Option, + #[serde(rename = "kafkaProducerTopic")] + kafka_producer_topic: Option, + #[serde(rename = "kafkaProducerMessage")] + kafka_producer_message: Option, + #[serde(rename = "kafkaProducerSsl")] + #[serde_as(as = "Option")] + kafka_producer_ssl: Option, + #[serde(rename = "kafkaProducerAllowAutoTopicCreation")] + #[serde_as(as = "Option")] + kafka_producer_allow_auto_topic_creation: Option, +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorDatabase { + #[serde(rename = "databaseConnectionString")] + database_connection_string: Option, +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorRadius { + #[serde(rename = "hostname")] + hostname: Option, + #[serde(rename = "port")] + port: Option, + #[serde(rename = "radiusUsername")] + radius_username: String, + #[serde(rename = "radiusPassword")] + radius_password: String, + #[serde(rename = "radiusSecret")] + radius_secret: String, + #[serde(rename = "radiusCalledStationId")] + radius_called_station_id: String, + #[serde(rename = "radiusCallingStationId")] + radius_calling_station_id: String, +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct MonitorTailscale { + #[serde(rename = "hostname")] + hostname: String, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(tag = "type")] +pub enum Monitor { + #[serde(rename = "group")] + Group { + #[serde(flatten)] + common: MonitorCommon, + }, + #[serde(rename = "http")] + Http { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + http: MonitorHttp, + }, + #[serde(rename = "port")] + Port { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + port: MonitorPort, + }, + #[serde(rename = "ping")] + Ping { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + ping: MonitorPing, + }, + #[serde(rename = "keyword")] + Keyword { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + http: MonitorHttp, + #[serde(flatten)] + keyword: MonitorKeyword, + }, + #[serde(rename = "json-query")] + JsonQuery { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + http: MonitorHttp, + #[serde(flatten)] + json_query: MonitorJsonQuery, + }, + #[serde(rename = "grpc-keyword")] + GrpcKeyword { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + keyword: MonitorKeyword, + #[serde(flatten)] + grpc_keyword: MonitorGrpcKeyword, + }, + #[serde(rename = "dns")] + Dns { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + dns: MonitorDns, + }, + #[serde(rename = "docker")] + Docker { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + docker: MonitorDocker, + }, + #[serde(rename = "real-browser")] + RealBrowser { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + real_browser: MonitorRealBrowser, + }, + #[serde(rename = "push")] + Push { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + push: MonitorPush, + }, + #[serde(rename = "steam")] + Steam { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + steam: MonitorSteam, + }, + #[serde(rename = "gamedig")] + GameDig { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + gamedig: MonitorGameDig, + }, + #[serde(rename = "mqtt")] + Mqtt { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + mqtt: MonitorMqtt, + }, + #[serde(rename = "kafka-producer")] + KafkaProducer { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + kafka_producer: MonitorKafkaProducer, + }, + #[serde(rename = "sqlserver")] + SqlServer { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + database: MonitorDatabase, + }, + #[serde(rename = "postgres")] + Postgres { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + database: MonitorDatabase, + }, + #[serde(rename = "mysql")] + Mysql { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + database: MonitorDatabase, + #[serde(rename = "radiusPassword")] + password: Option, + }, + #[serde(rename = "mongodb")] + Mongodb { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + database: MonitorDatabase, + }, + #[serde(rename = "radius")] + Radius { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + radius: MonitorRadius, + }, + #[serde(rename = "redis")] + Redis { + #[serde(flatten)] + common: MonitorCommon, + #[serde(flatten)] + database: MonitorDatabase, + }, + #[serde(rename = "tailscale-ping")] + TailscalePing { + #[serde(flatten)] + common: MonitorCommon, + }, +} + +impl Monitor { + pub fn common(&self) -> &MonitorCommon { + match self { + Monitor::Group { common } => common, + Monitor::Http { common, .. } => common, + Monitor::Port { common, .. } => common, + Monitor::Ping { common, .. } => common, + Monitor::Keyword { common, .. } => common, + Monitor::JsonQuery { common, .. } => common, + Monitor::GrpcKeyword { common, .. } => common, + Monitor::Dns { common, .. } => common, + Monitor::Docker { common, .. } => common, + Monitor::RealBrowser { common, .. } => common, + Monitor::Push { common, .. } => common, + Monitor::Steam { common, .. } => common, + Monitor::GameDig { common, .. } => common, + Monitor::Mqtt { common, .. } => common, + Monitor::KafkaProducer { common, .. } => common, + Monitor::SqlServer { common, .. } => common, + Monitor::Postgres { common, .. } => common, + Monitor::Mysql { common, .. } => common, + Monitor::Mongodb { common, .. } => common, + Monitor::Radius { common, .. } => common, + Monitor::Redis { common, .. } => common, + Monitor::TailscalePing { common, .. } => common, + } + } + pub fn common_mut(&mut self) -> &mut MonitorCommon { + match self { + Monitor::Group { common } => common, + Monitor::Http { common, .. } => common, + Monitor::Port { common, .. } => common, + Monitor::Ping { common, .. } => common, + Monitor::Keyword { common, .. } => common, + Monitor::JsonQuery { common, .. } => common, + Monitor::GrpcKeyword { common, .. } => common, + Monitor::Dns { common, .. } => common, + Monitor::Docker { common, .. } => common, + Monitor::RealBrowser { common, .. } => common, + Monitor::Push { common, .. } => common, + Monitor::Steam { common, .. } => common, + Monitor::GameDig { common, .. } => common, + Monitor::Mqtt { common, .. } => common, + Monitor::KafkaProducer { common, .. } => common, + Monitor::SqlServer { common, .. } => common, + Monitor::Postgres { common, .. } => common, + Monitor::Mysql { common, .. } => common, + Monitor::Mongodb { common, .. } => common, + Monitor::Radius { common, .. } => common, + Monitor::Redis { common, .. } => common, + Monitor::TailscalePing { common, .. } => common, + } + } + + pub fn monitor_type(&self) -> MonitorType { + match self { + Monitor::Group { .. } => MonitorType::Group, + Monitor::Http { .. } => MonitorType::Http, + Monitor::Port { .. } => MonitorType::Port, + Monitor::Ping { .. } => MonitorType::Ping, + Monitor::Keyword { .. } => MonitorType::Keyword, + Monitor::JsonQuery { .. } => MonitorType::JsonQuery, + Monitor::GrpcKeyword { .. } => MonitorType::GrpcKeyword, + Monitor::Dns { .. } => MonitorType::Dns, + Monitor::Docker { .. } => MonitorType::Docker, + Monitor::RealBrowser { .. } => MonitorType::RealBrowser, + Monitor::Push { .. } => MonitorType::Push, + Monitor::Steam { .. } => MonitorType::Steam, + Monitor::GameDig { .. } => MonitorType::GameDig, + Monitor::Mqtt { .. } => MonitorType::Mqtt, + Monitor::KafkaProducer { .. } => MonitorType::KafkaProducer, + Monitor::SqlServer { .. } => MonitorType::SqlServer, + Monitor::Postgres { .. } => MonitorType::Postgres, + Monitor::Mysql { .. } => MonitorType::Mysql, + Monitor::Mongodb { .. } => MonitorType::Mongodb, + Monitor::Radius { .. } => MonitorType::Radius, + Monitor::Redis { .. } => MonitorType::Redis, + Monitor::TailscalePing { .. } => MonitorType::TailscalePing, + } + } +} + +#[skip_serializing_none] +#[serde_alias(SnakeCase)] +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)] +pub struct Tag { + #[serde_as(as = "Option")] + pub id: Option, + pub name: String, + pub color: String, + #[serde_as(as = "Option")] + pub tag_id: Option, + pub value: Option, +} + +// #[derive(Debug, Serialize, Deserialize, Default)] +// pub struct MonitorList { +// monitors: Vec, +// } +pub type MonitorList = HashMap; + +// #[serde_alias(SnakeCase)] +// #[derive(Debug, Serialize, Deserialize, Clone)] +// pub struct TagList { +// pub tags: Vec, +// } +pub type TagList = Vec; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..15bc164 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,20 @@ +use std::sync::Arc; + +mod config; +mod kuma; +mod sync; +mod util; + +#[tokio::main()] +async fn main() { + let config = Arc::new( + confique::Config::builder() + .env() + .file("config.toml") + .load() + .expect("Invalid config"), + ); + + let sync = sync::Sync::new(config); + sync.run().await; +} diff --git a/src/sync.rs b/src/sync.rs new file mode 100644 index 0000000..20f97a4 --- /dev/null +++ b/src/sync.rs @@ -0,0 +1,219 @@ +use crate::{ + config::Config, + kuma::{Client, Monitor, Tag}, + util::{group_by_prefix, ResultLogger}, +}; +use bollard::{ + container::ListContainersOptions, service::ContainerSummary, Docker, API_DEFAULT_VERSION, +}; +use itertools::Itertools; +use std::{collections::HashMap, sync::Arc, time::Duration}; + +pub struct Sync { + config: Arc, +} + +impl Sync { + pub fn new(config: Arc) -> Self { + Self { config: config } + } + + async fn get_kuma_containers(&self, docker: &Docker) -> Vec { + docker + .list_containers(Some(ListContainersOptions:: { + all: true, + ..Default::default() + })) + .await + .unwrap() + .into_iter() + .filter(|c| { + c.labels.as_ref().map_or_else( + || false, + |labels| { + labels.keys().any(|key| { + key.starts_with(&format!("{}.", self.config.docker.label_prefix)) + }) + }, + ) + }) + .collect::>() + } + + fn get_monitor_from_labels( + &self, + id: &str, + monitor_type: &str, + settings: Vec<(String, String)>, + ) -> Option { + let config = vec![("type".to_owned(), monitor_type.to_owned())] + .into_iter() + .chain(settings.into_iter()) + .map(|(key, value)| format!("{} = \"{}\"", key, value)) + .join("\n"); + + if let Ok(toml) = toml::from_str::(&config) { + return serde_json::from_value::(toml) + .on_error_log(|e| format!("Error while parsing {}: {}!", id, e.to_string())) + .ok(); + } + + None + } + + fn get_monitors_from_labels(&self, labels: Vec<(String, String)>) -> Vec<(String, Monitor)> { + group_by_prefix( + labels.iter().map(|(key, value)| { + ( + key.trim_start_matches(&format!("{}.", self.config.docker.label_prefix)), + value, + ) + }), + ".", + ) + .into_iter() + .map(|(key, value)| (key, group_by_prefix(value, "."))) + .flat_map(|(id, monitors)| { + monitors + .into_iter() + .filter_map(move |(monitor_type, settings)| { + self.get_monitor_from_labels(&id, &monitor_type, settings) + .map(|monitor| (id.clone(), monitor)) + }) + }) + .collect() + } + + fn get_monitors_from_containers( + &self, + containers: &Vec, + ) -> HashMap { + containers + .into_iter() + .flat_map(|container| { + let kuma_labels = container.labels.as_ref().map_or_else( + || vec![], + |labels| { + labels + .iter() + .filter(|(key, _)| { + key.starts_with(&format!("{}.", self.config.docker.label_prefix)) + }) + .map(|(key, value)| (key.to_owned(), value.to_owned())) + .collect::>() + }, + ); + + self.get_monitors_from_labels(kuma_labels) + }) + .sorted_by(|a, b| { + Ord::cmp( + &if a.1.common().parent.is_some() { 1 } else { -1 }, + &if b.1.common().parent.is_some() { 1 } else { -1 }, + ) + }) + .collect::>() + } + + async fn get_autokuma_tag(&self, kuma: &Client) -> Tag { + match kuma + .tags() + .await + .into_iter() + .find(|tag| tag.name == self.config.kuma.tag_name) + { + Some(tag) => tag, + None => { + kuma.add_tag(Tag { + name: self.config.kuma.tag_name.clone(), + color: self.config.kuma.tag_color.clone(), + ..Default::default() + }) + .await + } + } + } + + async fn get_managed_monitors(&self, kuma: &Client) -> HashMap { + kuma.monitors() + .await + .into_iter() + .filter_map(|(_, monitor)| { + match monitor + .common() + .tags + .iter() + .filter(|tag| tag.name == self.config.kuma.tag_name) + .find_map(|tag| tag.value.as_ref()) + { + Some(id) => Some((id.to_owned(), monitor)), + None => None, + } + }) + .collect::>() + } + + pub async fn run(&self) { + let docker = + Docker::connect_with_socket(&self.config.docker.socket_path, 120, API_DEFAULT_VERSION) + .unwrap(); + let kuma = Client::connect(self.config.clone()).await; + + loop { + let autokuma_tag = self.get_autokuma_tag(&kuma).await; + let containers = self.get_kuma_containers(&docker).await; + let new_monitors = self.get_monitors_from_containers(&containers); + let current_monitors = self.get_managed_monitors(&kuma).await; + + let to_delete = current_monitors + .iter() + .filter(|(id, _)| !new_monitors.contains_key(*id)) + .collect_vec(); + + let to_create = new_monitors + .iter() + .filter(|(id, _)| !current_monitors.contains_key(*id)) + .collect_vec(); + + let to_update = current_monitors + .keys() + .filter_map( + |id| match (current_monitors.get(id), new_monitors.get(id)) { + (Some(current), Some(new)) => Some((id, current, new)), + _ => None, + }, + ) + .collect_vec(); + + for (id, monitor) in to_create { + println!("Creating new monitor: {}", id); + + let mut monitor = monitor.clone(); + let mut tag = autokuma_tag.clone(); + + tag.value = Some(id.clone()); + monitor.common_mut().tags.push(tag); + + kuma.add_monitor(monitor).await; + } + + for (id, current, new) in to_update { + let merge: Monitor = serde_merge::omerge(current, new).unwrap(); + + if current != &merge { + println!("Updating monitor: {}", id); + kuma.edit_monitor(merge).await; + } + } + + for (id, monitor) in to_delete { + println!("Deleting monitor: {}", id); + if let Some(id) = monitor.common().id { + kuma.delete_monitor(id).await; + } + } + + tokio::time::sleep(Duration::from_secs(5)).await; + } + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..c44d4c7 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,134 @@ +use std::{collections::BTreeMap, str::FromStr}; + +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use serde_with::{DeserializeAs, SerializeAs}; + +pub struct DeserializeNumberLenient; + +impl<'de, T> DeserializeAs<'de, T> for DeserializeNumberLenient +where + T: FromStr + TryFrom, +{ + fn deserialize_as(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let value = Value::deserialize(deserializer).map_err(serde::de::Error::custom)?; + let result = match value { + Value::Number(n) => Ok(n.as_i64().and_then(|n| n.try_into().ok()).ok_or_else(|| { + serde::de::Error::custom(format!( + "Unable to represent {} as {}", + n, + std::any::type_name::() + )) + }))?, + Value::String(s) => s.parse::().map_err(|_| { + serde::de::Error::custom(format!( + "Unable to parse {} as {}", + s, + std::any::type_name::() + )) + }), + _ => Err(serde::de::Error::custom("Unexpected type for x")), + }; + + result + } +} + +impl SerializeAs for DeserializeNumberLenient +where + T: Serialize, +{ + fn serialize_as(source: &T, serializer: S) -> Result + where + S: serde::Serializer, + { + source.serialize(serializer) + } +} + +pub struct DeserializeBoolLenient; + +impl<'de> DeserializeAs<'de, bool> for DeserializeBoolLenient { + fn deserialize_as(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let value = Value::deserialize(deserializer).map_err(serde::de::Error::custom)?; + let result = match value { + Value::Bool(b) => Ok(b), + Value::String(s) => s.to_lowercase().parse::().map_err(|_| { + serde::de::Error::custom(format!( + "Unable to parse {} as {}", + s, + std::any::type_name::() + )) + }), + _ => Err(serde::de::Error::custom("Unexpected type for x")), + }; + + result + } +} + +impl SerializeAs for DeserializeBoolLenient +where + T: Serialize, +{ + fn serialize_as(source: &T, serializer: S) -> Result + where + S: serde::Serializer, + { + source.serialize(serializer) + } +} + +pub fn group_by_prefix(v: I, delimiter: &str) -> BTreeMap> +where + A: AsRef, + B: AsRef, + I: IntoIterator, +{ + v.into_iter() + .fold(BTreeMap::new(), |mut groups, (key, value)| { + if let Some((prefix, key)) = key.as_ref().split_once(delimiter) { + groups + .entry(prefix.to_owned()) + .or_default() + .push((key.to_owned(), value.as_ref().to_owned())); + } + groups + }) +} + +pub trait ResultLogger { + fn on_error_log(self, cb: F) -> Self; +} + +impl ResultLogger for std::result::Result +where + S: AsRef, + F: FnOnce(&E) -> S, +{ + fn on_error_log(self, cb: F) -> Self { + return self.map_err(|e| { + println!("{}", cb(&e).as_ref()); + e + }); + } +} + +impl ResultLogger for Option +where + S: AsRef, + F: FnOnce() -> S, +{ + fn on_error_log(self, cb: F) -> Self { + if self.is_none() { + println!("{}", cb().as_ref()) + } + self + } +}