mirror of
https://github.com/moghtech/komodo.git
synced 2026-11-01 00:12:55 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae5f36fe51 | ||
|
|
69ce1e4f36 | ||
|
|
6e444b9032 | ||
|
|
73eff72da4 | ||
|
|
698e3c214b | ||
|
|
9da77667dc | ||
|
|
c30793fb8f | ||
|
|
84fdaab24d | ||
|
|
cbd67bb609 | ||
|
|
00f58e9008 | ||
|
|
7738fab351 | ||
|
|
06e8f6589b | ||
|
|
57d9287724 | ||
|
|
2cc65595ee | ||
|
|
3dd2b97873 | ||
|
|
3c805ebbf7 | ||
|
|
a854160018 | ||
|
|
a99d9e5969 | ||
|
|
813b6c1182 | ||
|
|
2958f9589b | ||
|
|
69b4e26176 | ||
|
|
78b00f139d | ||
|
|
dc1e8de851 | ||
|
|
3187b335a3 | ||
|
|
54b5a2b420 | ||
|
|
14c6bd00a8 | ||
|
|
e9c3646450 | ||
|
|
4f20257479 | ||
|
|
65749991de | ||
|
|
237a1d802d | ||
|
|
e4336f19f3 | ||
|
|
c895e5e67f | ||
|
|
4e4e210736 | ||
|
|
09dfc8faa3 |
Generated
+69
-67
@@ -130,9 +130,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "axum-extra"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a320103719de37b7b4da4c8eb629d4573f6bcfd3dfe80d3208806895ccf81d"
|
||||
checksum = "51227033e4d3acad15c879092ac8a228532707b5db5ff2628f638334f63e1b7a"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"bytes",
|
||||
@@ -182,13 +182,14 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7e7c93a3fb23b2fdde989b2c9ec4dd153063ec81f408507f84c090cd91c6641"
|
||||
checksum = "9df288bec72232f78c1ec5fe4e8f1d108aa0265476e93097593c803c8c02062a"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"base64 0.21.0",
|
||||
"blowfish",
|
||||
"getrandom",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -300,9 +301,9 @@ checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.78"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
@@ -398,7 +399,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "core"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -414,8 +415,8 @@ dependencies = [
|
||||
"hex",
|
||||
"hmac",
|
||||
"jwt",
|
||||
"monitor_helpers 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_helpers 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mungos",
|
||||
"periphery_client",
|
||||
"serde",
|
||||
@@ -520,9 +521,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e"
|
||||
checksum = "322296e2f2e5af4270b54df9e85a02ff037e271af20ba3e7fe1575515dc840b8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@@ -532,9 +533,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200"
|
||||
checksum = "017a1385b05d631e7875b1f151c9f012d37b53491e2a87f65bff5c262b2111d8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -547,15 +548,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea"
|
||||
checksum = "c26bbb078acf09bc1ecda02d4223f03bdd28bd4874edcb0379138efc499ce971"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e"
|
||||
checksum = "357f40d1f06a24b60ae1fe122542c1fb05d28d32acb2aed064e84bc2ad1e252e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -650,10 +651,10 @@ checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||
|
||||
[[package]]
|
||||
name = "db_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mungos",
|
||||
]
|
||||
|
||||
@@ -740,9 +741,9 @@ checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
@@ -846,9 +847,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -861,9 +862,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -871,15 +872,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
|
||||
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -888,15 +889,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -905,21 +906,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1478,7 +1479,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_cli"
|
||||
version = "0.1.18"
|
||||
version = "0.1.21"
|
||||
dependencies = [
|
||||
"async_timing_util",
|
||||
"clap",
|
||||
@@ -1494,11 +1495,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"envy",
|
||||
"futures-util",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -1510,7 +1512,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_helpers"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -1518,7 +1520,7 @@ dependencies = [
|
||||
"bollard",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand",
|
||||
"run_command",
|
||||
"serde",
|
||||
@@ -1529,9 +1531,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_helpers"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b1ff7bac3697e1fc06044b669b029964b11182483ce3066e3996c5c0f66b57"
|
||||
checksum = "f2e58a1c8a1e92c967b5c4ba10511bc4bbf65c824230d234b78ad9d3ed992b75"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -1539,7 +1541,7 @@ dependencies = [
|
||||
"bollard",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand",
|
||||
"run_command",
|
||||
"serde",
|
||||
@@ -1550,7 +1552,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_periphery"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -1561,8 +1563,8 @@ dependencies = [
|
||||
"dotenv",
|
||||
"envy",
|
||||
"futures-util",
|
||||
"monitor_helpers 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_helpers 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"run_command",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -1576,7 +1578,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_types"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
@@ -1593,9 +1595,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_types"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31a6a8d9bf88d5f6f0ede8d35efa7b7018ebf4b62cf7b9d052c18eed185ca805"
|
||||
checksum = "bc2fc31230469c5a56e57eacf0200422897d4e0808ba176a767ceb59a872d543"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
@@ -1854,12 +1856,12 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "periphery_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
"monitor_helpers 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_helpers 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2199,9 +2201,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.8.1"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c4437699b6d34972de58652c68b98cb5b53a4199ab126db8e20ec8ded29a721"
|
||||
checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
@@ -2614,9 +2616,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.24.2"
|
||||
version = "1.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
|
||||
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@@ -2694,9 +2696,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217"
|
||||
checksum = "2f560bc7fb3eb31f5eee1340c68a2160cad39605b7b9c9ec32045ddbdee13b85"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@@ -2706,18 +2708,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.5.1"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
|
||||
checksum = "886f31a9b85b6182cabd4d8b07df3b451afcc216563748201490940d2a28ed36"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.18.0"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581"
|
||||
checksum = "233d8716cdc5d20ec88a18a839edaf545edc71efa4a5ff700ef4a102c26cd8fa"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"nom8",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_cli"
|
||||
version = "0.1.18"
|
||||
version = "0.1.21"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "monitor cli | tools to setup monitor system"
|
||||
@@ -18,7 +18,7 @@ async_timing_util = "0.1.14"
|
||||
rand = "0.8"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
toml = "0.6"
|
||||
toml = "0.7"
|
||||
run_command = "0.0.5"
|
||||
colored = "2"
|
||||
strum = "0.24"
|
||||
|
||||
+5
-2
@@ -81,6 +81,7 @@ pub fn gen_core_config(sub_matches: &ArgMatches) {
|
||||
},
|
||||
jwt_secret: generate_secret(40),
|
||||
github_webhook_secret: generate_secret(30),
|
||||
passkey: generate_secret(30),
|
||||
};
|
||||
|
||||
write_to_toml(&path, &config);
|
||||
@@ -176,7 +177,7 @@ pub fn start_mongo(sub_matches: &ArgMatches) {
|
||||
}
|
||||
}
|
||||
|
||||
let command = format!("docker stop {name} && docker container rm {name} && docker run -d --name {name} -p {port}:27017 --network {network} -v {mount}:/data/db{env} --restart {restart} mongo --quiet");
|
||||
let command = format!("docker stop {name} && docker container rm {name} && docker run -d --name {name} -p {port}:27017 --network {network} -v {mount}:/data/db{env} --restart {restart} --log-opt max-size=15m --log-opt max-file=3 mongo --quiet");
|
||||
|
||||
let output = run_command_pipe_to_terminal(&command);
|
||||
|
||||
@@ -318,9 +319,10 @@ pub fn gen_periphery_config(sub_matches: &ArgMatches) {
|
||||
|
||||
let config = PeripheryConfig {
|
||||
port,
|
||||
repo_dir,
|
||||
stats_polling_rate,
|
||||
allowed_ips,
|
||||
repo_dir,
|
||||
passkeys: vec![],
|
||||
secrets: Default::default(),
|
||||
github_accounts: Default::default(),
|
||||
docker_accounts: Default::default(),
|
||||
@@ -588,6 +590,7 @@ Description=agent to connect with monitor core
|
||||
|
||||
[Service]
|
||||
ExecStart={home}/.monitor/bin/periphery --config-path {config_path} --home-dir {home}
|
||||
Restart=on-failure
|
||||
TimeoutStartSec=0
|
||||
|
||||
[Install]
|
||||
|
||||
@@ -32,6 +32,9 @@ pub struct CoreConfig {
|
||||
// used to verify validity from github webhooks
|
||||
pub github_webhook_secret: String,
|
||||
|
||||
// sent in auth header with req to periphery
|
||||
pub passkey: String,
|
||||
|
||||
// integration with slack app
|
||||
pub slack_url: Option<String>,
|
||||
|
||||
@@ -104,6 +107,8 @@ pub struct PeripheryConfig {
|
||||
#[serde(default)]
|
||||
pub allowed_ips: Vec<IpAddr>,
|
||||
#[serde(default)]
|
||||
pub passkeys: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub secrets: SecretsMap,
|
||||
#[serde(default)]
|
||||
pub github_accounts: GithubAccounts,
|
||||
|
||||
@@ -22,6 +22,9 @@ slack_url = "your_slack_app_webhook_url"
|
||||
# token that has to be given to github during webhook config as the Secret
|
||||
github_webhook_secret = "your_random_webhook_secret"
|
||||
|
||||
# token used to authenticate core requests to periphery
|
||||
passkey = "your_random_passkey"
|
||||
|
||||
# can be 30-sec, 1-min, 2-min, 5-min
|
||||
monitoring_interval = "1-min"
|
||||
|
||||
@@ -29,10 +32,12 @@ monitoring_interval = "1-min"
|
||||
local_auth = true
|
||||
|
||||
[github_oauth]
|
||||
enabled = true
|
||||
id = "your_github_client_id"
|
||||
secret = "your_github_client_secret"
|
||||
|
||||
[google_oauth]
|
||||
enabled = true
|
||||
id = "your_google_client_id"
|
||||
secret = "your_google_client_secret"
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
port = 9001 # optional. 9001 is default
|
||||
port = 8000 # optional. 8000 is default
|
||||
repo_dir = "/repos" # optional. /repos is default. no reason to change if running the docker container, just mount your desired repo dir to /repos in the container
|
||||
stats_polling_rate = "5-sec" # optional. 5-sec is default. can use 1-sec, 5-sec, 10-sec, 30-sec, 1-min. controls granularity of system stats recorded
|
||||
allowed_ips = ["127.0.0.1"] # optional. default is empty, which will not block any request by ip.
|
||||
passkeys = ["abcdefghijk"] # optional. default is empty, which will not require any passkey to be passed by core.
|
||||
|
||||
[secrets] # optional. can inject these values into your deployments configuration.
|
||||
secret_variable = "secret_value"
|
||||
@@ -12,4 +13,4 @@ github_username2 = "github_token2"
|
||||
|
||||
[docker_accounts] # optional
|
||||
docker_username1 = "docker_token1"
|
||||
docker_username2 = "docker_token2"
|
||||
docker_username2 = "docker_token2"
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "core"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -8,16 +8,16 @@ edition = "2021"
|
||||
[dependencies]
|
||||
# helpers = { package = "monitor_helpers", path = "../lib/helpers" }
|
||||
# types = { package = "monitor_types", path = "../lib/types" }
|
||||
helpers = { package = "monitor_helpers", version = "0.1.11" }
|
||||
types = { package = "monitor_types", version = "0.1.11" }
|
||||
helpers = { package = "monitor_helpers", version = "0.1.15" }
|
||||
types = { package = "monitor_types", version = "0.1.15" }
|
||||
db = { package = "db_client", path = "../lib/db_client" }
|
||||
periphery = { package = "periphery_client", path = "../lib/periphery_client" }
|
||||
axum_oauth2 = { path = "../lib/axum_oauth2" }
|
||||
tokio = { version = "1.24", features = ["full"] }
|
||||
tokio = { version = "1.25", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||
tokio-util = "0.7"
|
||||
axum = { version = "0.6", features = ["ws", "json"] }
|
||||
axum-extra = { version = "0.4", features = ["spa"] }
|
||||
axum-extra = { version = "0.5", features = ["spa"] }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
tower-http = { version = "0.3", features = ["cors"] }
|
||||
slack = { package = "slack_client_rs", version = "0.0.8" }
|
||||
@@ -28,7 +28,7 @@ serde_json = "1.0"
|
||||
dotenv = "0.15"
|
||||
envy = "0.4"
|
||||
anyhow = "1.0"
|
||||
bcrypt = "0.13"
|
||||
bcrypt = "0.14"
|
||||
jwt = "0.16"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
|
||||
@@ -5,7 +5,7 @@ use mungos::{doc, to_bson};
|
||||
use types::{
|
||||
monitor_timestamp,
|
||||
traits::{Busy, Permissioned},
|
||||
Build, Log, Operation, PermissionLevel, Update, UpdateStatus, UpdateTarget,
|
||||
Build, Log, Operation, PermissionLevel, Update, UpdateStatus, UpdateTarget, Version,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -104,6 +104,7 @@ impl State {
|
||||
.await?;
|
||||
build.name = new_name;
|
||||
build.server_id = new_server_id;
|
||||
build.version = Version::default();
|
||||
let build = self.create_full_build(build, user).await?;
|
||||
Ok(build)
|
||||
}
|
||||
@@ -117,11 +118,10 @@ impl State {
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let server = self.db.get_server(&build.server_id).await?;
|
||||
let delete_repo_log = self
|
||||
.periphery
|
||||
.delete_repo(&server, &build.name)
|
||||
.await
|
||||
.context("failed at deleting repo")?;
|
||||
let delete_repo_log = match self.periphery.delete_repo(&server, &build.name).await {
|
||||
Ok(log) => log,
|
||||
Err(e) => Log::error("delete repo", format!("{e:#?}")),
|
||||
};
|
||||
self.db.builds.delete_one(build_id).await?;
|
||||
let update = Update {
|
||||
target: UpdateTarget::Build(build_id.to_string()),
|
||||
|
||||
@@ -119,10 +119,14 @@ impl State {
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let server = self.db.get_server(&deployment.server_id).await?;
|
||||
let log = self
|
||||
let log = match self
|
||||
.periphery
|
||||
.container_remove(&server, &deployment.name)
|
||||
.await?;
|
||||
.await
|
||||
{
|
||||
Ok(log) => log,
|
||||
Err(e) => Log::error("destroy container", format!("{e:#?}")),
|
||||
};
|
||||
self.db
|
||||
.deployments
|
||||
.delete_one(deployment_id)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use diff::Diff;
|
||||
use futures_util::future::join_all;
|
||||
use helpers::to_monitor_name;
|
||||
use mungos::doc;
|
||||
use types::{
|
||||
monitor_timestamp,
|
||||
traits::{Busy, Permissioned},
|
||||
@@ -102,21 +104,59 @@ impl State {
|
||||
.get_server_check_permissions(server_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
self.db.servers.delete_one(&server_id).await?;
|
||||
let update = Update {
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Server(server_id.to_string()),
|
||||
operation: Operation::DeleteServer,
|
||||
start_ts,
|
||||
end_ts: Some(monitor_timestamp()),
|
||||
operator: user.id.clone(),
|
||||
logs: vec![Log::simple(
|
||||
"delete server",
|
||||
format!("deleted server {}", server.name),
|
||||
)],
|
||||
success: true,
|
||||
status: UpdateStatus::InProgress,
|
||||
..Default::default()
|
||||
};
|
||||
self.add_update(update).await?;
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
let res = {
|
||||
let delete_deployments = self
|
||||
.db
|
||||
.deployments
|
||||
.get_some(doc! { "server_id": server_id }, None)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|d| async move { self.delete_deployment(&d.id, user).await });
|
||||
let delete_builds = self
|
||||
.db
|
||||
.builds
|
||||
.get_some(doc! { "server_id": server_id }, None)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|d| async move { self.delete_deployment(&d.id, user).await });
|
||||
let update_groups = self
|
||||
.db
|
||||
.groups
|
||||
.update_many(doc! {}, doc! { "$pull": { "servers": server_id } });
|
||||
let (dep_res, build_res, group_res) = tokio::join!(
|
||||
join_all(delete_deployments),
|
||||
join_all(delete_builds),
|
||||
update_groups
|
||||
);
|
||||
dep_res.into_iter().collect::<anyhow::Result<Vec<_>>>()?;
|
||||
build_res.into_iter().collect::<anyhow::Result<Vec<_>>>()?;
|
||||
group_res?;
|
||||
self.db.servers.delete_one(&server_id).await?;
|
||||
anyhow::Ok(())
|
||||
};
|
||||
|
||||
let log = match res {
|
||||
Ok(_) => Log::simple("delete server", format!("deleted server {}", server.name)),
|
||||
Err(e) => Log::error("delete server", format!("failed to delete server\n{e:#?}")),
|
||||
};
|
||||
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.success = log.success;
|
||||
update.logs.push(log);
|
||||
|
||||
self.update_update(update).await?;
|
||||
Ok(server)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -32,9 +32,9 @@ impl State {
|
||||
let state = State {
|
||||
db: DbClient::new(config.mongo.clone()).await,
|
||||
slack: config.slack_url.clone().map(|url| slack::Client::new(&url)),
|
||||
periphery: PeripheryClient::new(config.passkey.clone()),
|
||||
config,
|
||||
update: UpdateWsChannel::new(),
|
||||
periphery: PeripheryClient::default(),
|
||||
build_action_states: Default::default(),
|
||||
deployment_action_states: Default::default(),
|
||||
server_action_states: Default::default(),
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# building images
|
||||
|
||||
Monitor builds docker images by cloning the source repository from Github and running ```docker build``` on the configured Dockerfile, which should be present in the source repository.
|
||||
|
||||
## repo configuration
|
||||
Setting related to the github repo are under the *repo* tab on respective build's page.
|
||||
|
||||
To specify the github repo to build, just give it the name of the repo and the branch under *github config*. The name is given like ```mbecker20/monitor```, it includes the username / organization that owns the repo.
|
||||
|
||||
Many repos are private, in this case a Github access token is required in the periphery.config.toml of the building server. these are specified in the config like ```username = "access_token"```. An account which has access to the repo and is available on the periphery server can be selected to use via the *github account* dropdown menu.
|
||||
|
||||
Sometimes a command needs to be run when the repo is cloned, you can configure this in the *on clone* section.
|
||||
|
||||
There are two fields to pass for *on clone*. the first is *path*, which changes to working directory. To run the command in the root of the repo, just pass ".". The second field is *command*, this is the shell command to be executed after the repo is cloned.
|
||||
|
||||
For example, say your repo had a folder in it called "scripts" with a shell script "on-clone.sh". You would give *path* as "scripts" and command as "sh on-clone.sh". Or you could make *path* just "." and then command would be "sh scripts/on-clone.sh". Either way works fine.
|
||||
|
||||
## build configuration
|
||||
|
||||
|
||||
## versioning
|
||||
|
||||
Monitor uses a major.minor.patch versioning scheme. Every build will auto increment the patch number, and push the image to docker hub with the version tag as well as the "latest" tag.
|
||||
|
||||
|
||||
[next: deploying](https://github.com/mbecker20/monitor/blob/main/docs/deployments.md)
|
||||
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
## deploying applications
|
||||
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
|
||||
# introduction
|
||||
|
||||
If you have many servers running many applications, it can be a challenge to keep things organized and easily accessible. Without structure, things can become messy quickly, which means operational issues are more likely to arise and they can take longer to resolve. Ultimately these issues hinder productivity and waste valuable time. Monitor is a web app to provide this structure for how applications are built, deployed, and managed across many servers.
|
||||
|
||||
## docker
|
||||
|
||||
Monitor is opinionated by design, and [docker](https://docs.docker.com/) is the tool of choice. Docker provides the ability to package applications and their runtime dependencies into a standalone bundle, called an *image*. This makes them easy to "ship" to any server and run without the hassle of setting up the runtime environment. Docker uses the image as a sort of template to create *containers*. Containers are kind of like virtual machines but with different performance characteristics, namely that processes contained still run natively on the system kernel. The file system is seperate though, and like virtual machines, they can be created, started, stopped, and destroyed.
|
||||
|
||||
## monitor
|
||||
|
||||
Monitor is a solution for handling for the following:
|
||||
|
||||
1. Build application source into auto-versioned images.
|
||||
2. Create, start, stop, and restart Docker containers, and view their status and logs.
|
||||
3. Keep a record of all the actions that are performed and by whom.
|
||||
4. View realtime and historical system resource usage.
|
||||
5. Alerting for server health, like high cpu, memory, disk, etc.
|
||||
|
||||
## architecture and components
|
||||
|
||||
Monitor is composed of a single core and any amount of connected servers running the periphery application.
|
||||
|
||||
### monitor core
|
||||
The core is a web server that hosts the core API and serves the frontend to be accessed in a web browser. All user interaction with the connected servers flow through the core. It is the stateful part of the system, with the application state stored on an instance of MongoDB.
|
||||
|
||||
### monitor periphery
|
||||
The periphery is a stateless web server that exposes API called by the core. The core calls this API to get system usage and container status / logs, clone git repos, and perform docker actions. It is only intended to be reached from the core, and has an address whitelist to limit the IPs allowed to call this API.
|
||||
|
||||
### monitor cli
|
||||
This is a simple standalone cli that helps perform some actions required to setup monitor core and periphery, like generating config files.
|
||||
|
||||
## core API
|
||||
|
||||
Monitor exposes powerful functionality over the core's REST API, enabling infrastructure engineers to manage deployments programmatically in addition to with the GUI. There is a [rust crate](https://crates.io/crates/monitor_client) to simplify programmatic interaction with the API, but in general this can be accomplished using any programming language that can make REST requests.
|
||||
|
||||
## permissioning
|
||||
|
||||
Monitor is a system designed to be used by many users, whether they are developers, operations personnel, or administrators. The ability to affect an applications state is very powerful, so monitor has a granular permissioning system to only provide this functionality to the intended users. The permissioning system is explained in detail in the [permissioning](https://github.com/mbecker20/monitor/blob/main/docs/permissions.md) section.
|
||||
|
||||
User sign-on is possible using username / password, or with Oauth (Github and Google). Allowed login methods can be configured from the [core config](https://github.com/mbecker20/monitor/blob/main/config_example/core.config.example.toml).
|
||||
|
||||
[next: connecting servers](https://github.com/mbecker20/monitor/blob/main/docs/servers.md)
|
||||
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
@@ -0,0 +1 @@
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
@@ -0,0 +1,30 @@
|
||||
# connecting servers
|
||||
|
||||
Integrating a device into the monitor system has 2 steps:
|
||||
|
||||
1. Setup and start the periphery agent on the server
|
||||
2. Adding the server to monitor via the core API
|
||||
|
||||
## setup monitor periphery
|
||||
|
||||
The easiest way to do this is to follow the [monitor guide](https://github.com/mbecker20/monitor-guide). This is a repo containing directions and scripts enabling command line installation via ssh or remotely.
|
||||
|
||||
### manual install steps
|
||||
|
||||
1. Download the periphery binary from the latest [release](https://github.com/mbecker20/monitor/releases) or install it using [cargo](https://crates.io/crates/monitor_periphery). If the monitor cli.
|
||||
2. Create and edit ~/.monitor/periphery.config.toml, following the [config example](https://github.com/mbecker20/monitor/blob/main/config_example/periphery.config.example.toml). The file can be anywhere, it can be passed to periphery via the --config-path flag or with the CONFIG_PATH environment variable. The monitor cli can also be used: ```monitor periphery gen-config```
|
||||
3. Ensure that inbound connectivity is allowed on the port specified in periphery.config.toml (default 8000).
|
||||
4. Install docker. Make sure whatever user periphery is run as has access to the docker group without sudo.
|
||||
5. Start the periphery binary with your preferred process manager, like systemd. The config read from the file is printed on startup, ensure that it is as expected.
|
||||
|
||||
## adding the server to monitor
|
||||
|
||||
The easiest way to add the server is with the GUI. On the home page, click the + button to the right of the server search bar, configure the name and address of the server. The address is the full http/s url to the periphery server, eg http://12.34.56.78:8000.
|
||||
|
||||
Once it is added, you can use access the GUI to modify some config, like the alerting thresholds for cpu, memory and disk usage. A server can also be temporarily disabled, this will prevent alerting if it goes offline.
|
||||
|
||||
Since no state is stored on the periphery servers, you can easily redirect all builds / deployments to be hosted on a different server. Just update the address to point to the new server.
|
||||
|
||||
[next: building](https://github.com/mbecker20/monitor/blob/main/docs/builds.md)
|
||||
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
@@ -0,0 +1,5 @@
|
||||
# setting up monitor core
|
||||
|
||||
|
||||
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Rounded_Rectangle_2_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
|
||||
<g id="Rounded_Rectangle_2">
|
||||
<g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#fceade" d="M15,4H1C0.45,4,0,4.45,0,5v14c0,0.55,0.45,1,1,1h14c0.55,0,1-0.45,1-1V5
|
||||
C16,4.45,15.55,4,15,4z M14,18H2V6h12V18z M19,0H5C4.45,0,4,0.45,4,1v2h2V2h12v12h-1v2h2c0.55,0,1-0.45,1-1V1
|
||||
C20,0.45,19.55,0,19,0z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 679 B |
@@ -0,0 +1,105 @@
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import { Component, createSignal } from "solid-js";
|
||||
import { client, pushNotification } from "..";
|
||||
import { useAppState } from "../state/StateProvider";
|
||||
import { Build, Deployment } from "../types";
|
||||
import { getId } from "../util/helpers";
|
||||
import { useToggle } from "../util/hooks";
|
||||
import ConfirmButton from "./shared/ConfirmButton";
|
||||
import Icon from "./shared/Icon";
|
||||
import Input from "./shared/Input";
|
||||
import Flex from "./shared/layout/Flex";
|
||||
import Grid from "./shared/layout/Grid";
|
||||
import CenterMenu from "./shared/menu/CenterMenu";
|
||||
import Selector from "./shared/menu/Selector";
|
||||
|
||||
const CopyMenu: Component<{
|
||||
type: "deployment" | "build";
|
||||
id: string;
|
||||
}> = (p) => {
|
||||
const navigate = useNavigate();
|
||||
const [show, toggleShow] = useToggle();
|
||||
const [newName, setNewName] = createSignal("");
|
||||
const { builds, deployments, servers } = useAppState();
|
||||
const curr_server = () => {
|
||||
if (p.type === "build") {
|
||||
return builds.get(p.id)!.server_id;
|
||||
} else {
|
||||
return deployments.get(p.id)!.deployment.server_id;
|
||||
}
|
||||
}
|
||||
const [selectedId, setSelected] = createSignal(curr_server());
|
||||
const name = () => {
|
||||
if (p.type === "build") {
|
||||
return builds.get(p.id)?.name;
|
||||
} else if (p.type === "deployment") {
|
||||
return deployments.get(p.id)?.deployment.name;
|
||||
}
|
||||
};
|
||||
const copy = () => {
|
||||
if (newName().length !== 0) {
|
||||
let promise: Promise<Build | Deployment>;
|
||||
if (p.type === "build") {
|
||||
promise = client.copy_build(p.id, {
|
||||
name: newName(),
|
||||
server_id: selectedId(),
|
||||
});
|
||||
} else {
|
||||
promise = client.copy_deployment(p.id, {
|
||||
name: newName(),
|
||||
server_id: selectedId(),
|
||||
});
|
||||
}
|
||||
toggleShow();
|
||||
promise.then((val) => {
|
||||
navigate(`/${p.type}/${getId(val)}`);
|
||||
});
|
||||
} else {
|
||||
pushNotification("bad", "copy name cannot be empty");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<CenterMenu
|
||||
show={show}
|
||||
toggleShow={toggleShow}
|
||||
title={`copy ${p.type} | ${name()}`}
|
||||
target={<Icon type="duplicate" />}
|
||||
targetClass="blue"
|
||||
content={() => (
|
||||
<Grid placeItems="center">
|
||||
<Flex alignItems="center">
|
||||
<Input
|
||||
placeholder="copy name"
|
||||
class="card dark"
|
||||
style={{ padding: "0.5rem" }}
|
||||
value={newName()}
|
||||
onEdit={setNewName}
|
||||
/>
|
||||
<Selector
|
||||
label="target: "
|
||||
selected={selectedId()}
|
||||
items={servers.ids()!}
|
||||
onSelect={setSelected}
|
||||
itemMap={(id) => servers.get(id)!.server.name}
|
||||
targetClass="blue"
|
||||
targetStyle={{ display: "flex", gap: "0.5rem" }}
|
||||
searchStyle={{ width: "100%" }}
|
||||
position="bottom right"
|
||||
useSearch
|
||||
/>
|
||||
</Flex>
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
style={{ width: "100%" }}
|
||||
onConfirm={copy}
|
||||
>
|
||||
copy {p.type}
|
||||
</ConfirmButton>
|
||||
</Grid>
|
||||
)}
|
||||
position="center"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CopyMenu;
|
||||
@@ -10,20 +10,21 @@ import { useActionStates } from "./ActionStateProvider";
|
||||
import { client } from "../..";
|
||||
import { combineClasses, getId } from "../../util/helpers";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { PermissionLevel } from "../../types";
|
||||
import { PermissionLevel, ServerStatus } from "../../types";
|
||||
|
||||
const Actions: Component<{}> = (p) => {
|
||||
const { user } = useUser();
|
||||
const params = useParams() as { id: string };
|
||||
const { builds } = useAppState();
|
||||
const { builds, servers } = useAppState();
|
||||
const build = () => builds.get(params.id)!;
|
||||
const server = () => build() && servers.get(build()!.server_id);
|
||||
const actions = useActionStates();
|
||||
const userCanExecute = () =>
|
||||
user().admin ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Execute ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Update;
|
||||
return (
|
||||
<Show when={userCanExecute()}>
|
||||
<Show when={userCanExecute() && server()?.status === ServerStatus.Ok}>
|
||||
<Grid class={combineClasses("card shadow")} gridTemplateRows="auto 1fr">
|
||||
<h1>actions</h1>
|
||||
<Grid style={{ height: "fit-content" }}>
|
||||
|
||||
@@ -13,6 +13,8 @@ import { A, useParams } from "@solidjs/router";
|
||||
import { PermissionLevel } from "../../types";
|
||||
import { client } from "../..";
|
||||
import HoverMenu from "../shared/menu/HoverMenu";
|
||||
import CopyMenu from "../CopyMenu";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
|
||||
const Header: Component<{}> = (p) => {
|
||||
const { builds, servers } = useAppState();
|
||||
@@ -43,21 +45,26 @@ const Header: Component<{}> = (p) => {
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>{build().name}</h1>
|
||||
<Show when={userCanUpdate()}>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmButton
|
||||
onConfirm={() => {
|
||||
client.delete_build(params.id);
|
||||
}}
|
||||
class="red"
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmButton>
|
||||
}
|
||||
content="delete build"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
<Flex alignItems="center">
|
||||
<CopyMenu type="build" id={params.id} />
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmMenuButton
|
||||
onConfirm={() => {
|
||||
client.delete_build(params.id);
|
||||
}}
|
||||
class="red"
|
||||
title={`delete build | ${build().name}`}
|
||||
match={build().name}
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmMenuButton>
|
||||
}
|
||||
content="delete build"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
</Flex>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
|
||||
@@ -10,7 +10,7 @@ import { createStore, SetStoreFunction } from "solid-js/store";
|
||||
import { client } from "../../..";
|
||||
import { useAppState } from "../../../state/StateProvider";
|
||||
import { useUser } from "../../../state/UserProvider";
|
||||
import { Build, Operation, PermissionLevel } from "../../../types";
|
||||
import { Build, Operation, PermissionLevel, ServerWithStatus } from "../../../types";
|
||||
import { getId } from "../../../util/helpers";
|
||||
|
||||
type ConfigBuild = Build & {
|
||||
@@ -22,6 +22,7 @@ type ConfigBuild = Build & {
|
||||
type State = {
|
||||
build: ConfigBuild;
|
||||
setBuild: SetStoreFunction<ConfigBuild>;
|
||||
server: () => ServerWithStatus | undefined
|
||||
reset: () => void;
|
||||
save: () => void;
|
||||
userCanUpdate: () => boolean;
|
||||
@@ -30,7 +31,7 @@ type State = {
|
||||
const context = createContext<State>();
|
||||
|
||||
export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
const { ws, builds } = useAppState();
|
||||
const { ws, builds, servers } = useAppState();
|
||||
const params = useParams();
|
||||
const { user } = useUser();
|
||||
const [build, set] = createStore({
|
||||
@@ -44,6 +45,7 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
set(...args);
|
||||
set("updated", true);
|
||||
};
|
||||
const server = () => servers.get(builds.get(params.id)!.server_id);
|
||||
|
||||
const load = () => {
|
||||
// console.log("load build");
|
||||
@@ -105,6 +107,7 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
const state = {
|
||||
build,
|
||||
setBuild,
|
||||
server,
|
||||
reset: load,
|
||||
save,
|
||||
userCanUpdate,
|
||||
|
||||
@@ -74,7 +74,7 @@ const EditBuildArgs: Component<{}> = (p) => {
|
||||
value={buildArgs()}
|
||||
onEdit={setBuildArgs}
|
||||
style={{
|
||||
width: "700px",
|
||||
width: "1000px",
|
||||
"max-width": "90vw",
|
||||
height: "80vh",
|
||||
padding: "1rem",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import { client } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { ServerStatus } from "../../../../types";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import Input from "../../../shared/Input";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
@@ -9,10 +10,14 @@ import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const Docker: Component<{}> = (p) => {
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
const { build, setBuild, server, userCanUpdate } = useConfig();
|
||||
const [dockerAccounts, setDockerAccounts] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
client.get_server_docker_accounts(build.server_id).then(setDockerAccounts);
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_docker_accounts(build.server_id)
|
||||
.then(setDockerAccounts);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import { Component, createEffect, createSignal } from "solid-js";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
@@ -6,12 +6,15 @@ import Input from "../../../shared/Input";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import { client } from "../../../..";
|
||||
import { ServerStatus } from "../../../../types";
|
||||
|
||||
const Git: Component<{}> = (p) => {
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
const { build, setBuild, server, userCanUpdate } = useConfig();
|
||||
const [githubAccounts, setGithubAccounts] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
client.get_server_github_accounts(build.server_id).then(setGithubAccounts)
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client.get_server_github_accounts(build.server_id).then(setGithubAccounts);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, Match, Show, Switch } from "solid-js";
|
||||
import { client, pushNotification } from "../..";
|
||||
import { client } from "../..";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
import ConfirmButton from "../shared/ConfirmButton";
|
||||
@@ -9,24 +9,33 @@ import Grid from "../shared/layout/Grid";
|
||||
import Loading from "../shared/loading/Loading";
|
||||
import HoverMenu from "../shared/menu/HoverMenu";
|
||||
import { useActionStates } from "./ActionStateProvider";
|
||||
import { combineClasses, getId } from "../../util/helpers";
|
||||
import { combineClasses } from "../../util/helpers";
|
||||
import { A, useParams } from "@solidjs/router";
|
||||
import { DockerContainerState, PermissionLevel } from "../../types";
|
||||
import {
|
||||
DockerContainerState,
|
||||
PermissionLevel,
|
||||
ServerStatus,
|
||||
} from "../../types";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
|
||||
const Actions: Component<{}> = (p) => {
|
||||
const { deployments, builds, getPermissionOnDeployment } = useAppState();
|
||||
const { deployments, builds, servers, getPermissionOnDeployment } =
|
||||
useAppState();
|
||||
const params = useParams();
|
||||
const { user, user_id } = useUser();
|
||||
const deployment = () => deployments.get(params.id)!;
|
||||
const server = () =>
|
||||
deployment() && servers.get(deployment()!.deployment.server_id);
|
||||
const show = () => {
|
||||
const permissions = getPermissionOnDeployment(params.id);
|
||||
return (
|
||||
server()?.status === ServerStatus.Ok &&
|
||||
deployment() &&
|
||||
(user().admin ||
|
||||
permissions === PermissionLevel.Execute ||
|
||||
permissions === PermissionLevel.Update)
|
||||
);
|
||||
};
|
||||
const deployment = () => deployments.get(params.id)!;
|
||||
const showBuild = () => {
|
||||
const build = deployment().deployment.build_id
|
||||
? builds.get(deployment().deployment.build_id!)
|
||||
@@ -117,13 +126,17 @@ const Actions: Component<{}> = (p) => {
|
||||
};
|
||||
|
||||
const Build: Component = () => {
|
||||
const { ws, deployments } = useAppState();
|
||||
const { deployments } = useAppState();
|
||||
const params = useParams();
|
||||
const actions = useActionStates();
|
||||
const buildID = () => deployments.get(params.id)!.deployment.build_id!;
|
||||
return (
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
<A href={`/build/${buildID()}`} class="pointer">
|
||||
<A
|
||||
href={`/build/${buildID()}`}
|
||||
class="pointer"
|
||||
style={{ padding: 0, "font-size": "16px" }}
|
||||
>
|
||||
build
|
||||
</A>
|
||||
<Show
|
||||
@@ -152,6 +165,8 @@ const Deploy: Component<{ redeploy?: boolean }> = (p) => {
|
||||
const params = useParams();
|
||||
// const deployment = () => deployments.get(params.id)!;
|
||||
const actions = useActionStates();
|
||||
const { deployments } = useAppState();
|
||||
const name = () => deployments.get(params.id)?.deployment.name;
|
||||
return (
|
||||
<Show
|
||||
when={!actions.deploying}
|
||||
@@ -163,14 +178,30 @@ const Deploy: Component<{ redeploy?: boolean }> = (p) => {
|
||||
>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.deploy_container(params.id);
|
||||
}}
|
||||
<Show
|
||||
when={p.redeploy}
|
||||
fallback={
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.deploy_container(params.id);
|
||||
}}
|
||||
>
|
||||
<Icon type={"play"} />
|
||||
</ConfirmButton>
|
||||
}
|
||||
>
|
||||
<Icon type={p.redeploy ? "reset" : "play"} />
|
||||
</ConfirmButton>
|
||||
<ConfirmMenuButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.deploy_container(params.id);
|
||||
}}
|
||||
title={`redeploy container | ${name()}`}
|
||||
match={name()!}
|
||||
>
|
||||
<Icon type={"reset"} />
|
||||
</ConfirmMenuButton>
|
||||
</Show>
|
||||
}
|
||||
content={p.redeploy ? "redeploy container" : "deploy container"}
|
||||
position="bottom center"
|
||||
@@ -183,6 +214,8 @@ const Deploy: Component<{ redeploy?: boolean }> = (p) => {
|
||||
const RemoveContainer = () => {
|
||||
const params = useParams();
|
||||
const actions = useActionStates();
|
||||
const { deployments } = useAppState();
|
||||
const name = () => deployments.get(params.id)?.deployment.name;
|
||||
return (
|
||||
<Show
|
||||
when={!actions.removing}
|
||||
@@ -194,14 +227,16 @@ const RemoveContainer = () => {
|
||||
>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmButton
|
||||
<ConfirmMenuButton
|
||||
class="red"
|
||||
onConfirm={() => {
|
||||
client.remove_container(params.id);
|
||||
}}
|
||||
title={`destroy container | ${name()}`}
|
||||
match={name()!}
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmButton>
|
||||
</ConfirmMenuButton>
|
||||
}
|
||||
content="delete container"
|
||||
position="bottom center"
|
||||
@@ -245,6 +280,8 @@ const Start = () => {
|
||||
const Stop = () => {
|
||||
const params = useParams();
|
||||
const actions = useActionStates();
|
||||
const { deployments } = useAppState();
|
||||
const name = () => deployments.get(params.id)?.deployment.name;
|
||||
return (
|
||||
<Show
|
||||
when={!actions.stopping}
|
||||
@@ -256,14 +293,16 @@ const Stop = () => {
|
||||
>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmButton
|
||||
<ConfirmMenuButton
|
||||
class="orange"
|
||||
onConfirm={() => {
|
||||
client.stop_container(params.id);
|
||||
}}
|
||||
title={`stop container | ${name()}`}
|
||||
match={name()!}
|
||||
>
|
||||
<Icon type="pause" />
|
||||
</ConfirmButton>
|
||||
</ConfirmMenuButton>
|
||||
}
|
||||
content="stop container"
|
||||
position="bottom center"
|
||||
|
||||
@@ -1,27 +1,35 @@
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { Component, Show } from "solid-js";
|
||||
import { MAX_PAGE_WIDTH } from "../..";
|
||||
import { Component, onCleanup, Show } from "solid-js";
|
||||
import { client } from "../..";
|
||||
import { useAppDimensions } from "../../state/DimensionProvider";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
import { PermissionLevel } from "../../types";
|
||||
import { combineClasses, getId } from "../../util/helpers";
|
||||
import { ServerStatus } from "../../types";
|
||||
import NotFound from "../NotFound";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import Actions from "./Actions";
|
||||
import { ActionStateProvider } from "./ActionStateProvider";
|
||||
import Header from "./Header";
|
||||
import { ConfigProvider } from "./tabs/config/Provider";
|
||||
import DeploymentTabs from "./tabs/Tabs";
|
||||
import Updates from "./Updates";
|
||||
|
||||
const Deployment2: Component<{}> = (p) => {
|
||||
const POLLING_RATE = 10000;
|
||||
// let interval = -1;
|
||||
|
||||
const Deployment: Component<{}> = (p) => {
|
||||
const { servers, deployments } = useAppState();
|
||||
const { isSemiMobile } = useAppDimensions();
|
||||
const params = useParams();
|
||||
const deployment = () => deployments.get(params.id);
|
||||
const server = () =>
|
||||
deployment() && servers.get(deployment()!.deployment.server_id);
|
||||
// clearInterval(interval);
|
||||
// interval = setInterval(async () => {
|
||||
// if (server()?.status === ServerStatus.Ok) {
|
||||
// const deployment = await client.get_deployment(params.id);
|
||||
// deployments.update(deployment);
|
||||
// }
|
||||
// }, POLLING_RATE);
|
||||
// onCleanup(() => clearInterval(interval));
|
||||
return (
|
||||
<Show
|
||||
when={deployment() && server()}
|
||||
@@ -53,46 +61,4 @@ const Deployment2: Component<{}> = (p) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Deployment: Component<{}> = (p) => {
|
||||
const { servers, deployments } = useAppState();
|
||||
const params = useParams();
|
||||
const deployment = () => deployments.get(params.id);
|
||||
const server = () => deployment() && servers.get(deployment()!.deployment.server_id);
|
||||
const { isSemiMobile } = useAppDimensions();
|
||||
const { user } = useUser();
|
||||
const userCanUpdate = () => user().admin || deployment()?.deployment.permissions![getId(user())] === PermissionLevel.Update;
|
||||
return (
|
||||
<Show
|
||||
when={deployment() && server()}
|
||||
fallback={<NotFound type="deployment" />}
|
||||
>
|
||||
<ActionStateProvider>
|
||||
<Grid class={combineClasses("content")}>
|
||||
{/* left / actions */}
|
||||
<Grid class="left-content">
|
||||
<Header />
|
||||
<Actions />
|
||||
<Show when={!isSemiMobile() && userCanUpdate()}>
|
||||
<Updates />
|
||||
</Show>
|
||||
</Grid>
|
||||
{/* right / tabs */}
|
||||
<Show
|
||||
when={userCanUpdate()}
|
||||
fallback={
|
||||
<h2 class={combineClasses("card tabs shadow")}>
|
||||
you do not have permission to view this deployment
|
||||
</h2>
|
||||
}
|
||||
>
|
||||
<ConfigProvider>
|
||||
<DeploymentTabs />
|
||||
</ConfigProvider>
|
||||
</Show>
|
||||
</Grid>
|
||||
</ActionStateProvider>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
export default Deployment2;
|
||||
export default Deployment;
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
deploymentHeaderStateClass,
|
||||
getId,
|
||||
} from "../../util/helpers";
|
||||
import ConfirmButton from "../shared/ConfirmButton";
|
||||
import Icon from "../shared/Icon";
|
||||
import Flex from "../shared/layout/Flex";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
@@ -17,6 +16,8 @@ import Updates from "./Updates";
|
||||
import { DockerContainerState, PermissionLevel } from "../../types";
|
||||
import { A, useParams } from "@solidjs/router";
|
||||
import { client } from "../..";
|
||||
import CopyMenu from "../CopyMenu";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
|
||||
const Header: Component<{}> = (p) => {
|
||||
const { deployments, servers } = useAppState();
|
||||
@@ -53,21 +54,35 @@ const Header: Component<{}> = (p) => {
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>{deployment()!.deployment.name}</h1>
|
||||
<Show when={userCanUpdate()}>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmButton
|
||||
onConfirm={() => {
|
||||
client.delete_deployment(params.id);
|
||||
}}
|
||||
class="red"
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmButton>
|
||||
}
|
||||
content="delete deployment"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
<Flex alignItems="center">
|
||||
<CopyMenu type="deployment" id={params.id} />
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmMenuButton
|
||||
onConfirm={() => {
|
||||
client.delete_deployment(params.id);
|
||||
}}
|
||||
class="red"
|
||||
title={`delete deployment | ${
|
||||
deployment().deployment.name
|
||||
}`}
|
||||
match={deployment().deployment.name}
|
||||
info={
|
||||
<Show when={deployment().container}>
|
||||
<div style={{ opacity: 0.7 }}>
|
||||
warning! this will destroy this deployments container
|
||||
</div>
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmMenuButton>
|
||||
}
|
||||
content="delete deployment"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
</Flex>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
DockerContainerState,
|
||||
Log as LogType,
|
||||
Operation,
|
||||
ServerStatus,
|
||||
} from "../../../types";
|
||||
import { client } from "../../..";
|
||||
import SimpleTabs from "../../shared/tabs/SimpleTabs";
|
||||
@@ -26,18 +27,23 @@ import { useUser } from "../../../state/UserProvider";
|
||||
|
||||
const DeploymentTabs: Component<{}> = () => {
|
||||
const { user } = useUser();
|
||||
const { deployments, ws } = useAppState();
|
||||
const { deployments, ws, servers } = useAppState();
|
||||
const params = useParams();
|
||||
const deployment = () => deployments.get(params.id);
|
||||
const server = () =>
|
||||
deployment() && servers.get(deployment()!.deployment.server_id);
|
||||
const [logTail, setLogTail] = createSignal(50);
|
||||
const [log, setLog] = createSignal<LogType>();
|
||||
const status = () =>
|
||||
deployment()!.state === DockerContainerState.NotDeployed
|
||||
? "not deployed"
|
||||
: deployment()!.container?.state;
|
||||
// const status = () =>
|
||||
// deployment()!.state === DockerContainerState.NotDeployed
|
||||
// ? "not deployed"
|
||||
// : deployment()!.container?.state;
|
||||
const log_available = () =>
|
||||
server()?.status === ServerStatus.Ok &&
|
||||
deployment()?.state !== DockerContainerState.NotDeployed;
|
||||
const loadLog = async () => {
|
||||
console.log("load log");
|
||||
if (deployment()?.state !== DockerContainerState.NotDeployed) {
|
||||
if (log_available()) {
|
||||
console.log("load log");
|
||||
const log = await client.get_deployment_container_log(
|
||||
params.id,
|
||||
logTail()
|
||||
@@ -78,7 +84,7 @@ const DeploymentTabs: Component<{}> = () => {
|
||||
title: "config",
|
||||
element: () => <Config />,
|
||||
},
|
||||
status() !== "not deployed" && [
|
||||
log_available() && [
|
||||
{
|
||||
title: "log",
|
||||
element: () => (
|
||||
@@ -90,7 +96,7 @@ const DeploymentTabs: Component<{}> = () => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
status() !== "not deployed" && {
|
||||
{
|
||||
title: "error log",
|
||||
titleElement: () => (
|
||||
<Flex gap="0.5rem" alignItems="center">
|
||||
|
||||
@@ -12,7 +12,7 @@ import { createStore, SetStoreFunction } from "solid-js/store";
|
||||
import { client, pushNotification } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { useUser } from "../../../../state/UserProvider";
|
||||
import { Deployment, Operation, PermissionLevel } from "../../../../types";
|
||||
import { Deployment, Operation, PermissionLevel, ServerStatus, ServerWithStatus } from "../../../../types";
|
||||
import { getId } from "../../../../util/helpers";
|
||||
|
||||
type ConfigDeployment = Deployment & {
|
||||
@@ -25,6 +25,7 @@ type State = {
|
||||
editing: Accessor<boolean>;
|
||||
deployment: ConfigDeployment;
|
||||
setDeployment: SetStoreFunction<ConfigDeployment>;
|
||||
server: () => ServerWithStatus | undefined;
|
||||
reset: () => void;
|
||||
save: () => void;
|
||||
networks: Accessor<any[]>;
|
||||
@@ -34,7 +35,7 @@ type State = {
|
||||
const context = createContext<State>();
|
||||
|
||||
export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
const { ws, deployments } = useAppState();
|
||||
const { ws, deployments, servers } = useAppState();
|
||||
const params = useParams();
|
||||
const { user } = useUser();
|
||||
const [editing] = createSignal(false);
|
||||
@@ -87,11 +88,13 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
createEffect(load);
|
||||
|
||||
const [networks, setNetworks] = createSignal<any[]>([]);
|
||||
const server = () => servers.get(deployments.get(params.id)!.deployment.server_id);
|
||||
createEffect(() => {
|
||||
console.log("load networks");
|
||||
client
|
||||
.get_docker_networks(deployments.get(params.id)!.deployment.server_id)
|
||||
.then(setNetworks);
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_docker_networks(deployments.get(params.id)!.deployment.server_id)
|
||||
.then(setNetworks);
|
||||
}
|
||||
});
|
||||
|
||||
const save = () => {
|
||||
@@ -141,6 +144,7 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
editing,
|
||||
deployment,
|
||||
setDeployment,
|
||||
server,
|
||||
reset: load,
|
||||
save,
|
||||
networks,
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import { client } from "../../../../..";
|
||||
import { useAppState } from "../../../../../state/StateProvider";
|
||||
import { ServerStatus } from "../../../../../types";
|
||||
import { combineClasses } from "../../../../../util/helpers";
|
||||
import Flex from "../../../../shared/layout/Flex";
|
||||
import Selector from "../../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const DockerAccount: Component<{}> = (p) => {
|
||||
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||
const { deployment, setDeployment, server, userCanUpdate } = useConfig();
|
||||
const [dockerAccounts, setDockerAccounts] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
client
|
||||
.get_server_docker_accounts(deployment.server_id)
|
||||
.then(setDockerAccounts);
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_docker_accounts(deployment.server_id)
|
||||
.then(setDockerAccounts);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Flex
|
||||
|
||||
@@ -74,7 +74,7 @@ const EditDotEnv: Component<{}> = (p) => {
|
||||
value={dotenv()}
|
||||
onEdit={setDotEnv}
|
||||
style={{
|
||||
width: "700px",
|
||||
width: "1000px",
|
||||
"max-width": "90vw",
|
||||
height: "80vh",
|
||||
padding: "1rem",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, createEffect, createSignal } from "solid-js";
|
||||
import { client } from "../../../../..";
|
||||
import { ServerStatus } from "../../../../../types";
|
||||
import { combineClasses } from "../../../../../util/helpers";
|
||||
import Input from "../../../../shared/Input";
|
||||
import Flex from "../../../../shared/layout/Flex";
|
||||
@@ -8,10 +9,14 @@ import Selector from "../../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const Git: Component<{}> = (p) => {
|
||||
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||
const { deployment, server, setDeployment, userCanUpdate } = useConfig();
|
||||
const [githubAccounts, setGithubAccounts] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
client.get_server_github_accounts(deployment.server_id).then(setGithubAccounts);
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_github_accounts(deployment.server_id)
|
||||
.then(setGithubAccounts);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import {
|
||||
Component,
|
||||
createEffect,
|
||||
createMemo,
|
||||
createSignal,
|
||||
onCleanup,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { client, pushNotification } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { DockerContainerState, Log as LogType } from "../../../../types";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import { useBuffer } from "../../../../util/hooks";
|
||||
import { useBuffer, useLocalStorageToggle } from "../../../../util/hooks";
|
||||
import Icon from "../../../shared/Icon";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
@@ -12,6 +19,10 @@ import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../config/Provider";
|
||||
import s from "./log.module.scss";
|
||||
|
||||
const POLLING_RATE = 5000;
|
||||
|
||||
let interval = -1;
|
||||
|
||||
const Log: Component<{
|
||||
log?: LogType;
|
||||
logTail: number;
|
||||
@@ -57,9 +68,23 @@ const Log: Component<{
|
||||
}
|
||||
};
|
||||
const buffer = useBuffer(scrolled, 250);
|
||||
const [poll, togglePoll] = useLocalStorageToggle(
|
||||
"deployment-log-polling",
|
||||
true
|
||||
);
|
||||
clearInterval(interval);
|
||||
interval = setInterval(() => {
|
||||
if (poll() && deployment()?.state === DockerContainerState.Running) {
|
||||
p.reload();
|
||||
}
|
||||
}, POLLING_RATE);
|
||||
onCleanup(() => clearInterval(interval));
|
||||
return (
|
||||
<Show when={p.log}>
|
||||
<Grid gap="0.5rem" style={{ height: "100%", "grid-template-rows": "auto 1fr" }}>
|
||||
<Grid
|
||||
gap="0.5rem"
|
||||
style={{ height: "100%", "grid-template-rows": "auto 1fr" }}
|
||||
>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
justifyContent="flex-end"
|
||||
@@ -100,6 +125,9 @@ const Log: Component<{
|
||||
>
|
||||
<Icon type="refresh" />
|
||||
</button>
|
||||
<button class={poll() ? "green" : "red"} onClick={togglePoll}>
|
||||
{poll() ? "" : "don't "}poll
|
||||
</button>
|
||||
</Flex>
|
||||
<div style={{ position: "relative", height: "100%" }}>
|
||||
<div
|
||||
|
||||
@@ -25,25 +25,27 @@ const Actions: Component<{}> = (p) => {
|
||||
<Show
|
||||
when={server() && server().status === ServerStatus.Ok && userCanExecute()}
|
||||
>
|
||||
<Grid class={combineClasses("card shadow")}>
|
||||
<Grid class={combineClasses("card shadow")} gridTemplateRows="auto 1fr">
|
||||
<h1>actions</h1>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune images <PruneImages />
|
||||
</Flex>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune containers <PruneContainers />
|
||||
</Flex>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune networks{" "}
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.prune_docker_networks(params.id);
|
||||
}}
|
||||
>
|
||||
<Icon type="cut" />
|
||||
</ConfirmButton>
|
||||
</Flex>
|
||||
<Grid style={{ height: "fit-content" }}>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune images <PruneImages />
|
||||
</Flex>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune containers <PruneContainers />
|
||||
</Flex>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune networks{" "}
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.prune_docker_networks(params.id);
|
||||
}}
|
||||
>
|
||||
<Icon type="cut" />
|
||||
</ConfirmButton>
|
||||
</Flex>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
|
||||
@@ -9,10 +9,12 @@ import Grid from "../shared/layout/Grid";
|
||||
import { useAppDimensions } from "../../state/DimensionProvider";
|
||||
import { useLocalStorageToggle } from "../../util/hooks";
|
||||
import Updates from "./Updates";
|
||||
import { PermissionLevel, Server } from "../../types";
|
||||
import { PermissionLevel, ServerStatus } from "../../types";
|
||||
import { A, useParams } from "@solidjs/router";
|
||||
import { client } from "../..";
|
||||
import Loading from "../shared/loading/Loading";
|
||||
import HoverMenu from "../shared/menu/HoverMenu";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
|
||||
const Header: Component<{}> = (p) => {
|
||||
const { servers } = useAppState();
|
||||
@@ -26,25 +28,73 @@ const Header: Component<{}> = (p) => {
|
||||
const userCanUpdate = () =>
|
||||
user().admin ||
|
||||
server().server.permissions![getId(user())] === PermissionLevel.Update;
|
||||
const [version] = createResource(async () => {
|
||||
return await client.get_server_version(params.id).catch();
|
||||
});
|
||||
const [version] = createResource(
|
||||
() => server() && server().status === ServerStatus.Ok,
|
||||
async (do_it?: boolean) => {
|
||||
if (!do_it) return;
|
||||
return await client.get_server_version(params.id).catch();
|
||||
}
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
<Grid
|
||||
gap="0.5rem"
|
||||
class={combineClasses("card shadow")}
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
style={{
|
||||
position: "relative",
|
||||
cursor: isSemiMobile() ? "pointer" : undefined,
|
||||
height: "fit-content",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (isSemiMobile()) toggleShowUpdates();
|
||||
}}
|
||||
>
|
||||
<Grid gap="0.1rem">
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>{server().server.name}</h1>
|
||||
<Show when={userCanUpdate()}>
|
||||
<Flex alignItems="center">
|
||||
<div class={serverStatusClass(server().status)}>{status()}</div>
|
||||
<HoverMenu
|
||||
target={
|
||||
<A
|
||||
href={`/server/${params.id}/stats`}
|
||||
class="blue"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Icon type="timeline-line-chart" />
|
||||
</A>
|
||||
}
|
||||
content="server stats"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmMenuButton
|
||||
onConfirm={() => {
|
||||
client.delete_server(params.id);
|
||||
}}
|
||||
class="red"
|
||||
title={`delete server | ${server().server.name}`}
|
||||
match={server().server.name}
|
||||
info={
|
||||
<div style={{ opacity: 0.7 }}>
|
||||
warning! this will also delete all builds and
|
||||
deployments on this server
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmMenuButton>
|
||||
}
|
||||
content="delete server"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
</Flex>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<Flex gap="0.2rem" alignItems="center" style={{ opacity: 0.8 }}>
|
||||
<div>server</div>
|
||||
<Show when={server().server.region}>
|
||||
@@ -52,42 +102,13 @@ const Header: Component<{}> = (p) => {
|
||||
{server().server.region}
|
||||
</Show>
|
||||
</Flex>
|
||||
</Grid>
|
||||
<Flex alignItems="center">
|
||||
<Show when={!isMobile()}>
|
||||
<Show when={version()} fallback={<Loading type="three-dot" />}>
|
||||
<Show when={version()}>
|
||||
<div style={{ opacity: 0.7 }}>periphery v{version()}</div>
|
||||
</Show>
|
||||
</Show>
|
||||
<div class={serverStatusClass(server().status)}>{status()}</div>
|
||||
<A
|
||||
href={`/server/${params.id}/stats`}
|
||||
class="blue"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Icon type="timeline-line-chart" />
|
||||
</A>
|
||||
<Show when={userCanUpdate()}>
|
||||
<ConfirmButton
|
||||
onConfirm={() => {
|
||||
client.delete_server(params.id);
|
||||
}}
|
||||
class="red"
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmButton>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Show when={isSemiMobile()}>
|
||||
<Flex gap="0.5rem" alignItems="center" class="show-updates-indicator">
|
||||
updates{" "}
|
||||
<Icon
|
||||
type={showUpdates() ? "chevron-up" : "chevron-down"}
|
||||
width="0.9rem"
|
||||
/>
|
||||
</Flex>
|
||||
</Show>
|
||||
</Flex>
|
||||
</Grid>
|
||||
<Show when={isSemiMobile() && showUpdates()}>
|
||||
<Updates />
|
||||
</Show>
|
||||
|
||||
@@ -32,7 +32,7 @@ const Server: Component<{}> = (p) => {
|
||||
style={{ width: "100%" }}
|
||||
gridTemplateColumns={isSemiMobile() ? "1fr" : "1fr 1fr"}
|
||||
>
|
||||
<Grid>
|
||||
<Grid style={{ "flex-grow": 1, "grid-auto-rows": "auto 1fr" }}>
|
||||
<Header />
|
||||
<Actions />
|
||||
</Grid>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useParams } from "@solidjs/router";
|
||||
import { Component, Show } from "solid-js";
|
||||
import { useAppState } from "../../../state/StateProvider";
|
||||
import { useUser } from "../../../state/UserProvider";
|
||||
import { ServerStatus } from "../../../types";
|
||||
import SimpleTabs from "../../shared/tabs/SimpleTabs";
|
||||
import { Tab } from "../../shared/tabs/Tabs";
|
||||
import Config from "./config/Config";
|
||||
@@ -26,7 +27,7 @@ const ServerTabs: Component<{}> = (p) => {
|
||||
title: "config",
|
||||
element: () => <Config />,
|
||||
},
|
||||
{
|
||||
server()?.status === ServerStatus.Ok && {
|
||||
title: "info",
|
||||
element: () => <Info />
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@ import { createStore, SetStoreFunction } from "solid-js/store";
|
||||
import { client } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { useUser } from "../../../../state/UserProvider";
|
||||
import { Server, Operation, PermissionLevel } from "../../../../types";
|
||||
import { Server, Operation, PermissionLevel, ServerStatus } from "../../../../types";
|
||||
import { getId } from "../../../../util/helpers";
|
||||
|
||||
type ConfigServer = Server & { loaded: boolean; updated: boolean };
|
||||
@@ -59,7 +59,9 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
const [networks, setNetworks] = createSignal<any[]>([]);
|
||||
const loadNetworks = () => {
|
||||
// console.log("load networks");
|
||||
client.get_docker_networks(params.id).then(setNetworks);
|
||||
if (servers.get(params.id)?.status === ServerStatus.Ok) {
|
||||
client.get_docker_networks(params.id).then(setNetworks);
|
||||
}
|
||||
};
|
||||
createEffect(loadNetworks);
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Component, createSignal, JSX } from "solid-js";
|
||||
import { pushNotification } from "../..";
|
||||
import { useToggle } from "../../util/hooks";
|
||||
import ConfirmButton from "./ConfirmButton";
|
||||
import Input from "./Input";
|
||||
import Grid from "./layout/Grid";
|
||||
import CenterMenu from "./menu/CenterMenu";
|
||||
|
||||
const ConfirmMenuButton: Component<{
|
||||
onConfirm?: () => void;
|
||||
class?: string;
|
||||
style?: JSX.CSSProperties;
|
||||
title: string;
|
||||
match: string;
|
||||
info?: JSX.Element;
|
||||
children: JSX.Element;
|
||||
}> = (p) => {
|
||||
const [show, toggleShow] = useToggle();
|
||||
|
||||
return (
|
||||
<CenterMenu
|
||||
show={show}
|
||||
toggleShow={toggleShow}
|
||||
title={p.title}
|
||||
targetClass={p.class}
|
||||
target={p.children}
|
||||
content={() => (
|
||||
<ConfirmMenuContent
|
||||
class={p.class}
|
||||
title={p.title}
|
||||
match={p.match}
|
||||
info={p.info}
|
||||
onConfirm={p.onConfirm}
|
||||
/>
|
||||
)}
|
||||
position="center"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfirmMenuContent: Component<{
|
||||
class?: string;
|
||||
title: string;
|
||||
match: string;
|
||||
onConfirm?: () => void;
|
||||
info?: JSX.Element;
|
||||
}> = (p) => {
|
||||
const [input, setInput] = createSignal("");
|
||||
return (
|
||||
<Grid placeItems="center">
|
||||
{p.info}
|
||||
<Input
|
||||
class="darkgrey"
|
||||
style={{
|
||||
padding: "0.5rem",
|
||||
width: "100%",
|
||||
"border-style": "solid",
|
||||
"border-width": "1px",
|
||||
"border-color": input() === p.match ? "#41764c" : "#952E23",
|
||||
}}
|
||||
placeholder={`enter '${p.match}'`}
|
||||
onEdit={setInput}
|
||||
value={input()}
|
||||
autofocus
|
||||
/>
|
||||
<ConfirmButton
|
||||
class={p.class}
|
||||
style={{ width: "100%" }}
|
||||
onConfirm={() => {
|
||||
if (input() === p.match) {
|
||||
p.onConfirm && p.onConfirm();
|
||||
} else {
|
||||
pushNotification("bad", "must enter value to confirm");
|
||||
}
|
||||
}}
|
||||
>
|
||||
{p.title}
|
||||
</ConfirmButton>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmMenuButton;
|
||||
@@ -45,7 +45,8 @@ export type IconType =
|
||||
| "cog"
|
||||
| "home"
|
||||
| "timeline-line-chart"
|
||||
| "arrow-right";
|
||||
| "arrow-right"
|
||||
| "duplicate";
|
||||
|
||||
const ICON_DIR = import.meta.env.VITE_ICON_DIR || "/assets/icons"
|
||||
|
||||
|
||||
@@ -33,10 +33,12 @@ const Selector: Component<{
|
||||
itemClass?: string;
|
||||
itemStyle?: JSX.CSSProperties;
|
||||
label?: JSXElement;
|
||||
itemMap?: (item: string) => string;
|
||||
}> = (p) => {
|
||||
const [show, toggle] = useToggle();
|
||||
const [search, setSearch] = createSignal("");
|
||||
let ref: HTMLInputElement | undefined;
|
||||
const current = () => (p.itemMap ? p.itemMap(p.selected) : p.selected);
|
||||
createEffect(() => {
|
||||
if (show()) setTimeout(() => ref?.focus(), 200);
|
||||
});
|
||||
@@ -46,7 +48,7 @@ const Selector: Component<{
|
||||
fallback={
|
||||
<div class={p.disabledClass} style={p.disabledStyle}>
|
||||
<Show when={p.label}>{p.label}</Show>
|
||||
{p.selected}
|
||||
{current()}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
@@ -60,7 +62,7 @@ const Selector: Component<{
|
||||
target={
|
||||
<button class={p.targetClass} onClick={toggle} style={p.targetStyle}>
|
||||
<Show when={p.label}>{p.label}</Show>
|
||||
{p.selected}
|
||||
{current()}
|
||||
<Icon type="chevron-down" />
|
||||
</button>
|
||||
}
|
||||
@@ -84,7 +86,11 @@ const Selector: Component<{
|
||||
<For
|
||||
each={
|
||||
p.useSearch
|
||||
? p.items.filter((item) => item.includes(search()))
|
||||
? p.items.filter((item) =>
|
||||
p.itemMap
|
||||
? p.itemMap(item).includes(search())
|
||||
: item.includes(search())
|
||||
)
|
||||
: p.items
|
||||
}
|
||||
>
|
||||
@@ -101,7 +107,7 @@ const Selector: Component<{
|
||||
}}
|
||||
class={combineClasses(p.itemClass, s.SelectorItem)}
|
||||
>
|
||||
{item}
|
||||
{p.itemMap ? p.itemMap(item) : item}
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
|
||||
@@ -52,7 +52,7 @@ export default UpdateMenu;
|
||||
|
||||
const UpdateMenuContent: Component<{ update: UpdateType }> = (p) => {
|
||||
return (
|
||||
<Grid class={s.LogContainer}>
|
||||
<Grid class={combineClasses(s.LogContainer, "scroller")}>
|
||||
<UpdateSummary update={p.update} />
|
||||
<UpdateLogs update={p.update} />
|
||||
</Grid>
|
||||
@@ -66,7 +66,7 @@ const UpdateSummary: Component<{ update: UpdateType }> = (p) => {
|
||||
gap="0.5rem"
|
||||
class="card light shadow"
|
||||
gridTemplateColumns="1fr 1fr"
|
||||
style={{ width: "40rem", "max-width": "90vw" }}
|
||||
style={{ width: "100%", "box-sizing": "border-box" }}
|
||||
>
|
||||
<Flex gap="0.5rem" alignItems="center">
|
||||
status: <h2>{p.update.status.replaceAll("_", " ")}</h2>
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
|
||||
.LogContainer {
|
||||
max-height: 80vh;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.Log {
|
||||
@@ -49,8 +48,8 @@
|
||||
overflow-wrap: anywhere;
|
||||
/* word-wrap: break-word; */
|
||||
tab-size: 2;
|
||||
width: 40rem;
|
||||
max-width: 90vw;
|
||||
width: 1000px;
|
||||
max-width: 80vw;
|
||||
box-sizing: border-box;
|
||||
background-color: rgba(c.$darkgrey, 0.6);
|
||||
padding: 1rem;
|
||||
|
||||
@@ -102,8 +102,8 @@ export const AppStateProvider: ParentComponent = (p) => {
|
||||
return PermissionLevel.None;
|
||||
}
|
||||
},
|
||||
serverStats: useServerStats(),
|
||||
serverInfo: useServerInfo(),
|
||||
serverStats: useServerStats(servers),
|
||||
serverInfo: useServerInfo(servers),
|
||||
groups,
|
||||
getPermissionOnGroup: (id: string) => {
|
||||
const group = groups.get(id)!;
|
||||
|
||||
@@ -47,13 +47,19 @@ export function useServers() {
|
||||
);
|
||||
}
|
||||
|
||||
export function useServerStats() {
|
||||
export function useServerStats(servers: ReturnType<typeof useServers>) {
|
||||
const [stats, set] = createSignal<Record<string, SystemStats | undefined>>(
|
||||
{}
|
||||
);
|
||||
const load = async (serverID: string) => {
|
||||
const stats = await client.get_server_stats(serverID);
|
||||
set((s) => ({ ...s, [serverID]: stats }));
|
||||
if (servers.get(serverID)?.status === ServerStatus.Ok) {
|
||||
try {
|
||||
const stats = await client.get_server_stats(serverID);
|
||||
set((s) => ({ ...s, [serverID]: stats }));
|
||||
} catch (error) {
|
||||
console.log("error getting server stats");
|
||||
}
|
||||
}
|
||||
};
|
||||
const loading: Record<string, boolean> = {};
|
||||
setTimeout(() => Object.keys(stats()).forEach(load), 30000);
|
||||
@@ -74,13 +80,19 @@ export function useServerStats() {
|
||||
};
|
||||
}
|
||||
|
||||
export function useServerInfo() {
|
||||
export function useServerInfo(servers: ReturnType<typeof useServers>) {
|
||||
const [info, set] = createSignal<
|
||||
Record<string, SystemInformation | undefined>
|
||||
>({});
|
||||
const load = async (serverID: string) => {
|
||||
const info = await client.get_server_system_info(serverID);
|
||||
set((s) => ({ ...s, [serverID]: info }));
|
||||
if (servers.get(serverID)?.status === ServerStatus.Ok) {
|
||||
try {
|
||||
const info = await client.get_server_system_info(serverID);
|
||||
set((s) => ({ ...s, [serverID]: info }));
|
||||
} catch (error) {
|
||||
console.log("error getting server info", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
const loading: Record<string, boolean> = {};
|
||||
return {
|
||||
|
||||
@@ -11,9 +11,12 @@ body {
|
||||
background-color: c.$darkgrey;
|
||||
color: c.$app-color;
|
||||
|
||||
scrollbar-color: rgba(252, 234, 222, 0.3) transparent;
|
||||
scrollbar-width: thin;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
place-items: center;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
code {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "db_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
types = { package = "monitor_types", version = "0.1.11" }
|
||||
types = { package = "monitor_types", version = "0.1.15" }
|
||||
# types = { package = "monitor_types", path = "../types" }
|
||||
mungos = "0.3.0"
|
||||
anyhow = "1.0"
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_helpers"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "helpers used as dependency for mogh tech monitor"
|
||||
@@ -10,7 +10,7 @@ license = "GPL-3.0-or-later"
|
||||
|
||||
[dependencies]
|
||||
# types = { package = "monitor_types", path = "../types" }
|
||||
types = { package = "monitor_types", version = "0.1.11" }
|
||||
types = { package = "monitor_types", version = "0.1.15" }
|
||||
async_timing_util = "0.1.14"
|
||||
bollard = "0.13"
|
||||
anyhow = "1.0"
|
||||
@@ -18,7 +18,7 @@ axum = { version = "0.6", features = ["ws", "json"] }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
toml = "0.6"
|
||||
toml = "0.7"
|
||||
run_command = { version = "0.0.5", features = ["async_tokio"] }
|
||||
rand = "0.8"
|
||||
futures = "0.3"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "a client to interact with the monitor system"
|
||||
@@ -9,14 +9,15 @@ license = "GPL-3.0-or-later"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
monitor_types = "0.1.11"
|
||||
monitor_types = "0.1.15"
|
||||
# monitor_types = { path = "../types" }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||
tokio = { version = "1.24", features = ["full"] }
|
||||
tokio = { version = "1.25", features = ["full"] }
|
||||
tokio-util = "0.7"
|
||||
anyhow = "1.0"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
futures-util = "0.3"
|
||||
futures-util = "0.3"
|
||||
envy = "0.4"
|
||||
@@ -3,7 +3,7 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use monitor_types::User;
|
||||
use reqwest::StatusCode;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
pub use futures_util;
|
||||
@@ -20,6 +20,15 @@ mod secret;
|
||||
mod server;
|
||||
mod update;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct MonitorEnv {
|
||||
monitor_url: String,
|
||||
monitor_token: Option<String>,
|
||||
monitor_username: Option<String>,
|
||||
monitor_password: Option<String>,
|
||||
monitor_secret: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorClient {
|
||||
http_client: reqwest::Client,
|
||||
@@ -80,6 +89,26 @@ impl MonitorClient {
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub async fn new_from_env() -> anyhow::Result<MonitorClient> {
|
||||
let env = envy::from_env::<MonitorEnv>()
|
||||
.context("failed to parse environment for monitor client")?;
|
||||
if let Some(token) = env.monitor_token {
|
||||
Ok(MonitorClient::new_with_token(&env.monitor_url, token))
|
||||
} else if let Some(password) = env.monitor_password {
|
||||
let username = env.monitor_username.ok_or(anyhow!(
|
||||
"must provide MONITOR_USERNAME to authenticate with MONITOR_PASSWORD"
|
||||
))?;
|
||||
MonitorClient::new_with_password(&env.monitor_url, username, password).await
|
||||
} else if let Some(secret) = env.monitor_secret {
|
||||
let username = env.monitor_username.ok_or(anyhow!(
|
||||
"must provide MONITOR_USERNAME to authenticate with MONITOR_SECRET"
|
||||
))?;
|
||||
MonitorClient::new_with_secret(&env.monitor_url, username, secret).await
|
||||
} else {
|
||||
Err(anyhow!("failed to initialize monitor client from env | must provide one of: (MONITOR_TOKEN), (MONITOR_USERNAME and MONITOR_PASSWORD), (MONITOR_USERNAME and MONITOR_SECRET)"))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_user(
|
||||
&self,
|
||||
username: impl Into<String>,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
[package]
|
||||
name = "periphery_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
helpers = { package = "monitor_helpers", version = "0.1.11" }
|
||||
types = { package = "monitor_types", version = "0.1.11" }
|
||||
helpers = { package = "monitor_helpers", version = "0.1.15" }
|
||||
types = { package = "monitor_types", version = "0.1.15" }
|
||||
# types = { package = "monitor_types", path = "../types" }
|
||||
# helpers = { package = "monitor_helpers", path = "../helpers" }
|
||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||
tokio = "1.24"
|
||||
tokio = "1.25"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
@@ -14,12 +14,19 @@ mod git;
|
||||
mod image;
|
||||
mod network;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PeripheryClient {
|
||||
http_client: reqwest::Client,
|
||||
passkey: String,
|
||||
}
|
||||
|
||||
impl PeripheryClient {
|
||||
pub fn new(passkey: String) -> PeripheryClient {
|
||||
PeripheryClient {
|
||||
http_client: Default::default(),
|
||||
passkey
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn health_check(&self, server: &Server) -> anyhow::Result<String> {
|
||||
self.get_text(server, "/health", 1000)
|
||||
.await
|
||||
@@ -95,7 +102,8 @@ impl PeripheryClient {
|
||||
) -> anyhow::Result<String> {
|
||||
let mut req = self
|
||||
.http_client
|
||||
.get(format!("{}{endpoint}", server.address));
|
||||
.get(format!("{}{endpoint}", server.address))
|
||||
.header("authorization", &self.passkey);
|
||||
|
||||
if let Some(timeout) = timeout_ms.into() {
|
||||
req = req.timeout(Duration::from_millis(timeout))
|
||||
@@ -130,6 +138,7 @@ impl PeripheryClient {
|
||||
let res = self
|
||||
.http_client
|
||||
.get(format!("{}{endpoint}", server.address))
|
||||
.header("authorization", &self.passkey)
|
||||
.send()
|
||||
.await
|
||||
.context(format!(
|
||||
@@ -165,6 +174,7 @@ impl PeripheryClient {
|
||||
let res = self
|
||||
.http_client
|
||||
.post(format!("{}{endpoint}", server.address))
|
||||
.header("authorization", &self.passkey)
|
||||
.json(body)
|
||||
.send()
|
||||
.await
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_types"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "types for the mogh tech monitor"
|
||||
|
||||
@@ -35,28 +35,36 @@ pub struct Build {
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub server_id: String, // server which this image should be built on
|
||||
|
||||
#[builder(default)]
|
||||
pub version: Version,
|
||||
|
||||
// git related
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub repo: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub branch: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub github_account: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub on_clone: Option<Command>,
|
||||
|
||||
// build related
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub pre_build: Option<Command>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub docker_build_args: Option<DockerBuildArgs>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub docker_account: Option<String>,
|
||||
|
||||
@@ -128,8 +136,10 @@ impl Version {
|
||||
#[diff(attr(#[derive(Debug, Serialize, PartialEq)]))]
|
||||
pub struct DockerBuildArgs {
|
||||
pub build_path: String,
|
||||
#[builder(default)]
|
||||
pub dockerfile_path: Option<String>,
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub build_args: Vec<EnvironmentVar>,
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ pub struct CoreConfig {
|
||||
// used to verify validity from github webhooks
|
||||
pub github_webhook_secret: String,
|
||||
|
||||
// sent in auth header with req to periphery
|
||||
pub passkey: String,
|
||||
|
||||
// integration with slack app
|
||||
pub slack_url: Option<String>,
|
||||
|
||||
@@ -104,6 +107,8 @@ pub struct PeripheryConfig {
|
||||
#[serde(default)]
|
||||
pub allowed_ips: Vec<IpAddr>,
|
||||
#[serde(default)]
|
||||
pub passkeys: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub secrets: SecretsMap,
|
||||
#[serde(default)]
|
||||
pub github_accounts: GithubAccounts,
|
||||
|
||||
@@ -32,31 +32,40 @@ pub struct Deployment {
|
||||
#[builder(setter(skip))]
|
||||
pub permissions: PermissionsMap,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "docker_run_args_diff_no_change")]))]
|
||||
pub docker_run_args: DockerRunArgs,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub build_id: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub build_version: Option<Version>,
|
||||
|
||||
// deployment repo related
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub repo: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub branch: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub github_account: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub on_clone: Option<Command>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub on_pull: Option<Command>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub repo_mount: Option<Conversion>,
|
||||
|
||||
@@ -94,39 +103,49 @@ pub struct DeploymentActionState {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Diff, Builder)]
|
||||
#[diff(attr(#[derive(Debug, PartialEq, Serialize)]))]
|
||||
pub struct DockerRunArgs {
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub image: String,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub ports: Vec<Conversion>,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub volumes: Vec<Conversion>,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub environment: Vec<EnvironmentVar>,
|
||||
|
||||
#[serde(default = "default_network")]
|
||||
#[builder(default = "default_network()")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub network: String,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "restart_mode_diff_no_change")]))]
|
||||
pub restart: RestartMode,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub post_image: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub container_user: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub extra_args: Vec<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub docker_account: Option<String>, // the username of the dockerhub account
|
||||
}
|
||||
|
||||
@@ -28,18 +28,23 @@ pub struct Group {
|
||||
#[builder(setter(skip))]
|
||||
pub permissions: PermissionsMap,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub builds: Vec<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub deployments: Vec<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub servers: Vec<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub procedures: Vec<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub groups: Vec<String>,
|
||||
|
||||
|
||||
@@ -24,10 +24,12 @@ pub struct Procedure {
|
||||
pub name: String,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub stages: Vec<ProcedureStage>,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub webhook_branches: Vec<String>,
|
||||
|
||||
|
||||
@@ -34,36 +34,45 @@ pub struct Server {
|
||||
#[builder(setter(skip))]
|
||||
pub permissions: PermissionsMap,
|
||||
|
||||
#[builder(default = "true")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub enabled: bool,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub to_notify: Vec<String>, // slack users to notify
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default = "true")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub auto_prune: bool,
|
||||
|
||||
#[serde(default = "default_cpu_alert")]
|
||||
#[builder(default = "default_cpu_alert()")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "f32_diff_no_change")]))]
|
||||
pub cpu_alert: f32,
|
||||
|
||||
#[serde(default = "default_mem_alert")]
|
||||
#[builder(default = "default_mem_alert()")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "f64_diff_no_change")]))]
|
||||
pub mem_alert: f64,
|
||||
|
||||
#[serde(default = "default_disk_alert")]
|
||||
#[builder(default = "default_disk_alert()")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "f64_diff_no_change")]))]
|
||||
pub disk_alert: f64,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "timelength_diff_no_change")]))]
|
||||
pub stats_interval: Timelength,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub region: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub instance_id: Option<String>,
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_periphery"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "monitor periphery binary | run monitor periphery as system daemon"
|
||||
@@ -15,11 +15,11 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
# helpers = { package = "monitor_helpers", path = "../lib/helpers" }
|
||||
# types = { package = "monitor_types", path = "../lib/types" }
|
||||
helpers = { package = "monitor_helpers", version = "0.1.11" }
|
||||
types = { package = "monitor_types", version = "0.1.11" }
|
||||
helpers = { package = "monitor_helpers", version = "0.1.15" }
|
||||
types = { package = "monitor_types", version = "0.1.15" }
|
||||
run_command = { version = "0.0.5", features = ["async_tokio"] }
|
||||
async_timing_util = "0.1.14"
|
||||
tokio = { version = "1.24", features = ["full"] }
|
||||
tokio = { version = "1.25", features = ["full"] }
|
||||
axum = { version = "0.6", features = ["ws"] }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
dotenv = "0.15"
|
||||
@@ -30,7 +30,7 @@ bollard = "0.13"
|
||||
anyhow = "1.0"
|
||||
envy = "0.4"
|
||||
sysinfo = "0.27.7"
|
||||
toml = "0.6"
|
||||
toml = "0.7"
|
||||
daemonize = "0.4"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
futures-util = "0.3"
|
||||
|
||||
@@ -43,7 +43,8 @@ pub fn router(config: PeripheryConfigExtension, home_dir: HomeDirExtension) -> R
|
||||
.nest("/build", build::router())
|
||||
.nest("/image", image::router())
|
||||
.layer(DockerClient::extension())
|
||||
.layer(middleware::from_fn(guard_request))
|
||||
.layer(middleware::from_fn(guard_request_by_ip))
|
||||
.layer(middleware::from_fn(guard_request_by_passkey))
|
||||
.layer(StatsClient::extension(
|
||||
config.stats_polling_rate.to_string().parse().unwrap(),
|
||||
))
|
||||
@@ -51,7 +52,59 @@ pub fn router(config: PeripheryConfigExtension, home_dir: HomeDirExtension) -> R
|
||||
.layer(home_dir)
|
||||
}
|
||||
|
||||
async fn guard_request(
|
||||
async fn guard_request_by_passkey(
|
||||
req: Request<Body>,
|
||||
next: Next<Body>,
|
||||
) -> Result<Response, (StatusCode, String)> {
|
||||
let config = req.extensions().get::<Arc<PeripheryConfig>>().ok_or((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"could not get periphery config".to_string(),
|
||||
))?;
|
||||
if config.passkeys.is_empty() {
|
||||
return Ok(next.run(req).await);
|
||||
}
|
||||
let req_passkey = req.headers().get("authorization");
|
||||
if req_passkey.is_none() {
|
||||
return Err((
|
||||
StatusCode::UNAUTHORIZED,
|
||||
format!("request was not sent with passkey"),
|
||||
));
|
||||
}
|
||||
let req_passkey = req_passkey
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::UNAUTHORIZED,
|
||||
format!("failed to get passkey from authorization header as str: {e:?}"),
|
||||
)
|
||||
})?
|
||||
.to_string();
|
||||
if config.passkeys.contains(&req_passkey) {
|
||||
Ok(next.run(req).await)
|
||||
} else {
|
||||
let ConnectInfo(socket_addr) =
|
||||
req.extensions().get::<ConnectInfo<SocketAddr>>().ok_or((
|
||||
StatusCode::UNAUTHORIZED,
|
||||
"could not get socket addr of request".to_string(),
|
||||
))?;
|
||||
let ip = socket_addr.ip();
|
||||
let method = req.method().to_owned();
|
||||
let uri = req.uri().to_owned();
|
||||
let body = req
|
||||
.extract::<Json<Value>, _>()
|
||||
.await
|
||||
.ok()
|
||||
.map(|Json(body)| body);
|
||||
eprintln!(
|
||||
"{} | unauthorized request from {ip} (bad passkey) | method: {method} | uri: {uri} | body: {body:?}",
|
||||
monitor_timestamp(),
|
||||
);
|
||||
Err((StatusCode::UNAUTHORIZED, format!("request passkey invalid")))
|
||||
}
|
||||
}
|
||||
|
||||
async fn guard_request_by_ip(
|
||||
req: Request<Body>,
|
||||
next: Next<Body>,
|
||||
) -> Result<Response, (StatusCode, String)> {
|
||||
@@ -78,9 +131,8 @@ async fn guard_request(
|
||||
.ok()
|
||||
.map(|Json(body)| body);
|
||||
eprintln!(
|
||||
"{} | unauthorized request from {ip} | method: {method} | uri: {uri} | body: {:?}",
|
||||
monitor_timestamp(),
|
||||
body
|
||||
"{} | unauthorized request from {ip} | method: {method} | uri: {uri} | body: {body:?}",
|
||||
monitor_timestamp()
|
||||
);
|
||||
Err((
|
||||
StatusCode::UNAUTHORIZED,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# monitor 🦎
|
||||
|
||||
a tool to build and deploy software across many servers
|
||||
|
||||
## table of contents
|
||||
|
||||
1. [introduction](https://github.com/mbecker20/monitor/blob/main/docs/introduction.md)
|
||||
2. [connecting servers](https://github.com/mbecker20/monitor/blob/main/docs/servers.md)
|
||||
3. [building](https://github.com/mbecker20/monitor/blob/main/docs/builds.md)
|
||||
4. [deploying](https://github.com/mbecker20/monitor/blob/main/docs/deployments.md)
|
||||
5. [permissioning](https://github.com/mbecker20/monitor/blob/main/docs/permissions.md)
|
||||
Reference in New Issue
Block a user