forked from github-starred/komodo
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b375708bbd | ||
|
|
10b6a9482b | ||
|
|
84d45c5df8 | ||
|
|
c6559814b1 | ||
|
|
c8c080183f | ||
|
|
597b67f799 | ||
|
|
ec52d5f422 | ||
|
|
34806304d6 | ||
|
|
87953d5495 | ||
|
|
b6c7c80c95 | ||
|
|
77e568d5c3 | ||
|
|
699fc51cf7 | ||
|
|
21029c90b7 | ||
|
|
6b0530eb7f | ||
|
|
f7061c7225 | ||
|
|
750f698369 | ||
|
|
ec5ef42298 | ||
|
|
46820b0044 | ||
|
|
425a6648f7 | ||
|
|
349fc297ce | ||
|
|
5ad87c03ed | ||
|
|
d16006f28f | ||
|
|
7f0452a5f5 | ||
|
|
c605b2f6fc | ||
|
|
6c2d8a8494 | ||
|
|
874691f729 | ||
|
|
cdf702e17d | ||
|
|
25fdb32627 | ||
|
|
e976ea0a3a | ||
|
|
34e6b4fc69 | ||
|
|
a2d77567b3 | ||
|
|
ecb460f9b5 | ||
|
|
63444b089c | ||
|
|
c787984b77 | ||
|
|
bf3d03e801 | ||
|
|
bc2e69b975 |
8
.vscode/tasks.json
vendored
8
.vscode/tasks.json
vendored
@@ -100,14 +100,6 @@
|
||||
"cwd": "${workspaceFolder}/lib/monitor_client"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "publish",
|
||||
"label": "publish monitor cli",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/cli"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"command": "docker compose up -d",
|
||||
|
||||
391
Cargo.lock
generated
391
Cargo.lock
generated
@@ -37,6 +37,46 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-wincon",
|
||||
"concolor-override",
|
||||
"concolor-query",
|
||||
"is-terminal",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.69"
|
||||
@@ -51,7 +91,7 @@ checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -86,9 +126,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "aws-config"
|
||||
version = "0.54.1"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3d1e2a1f1ab3ac6c4b884e37413eaa03eb9d901e4fc68ee8f5c1d49721680e"
|
||||
checksum = "1854be4730cc87602316707045a5c0585287419d54f293bbb52a82c895d9086a"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-http",
|
||||
@@ -102,6 +142,7 @@ dependencies = [
|
||||
"aws-smithy-types",
|
||||
"aws-types",
|
||||
"bytes",
|
||||
"fastrand",
|
||||
"hex",
|
||||
"http",
|
||||
"hyper",
|
||||
@@ -115,12 +156,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-credential-types"
|
||||
version = "0.54.1"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0696a0523a39a19087747e4dafda0362dc867531e3d72a3f195564c84e5e08"
|
||||
checksum = "77e37e62f59cf3284067337da7467d842df8cfe3f5e5c06487ac7521819cf16d"
|
||||
dependencies = [
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-types",
|
||||
"fastrand",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"zeroize",
|
||||
@@ -128,9 +170,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-endpoint"
|
||||
version = "0.54.1"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80a4f935ab6a1919fbfd6102a80c4fccd9ff5f47f94ba154074afe1051903261"
|
||||
checksum = "9f38276d5875b19a9bb2b4ae049fd776c932fcc62068f78b71ce475093ccb4c8"
|
||||
dependencies = [
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-types",
|
||||
@@ -142,9 +184,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-http"
|
||||
version = "0.54.1"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82976ca4e426ee9ca3ffcf919d9b2c8d14d0cd80d43cc02173737a8f07f28d4d"
|
||||
checksum = "67224bfece71e21a63ae82b1ebbfda05be28678a0fab06def03229c7a445d3fb"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-smithy-http",
|
||||
@@ -161,9 +203,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ec2"
|
||||
version = "0.24.0"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b40ee2d853d8300a49513778beb79b1574ff9e9c94b30b1531bc0171d730ad64"
|
||||
checksum = "fd180c0cb50c9b4306288a9c67f51a0e252ce09f05462b34a437b8b37107982e"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-endpoint",
|
||||
@@ -189,9 +231,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sso"
|
||||
version = "0.24.0"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca0119bacf0c42f587506769390983223ba834e605f049babe514b2bd646dbb2"
|
||||
checksum = "f4c10657158e12163d6b3fb1e0c9154e43656843794a3071d9ee63ec82a4de2d"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-endpoint",
|
||||
@@ -209,13 +251,14 @@ dependencies = [
|
||||
"regex",
|
||||
"tokio-stream",
|
||||
"tower",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sts"
|
||||
version = "0.24.0"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "270b6a33969ebfcb193512fbd5e8ee5306888ad6c6d5d775cdbfb2d50d94de26"
|
||||
checksum = "706a308b7277ac9aac78ab37933509659c6f978a8473e95d8e7a8103093133c3"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-endpoint",
|
||||
@@ -239,9 +282,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sig-auth"
|
||||
version = "0.54.1"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "660a02a98ab1af83bd8d714afbab2d502ba9b18c49e7e4cddd6bf8837ff778cb"
|
||||
checksum = "cebfa0f118afd7197185d5e097bfcdfca9f8410dca50435d67784405f1fd5a05"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-sigv4",
|
||||
@@ -253,9 +296,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sigv4"
|
||||
version = "0.54.1"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdaf11005b7444e6cd66f600d09861a3aeb6eb89a0f003c7c9820dbab2d15297"
|
||||
checksum = "19a4f5c05c8646d12b7bb3f18c04edc5ac5e8928ab80e1649e568190f2bc7b79"
|
||||
dependencies = [
|
||||
"aws-smithy-http",
|
||||
"form_urlencoded",
|
||||
@@ -272,9 +315,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-async"
|
||||
version = "0.54.4"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63c712a28a4f2f2139759235c08bf98aca99d4fdf1b13c78c5f95613df0a5db9"
|
||||
checksum = "8cd4b9b7d99263f75304fc1fcd752361cbc4cbf068b832acd8daeaaff44267eb"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
@@ -284,9 +327,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-client"
|
||||
version = "0.54.4"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "104ca17f56cde00a10207169697dfe9c6810db339d52fb352707e64875b30a44"
|
||||
checksum = "748298b60bbd0594223ea136ceed2ed4b6d50970bcefa69a5ff6d710ce593854"
|
||||
dependencies = [
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-http",
|
||||
@@ -300,6 +343,7 @@ dependencies = [
|
||||
"hyper-rustls",
|
||||
"lazy_static",
|
||||
"pin-project-lite",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tracing",
|
||||
@@ -307,9 +351,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-http"
|
||||
version = "0.54.4"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "873f316f1833add0d3aa54ed1b0cd252ddd88c792a0cf839886400099971e844"
|
||||
checksum = "d78510732b81040689dc146e3693bfbcf388ab88cbda667d3ef67f8869b0744a"
|
||||
dependencies = [
|
||||
"aws-smithy-types",
|
||||
"bytes",
|
||||
@@ -329,9 +373,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-http-tower"
|
||||
version = "0.54.4"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f38231d3f5dac9ac7976f44e12803add1385119ffca9e5f050d8e980733d164"
|
||||
checksum = "dc33689c27bbd8184412b45c4d1ab795d9a35402562d9fde6c53695a90969740"
|
||||
dependencies = [
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-types",
|
||||
@@ -345,18 +389,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-json"
|
||||
version = "0.54.4"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bd83ff2b79e9f729746fcc8ad798676b68fe6ea72986571569a5306a277a182"
|
||||
checksum = "ada31cab1b1d1f0abc5c4d1183de5b278597704851aa703801b82feabf19aa74"
|
||||
dependencies = [
|
||||
"aws-smithy-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-query"
|
||||
version = "0.54.4"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2f0445dafe9d2cd50b44339ae3c3ed46549aad8ac696c52ad660b3e7ae8682b"
|
||||
checksum = "b55358401b657d192f70f093927f01d73cc4859e2907956b20c4043c76624006"
|
||||
dependencies = [
|
||||
"aws-smithy-types",
|
||||
"urlencoding",
|
||||
@@ -364,9 +408,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-types"
|
||||
version = "0.54.4"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8161232eda10290f5136610a1eb9de56aceaccd70c963a26a260af20ac24794f"
|
||||
checksum = "474d145c2e0f82892841d2502bd546ca0dbc1e4e242c3563d96e7061054c268f"
|
||||
dependencies = [
|
||||
"base64-simd",
|
||||
"itoa",
|
||||
@@ -377,18 +421,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-xml"
|
||||
version = "0.54.4"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "343ffe9a9bb3f542675f4df0e0d5933513d6ad038ca3907ad1767ba690a99684"
|
||||
checksum = "bb159921734d090b01c586a4fef73964f42fcb7eb53a8184b2db374bd6a6fc99"
|
||||
dependencies = [
|
||||
"xmlparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-types"
|
||||
version = "0.54.1"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8f15b34253b68cde08e39b0627cc6101bcca64351229484b4743392c035d057"
|
||||
checksum = "81fb02591b5075d318e0083dcb76df0e151db4ce48f987ecd00e5b53c7a6ba59"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-smithy-async",
|
||||
@@ -602,7 +646,7 @@ dependencies = [
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"time 0.3.20",
|
||||
"uuid 1.3.0",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -676,40 +720,45 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.1.6"
|
||||
version = "4.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
|
||||
checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.1.0"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
|
||||
checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.2"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
@@ -732,9 +781,30 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concolor-override"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f"
|
||||
|
||||
[[package]]
|
||||
name = "concolor-query"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf"
|
||||
dependencies = [
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "core"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -752,8 +822,9 @@ dependencies = [
|
||||
"hex",
|
||||
"hmac",
|
||||
"jwt",
|
||||
"merge_config_files",
|
||||
"monitor_helpers",
|
||||
"monitor_types 0.2.9",
|
||||
"monitor_types 0.2.13",
|
||||
"mungos",
|
||||
"periphery_client",
|
||||
"serde",
|
||||
@@ -880,7 +951,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -897,7 +968,7 @@ checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -940,7 +1011,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -954,7 +1025,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -965,7 +1036,7 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||
dependencies = [
|
||||
"darling_core 0.13.4",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -976,7 +1047,7 @@ checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685"
|
||||
dependencies = [
|
||||
"darling_core 0.14.3",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -987,10 +1058,10 @@ checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||
|
||||
[[package]]
|
||||
name = "db_client"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"monitor_types 0.2.9",
|
||||
"monitor_types 0.2.13",
|
||||
"mungos",
|
||||
]
|
||||
|
||||
@@ -1002,7 +1073,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1023,7 +1094,7 @@ dependencies = [
|
||||
"darling 0.14.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1033,7 +1104,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version 0.4.0",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1055,7 +1139,7 @@ checksum = "fe165e7ead196bbbf44c7ce11a7a21157b5c002ce46d7098ff9c556784a4912d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1099,7 +1183,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1237,7 +1321,7 @@ checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1424,9 +1508,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.24"
|
||||
version = "0.14.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c"
|
||||
checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@@ -1749,6 +1833,18 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "merge_config_files"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eb90e0f8c47b81cd4bb84865e595ac3b0c5fabe6bf73bbd0507162b2fcc3c8a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.16"
|
||||
@@ -1788,9 +1884,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mongodb"
|
||||
version = "2.3.1"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a1df476ac9541b0e4fdc8e2cc48884e66c92c933cd17a1fd75e68caf75752e"
|
||||
checksum = "a37fe10c1485a0cd603468e284a1a8535b4ecf46808f5f7de3639a1e1252dbf8"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
@@ -1798,21 +1894,22 @@ dependencies = [
|
||||
"bson",
|
||||
"chrono",
|
||||
"derivative",
|
||||
"derive_more",
|
||||
"flate2",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"hmac",
|
||||
"lazy_static",
|
||||
"md-5",
|
||||
"os_info",
|
||||
"pbkdf2",
|
||||
"percent-encoding",
|
||||
"rand",
|
||||
"rustc_version_runtime",
|
||||
"rustls",
|
||||
"rustls-pemfile 0.3.0",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_with 1.14.0",
|
||||
@@ -1830,19 +1927,19 @@ dependencies = [
|
||||
"trust-dns-proto",
|
||||
"trust-dns-resolver",
|
||||
"typed-builder",
|
||||
"uuid 0.8.2",
|
||||
"uuid",
|
||||
"webpki-roots",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monitor_cli"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
dependencies = [
|
||||
"async_timing_util",
|
||||
"clap",
|
||||
"colored",
|
||||
"monitor_types 0.2.9",
|
||||
"monitor_types 0.2.13",
|
||||
"rand",
|
||||
"run_command",
|
||||
"serde",
|
||||
@@ -1854,12 +1951,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_client"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"envy",
|
||||
"futures-util",
|
||||
"monitor_types 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -1871,11 +1968,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_helpers"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"monitor_types 0.2.9",
|
||||
"monitor_types 0.2.13",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -1884,7 +1981,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_periphery"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -1895,8 +1992,9 @@ dependencies = [
|
||||
"dotenv",
|
||||
"envy",
|
||||
"futures",
|
||||
"merge_config_files",
|
||||
"monitor_helpers",
|
||||
"monitor_types 0.2.9",
|
||||
"monitor_types 0.2.13",
|
||||
"run_command",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -1910,7 +2008,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_types"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
@@ -1927,9 +2025,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_types"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de716e157711aac3646ae9faddf6d48cefc0d3e163cdf1b5a72bb034a353fef"
|
||||
checksum = "16dc62e1626477f66340013650aebcd152ee14880b767f76359fbce2b0d621ed"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
@@ -1946,11 +2044,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mungos"
|
||||
version = "0.3.8"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "588d78564faa32532f258d8e9080035b36b10f65289d3bbb6bdbbcccf5cbd3cd"
|
||||
checksum = "2b8fabef8c6e29f25a64c58736ab58b191e28aa3bafc3e84a3b0e78a1ba00665"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"envy",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"mongodb",
|
||||
@@ -2101,7 +2200,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2123,22 +2222,6 @@ dependencies = [
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
version = "3.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c424bc68d15e0778838ac013b5b3449544d8133633d8016319e7e05a820b8c0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "outref"
|
||||
version = "0.5.1"
|
||||
@@ -2170,9 +2253,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.10.1"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7"
|
||||
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
@@ -2185,11 +2268,11 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "periphery_client"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
"monitor_types 0.2.9",
|
||||
"monitor_types 0.2.13",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2214,7 +2297,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2241,35 +2324,11 @@ version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
version = "1.0.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -2282,9 +2341,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -2499,20 +2558,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile 1.0.2",
|
||||
"rustls-pemfile",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.2"
|
||||
@@ -2635,7 +2685,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2667,7 +2717,7 @@ checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2725,7 +2775,7 @@ dependencies = [
|
||||
"darling 0.13.4",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2854,7 +2904,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2883,6 +2933,17 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
@@ -2964,7 +3025,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3048,7 +3109,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3105,6 +3166,7 @@ checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
@@ -3242,7 +3304,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3333,7 +3395,7 @@ checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3361,7 +3423,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc670d0e358428857cc3b4bf504c691e572fccaec9542ff09212d3f13d74b7a9"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3430,13 +3492,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
@@ -3509,7 +3568,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -3543,7 +3602,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_cli"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "monitor cli | tools to setup monitor system"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "core"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -19,7 +19,7 @@ axum-extra = { version = "0.5.0", features = ["spa"] }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
tower-http = { version = "0.4.0", features = ["cors"] }
|
||||
slack = { package = "slack_client_rs", version = "0.0.8" }
|
||||
mungos = "0.3.8"
|
||||
mungos = "0.3.14"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
@@ -35,5 +35,6 @@ futures-util = "0.3"
|
||||
diff-struct = "0.5"
|
||||
typeshare = "1.0.0"
|
||||
hex = "0.4"
|
||||
aws-config = "0.54"
|
||||
aws-sdk-ec2 = "0.24"
|
||||
aws-config = "0.55"
|
||||
aws-sdk-ec2 = "0.25"
|
||||
merge_config_files = "0.1.1"
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use aws_sdk_ec2::Client;
|
||||
use diff::Diff;
|
||||
use helpers::{all_logs_success, to_monitor_name};
|
||||
use mungos::{doc, to_bson};
|
||||
@@ -14,7 +15,7 @@ use types::{
|
||||
use crate::{
|
||||
auth::RequestUser,
|
||||
cloud::aws::{
|
||||
self, create_ec2_client, create_instance_with_ami, terminate_ec2_instance, Ec2Instance,
|
||||
create_ec2_client, create_instance_with_ami, terminate_ec2_instance, Ec2Instance,
|
||||
},
|
||||
helpers::empty_or_only_spaces,
|
||||
state::State,
|
||||
@@ -426,7 +427,7 @@ impl State {
|
||||
async fn create_ec2_instance_for_build(
|
||||
&self,
|
||||
build: &Build,
|
||||
) -> anyhow::Result<(Ec2Instance, Option<aws::Client>, Vec<Log>)> {
|
||||
) -> anyhow::Result<(Ec2Instance, Option<Client>, Vec<Log>)> {
|
||||
if build.aws_config.is_none() {
|
||||
return Err(anyhow!("build has no aws_config attached"));
|
||||
}
|
||||
@@ -527,7 +528,7 @@ impl State {
|
||||
|
||||
async fn terminate_ec2_instance(
|
||||
&self,
|
||||
aws_client: aws::Client,
|
||||
aws_client: Client,
|
||||
server: &Ec2Instance,
|
||||
update: &mut Update,
|
||||
) {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use diff::Diff;
|
||||
use helpers::{all_logs_success, to_monitor_name};
|
||||
use mungos::doc;
|
||||
use types::{
|
||||
monitor_timestamp,
|
||||
traits::{Busy, Permissioned},
|
||||
Deployment, Log, Operation, PermissionLevel, Update, UpdateStatus, UpdateTarget,
|
||||
Deployment, DeploymentWithContainerState, DockerContainerState, Log, Operation,
|
||||
PermissionLevel, ServerStatus, ServerWithStatus, Update, UpdateStatus, UpdateTarget,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -274,6 +276,157 @@ impl State {
|
||||
Ok(new_deployment)
|
||||
}
|
||||
|
||||
pub async fn rename_deployment(
|
||||
&self,
|
||||
deployment_id: &str,
|
||||
new_name: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(&deployment_id).await {
|
||||
return Err(anyhow!("deployment busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.renaming = true;
|
||||
}
|
||||
let res = self
|
||||
.rename_deployment_inner(deployment_id, new_name, user)
|
||||
.await;
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.renaming = false;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
async fn rename_deployment_inner(
|
||||
&self,
|
||||
deployment_id: &str,
|
||||
new_name: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
let start_ts = monitor_timestamp();
|
||||
let deployment = self
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Deployment(deployment_id.to_string()),
|
||||
operation: Operation::RenameDeployment,
|
||||
start_ts,
|
||||
status: UpdateStatus::InProgress,
|
||||
operator: user.id.to_string(),
|
||||
success: true,
|
||||
..Default::default()
|
||||
};
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
let server_with_status = self.get_server(&deployment.server_id, user).await;
|
||||
if server_with_status.is_err() {
|
||||
update.logs.push(Log::error(
|
||||
"get server",
|
||||
format!(
|
||||
"failed to get server info: {:?}",
|
||||
server_with_status.as_ref().err().unwrap()
|
||||
),
|
||||
));
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = monitor_timestamp().into();
|
||||
update.success = false;
|
||||
self.update_update(update).await?;
|
||||
return Err(server_with_status.err().unwrap());
|
||||
}
|
||||
let ServerWithStatus { server, status } = server_with_status.unwrap();
|
||||
if status != ServerStatus::Ok {
|
||||
update.logs.push(Log::error(
|
||||
"check server status",
|
||||
String::from("cannot rename deployment when periphery is disabled or unreachable"),
|
||||
));
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = monitor_timestamp().into();
|
||||
update.success = false;
|
||||
self.update_update(update).await?;
|
||||
return Err(anyhow!(
|
||||
"cannot rename deployment when periphery is disabled or unreachable"
|
||||
));
|
||||
}
|
||||
let deployment_state = self
|
||||
.get_deployment_with_container_state(user, deployment_id)
|
||||
.await;
|
||||
if deployment_state.is_err() {
|
||||
update.logs.push(Log::error(
|
||||
"check deployment status",
|
||||
format!(
|
||||
"could not get current state of deployment: {:?}",
|
||||
deployment_state.as_ref().err().unwrap()
|
||||
),
|
||||
));
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = monitor_timestamp().into();
|
||||
update.success = false;
|
||||
self.update_update(update).await?;
|
||||
return Err(deployment_state.err().unwrap());
|
||||
}
|
||||
let DeploymentWithContainerState { state, .. } = deployment_state.unwrap();
|
||||
if state != DockerContainerState::NotDeployed {
|
||||
let log = self
|
||||
.periphery
|
||||
.container_rename(&server, &deployment.name, new_name)
|
||||
.await;
|
||||
if log.is_err() {
|
||||
update.logs.push(Log::error(
|
||||
"rename container",
|
||||
format!("{:?}", log.as_ref().err().unwrap()),
|
||||
));
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = monitor_timestamp().into();
|
||||
update.success = false;
|
||||
self.update_update(update).await?;
|
||||
return Err(log.err().unwrap());
|
||||
}
|
||||
let log = log.unwrap();
|
||||
if !log.success {
|
||||
update.logs.push(log);
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = monitor_timestamp().into();
|
||||
update.success = false;
|
||||
self.update_update(update).await?;
|
||||
return Err(anyhow!("rename container on periphery not successful"));
|
||||
}
|
||||
update.logs.push(log);
|
||||
}
|
||||
let res = self
|
||||
.db
|
||||
.deployments
|
||||
.update_one(
|
||||
deployment_id,
|
||||
mungos::Update::<()>::Set(
|
||||
doc! { "name": to_monitor_name(new_name), "updated_at": monitor_timestamp() },
|
||||
),
|
||||
)
|
||||
.await
|
||||
.context("failed to update deployment name on mongo");
|
||||
|
||||
if let Err(e) = res {
|
||||
update
|
||||
.logs
|
||||
.push(Log::error("mongo update", format!("{e:?}")));
|
||||
} else {
|
||||
update.logs.push(Log::simple(
|
||||
"mongo update",
|
||||
String::from("updated name on mongo"),
|
||||
))
|
||||
}
|
||||
|
||||
update.end_ts = monitor_timestamp().into();
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.success = all_logs_success(&update.logs);
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
pub async fn reclone_deployment(
|
||||
&self,
|
||||
deployment_id: &str,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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,
|
||||
@@ -49,7 +48,7 @@ impl State {
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let server = Server {
|
||||
name: to_monitor_name(name),
|
||||
name: name.to_string(),
|
||||
address,
|
||||
permissions: [(user.id.clone(), PermissionLevel::Update)]
|
||||
.into_iter()
|
||||
|
||||
@@ -43,6 +43,12 @@ pub struct CopyDeploymentBody {
|
||||
server_id: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct RenameDeploymentBody {
|
||||
new_name: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Deserialize)]
|
||||
pub struct GetContainerLogQuery {
|
||||
@@ -162,6 +168,24 @@ pub fn router() -> Router {
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/rename",
|
||||
patch(
|
||||
|state: StateExtension,
|
||||
user: RequestUserExtension,
|
||||
deployment: Path<DeploymentId>,
|
||||
body: Json<RenameDeploymentBody>| async move {
|
||||
let update = spawn_request_action(async move {
|
||||
state
|
||||
.rename_deployment(&deployment.id, &body.new_name, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
})
|
||||
.await??;
|
||||
response!(Json(update))
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/reclone",
|
||||
post(
|
||||
@@ -324,7 +348,7 @@ pub fn router() -> Router {
|
||||
}
|
||||
|
||||
impl State {
|
||||
async fn get_deployment_with_container_state(
|
||||
pub async fn get_deployment_with_container_state(
|
||||
&self,
|
||||
user: &RequestUser,
|
||||
id: &str,
|
||||
|
||||
@@ -370,7 +370,11 @@ pub fn router() -> Router {
|
||||
}
|
||||
|
||||
impl State {
|
||||
async fn get_server(&self, id: &str, user: &RequestUser) -> anyhow::Result<ServerWithStatus> {
|
||||
pub async fn get_server(
|
||||
&self,
|
||||
id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<ServerWithStatus> {
|
||||
let server = self
|
||||
.get_server_check_permissions(id, user, PermissionLevel::Read)
|
||||
.await?;
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
state::{State, StateExtension},
|
||||
};
|
||||
|
||||
const NUM_UPDATES_PER_PAGE: usize = 10;
|
||||
const NUM_UPDATES_PER_PAGE: usize = 20;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new().route(
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use aws_sdk_ec2::model::{
|
||||
BlockDeviceMapping, EbsBlockDevice, InstanceNetworkInterfaceSpecification, InstanceStateChange,
|
||||
InstanceStateName, InstanceStatus, ResourceType, Tag, TagSpecification,
|
||||
};
|
||||
pub use aws_sdk_ec2::{
|
||||
model::InstanceType,
|
||||
output::{DescribeInstanceStatusOutput, TerminateInstancesOutput},
|
||||
Client, Region,
|
||||
use aws_sdk_ec2::{
|
||||
config::Region,
|
||||
types::{
|
||||
BlockDeviceMapping, EbsBlockDevice, InstanceNetworkInterfaceSpecification,
|
||||
InstanceStateChange, InstanceStateName, InstanceStatus, InstanceType, ResourceType, Tag,
|
||||
TagSpecification,
|
||||
},
|
||||
Client,
|
||||
};
|
||||
use types::Server;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use axum_extra::routing::SpaRouter;
|
||||
use dotenv::dotenv;
|
||||
use helpers::parse_config_file;
|
||||
use merge_config_files::parse_config_file;
|
||||
use mungos::Deserialize;
|
||||
use types::CoreConfig;
|
||||
|
||||
@@ -15,7 +15,7 @@ struct Env {
|
||||
pub fn load() -> (CoreConfig, SpaRouter) {
|
||||
dotenv().ok();
|
||||
let env: Env = envy::from_env().expect("failed to parse environment variables");
|
||||
let config = parse_config_file(&env.config_path).expect("failed to parse config");
|
||||
let config = parse_config_file(env.config_path).expect("failed to parse config");
|
||||
let spa_router = SpaRouter::new("/assets", env.frontend_path);
|
||||
(config, spa_router)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ const Users = lazy(() => import("./components/users/Users"));
|
||||
const User = lazy(() => import("./components/users/User"));
|
||||
const Stats = lazy(() => import("./components/stats/Stats"));
|
||||
const Account = lazy(() => import("./components/account/Account"));
|
||||
const Updates = lazy(() => import("./components/Updates"));
|
||||
|
||||
const App: Component = () => {
|
||||
const { user } = useUser();
|
||||
@@ -19,6 +20,7 @@ const App: Component = () => {
|
||||
<Topbar />
|
||||
<Routes>
|
||||
<Route path="/" component={Home} />
|
||||
<Route path="/updates" component={Updates} />
|
||||
<Route path="/build/:id" component={Build} />
|
||||
<Route path="/deployment/:id" component={Deployment} />
|
||||
<Route path="/server/:id" component={Server} />
|
||||
|
||||
@@ -69,10 +69,10 @@ const CopyMenu: Component<{
|
||||
targetClass="blue"
|
||||
content={() => (
|
||||
<Grid placeItems="center">
|
||||
<Flex alignItems="center">
|
||||
<Flex class="full-width" alignItems="center">
|
||||
<Input
|
||||
placeholder="copy name"
|
||||
class="card dark"
|
||||
class="card dark full-width"
|
||||
style={{ padding: "0.5rem" }}
|
||||
value={newName()}
|
||||
onEdit={setNewName}
|
||||
@@ -87,6 +87,8 @@ const CopyMenu: Component<{
|
||||
targetClass="blue"
|
||||
targetStyle={{ display: "flex", gap: "0.5rem" }}
|
||||
searchStyle={{ width: "100%" }}
|
||||
menuClass="scroller"
|
||||
menuStyle={{ "max-height": "40vh" }}
|
||||
position="bottom right"
|
||||
useSearch
|
||||
/>
|
||||
|
||||
157
frontend/src/components/Updates.tsx
Normal file
157
frontend/src/components/Updates.tsx
Normal file
@@ -0,0 +1,157 @@
|
||||
import { A } from "@solidjs/router";
|
||||
import {
|
||||
Component,
|
||||
createEffect,
|
||||
createMemo,
|
||||
createSignal,
|
||||
For,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { OPERATIONS } from "..";
|
||||
import { useAppDimensions } from "../state/DimensionProvider";
|
||||
import { useAppState } from "../state/StateProvider";
|
||||
import { Operation, Update as UpdateType, UpdateStatus } from "../types";
|
||||
import { readableMonitorTimestamp, readableVersion } from "../util/helpers";
|
||||
import Icon from "./shared/Icon";
|
||||
import Input from "./shared/Input";
|
||||
import Flex from "./shared/layout/Flex";
|
||||
import Grid from "./shared/layout/Grid";
|
||||
import Loading from "./shared/loading/Loading";
|
||||
import Selector from "./shared/menu/Selector";
|
||||
import UpdateMenu from "./update/UpdateMenu";
|
||||
|
||||
const Updates: Component<{}> = (p) => {
|
||||
const { isMobile } = useAppDimensions();
|
||||
const { updates, usernames, name_from_update_target } = useAppState();
|
||||
const [operation, setOperation] = createSignal<Operation>();
|
||||
createEffect(() => {
|
||||
if (operation()) {
|
||||
updates.load([operation()!]);
|
||||
} else {
|
||||
updates.load();
|
||||
}
|
||||
});
|
||||
const [search, setSearch] = createSignal("");
|
||||
const filtered_updates = createMemo(() => {
|
||||
return updates.collection()?.filter((u) => {
|
||||
const name = name_from_update_target(u.target);
|
||||
if (name.includes(search())) return true;
|
||||
const username = usernames.get(u.operator);
|
||||
if (username?.includes(search())) return true;
|
||||
});
|
||||
});
|
||||
return (
|
||||
<Grid class="full-width card shadow">
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>updates</h1>
|
||||
<Flex alignItems="center">
|
||||
<Input class="lightgrey" placeholder="search" onEdit={setSearch} />
|
||||
<Selector
|
||||
label={isMobile() ? undefined : "operation: "}
|
||||
selected={operation() ? operation()! : "all"}
|
||||
items={["all", ...OPERATIONS]}
|
||||
onSelect={(o) =>
|
||||
o === "all"
|
||||
? setOperation(undefined)
|
||||
: setOperation(o.replaceAll(" ", "_") as Operation)
|
||||
}
|
||||
targetClass="blue"
|
||||
position="bottom right"
|
||||
searchStyle={{ width: "15rem" }}
|
||||
menuClass="scroller"
|
||||
menuStyle={{ "max-height": "50vh" }}
|
||||
useSearch
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Show
|
||||
when={updates.loaded()}
|
||||
fallback={
|
||||
<Flex justifyContent="center">
|
||||
<Loading type="three-dot" />
|
||||
</Flex>
|
||||
}
|
||||
>
|
||||
<For each={filtered_updates()}>
|
||||
{(update) => <Update update={update} />}
|
||||
</For>
|
||||
<Show when={!updates.noMore()}>
|
||||
<button
|
||||
class="grey full-width"
|
||||
onClick={() =>
|
||||
operation()
|
||||
? updates.loadMore([operation()!])
|
||||
: updates.loadMore()
|
||||
}
|
||||
>
|
||||
load more
|
||||
</button>
|
||||
</Show>
|
||||
</Show>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default Updates;
|
||||
|
||||
const Update: Component<{ update: UpdateType }> = (p) => {
|
||||
const { isMobile } = useAppDimensions();
|
||||
const { usernames, name_from_update_target } = useAppState();
|
||||
const name = () => name_from_update_target(p.update.target);
|
||||
const operation = () => {
|
||||
if (p.update.operation === Operation.BuildBuild) {
|
||||
return `build ${readableVersion(p.update.version!)}`;
|
||||
}
|
||||
return `${p.update.operation.replaceAll("_", " ")}${
|
||||
p.update.version ? " " + readableVersion(p.update.version) : ""
|
||||
}`;
|
||||
};
|
||||
const link_to = () => {
|
||||
return p.update.target.type === "System"
|
||||
? "/"
|
||||
: `/${p.update.target.type.toLowerCase()}/${p.update.target.id}`;
|
||||
};
|
||||
return (
|
||||
<Flex
|
||||
class="card light shadow wrap"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
style={{ width: isMobile() ? "100%" : undefined }}
|
||||
>
|
||||
<A style={{ padding: 0 }} href={link_to()}>
|
||||
<h2 class="text-hover">{name()}</h2>
|
||||
</A>
|
||||
<div
|
||||
style={{
|
||||
color: !p.update.success ? "rgb(182, 47, 52)" : "inherit",
|
||||
}}
|
||||
>
|
||||
{operation()}
|
||||
</div>
|
||||
<Show when={p.update.status === UpdateStatus.InProgress}>
|
||||
<div style={{ opacity: 0.7 }}>(in progress)</div>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
style={{ width: isMobile() ? "100%" : undefined }}
|
||||
>
|
||||
<Flex gap="0.5rem">
|
||||
<Icon type="user" />
|
||||
<div>{usernames.get(p.update.operator)}</div>
|
||||
</Flex>
|
||||
<Flex alignItems="center">
|
||||
<div style={{ "place-self": "center end" }}>
|
||||
{readableMonitorTimestamp(p.update.start_ts)}
|
||||
</div>
|
||||
<UpdateMenu update={p.update} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -45,7 +45,10 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
set(...args);
|
||||
set("updated", true);
|
||||
};
|
||||
const server = () => build.server_id ? servers.get(build.server_id) : undefined;
|
||||
const server = () =>
|
||||
builds.get(params.id)?.server_id
|
||||
? servers.get(builds.get(params.id)!.server_id!)
|
||||
: undefined;
|
||||
|
||||
const load = () => {
|
||||
// console.log("load build");
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Component, createEffect, createSignal, For, Show } from "solid-js";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import {
|
||||
Component,
|
||||
createEffect,
|
||||
createResource,
|
||||
createSignal,
|
||||
For,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { client } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { ServerStatus } from "../../../../types";
|
||||
@@ -40,9 +48,10 @@ const BuildArgs: Component<{}> = (p) => {
|
||||
};
|
||||
|
||||
const EditBuildArgs: Component<{}> = (p) => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { aws_builder_config, builds, serverSecrets } = useAppState();
|
||||
const [show, toggle] = useToggle();
|
||||
const [buildArgs, setBuildArgs] = createSignal("");
|
||||
const params = useParams();
|
||||
const { build, setBuild, server } = useConfig();
|
||||
createEffect(() => {
|
||||
setBuildArgs(
|
||||
@@ -61,18 +70,14 @@ const EditBuildArgs: Component<{}> = (p) => {
|
||||
}
|
||||
toggle();
|
||||
};
|
||||
const [peripherySecrets, setPeripherySecrets] =
|
||||
createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_available_secrets(build.server_id!)
|
||||
.then(setPeripherySecrets);
|
||||
}
|
||||
});
|
||||
const secrets = () => {
|
||||
if (build.server_id) {
|
||||
return peripherySecrets() || [];
|
||||
if (builds.get(params.id)?.server_id) {
|
||||
return (
|
||||
serverSecrets.get(
|
||||
builds.get(params.id)!.server_id!,
|
||||
server()?.status || ServerStatus.NotOk
|
||||
) || []
|
||||
);
|
||||
} else if (build.aws_config) {
|
||||
const ami_name =
|
||||
build.aws_config?.ami_name || aws_builder_config()?.default_ami_name;
|
||||
|
||||
@@ -11,6 +11,8 @@ import BuildArgs from "./BuildArgs";
|
||||
import Version from "./Version";
|
||||
import Repo from "./Repo";
|
||||
import WebhookUrl from "./WebhookUrl";
|
||||
import ExtraArgs from "./ExtraArgs";
|
||||
import UseBuildx from "./UseBuildx";
|
||||
|
||||
const BuildConfig: Component<{}> = (p) => {
|
||||
const { build, reset, save, userCanUpdate } = useConfig();
|
||||
@@ -23,6 +25,8 @@ const BuildConfig: Component<{}> = (p) => {
|
||||
<Docker />
|
||||
<CliBuild />
|
||||
<BuildArgs />
|
||||
<ExtraArgs />
|
||||
<UseBuildx />
|
||||
<Show when={userCanUpdate()}>
|
||||
<WebhookUrl />
|
||||
</Show>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { Component, createEffect, createResource, createSignal, Show } from "solid-js";
|
||||
import {
|
||||
Component,
|
||||
createResource,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { client } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { ServerStatus } from "../../../../types";
|
||||
@@ -10,21 +14,11 @@ import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const Docker: Component<{}> = (p) => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { aws_builder_config, serverDockerAccounts, docker_organizations } = useAppState();
|
||||
const { build, setBuild, server, userCanUpdate } = useConfig();
|
||||
const [dockerOrgs] = createResource(() => client.get_docker_organizations());
|
||||
const [peripheryDockerAccounts, setPeripheryDockerAccounts] =
|
||||
createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_docker_accounts(build.server_id!)
|
||||
.then(setPeripheryDockerAccounts);
|
||||
}
|
||||
});
|
||||
const dockerAccounts = () => {
|
||||
if (build.server_id) {
|
||||
return peripheryDockerAccounts() || [];
|
||||
return serverDockerAccounts.get(build.server_id, server()?.status || ServerStatus.NotOk) || [];
|
||||
} else if (build.aws_config) {
|
||||
const ami_name =
|
||||
build.aws_config?.ami_name || aws_builder_config()?.default_ami_name;
|
||||
@@ -87,7 +81,7 @@ const Docker: Component<{}> = (p) => {
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
<Show when={build.docker_organization || (dockerOrgs() || []).length > 0}>
|
||||
<Show when={build.docker_organization || (docker_organizations() || []).length > 0}>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
@@ -97,7 +91,7 @@ const Docker: Component<{}> = (p) => {
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={build.docker_organization || "none"}
|
||||
items={["none", ...(dockerOrgs() || [])]}
|
||||
items={["none", ...(docker_organizations() || [])]}
|
||||
onSelect={(account) => {
|
||||
setBuild(
|
||||
"docker_organization",
|
||||
|
||||
59
frontend/src/components/build/tabs/config/ExtraArgs.tsx
Normal file
59
frontend/src/components/build/tabs/config/ExtraArgs.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Component, For, Show } from "solid-js";
|
||||
import Icon from "../../../shared/Icon";
|
||||
import Input from "../../../shared/Input";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const ExtraArgs: Component<{}> = (p) => {
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
const onAdd = () => {
|
||||
setBuild("docker_build_args", "extra_args", (extra_args: any) => [
|
||||
...extra_args,
|
||||
"",
|
||||
]);
|
||||
};
|
||||
const onRemove = (index: number) => {
|
||||
setBuild("docker_build_args", "extra_args", (extra_args) =>
|
||||
extra_args!.filter((_, i) => i !== index)
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Grid class="config-item shadow">
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<h1>extra args</h1>
|
||||
<Show when={userCanUpdate()}>
|
||||
<button class="green" onClick={onAdd}>
|
||||
<Icon type="plus" />
|
||||
</button>
|
||||
</Show>
|
||||
</Flex>
|
||||
<For each={[...build.docker_build_args!.extra_args!.keys()]}>
|
||||
{(_, index) => (
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<Input
|
||||
placeholder="--extra-arg=value"
|
||||
value={build.docker_build_args!.extra_args![index()]}
|
||||
style={{ width: "80%" }}
|
||||
onEdit={(value) =>
|
||||
setBuild("docker_build_args", "extra_args", index(), value)
|
||||
}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
<Show when={userCanUpdate()}>
|
||||
<button class="red" onClick={() => onRemove(index())}>
|
||||
<Icon type="minus" />
|
||||
</button>
|
||||
</Show>
|
||||
</Flex>
|
||||
)}
|
||||
</For>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExtraArgs;
|
||||
@@ -1,29 +1,24 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import { Component, Show } from "solid-js";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Input from "../../../shared/Input";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { client } from "../../../..";
|
||||
import { ServerStatus } from "../../../../types";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
|
||||
const Repo: Component<{}> = (p) => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { aws_builder_config, serverGithubAccounts } = useAppState();
|
||||
const { build, setBuild, server, userCanUpdate } = useConfig();
|
||||
const [peripheryGithubAccounts, setPeripheryGithubAccounts] =
|
||||
createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_github_accounts(build.server_id!)
|
||||
.then(setPeripheryGithubAccounts);
|
||||
}
|
||||
});
|
||||
const githubAccounts = () => {
|
||||
if (build.server_id) {
|
||||
return peripheryGithubAccounts() || [];
|
||||
return (
|
||||
serverGithubAccounts.get(
|
||||
build.server_id,
|
||||
server()?.status || ServerStatus.NotOk
|
||||
) || []
|
||||
);
|
||||
} else if (build.aws_config) {
|
||||
const ami_name =
|
||||
build.aws_config?.ami_name || aws_builder_config()?.default_ami_name;
|
||||
|
||||
30
frontend/src/components/build/tabs/config/UseBuildx.tsx
Normal file
30
frontend/src/components/build/tabs/config/UseBuildx.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const UseBuildx: Component<{}> = (p) => {
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
const use_buildx = () => build.docker_build_args?.use_buildx || false;
|
||||
return (
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>use buildx</h1>
|
||||
<Show
|
||||
when={userCanUpdate()}
|
||||
fallback={<div>{use_buildx() ? "enabled" : "disabled"}</div>}
|
||||
>
|
||||
<button
|
||||
class={use_buildx() ? "green" : "red"}
|
||||
onClick={() => setBuild("docker_build_args", "use_buildx", (c) => !c)}
|
||||
>
|
||||
{use_buildx() ? "enabled" : "disabled"}
|
||||
</button>
|
||||
</Show>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default UseBuildx;
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Component, createSignal, Show } from "solid-js";
|
||||
import { version_to_string } from "../../../../util/helpers";
|
||||
import Input from "../../../shared/Input";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, createResource, Show } from "solid-js";
|
||||
import { client } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { getId } from "../../../../util/helpers";
|
||||
import CopyClipboard from "../../../shared/CopyClipboard";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
@@ -8,13 +9,11 @@ import Loading from "../../../shared/loading/Loading";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const ListenerUrl: Component<{}> = (p) => {
|
||||
const { github_webhook_base_url } = useAppState();
|
||||
const { build } = useConfig();
|
||||
const [github_base_url] = createResource(() =>
|
||||
client.get_github_webhook_base_url()
|
||||
);
|
||||
const listenerUrl = () => {
|
||||
if (github_base_url()) {
|
||||
return `${github_base_url()}/api/listener/build/${getId(build)}`;
|
||||
if (github_webhook_base_url()) {
|
||||
return `${github_webhook_base_url()}/api/listener/build/${getId(build)}`;
|
||||
}
|
||||
};
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, createResource, Show } from "solid-js";
|
||||
import { Component, createResource, createSignal, Show } from "solid-js";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
import {
|
||||
@@ -19,6 +19,8 @@ import { A, useParams } from "@solidjs/router";
|
||||
import { client } from "../..";
|
||||
import CopyMenu from "../CopyMenu";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
import Loading from "../shared/loading/Loading";
|
||||
import { AutofocusInput } from "../shared/Input";
|
||||
|
||||
const Header: Component<{}> = (p) => {
|
||||
const { deployments, servers, builds } = useAppState();
|
||||
@@ -71,6 +73,8 @@ const Header: Component<{}> = (p) => {
|
||||
return "unknown";
|
||||
}
|
||||
};
|
||||
const [editingName, setEditingName] = createSignal(false);
|
||||
const [updatingName, setUpdatingName] = createSignal(false);
|
||||
return (
|
||||
<>
|
||||
<Grid
|
||||
@@ -87,8 +91,46 @@ const Header: Component<{}> = (p) => {
|
||||
>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<Flex alignItems="center">
|
||||
<h1>{deployment()!.deployment.name}</h1>
|
||||
<div style={{ opacity: 0.7 }}>{image()}</div>
|
||||
<Show
|
||||
when={editingName()}
|
||||
fallback={
|
||||
<button
|
||||
onClick={() => setEditingName(true)}
|
||||
style={{ padding: 0 }}
|
||||
>
|
||||
<h1>{deployment()!.deployment.name}</h1>
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<Show
|
||||
when={!updatingName()}
|
||||
fallback={<Loading type="three-dot" />}
|
||||
>
|
||||
<AutofocusInput
|
||||
value={deployment().deployment.name}
|
||||
placeholder={deployment().deployment.name}
|
||||
onEnter={async (new_name) => {
|
||||
setUpdatingName(true);
|
||||
await client.rename_deployment(params.id, new_name);
|
||||
setEditingName(false);
|
||||
setUpdatingName(false);
|
||||
}}
|
||||
onBlur={() => setEditingName(false)}
|
||||
/>
|
||||
</Show>
|
||||
</Show>
|
||||
<Show
|
||||
when={deployment().deployment.build_id}
|
||||
fallback={<div style={{ opacity: 0.7 }}>{image()}</div>}
|
||||
>
|
||||
<A
|
||||
href={`/build/${deployment().deployment.build_id}`}
|
||||
class="text-hover"
|
||||
style={{ opacity: 0.7, padding: 0 }}
|
||||
>
|
||||
{image()}
|
||||
</A>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Show when={userCanUpdate()}>
|
||||
<Flex alignItems="center">
|
||||
|
||||
@@ -3,16 +3,24 @@ import {
|
||||
Accessor,
|
||||
createContext,
|
||||
createEffect,
|
||||
createResource,
|
||||
createSignal,
|
||||
onCleanup,
|
||||
ParentComponent,
|
||||
Resource,
|
||||
useContext,
|
||||
} from "solid-js";
|
||||
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, ServerStatus, ServerWithStatus } from "../../../../types";
|
||||
import {
|
||||
Deployment,
|
||||
Operation,
|
||||
PermissionLevel,
|
||||
ServerStatus,
|
||||
ServerWithStatus,
|
||||
} from "../../../../types";
|
||||
import { getId } from "../../../../util/helpers";
|
||||
|
||||
type ConfigDeployment = Deployment & {
|
||||
@@ -28,7 +36,7 @@ type State = {
|
||||
server: () => ServerWithStatus | undefined;
|
||||
reset: () => void;
|
||||
save: () => void;
|
||||
networks: Accessor<any[]>;
|
||||
networks: Resource<any[]>;
|
||||
userCanUpdate: () => boolean;
|
||||
};
|
||||
|
||||
@@ -87,19 +95,20 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
};
|
||||
createEffect(load);
|
||||
|
||||
const [networks, setNetworks] = createSignal<any[]>([]);
|
||||
const server = () => servers.get(deployments.get(params.id)!.deployment.server_id);
|
||||
createEffect(() => {
|
||||
const server = () =>
|
||||
servers.get(deployments.get(params.id)!.deployment.server_id);
|
||||
|
||||
const [networks] = createResource(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_docker_networks(deployments.get(params.id)!.deployment.server_id)
|
||||
.then(setNetworks);
|
||||
}
|
||||
return client.get_docker_networks(
|
||||
deployments.get(params.id)!.deployment.server_id
|
||||
);
|
||||
} else return [];
|
||||
});
|
||||
|
||||
const save = () => {
|
||||
setDeployment("updating", true);
|
||||
client.update_deployment(deployment).catch(e => {
|
||||
client.update_deployment(deployment).catch((e) => {
|
||||
console.error(e);
|
||||
pushNotification("bad", "update deployment failed");
|
||||
setDeployment("updating", false);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, createEffect, createSignal } from "solid-js";
|
||||
import { client } from "../../../../..";
|
||||
import { Component } from "solid-js";
|
||||
import { useAppState } from "../../../../../state/StateProvider";
|
||||
import { ServerStatus } from "../../../../../types";
|
||||
import { combineClasses } from "../../../../../util/helpers";
|
||||
import Flex from "../../../../shared/layout/Flex";
|
||||
@@ -7,25 +7,21 @@ import Selector from "../../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const DockerAccount: Component<{}> = (p) => {
|
||||
const { serverDockerAccounts } = useAppState();
|
||||
const { deployment, setDeployment, server, userCanUpdate } = useConfig();
|
||||
const [dockerAccounts, setDockerAccounts] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_docker_accounts(deployment.server_id)
|
||||
.then(setDockerAccounts);
|
||||
}
|
||||
});
|
||||
const dockerAccounts = () =>
|
||||
serverDockerAccounts.get(
|
||||
deployment.server_id,
|
||||
server()?.status || ServerStatus.NotOk
|
||||
) || [];
|
||||
const when_none_selected = () => {
|
||||
if (deployment.build_id) {
|
||||
return "same as build"
|
||||
return "same as build";
|
||||
} else {
|
||||
return "none"
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
const accounts = () => {
|
||||
return [when_none_selected(), ...(dockerAccounts() || [])];
|
||||
}
|
||||
};
|
||||
const accounts = () => [when_none_selected(), ...dockerAccounts()];
|
||||
return (
|
||||
<Flex
|
||||
class={combineClasses("config-item shadow")}
|
||||
@@ -37,10 +33,13 @@ const DockerAccount: Component<{}> = (p) => {
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
items={accounts()}
|
||||
selected={deployment.docker_run_args.docker_account || when_none_selected()}
|
||||
selected={
|
||||
deployment.docker_run_args.docker_account || when_none_selected()
|
||||
}
|
||||
onSelect={(account) =>
|
||||
setDeployment("docker_run_args", {
|
||||
docker_account: account === when_none_selected() ? undefined : account,
|
||||
docker_account:
|
||||
account === when_none_selected() ? undefined : account,
|
||||
})
|
||||
}
|
||||
position="bottom right"
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { Component, createEffect, createSignal, For, Show } from "solid-js";
|
||||
import {
|
||||
Component,
|
||||
createEffect,
|
||||
createResource,
|
||||
createSignal,
|
||||
For,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { client } from "../../../../..";
|
||||
import { useAppState } from "../../../../../state/StateProvider";
|
||||
import { ServerStatus } from "../../../../../types";
|
||||
import {
|
||||
combineClasses,
|
||||
@@ -38,6 +46,7 @@ const Env: Component<{}> = (p) => {
|
||||
};
|
||||
|
||||
const EditDotEnv: Component<{}> = (p) => {
|
||||
const { serverSecrets } = useAppState();
|
||||
const [show, toggle] = useToggle();
|
||||
const [dotenv, setDotEnv] = createSignal("");
|
||||
const { deployment, setDeployment, server } = useConfig();
|
||||
@@ -58,14 +67,11 @@ const EditDotEnv: Component<{}> = (p) => {
|
||||
}
|
||||
toggle();
|
||||
};
|
||||
const [secrets, setSecrets] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_available_secrets(deployment.server_id)
|
||||
.then(setSecrets);
|
||||
}
|
||||
});
|
||||
const secrets = () =>
|
||||
serverSecrets.get(
|
||||
deployment.server_id,
|
||||
server()?.status || ServerStatus.NotOk
|
||||
) || [];
|
||||
let ref: HTMLTextAreaElement;
|
||||
return (
|
||||
<CenterMenu
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import { Component, createResource, Show } from "solid-js";
|
||||
import { client } from "../../../../..";
|
||||
import { useAppState } from "../../../../../state/StateProvider";
|
||||
import { BuildVersionsReponse } from "../../../../../types";
|
||||
import { combineClasses, string_to_version, version_to_string } from "../../../../../util/helpers";
|
||||
import {
|
||||
combineClasses,
|
||||
string_to_version,
|
||||
version_to_string,
|
||||
} from "../../../../../util/helpers";
|
||||
import Input from "../../../../shared/Input";
|
||||
import Flex from "../../../../shared/layout/Flex";
|
||||
import Selector from "../../../../shared/menu/Selector";
|
||||
@@ -11,10 +14,9 @@ import { useConfig } from "../Provider";
|
||||
const Image: Component<{}> = (p) => {
|
||||
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||
const { builds } = useAppState();
|
||||
const [versions, setVersions] = createSignal<BuildVersionsReponse[]>([]);
|
||||
createEffect(() => {
|
||||
const [versions] = createResource(() => {
|
||||
if (deployment.build_id) {
|
||||
client.get_build_versions(deployment.build_id).then(setVersions);
|
||||
return client.get_build_versions(deployment.build_id);
|
||||
}
|
||||
});
|
||||
return (
|
||||
@@ -72,7 +74,9 @@ const Image: Component<{}> = (p) => {
|
||||
}
|
||||
items={[
|
||||
"latest",
|
||||
...versions().map((v) => `v${version_to_string(v.version)}`),
|
||||
...(versions()?.map(
|
||||
(v) => `v${version_to_string(v.version)}`
|
||||
) || []),
|
||||
]}
|
||||
onSelect={(version) => {
|
||||
if (version === "latest") {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, createResource, Show } from "solid-js";
|
||||
import { client } from "../../../../..";
|
||||
import { useAppState } from "../../../../../state/StateProvider";
|
||||
import { getId } from "../../../../../util/helpers";
|
||||
import CopyClipboard from "../../../../shared/CopyClipboard";
|
||||
import Flex from "../../../../shared/layout/Flex";
|
||||
@@ -8,13 +8,11 @@ import Loading from "../../../../shared/loading/Loading";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const WebhookUrl: Component<{}> = (p) => {
|
||||
const { github_webhook_base_url } = useAppState();
|
||||
const { deployment } = useConfig();
|
||||
const [github_base_url] = createResource(() =>
|
||||
client.get_github_webhook_base_url()
|
||||
);
|
||||
const listenerUrl = () => {
|
||||
if (github_base_url()) {
|
||||
return `${github_base_url()}/api/listener/deployment/${getId(
|
||||
if (github_webhook_base_url()) {
|
||||
return `${github_webhook_base_url()}/api/listener/deployment/${getId(
|
||||
deployment
|
||||
)}`;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, createEffect, createSignal } from "solid-js";
|
||||
import { client } from "../../../../..";
|
||||
import { Component, createResource } from "solid-js";
|
||||
import { useAppState } from "../../../../../state/StateProvider";
|
||||
import { ServerStatus } from "../../../../../types";
|
||||
import { combineClasses } from "../../../../../util/helpers";
|
||||
import Input from "../../../../shared/Input";
|
||||
@@ -9,15 +9,13 @@ import Selector from "../../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const Git: Component<{}> = (p) => {
|
||||
const { serverGithubAccounts } = useAppState();
|
||||
const { deployment, server, setDeployment, userCanUpdate } = useConfig();
|
||||
const [githubAccounts, setGithubAccounts] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_github_accounts(deployment.server_id)
|
||||
.then(setGithubAccounts);
|
||||
}
|
||||
});
|
||||
const githubAccounts = () =>
|
||||
serverGithubAccounts.get(
|
||||
deployment.server_id,
|
||||
server()?.status || ServerStatus.NotOk
|
||||
) || [];
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<h1>github config</h1>
|
||||
@@ -56,7 +54,7 @@ const Git: Component<{}> = (p) => {
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={deployment.github_account || "none"}
|
||||
items={["none", ...githubAccounts()!]}
|
||||
items={["none", ...githubAccounts()]}
|
||||
onSelect={(account) => {
|
||||
setDeployment(
|
||||
"github_account",
|
||||
|
||||
@@ -1,126 +1,59 @@
|
||||
import { Component, createMemo, For, Show } from "solid-js";
|
||||
import { Accessor, Component, createMemo } from "solid-js";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { DockerContainerState, ServerStatus } from "../../types";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import Flex from "../shared/layout/Flex";
|
||||
import PieChart, { PieChartSection } from "../shared/PieChart";
|
||||
import { COLORS } from "../../style/colors";
|
||||
import { useAppDimensions } from "../../state/DimensionProvider";
|
||||
|
||||
const PIE_CHART_SIZE = 250;
|
||||
|
||||
const Summary: Component<{}> = (p) => {
|
||||
const { isMobile } = useAppDimensions();
|
||||
const deployentCount = useDeploymentCount();
|
||||
const serverCount = useServerCount();
|
||||
return (
|
||||
<Grid class="card shadow" gridTemplateRows="auto 1fr 1fr 1fr">
|
||||
<h1>summary</h1>
|
||||
<DeploymentsSummary />
|
||||
<ServersSummary />
|
||||
<BuildsSummary />
|
||||
<Grid
|
||||
class="full-size"
|
||||
gridTemplateColumns={isMobile() ? "1fr" : "1fr 1fr"}
|
||||
>
|
||||
<Grid class="card shadow full-size" placeItems="center">
|
||||
<div
|
||||
style={{
|
||||
width: `${PIE_CHART_SIZE}px`,
|
||||
height: `${PIE_CHART_SIZE}px`,
|
||||
}}
|
||||
>
|
||||
<PieChart title="deployments" sections={deployentCount()} />
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid class="card shadow full-size" placeItems="center">
|
||||
<div
|
||||
style={{
|
||||
width: `${PIE_CHART_SIZE}px`,
|
||||
height: `${PIE_CHART_SIZE}px`,
|
||||
}}
|
||||
>
|
||||
<PieChart title="servers" sections={serverCount()} />
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default Summary;
|
||||
|
||||
const SummaryItem: Component<{
|
||||
title: string;
|
||||
metrics: Array<{ title: string; class: string; count?: number }>;
|
||||
}> = (p) => {
|
||||
return (
|
||||
<Flex
|
||||
class="card light shadow wrap"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<h2>{p.title}</h2>
|
||||
<Flex class="wrap">
|
||||
<For each={p.metrics}>
|
||||
{(metric) => (
|
||||
<Show when={metric?.count && metric.count > 0}>
|
||||
<Flex gap="0.4rem" alignItems="center">
|
||||
<div>{metric.title}</div>
|
||||
<h2 class={metric.class}>{metric.count}</h2>
|
||||
</Flex>
|
||||
</Show>
|
||||
)}
|
||||
</For>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const BuildsSummary = () => {
|
||||
const { builds } = useAppState();
|
||||
return (
|
||||
<SummaryItem
|
||||
title="builds"
|
||||
metrics={[
|
||||
{ title: "total", class: "text-green", count: builds.ids()?.length },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const DeploymentsSummary = () => {
|
||||
const deployentCount = useDeploymentCount();
|
||||
return (
|
||||
<SummaryItem
|
||||
title="deployments"
|
||||
metrics={[
|
||||
{
|
||||
title: "total",
|
||||
class: "text-green",
|
||||
count: deployentCount().total,
|
||||
},
|
||||
{
|
||||
title: "running",
|
||||
class: "text-green",
|
||||
count: deployentCount().running,
|
||||
},
|
||||
{
|
||||
title: "stopped",
|
||||
class: "text-red",
|
||||
count: deployentCount().stopped,
|
||||
},
|
||||
{
|
||||
title: "not deployed",
|
||||
class: "text-blue",
|
||||
count: deployentCount().notDeployed,
|
||||
},
|
||||
{
|
||||
title: "unknown",
|
||||
class: "text-blue",
|
||||
count: deployentCount().unknown,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ServersSummary = () => {
|
||||
const serverCount = useServerCount();
|
||||
return (
|
||||
<SummaryItem
|
||||
title="servers"
|
||||
metrics={[
|
||||
{ title: "total", class: "text-green", count: serverCount().total },
|
||||
{ title: "healthy", class: "text-green", count: serverCount().healthy },
|
||||
{
|
||||
title: "unhealthy",
|
||||
class: "text-red",
|
||||
count: serverCount().unhealthy,
|
||||
},
|
||||
{
|
||||
title: "disabled",
|
||||
class: "text-blue",
|
||||
count: serverCount().disabled,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
function useDeploymentCount() {
|
||||
function useDeploymentCount(): Accessor<PieChartSection[]> {
|
||||
const { deployments } = useAppState();
|
||||
const count = createMemo(() => {
|
||||
const ids = deployments.ids();
|
||||
if (!ids)
|
||||
return { total: 0, running: 0, stopped: 0, notDeployed: 0, unknown: 0 };
|
||||
return [
|
||||
{ title: "running", amount: 0, color: COLORS.textgreen },
|
||||
{ title: "stopped", amount: 0, color: COLORS.textred },
|
||||
{ title: "not deployed", amount: 0, color: COLORS.textblue },
|
||||
{ title: "unknown", amount: 0, color: COLORS.textorange },
|
||||
];
|
||||
let running = 0;
|
||||
let stopped = 0;
|
||||
let notDeployed = 0;
|
||||
@@ -137,16 +70,26 @@ function useDeploymentCount() {
|
||||
unknown++;
|
||||
}
|
||||
}
|
||||
return { total: ids.length, running, stopped, notDeployed, unknown };
|
||||
return [
|
||||
{ title: "running", amount: running, color: COLORS.textgreen },
|
||||
{ title: "stopped", amount: stopped, color: COLORS.textred },
|
||||
{ title: "not deployed", amount: notDeployed, color: COLORS.textblue },
|
||||
{ title: "unknown", amount: unknown, color: COLORS.textorange },
|
||||
];
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
function useServerCount() {
|
||||
function useServerCount(): Accessor<PieChartSection[]> {
|
||||
const { servers } = useAppState();
|
||||
const count = createMemo(() => {
|
||||
const ids = servers.ids();
|
||||
if (!ids) return { total: 0, healthy: 0, unhealthy: 0, disabled: 0 };
|
||||
if (!ids)
|
||||
return [
|
||||
{ title: "healthy", amount: 0, color: COLORS.textgreen },
|
||||
{ title: "unhealthy", amount: 0, color: COLORS.textred },
|
||||
{ title: "disabled", amount: 0, color: COLORS.textblue },
|
||||
];
|
||||
let healthy = 0;
|
||||
let unhealthy = 0;
|
||||
let disabled = 0;
|
||||
@@ -160,7 +103,50 @@ function useServerCount() {
|
||||
unhealthy++;
|
||||
}
|
||||
}
|
||||
return { total: ids.length, healthy, unhealthy, disabled };
|
||||
return [
|
||||
{ title: "healthy", amount: healthy, color: COLORS.textgreen },
|
||||
{ title: "unhealthy", amount: unhealthy, color: COLORS.textred },
|
||||
{ title: "disabled", amount: disabled, color: COLORS.textblue },
|
||||
];
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
// const SummaryItem: Component<{
|
||||
// title: string;
|
||||
// metrics: Array<{ title: string; class: string; count?: number }>;
|
||||
// }> = (p) => {
|
||||
// return (
|
||||
// <Flex
|
||||
// class="card light shadow wrap"
|
||||
// justifyContent="space-between"
|
||||
// alignItems="center"
|
||||
// >
|
||||
// <h2>{p.title}</h2>
|
||||
// <Flex class="wrap">
|
||||
// <For each={p.metrics}>
|
||||
// {(metric) => (
|
||||
// <Show when={metric?.count && metric.count > 0}>
|
||||
// <Flex gap="0.4rem" alignItems="center">
|
||||
// <div>{metric.title}</div>
|
||||
// <h2 class={metric.class}>{metric.count}</h2>
|
||||
// </Flex>
|
||||
// </Show>
|
||||
// )}
|
||||
// </For>
|
||||
// </Flex>
|
||||
// </Flex>
|
||||
// );
|
||||
// };
|
||||
|
||||
// const BuildsSummary = () => {
|
||||
// const { builds } = useAppState();
|
||||
// return (
|
||||
// <SummaryItem
|
||||
// title="builds"
|
||||
// metrics={[
|
||||
// { title: "total", class: "text-green", count: builds.ids()?.length },
|
||||
// ]}
|
||||
// />
|
||||
// );
|
||||
// };
|
||||
@@ -14,18 +14,9 @@ import UpdateMenu from "../../update/UpdateMenu";
|
||||
import s from "./update.module.scss";
|
||||
|
||||
const Update: Component<{ update: UpdateType }> = (p) => {
|
||||
const { deployments, servers, builds, usernames } = useAppState();
|
||||
const name = () => {
|
||||
if (p.update.target.type === "Deployment" && deployments.loaded()) {
|
||||
return deployments.get(p.update.target.id!)?.deployment.name || "deleted";
|
||||
} else if (p.update.target.type === "Server" && servers.loaded()) {
|
||||
return servers.get(p.update.target.id)?.server.name || "deleted";
|
||||
} else if (p.update.target.type === "Build" && builds.loaded()) {
|
||||
return builds.get(p.update.target.id)?.name || "deleted";
|
||||
} else {
|
||||
return "monitor";
|
||||
}
|
||||
};
|
||||
const { usernames, name_from_update_target } =
|
||||
useAppState();
|
||||
const name = () => name_from_update_target(p.update.target);
|
||||
const operation = () => {
|
||||
if (p.update.operation === Operation.BuildBuild) {
|
||||
return `build ${readableVersion(p.update.version!)}`;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { A } from "@solidjs/router";
|
||||
import { Component, createEffect, createSignal, For, Show } from "solid-js";
|
||||
import { OPERATIONS } from "../../..";
|
||||
import { useAppState } from "../../../state/StateProvider";
|
||||
import { Operation } from "../../../types";
|
||||
import Flex from "../../shared/layout/Flex";
|
||||
@@ -7,10 +9,6 @@ import Loading from "../../shared/loading/Loading";
|
||||
import Selector from "../../shared/menu/Selector";
|
||||
import Update from "./Update";
|
||||
|
||||
const OPERATIONS = Object.values(Operation)
|
||||
.filter((e) => e !== "none" && !e.includes("user"))
|
||||
.map((e) => e.replaceAll("_", " "));
|
||||
|
||||
const Updates: Component<{}> = () => {
|
||||
const { updates } = useAppState();
|
||||
const [operation, setOperation] = createSignal<Operation>();
|
||||
@@ -24,8 +22,11 @@ const Updates: Component<{}> = () => {
|
||||
return (
|
||||
<Grid class="card shadow" style={{ "flex-grow": 1 }}>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>updates</h1>
|
||||
<A href="/updates" style={{ padding: 0 }}>
|
||||
<h1>updates</h1>
|
||||
</A>
|
||||
<Selector
|
||||
label="operation: "
|
||||
selected={operation() ? operation()! : "all"}
|
||||
items={["all", ...OPERATIONS]}
|
||||
onSelect={(o) =>
|
||||
@@ -50,7 +51,7 @@ const Updates: Component<{}> = () => {
|
||||
}
|
||||
>
|
||||
<Grid class="updates-container-small scroller">
|
||||
<For each={updates.collection()!}>
|
||||
<For each={updates.collection()}>
|
||||
{(update) => <Update update={update} />}
|
||||
</For>
|
||||
<Show when={!updates.noMore()}>
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ServerButton:hover {
|
||||
background-color: rgba(c.$lightblue, 0.5);
|
||||
}
|
||||
// .ServerButton:hover {
|
||||
// background-color: rgba(c.$lightblue, 0.5);
|
||||
// }
|
||||
|
||||
.Deployments {
|
||||
background-color: c.$lightgrey;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Component, createResource, Show } from "solid-js";
|
||||
import { Component, createResource, createSignal, Show } from "solid-js";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
import { combineClasses, getId, serverStatusClass } 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";
|
||||
@@ -15,6 +14,7 @@ import { client } from "../..";
|
||||
import Loading from "../shared/loading/Loading";
|
||||
import HoverMenu from "../shared/menu/HoverMenu";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
import Input, { AutofocusInput } from "../shared/Input";
|
||||
|
||||
const Header: Component<{}> = (p) => {
|
||||
const { servers } = useAppState();
|
||||
@@ -25,6 +25,8 @@ const Header: Component<{}> = (p) => {
|
||||
const { isMobile, isSemiMobile } = useAppDimensions();
|
||||
const [showUpdates, toggleShowUpdates] =
|
||||
useLocalStorageToggle("show-updates");
|
||||
const [editingName, setEditingName] = createSignal(false);
|
||||
const [updatingName, setUpdatingName] = createSignal(false);
|
||||
const userCanUpdate = () =>
|
||||
user().admin ||
|
||||
server().server.permissions![getId(user())] === PermissionLevel.Update;
|
||||
@@ -50,7 +52,38 @@ const Header: Component<{}> = (p) => {
|
||||
}}
|
||||
>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>{server().server.name}</h1>
|
||||
<Show
|
||||
when={editingName()}
|
||||
fallback={
|
||||
<button
|
||||
onClick={() => setEditingName(true)}
|
||||
style={{ padding: 0 }}
|
||||
>
|
||||
<h1>{server().server.name}</h1>
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<Show
|
||||
when={!updatingName()}
|
||||
fallback={<Loading type="three-dot" />}
|
||||
>
|
||||
<AutofocusInput
|
||||
value={server().server.name}
|
||||
placeholder={server().server.name}
|
||||
onEnter={async (new_name) => {
|
||||
setUpdatingName(true);
|
||||
await client.update_server({
|
||||
...server().server,
|
||||
name: new_name,
|
||||
});
|
||||
setEditingName(false);
|
||||
setUpdatingName(false);
|
||||
}}
|
||||
onBlur={() => setEditingName(false)}
|
||||
|
||||
/>
|
||||
</Show>
|
||||
</Show>
|
||||
<Show when={userCanUpdate()}>
|
||||
<Flex alignItems="center">
|
||||
<div class={serverStatusClass(server().status)}>{status()}</div>
|
||||
|
||||
@@ -7,7 +7,6 @@ import { readableStorageAmount } from "../../../util/helpers";
|
||||
import Flex from "../../shared/layout/Flex";
|
||||
import Grid from "../../shared/layout/Grid";
|
||||
import Loading from "../../shared/loading/Loading";
|
||||
import HoverMenu from "../../shared/menu/HoverMenu";
|
||||
|
||||
const Info: Component<{}> = (p) => {
|
||||
const { isMobile } = useAppDimensions();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Component, JSX } from "solid-js";
|
||||
import Flex from "./layout/Flex";
|
||||
|
||||
const CheckBox: Component<{
|
||||
label: JSX.Element;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Component, JSX, Show } from "solid-js";
|
||||
import { Component, JSX, onMount, Show } from "solid-js";
|
||||
|
||||
const Input: Component<
|
||||
{
|
||||
onEdit?: (value: string) => void;
|
||||
onConfirm?: (value: string) => void;
|
||||
onEnter?: (value: string) => void;
|
||||
onEsc?: (value: string) => void;
|
||||
disabled?: boolean;
|
||||
} & JSX.InputHTMLAttributes<HTMLInputElement> &
|
||||
JSX.HTMLAttributes<HTMLDivElement>
|
||||
@@ -14,17 +15,37 @@ const Input: Component<
|
||||
<input
|
||||
{...p}
|
||||
onInput={(e) => p.onEdit && p.onEdit(e.currentTarget.value)}
|
||||
onBlur={(e) => p.onConfirm && p.onConfirm(e.currentTarget.value)}
|
||||
onKeyDown={p.onKeyDown || ((e) => {
|
||||
if (e.key === "Enter") {
|
||||
p.onEnter
|
||||
? p.onEnter(e.currentTarget.value)
|
||||
: e.currentTarget.blur();
|
||||
}
|
||||
})}
|
||||
onBlur={
|
||||
p.onBlur || ((e) => p.onConfirm && p.onConfirm(e.currentTarget.value))
|
||||
}
|
||||
onKeyDown={
|
||||
p.onKeyDown ||
|
||||
((e) => {
|
||||
if (e.key === "Enter") {
|
||||
p.onEnter && p.onEnter(e.currentTarget.value);
|
||||
} else if (e.key === "Escape") {
|
||||
p.onEsc ? p.onEsc(e.currentTarget.value) : e.currentTarget.blur();
|
||||
}
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
export default Input;
|
||||
|
||||
export const AutofocusInput: Component<
|
||||
{
|
||||
onEdit?: (value: string) => void;
|
||||
onConfirm?: (value: string) => void;
|
||||
onEnter?: (value: string) => void;
|
||||
onEsc?: (value: string) => void;
|
||||
disabled?: boolean;
|
||||
} & JSX.InputHTMLAttributes<HTMLInputElement> &
|
||||
JSX.HTMLAttributes<HTMLDivElement>
|
||||
> = (p) => {
|
||||
let ref: HTMLInputElement;
|
||||
onMount(() => setTimeout(() => ref?.focus(), 100));
|
||||
return <Input ref={ref! as any} {...p} />;
|
||||
};
|
||||
|
||||
241
frontend/src/components/shared/PieChart.tsx
Normal file
241
frontend/src/components/shared/PieChart.tsx
Normal file
@@ -0,0 +1,241 @@
|
||||
import {
|
||||
Component,
|
||||
createEffect,
|
||||
createMemo,
|
||||
createSignal,
|
||||
For,
|
||||
onCleanup,
|
||||
onMount,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import Grid from "./layout/Grid";
|
||||
|
||||
export type PieChartSection = {
|
||||
title: string;
|
||||
amount: number;
|
||||
color: string;
|
||||
};
|
||||
|
||||
const PieChart: Component<{
|
||||
title: string;
|
||||
sections: (PieChartSection | undefined)[];
|
||||
donutProportion?: number;
|
||||
seperation?: number;
|
||||
}> = (p) => {
|
||||
let ref: HTMLDivElement;
|
||||
let canvas: HTMLCanvasElement;
|
||||
const [chart, setChart] = createSignal<PieChartCanvas>();
|
||||
const [selected, setSelected] = createSignal<number>();
|
||||
const sections = createMemo(
|
||||
() =>
|
||||
p.sections
|
||||
.filter((s) => s && s.amount > 0)
|
||||
.sort((a, b) => {
|
||||
if (a!.amount > b!.amount) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}) as PieChartSection[]
|
||||
);
|
||||
const onResize = () =>
|
||||
chart()?.updateCanvasDim(ref.clientWidth, ref.clientHeight);
|
||||
onMount(() => {
|
||||
const chart = new PieChartCanvas(
|
||||
canvas,
|
||||
sections(),
|
||||
setSelected,
|
||||
p.donutProportion,
|
||||
p.seperation
|
||||
);
|
||||
setChart(chart);
|
||||
onResize();
|
||||
window.addEventListener("resize", onResize);
|
||||
});
|
||||
onCleanup(() => {
|
||||
window.removeEventListener("resize", onResize);
|
||||
});
|
||||
createEffect(() => {
|
||||
chart()?.updateSections(sections());
|
||||
chart()?.draw();
|
||||
});
|
||||
return (
|
||||
<Grid
|
||||
ref={ref!}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
"box-sizing": "border-box",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
placeItems="center"
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "grid", gap: "0.2rem" }}>
|
||||
<h2 style={{ "margin-bottom": "0.5rem" }}>{p.title}</h2>
|
||||
<For each={sections()}>
|
||||
{(section, index) => (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "0.5rem",
|
||||
"justify-content": "space-between",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
opacity: selected() === index() ? 1 : 0.7,
|
||||
}}
|
||||
>
|
||||
{section.title}:
|
||||
</div>
|
||||
<div style={{ color: section.color }}>{section.amount}</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
<Show when={sections().length === 0}>
|
||||
<div style={{ opacity: 0.7 }}>none</div>
|
||||
</Show>
|
||||
</div>
|
||||
</Grid>
|
||||
<canvas ref={canvas!} style={{ "z-index": 1 }} />
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default PieChart;
|
||||
|
||||
type InnerPieChartSection = PieChartSection & {
|
||||
startAngle: number;
|
||||
endAngle: number;
|
||||
};
|
||||
|
||||
class PieChartCanvas {
|
||||
sections: InnerPieChartSection[];
|
||||
selected?: number;
|
||||
cx = 0;
|
||||
cy = 0;
|
||||
r = 0;
|
||||
|
||||
constructor(
|
||||
private canvas: HTMLCanvasElement,
|
||||
sections: PieChartSection[],
|
||||
private onSelectedUpdate: (selected: number | undefined) => void,
|
||||
private donutProportion = 0.8,
|
||||
private seperation = 0.02 // private initAngle = -Math.PI / 8
|
||||
) {
|
||||
this.sections = [];
|
||||
this.updateSections(sections);
|
||||
this.canvas.addEventListener("mousemove", (e) => this.onMouseOver(e));
|
||||
this.canvas.addEventListener("mouseout", () => {
|
||||
this.selected = undefined;
|
||||
this.onSelectedUpdate(this.selected);
|
||||
this.draw();
|
||||
});
|
||||
}
|
||||
|
||||
draw() {
|
||||
const ctx = this.canvas.getContext("2d");
|
||||
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
for (const segIndex in this.sections) {
|
||||
const seg = this.sections[segIndex];
|
||||
const outerStartAngle = seg.startAngle + this.seperation;
|
||||
const outerEndAngle = seg.endAngle - this.seperation;
|
||||
const innerStartAngle =
|
||||
seg.startAngle + this.seperation / this.donutProportion;
|
||||
const innerEndAngle =
|
||||
seg.endAngle - this.seperation / this.donutProportion;
|
||||
|
||||
ctx.fillStyle =
|
||||
Number(segIndex) === this.selected ? seg.color : `${seg.color}B3`;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(
|
||||
this.cx + this.donutProportion * this.r * Math.cos(innerStartAngle),
|
||||
this.cy + this.donutProportion * this.r * Math.sin(innerStartAngle)
|
||||
);
|
||||
ctx.lineTo(
|
||||
this.cx + this.r * Math.cos(outerStartAngle),
|
||||
this.cy + this.r * Math.sin(outerStartAngle)
|
||||
);
|
||||
ctx.arc(this.cx, this.cy, this.r, outerStartAngle, outerEndAngle);
|
||||
ctx.lineTo(
|
||||
this.cx + this.donutProportion * this.r * Math.cos(innerEndAngle),
|
||||
this.cy + this.donutProportion * this.r * Math.sin(innerEndAngle)
|
||||
);
|
||||
ctx.arc(
|
||||
this.cx,
|
||||
this.cy,
|
||||
this.donutProportion * this.r,
|
||||
innerEndAngle,
|
||||
innerStartAngle,
|
||||
true
|
||||
);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
updateSections(sections: PieChartSection[]) {
|
||||
let startAngle = 0;
|
||||
const total = sections.reduce((prev, curr) => prev + curr.amount, 0);
|
||||
this.sections = sections.map((s) => {
|
||||
const proportion = s.amount / total;
|
||||
const rads = Math.PI * 2 * proportion;
|
||||
startAngle += rads;
|
||||
return {
|
||||
...s,
|
||||
startAngle: startAngle - rads,
|
||||
endAngle: startAngle,
|
||||
};
|
||||
});
|
||||
this.draw();
|
||||
}
|
||||
|
||||
onMouseOver(e: MouseEvent) {
|
||||
const rect = this.canvas.getBoundingClientRect();
|
||||
const x = e.x - rect.x - this.cx;
|
||||
const y = e.y - rect.y - this.cy;
|
||||
if (x * x + y * y > this.r * this.r) {
|
||||
this.selected = undefined;
|
||||
this.onSelectedUpdate(this.selected);
|
||||
this.draw();
|
||||
return;
|
||||
}
|
||||
const atan = Math.atan(y / x);
|
||||
const angle =
|
||||
x >= 0 ? (y >= 0 ? atan : 2 * Math.PI + atan) : Math.PI + atan;
|
||||
for (const secIndex in this.sections) {
|
||||
if (angle < this.sections[secIndex].endAngle) {
|
||||
this.selected = Number(secIndex);
|
||||
this.onSelectedUpdate(this.selected);
|
||||
this.draw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateCanvasDim(width: number, height: number) {
|
||||
if (width <= 0 || height <= 0) return;
|
||||
this.canvas.width = width;
|
||||
this.canvas.height = height;
|
||||
this.cx = this.canvas.width / 2;
|
||||
this.cy = this.canvas.height / 2;
|
||||
this.r =
|
||||
this.canvas.width < this.canvas.height
|
||||
? this.canvas.width / 2 - 2
|
||||
: this.canvas.height / 2 - 2;
|
||||
this.draw();
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ const Child: Component<{
|
||||
>
|
||||
<Grid
|
||||
class={combineClasses(s.Menu, "shadow")}
|
||||
style={{ padding: (p.padding as any) || "1rem", ...p.style }}
|
||||
style={{ padding: (p.padding as any) || "2rem", ...p.style }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
|
||||
@@ -37,10 +37,10 @@ const Selector: Component<{
|
||||
}> = (p) => {
|
||||
const [show, toggle] = useToggle();
|
||||
const [search, setSearch] = createSignal("");
|
||||
let ref: HTMLInputElement | undefined;
|
||||
let search_ref: HTMLInputElement | undefined;
|
||||
const current = () => (p.itemMap ? p.itemMap(p.selected) : p.selected);
|
||||
createEffect(() => {
|
||||
if (show()) setTimeout(() => ref?.focus(), 200);
|
||||
if (show()) setTimeout(() => search_ref?.focus(), 200);
|
||||
});
|
||||
return (
|
||||
<Show
|
||||
@@ -70,7 +70,7 @@ const Selector: Component<{
|
||||
<>
|
||||
<Show when={p.useSearch}>
|
||||
<Input
|
||||
ref={ref}
|
||||
ref={search_ref}
|
||||
placeholder="search"
|
||||
value={search()}
|
||||
onEdit={setSearch}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
width: fit-content;
|
||||
/* border: solid 1px rgba(2, 107, 121, 0.25); */
|
||||
background-color: c.$grey;
|
||||
border: solid c.$darkgrey 2px;
|
||||
z-index: 21;
|
||||
border-radius: 0.25rem;
|
||||
box-sizing: border-box;
|
||||
@@ -142,6 +143,11 @@ $anim-time: 350ms;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.CenterMenuHeader {
|
||||
border-bottom: solid rgba(c.$lightgrey, 0.9) 2px;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.SelectorItem:hover {
|
||||
background-color: c.$lightgrey;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
.TabTitle {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 0.25rem 0.5rem;
|
||||
padding: 0.75rem;
|
||||
border-radius: 0rem;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
|
||||
@@ -1,35 +1,41 @@
|
||||
import { LineData, SingleValueData } from "lightweight-charts";
|
||||
import { Accessor, Component, For, ParentComponent, Show } from "solid-js";
|
||||
import {
|
||||
Accessor,
|
||||
Component,
|
||||
For,
|
||||
JSXElement,
|
||||
ParentComponent,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { COLORS } from "../../style/colors";
|
||||
import { SystemStats, SystemStatsRecord } from "../../types";
|
||||
import {
|
||||
convertTsMsToLocalUnixTsInSecs,
|
||||
get_to_one_sec_divisor,
|
||||
} from "../../util/helpers";
|
||||
import { useLocalStorage, useLocalStorageToggle } from "../../util/hooks";
|
||||
import Flex from "../shared/layout/Flex";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import LightweightChart, { LightweightValue } from "../shared/LightweightChart";
|
||||
import s from "./stats.module.scss";
|
||||
|
||||
export const COLORS = {
|
||||
blue: "#184e9f",
|
||||
orange: "#ac5c36",
|
||||
purple: "#5A0B4D",
|
||||
green: "#41764c",
|
||||
red: "#952E23",
|
||||
};
|
||||
|
||||
const CHART_HEIGHT = "250px";
|
||||
const SMALL_CHART_HEIGHT = "150px";
|
||||
|
||||
const SingleStatChart: Component<{
|
||||
line?: LightweightValue[];
|
||||
header: string;
|
||||
headerRight?: JSXElement;
|
||||
label: string;
|
||||
color: string;
|
||||
small?: boolean;
|
||||
disableScroll?: boolean;
|
||||
}> = (p) => {
|
||||
return (
|
||||
<StatChartContainer header={p.header} small={p.small}>
|
||||
<StatChartContainer
|
||||
header={p.header}
|
||||
headerRight={p.headerRight}
|
||||
small={p.small}
|
||||
>
|
||||
<Show when={p.line}>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
@@ -52,23 +58,25 @@ const SingleStatChart: Component<{
|
||||
|
||||
const StatChartContainer: ParentComponent<{
|
||||
header: string;
|
||||
headerRight?: JSXElement;
|
||||
small?: boolean;
|
||||
}> = (p) => {
|
||||
return (
|
||||
<Grid
|
||||
gap="0.5rem"
|
||||
class="card shadow"
|
||||
class="card shadow full-width"
|
||||
style={{
|
||||
height: "fit-content",
|
||||
width: "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-top": "0.5rem",
|
||||
"padding-bottom": "0.2rem",
|
||||
}}
|
||||
>
|
||||
<Show when={!p.small} fallback={<div>{p.header}</div>}>
|
||||
<h2>{p.header}</h2>
|
||||
</Show>
|
||||
<Flex justifyContent="space-between">
|
||||
<Show when={!p.small} fallback={<div>{p.header}</div>}>
|
||||
<h2>{p.header}</h2>
|
||||
</Show>
|
||||
{p.headerRight}
|
||||
</Flex>
|
||||
{p.children}
|
||||
</Grid>
|
||||
);
|
||||
@@ -160,20 +168,42 @@ export const MemChart: Component<{
|
||||
small?: boolean;
|
||||
disableScroll?: boolean;
|
||||
}> = (p) => {
|
||||
const [absolute, toggleAbsolute] = useLocalStorageToggle("stats-mem-mode-v2");
|
||||
const symbol = () => (absolute() ? "GiB" : "%");
|
||||
const line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value: (100 * s.mem_used_gb) / s.mem_total_gb,
|
||||
};
|
||||
});
|
||||
if (absolute()) {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value: s.mem_used_gb,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value: (100 * s.mem_used_gb) / s.mem_total_gb,
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
return (
|
||||
<SingleStatChart
|
||||
header="memory"
|
||||
label="mem %"
|
||||
headerRight={
|
||||
<button
|
||||
class="green"
|
||||
style={{ padding: "0.2rem" }}
|
||||
onClick={toggleAbsolute}
|
||||
>
|
||||
{symbol()}
|
||||
</button>
|
||||
}
|
||||
label={`mem ${symbol()}`}
|
||||
color={COLORS.green}
|
||||
line={line()}
|
||||
small={p.small}
|
||||
@@ -187,20 +217,43 @@ export const DiskChart: Component<{
|
||||
small?: boolean;
|
||||
disableScroll?: boolean;
|
||||
}> = (p) => {
|
||||
const [absolute, toggleAbsolute] =
|
||||
useLocalStorageToggle("stats-disk-mode-v2");
|
||||
const symbol = () => (absolute() ? "GiB" : "%");
|
||||
const line = () => {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value: (100 * s.disk.used_gb) / s.disk.total_gb,
|
||||
};
|
||||
});
|
||||
if (absolute()) {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value: s.disk.used_gb,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(
|
||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||
),
|
||||
value: (100 * s.disk.used_gb) / s.disk.total_gb,
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
return (
|
||||
<SingleStatChart
|
||||
header="disk"
|
||||
label="disk %"
|
||||
headerRight={
|
||||
<button
|
||||
class="orange"
|
||||
style={{ padding: "0.2rem" }}
|
||||
onClick={toggleAbsolute}
|
||||
>
|
||||
{symbol()}
|
||||
</button>
|
||||
}
|
||||
label={`disk ${symbol()}`}
|
||||
color={COLORS.orange}
|
||||
line={line()}
|
||||
small={p.small}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
Show,
|
||||
Switch,
|
||||
} from "solid-js";
|
||||
import { client, MAX_PAGE_WIDTH } from "../..";
|
||||
import { client } from "../..";
|
||||
import { SystemProcess, SystemStats } from "../../types";
|
||||
import { convert_timelength_to_ms } from "../../util/helpers";
|
||||
import { useLocalStorage } from "../../util/hooks";
|
||||
|
||||
@@ -29,20 +29,23 @@ const HistoricalStats: Component<{
|
||||
const params = useParams();
|
||||
const { timelength, page } = useStatsState();
|
||||
const [stats, setStats] = createSignal<SystemStatsRecord[]>();
|
||||
createEffect(() => {
|
||||
client
|
||||
const [loading, setLoading] = createSignal(false);
|
||||
createEffect(async () => {
|
||||
setLoading(true);
|
||||
const stats = await client
|
||||
.get_server_stats_history(params.id, {
|
||||
interval: timelength(),
|
||||
page: page(),
|
||||
limit: 500,
|
||||
networks: true,
|
||||
components: true,
|
||||
})
|
||||
.then(setStats);
|
||||
});
|
||||
setStats(stats);
|
||||
setLoading(false);
|
||||
});
|
||||
return (
|
||||
<Grid class={s.Content} placeItems="start center">
|
||||
<Show when={stats()} fallback={<Loading type="three-dot" />}>
|
||||
<Show when={stats() && !loading()} fallback={<Loading type="three-dot" />}>
|
||||
<SimpleTabs
|
||||
localStorageKey="historical-stats-view-v3"
|
||||
defaultSelected="basic"
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { ParentComponent, createContext, useContext, createSignal, createResource } from "solid-js";
|
||||
import { client } from "../..";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { SystemInformation, Timelength } from "../../types";
|
||||
import { useLocalStorage } from "../../util/hooks";
|
||||
|
||||
export enum StatsView {
|
||||
Current = "current",
|
||||
Historical = "historical",
|
||||
Info = "info"
|
||||
}
|
||||
|
||||
const value = () => {
|
||||
const params = useParams();
|
||||
const [view, setView] = useLocalStorage("current", "stats-view-v1");
|
||||
const [view, setView] = useLocalStorage(StatsView.Current, "stats-view-v2");
|
||||
const [timelength, setTimelength] = useLocalStorage(
|
||||
Timelength.OneMinute,
|
||||
"stats-timelength-v3"
|
||||
@@ -16,12 +23,7 @@ const value = () => {
|
||||
`${params.id}-stats-poll-v3`
|
||||
);
|
||||
const [page, setPage] = createSignal(0);
|
||||
// const [wsOpen, setWsOpen] = createSignal(false);
|
||||
const [sysInfo] = createResource<SystemInformation>(() =>
|
||||
client.get_server_system_info(params.id)
|
||||
);
|
||||
return {
|
||||
sysInfo,
|
||||
view,
|
||||
setView,
|
||||
timelength,
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import { A, useParams } from "@solidjs/router";
|
||||
import {
|
||||
Component,
|
||||
Match,
|
||||
Show,
|
||||
Switch,
|
||||
} from "solid-js";
|
||||
import { MAX_PAGE_WIDTH } from "../..";
|
||||
import { Component, createResource, For, Match, Show, Switch } from "solid-js";
|
||||
import { client } from "../..";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { ServerStatus, Timelength } from "../../types";
|
||||
import { readableStorageAmount } from "../../util/helpers";
|
||||
import Icon from "../shared/Icon";
|
||||
import Flex from "../shared/layout/Flex";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import Selector from "../shared/menu/Selector";
|
||||
import CurrentStats from "./CurrentStats";
|
||||
import HistoricalStats from "./HistoricalStats";
|
||||
import { StatsProvider, useStatsState } from "./Provider";
|
||||
import { StatsProvider, useStatsState, StatsView } from "./Provider";
|
||||
|
||||
const TIMELENGTHS = [
|
||||
Timelength.FifteenSeconds,
|
||||
@@ -38,115 +34,182 @@ const Stats = () => {
|
||||
const StatsComp: Component<{}> = () => {
|
||||
const { view } = useStatsState();
|
||||
return (
|
||||
<Grid
|
||||
style={{
|
||||
width: "100%",
|
||||
"box-sizing": "border-box",
|
||||
}}
|
||||
>
|
||||
<Flex justifyContent="space-between" style={{ width: "100%" }}>
|
||||
<Header />
|
||||
<SysInfo />
|
||||
</Flex>
|
||||
<Show when={view() === "historical"}>
|
||||
<Grid class="full-width">
|
||||
<Header />
|
||||
<Show when={view() === StatsView.Historical}>
|
||||
<Flex alignItems="center" style={{ "place-self": "center" }}>
|
||||
<PageManager />
|
||||
</Flex>
|
||||
</Show>
|
||||
<Switch>
|
||||
<Match when={view() === "current"}>
|
||||
<Match when={view() === StatsView.Current}>
|
||||
<CurrentStats />
|
||||
</Match>
|
||||
<Match when={view() === "historical"}>
|
||||
<Match when={view() === StatsView.Historical}>
|
||||
<HistoricalStats />
|
||||
</Match>
|
||||
<Match when={view() === StatsView.Info}>
|
||||
<SysInfo />
|
||||
</Match>
|
||||
</Switch>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export const Header: Component<{}> = (p) => {
|
||||
const { servers } = useAppState();
|
||||
const { servers, serverInfo } = useAppState();
|
||||
const params = useParams();
|
||||
const server = () => servers.get(params.id);
|
||||
const { view, setView, timelength, setTimelength, setPage, pollRate, setPollRate } = useStatsState();
|
||||
const {
|
||||
view,
|
||||
setView,
|
||||
timelength,
|
||||
setTimelength,
|
||||
setPage,
|
||||
pollRate,
|
||||
setPollRate,
|
||||
} = useStatsState();
|
||||
const sysInfo = () => serverInfo.get(params.id);
|
||||
return (
|
||||
<Flex alignItems="center" style={{ height: "fit-content" }}>
|
||||
<h1>{server()?.server.name}</h1>
|
||||
<A
|
||||
href={`/server/${params.id}`}
|
||||
class={
|
||||
server()?.server.enabled
|
||||
? server()?.status === ServerStatus.Ok
|
||||
? "green"
|
||||
: "red"
|
||||
: "blue"
|
||||
}
|
||||
style={{
|
||||
"border-radius": ".35rem",
|
||||
transition: "background-color 125ms ease-in-out",
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{server()?.status.replaceAll("_", " ").toUpperCase()}
|
||||
</A>
|
||||
<Grid gap="0" gridTemplateColumns="repeat(2, 1fr)">
|
||||
<button
|
||||
class={view() === "current" ? "selected" : "grey"}
|
||||
style={{ width: "100%" }}
|
||||
onClick={() => setView("current")}
|
||||
>
|
||||
current
|
||||
</button>
|
||||
<button
|
||||
class={view() === "historical" ? "selected" : "grey"}
|
||||
style={{ width: "100%" }}
|
||||
onClick={() => setView("historical")}
|
||||
>
|
||||
historical
|
||||
</button>
|
||||
</Grid>
|
||||
<Show when={view() === "historical"}>
|
||||
<Selector
|
||||
targetClass="grey"
|
||||
selected={timelength()}
|
||||
items={TIMELENGTHS}
|
||||
onSelect={(selected) => {
|
||||
setPage(0);
|
||||
setTimelength(selected as Timelength);
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<Flex alignItems="center" style={{ height: "fit-content" }}>
|
||||
<h1>{server()?.server.name}</h1>
|
||||
<A
|
||||
href={`/server/${params.id}`}
|
||||
class={
|
||||
server()?.server.enabled
|
||||
? server()?.status === ServerStatus.Ok
|
||||
? "green"
|
||||
: "red"
|
||||
: "blue"
|
||||
}
|
||||
style={{
|
||||
"border-radius": ".35rem",
|
||||
transition: "background-color 125ms ease-in-out",
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{server()?.status.replaceAll("_", " ").toUpperCase()}
|
||||
</A>
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={view()}
|
||||
items={Object.values(StatsView)}
|
||||
onSelect={(v) => setView(v as StatsView)}
|
||||
position="bottom right"
|
||||
/>
|
||||
</Show>
|
||||
<Show when={view() === "current"}>
|
||||
<Flex gap="0.5rem" alignItems="center">
|
||||
<div>poll:</div>
|
||||
<Show when={view() === "historical"}>
|
||||
<Selector
|
||||
targetClass="grey"
|
||||
selected={timelength()}
|
||||
items={TIMELENGTHS}
|
||||
itemMap={(t) => t.replaceAll("-", " ")}
|
||||
itemClass="full-width"
|
||||
onSelect={(selected) => {
|
||||
setPage(0);
|
||||
setTimelength(selected as Timelength);
|
||||
}}
|
||||
position="bottom right"
|
||||
/>
|
||||
</Show>
|
||||
<Show when={view() === "current"}>
|
||||
<Selector
|
||||
targetClass="grey"
|
||||
label="poll: "
|
||||
selected={pollRate()}
|
||||
items={[Timelength.OneSecond, Timelength.FiveSeconds]}
|
||||
onSelect={(selected) => {
|
||||
setPollRate(selected as Timelength);
|
||||
}}
|
||||
position="bottom right"
|
||||
/>
|
||||
</Flex>
|
||||
</Show>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<div>{sysInfo()?.cpu_brand}</div>
|
||||
<div>
|
||||
{sysInfo()?.core_count} core
|
||||
{sysInfo()?.core_count && sysInfo()?.core_count! > 1 ? "s" : ""}
|
||||
</div>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const SysInfo = () => {
|
||||
const { sysInfo } = useStatsState();
|
||||
const { serverInfo } = useAppState();
|
||||
const params = useParams();
|
||||
const sysInfo = () => serverInfo.get(params.id);
|
||||
const [stats] = createResource(() =>
|
||||
client.get_server_stats(params.id, { disks: true })
|
||||
);
|
||||
const os_cards = () => {
|
||||
return [
|
||||
{
|
||||
label: "os",
|
||||
info: sysInfo()?.os,
|
||||
},
|
||||
{
|
||||
label: "kernel",
|
||||
info: sysInfo()?.kernel,
|
||||
},
|
||||
].filter((i) => i.info) as Array<{ label: string; info: string }>;
|
||||
};
|
||||
const cpu_cards = () => {
|
||||
return [
|
||||
{
|
||||
label: "cpu",
|
||||
info: sysInfo()?.cpu_brand,
|
||||
},
|
||||
{
|
||||
label: "core count",
|
||||
info: `${sysInfo()?.core_count} cores`,
|
||||
},
|
||||
].filter((i) => i.info) as Array<{ label: string; info: string }>;
|
||||
};
|
||||
const stats_cards = () => {
|
||||
return [
|
||||
{
|
||||
label: "mem",
|
||||
info:
|
||||
stats()?.mem_total_gb &&
|
||||
readableStorageAmount(stats()?.mem_total_gb!),
|
||||
},
|
||||
{
|
||||
label: "disk",
|
||||
info:
|
||||
stats()?.disk.total_gb &&
|
||||
readableStorageAmount(stats()?.disk.total_gb!),
|
||||
},
|
||||
].filter((i) => i.info) as Array<{ label: string; info: string }>;
|
||||
};
|
||||
return (
|
||||
<Flex
|
||||
alignItems="center"
|
||||
style={{ "place-self": "center end", width: "fit-content" }}
|
||||
>
|
||||
<div>{sysInfo()?.os}</div>
|
||||
{/* <div>{sysInfo()?.kernel}</div> */}
|
||||
<div>{sysInfo()?.cpu_brand}</div>
|
||||
<div>{sysInfo()?.core_count} cores</div>
|
||||
<Grid class="full-width" placeItems="center">
|
||||
<Show when={sysInfo()?.host_name}>
|
||||
<Grid class="card full-width" style={{ "max-width": "700px" }}>
|
||||
<InfoCard info={{ label: "hostname", info: sysInfo()?.host_name! }} />
|
||||
</Grid>
|
||||
</Show>
|
||||
<Grid class="card full-width" style={{ "max-width": "700px" }}>
|
||||
<For each={os_cards()}>{(i) => <InfoCard info={i} />}</For>
|
||||
</Grid>
|
||||
<Grid class="card full-width" style={{ "max-width": "700px" }}>
|
||||
<For each={cpu_cards()}>{(i) => <InfoCard info={i} />}</For>
|
||||
</Grid>
|
||||
<Grid class="card full-width" style={{ "max-width": "700px" }}>
|
||||
<For each={stats_cards()}>{(i) => <InfoCard info={i} />}</For>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const InfoCard: Component<{ info: { label: string; info: string } }> = (p) => {
|
||||
return (
|
||||
<Flex class="full-width" justifyContent="space-between">
|
||||
<h2>{p.info.label}</h2>
|
||||
<div>{p.info.info}</div>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -58,7 +58,8 @@ export const Search: Component<{}> = (p) => {
|
||||
>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
class={s.SearchInput}
|
||||
class="lightgrey"
|
||||
style={{ width: "30rem" }}
|
||||
placeholder="search"
|
||||
value={search.value()}
|
||||
onEdit={input.onEdit}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { UserProvider } from "./state/UserProvider";
|
||||
import { Client } from "./util/client";
|
||||
import { Router } from "@solidjs/router";
|
||||
import { AppStateProvider } from "./state/StateProvider";
|
||||
import { Operation } from "./types";
|
||||
|
||||
export const TOPBAR_HEIGHT = 50;
|
||||
export const MAX_PAGE_WIDTH = 1200;
|
||||
@@ -29,6 +30,10 @@ const token =
|
||||
|
||||
export const client = new Client(MONITOR_BASE_URL, token);
|
||||
|
||||
export const OPERATIONS = Object.values(Operation)
|
||||
.filter((e) => e !== "none" && !e.includes("user"))
|
||||
.map((e) => e.replaceAll("_", " "));
|
||||
|
||||
export const { Notifications, pushNotification } = makeNotifications();
|
||||
|
||||
client.initialize().then(() => {
|
||||
|
||||
@@ -6,15 +6,18 @@ import {
|
||||
useDeployments,
|
||||
useGroups,
|
||||
useProcedures,
|
||||
useServerDockerAccounts,
|
||||
useServerGithubAccounts,
|
||||
useServerInfo,
|
||||
useServers,
|
||||
useServerSecrets,
|
||||
useServerStats,
|
||||
useUpdates,
|
||||
useUsernames,
|
||||
} from "./hooks";
|
||||
import connectToWs from "./ws";
|
||||
import { useUser } from "./UserProvider";
|
||||
import { AwsBuilderConfig, PermissionLevel } from "../types";
|
||||
import { AwsBuilderConfig, PermissionLevel, UpdateTarget } from "../types";
|
||||
import { client } from "..";
|
||||
|
||||
export type State = {
|
||||
@@ -23,6 +26,9 @@ export type State = {
|
||||
getPermissionOnServer: (id: string) => PermissionLevel;
|
||||
serverStats: ReturnType<typeof useServerStats>;
|
||||
serverInfo: ReturnType<typeof useServerInfo>;
|
||||
serverDockerAccounts: ReturnType<typeof useServerDockerAccounts>;
|
||||
serverGithubAccounts: ReturnType<typeof useServerGithubAccounts>;
|
||||
serverSecrets: ReturnType<typeof useServerSecrets>;
|
||||
ungroupedServerIds: () => string[] | undefined;
|
||||
builds: ReturnType<typeof useBuilds>;
|
||||
getPermissionOnBuild: (id: string) => PermissionLevel;
|
||||
@@ -34,6 +40,9 @@ export type State = {
|
||||
getPermissionOnProcedure: (id: string) => PermissionLevel;
|
||||
updates: ReturnType<typeof useUpdates>;
|
||||
aws_builder_config: Resource<AwsBuilderConfig>;
|
||||
docker_organizations: Resource<string[]>;
|
||||
github_webhook_base_url: Resource<string>;
|
||||
name_from_update_target: (target: UpdateTarget) => string;
|
||||
};
|
||||
|
||||
const context = createContext<
|
||||
@@ -54,6 +63,8 @@ export const AppStateProvider: ParentComponent = (p) => {
|
||||
const deployments = useDeployments();
|
||||
const usernames = useUsernames();
|
||||
const [aws_builder_config] = createResource(() => client.get_aws_builder_defaults());
|
||||
const [docker_organizations] = createResource(() => client.get_docker_organizations());
|
||||
const [github_webhook_base_url] = createResource(() => client.get_github_webhook_base_url());
|
||||
const state: State = {
|
||||
usernames,
|
||||
servers,
|
||||
@@ -107,6 +118,9 @@ export const AppStateProvider: ParentComponent = (p) => {
|
||||
},
|
||||
serverStats: useServerStats(servers),
|
||||
serverInfo: useServerInfo(servers),
|
||||
serverDockerAccounts: useServerDockerAccounts(servers),
|
||||
serverGithubAccounts: useServerGithubAccounts(servers),
|
||||
serverSecrets: useServerSecrets(servers),
|
||||
groups,
|
||||
getPermissionOnGroup: (id: string) => {
|
||||
const group = groups.get(id)!;
|
||||
@@ -133,6 +147,19 @@ export const AppStateProvider: ParentComponent = (p) => {
|
||||
},
|
||||
updates: useUpdates(),
|
||||
aws_builder_config,
|
||||
docker_organizations,
|
||||
github_webhook_base_url,
|
||||
name_from_update_target: (target) => {
|
||||
if (target.type === "Deployment" && deployments) {
|
||||
return deployments.get(target.id!)?.deployment.name || "deleted";
|
||||
} else if (target.type === "Server" && servers) {
|
||||
return servers.get(target.id)?.server.name || "deleted";
|
||||
} else if (target.type === "Build" && builds) {
|
||||
return builds.get(target.id)?.name || "deleted";
|
||||
} else {
|
||||
return "admin";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// createEffect(() => {
|
||||
|
||||
@@ -112,6 +112,106 @@ export function useServerInfo(servers: ReturnType<typeof useServers>) {
|
||||
};
|
||||
}
|
||||
|
||||
export function useServerGithubAccounts(servers: ReturnType<typeof useServers>) {
|
||||
const [accounts, set] = createSignal<
|
||||
Record<string, string[] | undefined>
|
||||
>({});
|
||||
const load = async (serverID: string) => {
|
||||
if (servers.get(serverID)?.status === ServerStatus.Ok) {
|
||||
try {
|
||||
const info = await client.get_server_github_accounts(serverID);
|
||||
set((s) => ({ ...s, [serverID]: info }));
|
||||
} catch (error) {
|
||||
console.log("error getting server github accounts", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
const loading: Record<string, boolean> = {};
|
||||
return {
|
||||
get: (serverID: string, serverStatus?: ServerStatus) => {
|
||||
const accts = accounts()[serverID];
|
||||
if (
|
||||
accts === undefined &&
|
||||
!loading[serverID] &&
|
||||
(serverStatus ? serverStatus === ServerStatus.Ok : true)
|
||||
) {
|
||||
loading[serverID] = true;
|
||||
load(serverID);
|
||||
}
|
||||
return accts;
|
||||
},
|
||||
load,
|
||||
};
|
||||
}
|
||||
|
||||
export function useServerDockerAccounts(
|
||||
servers: ReturnType<typeof useServers>
|
||||
) {
|
||||
const [accounts, set] = createSignal<Record<string, string[] | undefined>>(
|
||||
{}
|
||||
);
|
||||
const load = async (serverID: string) => {
|
||||
if (servers.get(serverID)?.status === ServerStatus.Ok) {
|
||||
try {
|
||||
const info = await client.get_server_docker_accounts(serverID);
|
||||
set((s) => ({ ...s, [serverID]: info }));
|
||||
} catch (error) {
|
||||
console.log("error getting server docker accounts", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
const loading: Record<string, boolean> = {};
|
||||
return {
|
||||
get: (serverID: string, serverStatus?: ServerStatus) => {
|
||||
const accts = accounts()[serverID];
|
||||
if (
|
||||
accts === undefined &&
|
||||
!loading[serverID] &&
|
||||
(serverStatus ? serverStatus === ServerStatus.Ok : true)
|
||||
) {
|
||||
loading[serverID] = true;
|
||||
load(serverID);
|
||||
}
|
||||
return accts;
|
||||
},
|
||||
load,
|
||||
};
|
||||
}
|
||||
|
||||
export function useServerSecrets(
|
||||
servers: ReturnType<typeof useServers>
|
||||
) {
|
||||
const [accounts, set] = createSignal<Record<string, string[] | undefined>>(
|
||||
{}
|
||||
);
|
||||
const load = async (serverID: string) => {
|
||||
if (servers.get(serverID)?.status === ServerStatus.Ok) {
|
||||
try {
|
||||
const info = await client.get_server_available_secrets(serverID);
|
||||
set((s) => ({ ...s, [serverID]: info }));
|
||||
} catch (error) {
|
||||
console.log("error getting server github_accounts", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
const loading: Record<string, boolean> = {};
|
||||
return {
|
||||
get: (serverID: string, serverStatus?: ServerStatus) => {
|
||||
const accts = accounts()[serverID];
|
||||
if (
|
||||
accts === undefined &&
|
||||
!loading[serverID] &&
|
||||
(serverStatus ? serverStatus === ServerStatus.Ok : true)
|
||||
) {
|
||||
loading[serverID] = true;
|
||||
load(serverID);
|
||||
}
|
||||
return accts;
|
||||
},
|
||||
load,
|
||||
};
|
||||
}
|
||||
|
||||
export function useUsernames() {
|
||||
const [usernames, set] = createSignal<Record<string, string | undefined>>({});
|
||||
const load = async (userID: string) => {
|
||||
@@ -188,7 +288,7 @@ export function useUpdates(target?: UpdateTarget, show_builds?: boolean) {
|
||||
operations
|
||||
);
|
||||
updates.addManyToEnd(newUpdates);
|
||||
if (newUpdates.length !== 10) {
|
||||
if (newUpdates.length !== 20) {
|
||||
setNoMore(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +110,11 @@ async function handleMessage(
|
||||
const deployment = await client.get_deployment(update.target.id!);
|
||||
deployments.update(deployment);
|
||||
}
|
||||
} else if (update.operation === Operation.RenameDeployment) {
|
||||
if (update.status === UpdateStatus.Complete) {
|
||||
const deployment = await client.get_deployment(update.target.id!);
|
||||
deployments.update(deployment);
|
||||
}
|
||||
} else if (
|
||||
[
|
||||
Operation.DeployContainer,
|
||||
|
||||
@@ -14,12 +14,12 @@ $lightgreen: #4f8d5c;
|
||||
$green: #41764c;
|
||||
$darkgreen: #2b4f33;
|
||||
|
||||
$textred: #f04633;
|
||||
$textred: #f76858;
|
||||
$lightred: #b13a2d;
|
||||
$red: #952E23;
|
||||
$darkred: #631F17;
|
||||
|
||||
$textorange: #984f2d;
|
||||
$textorange: #e77e4e;
|
||||
$lightorange: #d56b3a;
|
||||
$orange: #ac5c36;
|
||||
$darkorange: #984f2d;
|
||||
|
||||
31
frontend/src/style/colors.tsx
Normal file
31
frontend/src/style/colors.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
export const COLORS = {
|
||||
"app-color": "#fceade",
|
||||
|
||||
lightgrey: "#3f454d",
|
||||
grey: "#25292e",
|
||||
darkgrey: "#16181b",
|
||||
|
||||
textblue: "#5f9af4",
|
||||
lightblue: "#1c63cd",
|
||||
blue: "#184e9f",
|
||||
darkblue: "#12366d",
|
||||
|
||||
textgreen: "#80ea97",
|
||||
lightgreen: "#4f8d5c",
|
||||
green: "#41764c",
|
||||
darkgreen: "#2b4f33",
|
||||
|
||||
textred: "#f76858",
|
||||
lightred: "#b13a2d",
|
||||
red: "#952E23",
|
||||
darkred: "#631F17",
|
||||
|
||||
textorange: "#e77e4e",
|
||||
lightorange: "#d56b3a",
|
||||
orange: "#ac5c36",
|
||||
darkorange: "#984f2d",
|
||||
|
||||
lightpurple: "#720e61",
|
||||
purple: "#5A0B4D",
|
||||
darkpurple: "#3b0732",
|
||||
};
|
||||
@@ -318,6 +318,22 @@ svg {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.full-size {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.full-height {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// .hoverable {
|
||||
// transition: all 250ms ease-in-out;
|
||||
// }
|
||||
|
||||
@@ -56,6 +56,8 @@ export interface DockerBuildArgs {
|
||||
build_path: string;
|
||||
dockerfile_path?: string;
|
||||
build_args?: EnvironmentVar[];
|
||||
extra_args?: string[];
|
||||
use_buildx?: boolean;
|
||||
}
|
||||
|
||||
export interface BuildVersionsReponse {
|
||||
@@ -129,6 +131,7 @@ export interface DeploymentActionState {
|
||||
pulling: boolean;
|
||||
recloning: boolean;
|
||||
updating: boolean;
|
||||
renaming: boolean;
|
||||
}
|
||||
|
||||
export interface DockerRunArgs {
|
||||
@@ -422,6 +425,7 @@ export enum Operation {
|
||||
PruneImagesServer = "prune_images_server",
|
||||
PruneContainersServer = "prune_containers_server",
|
||||
PruneNetworksServer = "prune_networks_server",
|
||||
RenameServer = "rename_server",
|
||||
CreateBuild = "create_build",
|
||||
UpdateBuild = "update_build",
|
||||
DeleteBuild = "delete_build",
|
||||
@@ -435,6 +439,7 @@ export enum Operation {
|
||||
RemoveContainer = "remove_container",
|
||||
PullDeployment = "pull_deployment",
|
||||
RecloneDeployment = "reclone_deployment",
|
||||
RenameDeployment = "rename_deployment",
|
||||
CreateProcedure = "create_procedure",
|
||||
UpdateProcedure = "update_procedure",
|
||||
DeleteProcedure = "delete_procedure",
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
ModifyUserCreateServerBody,
|
||||
ModifyUserEnabledBody,
|
||||
PermissionsUpdateBody,
|
||||
RenameDeploymentBody,
|
||||
UpdateDescriptionBody,
|
||||
} from "./client_types";
|
||||
import { generateQuery, QueryObject } from "./helpers";
|
||||
@@ -50,6 +51,10 @@ import { generateQuery, QueryObject } from "./helpers";
|
||||
export class Client {
|
||||
loginOptions: LoginOptions | undefined;
|
||||
monitorTitle: string | undefined;
|
||||
secrets_cache: Record<string, string[]> = {};
|
||||
github_accounts_cache: Record<string, string[]> = {};
|
||||
docker_accounts_cache: Record<string, string[]> = {};
|
||||
server_version_cache: Record<string, string> = {};
|
||||
|
||||
constructor(private baseURL: string, public token: string | null) {}
|
||||
|
||||
@@ -200,6 +205,10 @@ export class Client {
|
||||
return this.patch("/api/deployment/update", deployment);
|
||||
}
|
||||
|
||||
rename_deployment(deployment_id: string, new_name: string) {
|
||||
return this.patch(`/api/deployment/${deployment_id}/rename`, { new_name });
|
||||
}
|
||||
|
||||
reclone_deployment(deployment_id: string): Promise<Update> {
|
||||
return this.post(`/api/deployment/${deployment_id}/reclone`);
|
||||
}
|
||||
@@ -254,18 +263,51 @@ export class Client {
|
||||
}
|
||||
|
||||
get_server_github_accounts(id: string): Promise<string[]> {
|
||||
// if (this.github_accounts_cache[id]) {
|
||||
// return this.github_accounts_cache[id];
|
||||
// } else {
|
||||
// this.github_accounts_cache[id] = [];
|
||||
// }
|
||||
// this.github_accounts_cache[id] = await this.get(
|
||||
// `/api/server/${id}/github_accounts`
|
||||
// );
|
||||
// return this.github_accounts_cache[id];
|
||||
return this.get(`/api/server/${id}/github_accounts`);
|
||||
}
|
||||
|
||||
get_server_docker_accounts(id: string): Promise<string[]> {
|
||||
// if (this.docker_accounts_cache[id]) {
|
||||
// return this.docker_accounts_cache[id];
|
||||
// } else {
|
||||
// this.docker_accounts_cache[id] = [];
|
||||
// };
|
||||
// this.docker_accounts_cache[id] = await this.get(
|
||||
// `/api/server/${id}/docker_accounts`
|
||||
// );
|
||||
// return this.docker_accounts_cache[id];
|
||||
return this.get(`/api/server/${id}/docker_accounts`);
|
||||
}
|
||||
|
||||
get_server_available_secrets(id: string): Promise<string[]> {
|
||||
// if (this.secrets_cache[id]) {
|
||||
// return this.secrets_cache[id];
|
||||
// } else {
|
||||
// this.secrets_cache[id] = [];
|
||||
// };
|
||||
// console.log("loading");
|
||||
// this.secrets_cache[id] = await this.get(`/api/server/${id}/secrets`);
|
||||
// return this.secrets_cache[id];
|
||||
return this.get(`/api/server/${id}/secrets`);
|
||||
}
|
||||
|
||||
get_server_version(id: string): Promise<string> {
|
||||
// if (this.server_version_cache[id]) {
|
||||
// return this.server_version_cache[id];
|
||||
// } else {
|
||||
// this.server_version_cache[id] = "loading...";
|
||||
// };
|
||||
// this.server_version_cache[id] = await this.get(`/api/server/${id}/version`);
|
||||
// return this.server_version_cache[id];
|
||||
return this.get(`/api/server/${id}/version`);
|
||||
}
|
||||
|
||||
@@ -355,7 +397,7 @@ export class Client {
|
||||
get_build_versions(
|
||||
id: string,
|
||||
query?: BuildVersionsQuery
|
||||
): Promise<BuildVersionsReponse> {
|
||||
): Promise<BuildVersionsReponse[]> {
|
||||
return this.get(`/api/build/${id}/versions${generateQuery(query as any)}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@ export interface CopyDeploymentBody {
|
||||
server_id: string;
|
||||
}
|
||||
|
||||
export interface RenameDeploymentBody {
|
||||
new_name: string;
|
||||
}
|
||||
|
||||
export interface GetContainerLogQuery {
|
||||
tail?: number;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import {
|
||||
Build,
|
||||
Deployment,
|
||||
DeploymentWithContainerState,
|
||||
DockerContainerState,
|
||||
EnvironmentVar,
|
||||
Server,
|
||||
ServerStatus,
|
||||
ServerWithStatus,
|
||||
Timelength,
|
||||
UpdateTarget,
|
||||
User,
|
||||
Version,
|
||||
} from "../types";
|
||||
@@ -238,10 +244,10 @@ export function readableVersion(version: Version) {
|
||||
|
||||
export function readableUserType(user: User) {
|
||||
if (user.github_id) {
|
||||
return "github"
|
||||
return "github";
|
||||
} else if (user.google_id) {
|
||||
return "google"
|
||||
return "google";
|
||||
} else {
|
||||
return "local"
|
||||
return "local";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "db_client"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use collections::{
|
||||
actions_collection, builds_collection, deployments_collection, groups_collection,
|
||||
@@ -29,7 +27,10 @@ pub struct DbClient {
|
||||
impl DbClient {
|
||||
pub async fn new(config: MongoConfig) -> DbClient {
|
||||
let db_name = &config.db_name;
|
||||
let mungos = Mungos::new(&config.uri, &config.app_name, Duration::from_secs(3), None)
|
||||
let mungos = Mungos::builder()
|
||||
.uri(&config.uri)
|
||||
.app_name(&config.app_name)
|
||||
.build()
|
||||
.await
|
||||
.expect("failed to initialize mungos");
|
||||
DbClient {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_helpers"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "helpers used as dependency for mogh tech monitor"
|
||||
|
||||
@@ -1,113 +1,10 @@
|
||||
use std::{borrow::Borrow, fs::File, io::Read, net::SocketAddr, str::FromStr};
|
||||
use std::{borrow::Borrow, net::SocketAddr, str::FromStr};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use anyhow::anyhow;
|
||||
use axum::http::StatusCode;
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::{Map, Value};
|
||||
use types::Log;
|
||||
|
||||
pub fn parse_config_files<'a, T: DeserializeOwned>(
|
||||
paths: impl IntoIterator<Item = impl Borrow<String>>,
|
||||
merge_nested: bool,
|
||||
extend_array: bool,
|
||||
) -> anyhow::Result<T> {
|
||||
let mut target = Map::new();
|
||||
for path in paths {
|
||||
target = merge_objects(
|
||||
target,
|
||||
parse_config_file(path.borrow())?,
|
||||
merge_nested,
|
||||
extend_array,
|
||||
)?;
|
||||
}
|
||||
serde_json::from_str(&serde_json::to_string(&target)?)
|
||||
.context("failed to parse final config into expected type")
|
||||
}
|
||||
|
||||
pub fn parse_config_file<T: DeserializeOwned>(path: &str) -> anyhow::Result<T> {
|
||||
let mut file = File::open(&path).expect(&format!("failed to find config at {path}"));
|
||||
let config = if path.ends_with("toml") {
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)
|
||||
.context(format!("failed to read toml at {path}"))?;
|
||||
toml::from_str(&contents).context(format!("failed to parse toml at {path}"))?
|
||||
} else if path.ends_with("json") {
|
||||
serde_json::from_reader(file).context(format!("failed to parse json at {path}"))?
|
||||
} else {
|
||||
panic!("unsupported config file type: {}", path)
|
||||
};
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// object is serde_json::Map<String, serde_json::Value>
|
||||
/// source will overide target
|
||||
/// will recurse when field is object if merge_object = true, otherwise object will be replaced
|
||||
/// will extend when field is array if extend_array = true, otherwise array will be replaced
|
||||
/// will return error when types on source and target fields do not match
|
||||
fn merge_objects(
|
||||
mut target: Map<String, Value>,
|
||||
source: Map<String, Value>,
|
||||
merge_nested: bool,
|
||||
extend_array: bool,
|
||||
) -> anyhow::Result<Map<String, Value>> {
|
||||
for (key, value) in source {
|
||||
let curr = target.remove(&key);
|
||||
if curr.is_none() {
|
||||
target.insert(key, value);
|
||||
continue;
|
||||
}
|
||||
let curr = curr.unwrap();
|
||||
match curr {
|
||||
Value::Object(target_obj) => {
|
||||
if !merge_nested {
|
||||
target.insert(key, value);
|
||||
continue;
|
||||
}
|
||||
match value {
|
||||
Value::Object(source_obj) => {
|
||||
target.insert(
|
||||
key,
|
||||
Value::Object(merge_objects(
|
||||
target_obj,
|
||||
source_obj,
|
||||
merge_nested,
|
||||
extend_array,
|
||||
)?),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!(
|
||||
"types on field {key} do not match. got {value:?}, expected object"
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
Value::Array(mut target_arr) => {
|
||||
if !extend_array {
|
||||
target.insert(key, value);
|
||||
continue;
|
||||
}
|
||||
match value {
|
||||
Value::Array(source_arr) => {
|
||||
target_arr.extend(source_arr);
|
||||
target.insert(key, Value::Array(target_arr));
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!(
|
||||
"types on field {key} do not match. got {value:?}, expected array"
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
target.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(target)
|
||||
}
|
||||
|
||||
pub fn parse_comma_seperated_list<T: FromStr>(
|
||||
comma_sep_list: impl Borrow<str>,
|
||||
) -> anyhow::Result<Vec<T>> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_client"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "a client to interact with the monitor system"
|
||||
@@ -9,8 +9,7 @@ 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.2.9"
|
||||
# monitor_types = { path = "../types" }
|
||||
monitor_types = "0.2.13"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||
tokio = { version = "1.25", features = ["full"] }
|
||||
|
||||
@@ -124,6 +124,15 @@ impl MonitorClient {
|
||||
.context("failed at updating deployment")
|
||||
}
|
||||
|
||||
pub async fn rename_deployment(&self, id: &str, new_name: &str) -> anyhow::Result<Update> {
|
||||
self.patch(
|
||||
&format!("/api/deployment/{id}/rename"),
|
||||
json!({ "new_name": new_name }),
|
||||
)
|
||||
.await
|
||||
.context("failed at renaming deployment")
|
||||
}
|
||||
|
||||
pub async fn reclone_deployment(&self, id: &str) -> anyhow::Result<Update> {
|
||||
self.post::<(), _>(&format!("/api/deployment/{id}/reclone"), None)
|
||||
.await
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "periphery_client"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -70,6 +70,21 @@ impl PeripheryClient {
|
||||
.context("failed to remove container on periphery")
|
||||
}
|
||||
|
||||
pub async fn container_rename(
|
||||
&self,
|
||||
server: &Server,
|
||||
curr_name: &str,
|
||||
new_name: &str,
|
||||
) -> anyhow::Result<Log> {
|
||||
self.post_json(
|
||||
server,
|
||||
"/container/rename",
|
||||
&json!({ "curr_name": curr_name, "new_name": new_name }),
|
||||
)
|
||||
.await
|
||||
.context("failed to rename container on periphery")
|
||||
}
|
||||
|
||||
pub async fn deploy(&self, server: &Server, deployment: &Deployment) -> anyhow::Result<Log> {
|
||||
self.post_json(server, "/container/deploy", deployment)
|
||||
.await
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_types"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "types for the mogh tech monitor"
|
||||
|
||||
@@ -155,6 +155,12 @@ pub struct DockerBuildArgs {
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub build_args: Vec<EnvironmentVar>,
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub extra_args: Vec<String>,
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub use_buildx: bool,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
|
||||
@@ -107,6 +107,7 @@ pub struct DeploymentActionState {
|
||||
pub pulling: bool,
|
||||
pub recloning: bool,
|
||||
pub updating: bool,
|
||||
pub renaming: bool,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
|
||||
@@ -100,6 +100,7 @@ pub enum Operation {
|
||||
PruneImagesServer,
|
||||
PruneContainersServer,
|
||||
PruneNetworksServer,
|
||||
RenameServer,
|
||||
|
||||
// build
|
||||
CreateBuild,
|
||||
@@ -117,6 +118,7 @@ pub enum Operation {
|
||||
RemoveContainer,
|
||||
PullDeployment,
|
||||
RecloneDeployment,
|
||||
RenameDeployment,
|
||||
|
||||
// procedure
|
||||
CreateProcedure,
|
||||
|
||||
@@ -60,6 +60,7 @@ impl Busy for DeploymentActionState {
|
||||
|| self.starting
|
||||
|| self.stopping
|
||||
|| self.updating
|
||||
|| self.renaming
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "monitor_periphery"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "monitor periphery binary | run monitor periphery as system daemon"
|
||||
description = "monitor periphery binary"
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[[bin]]
|
||||
@@ -31,5 +31,6 @@ envy = "0.4"
|
||||
sysinfo = "0.28"
|
||||
toml = "0.7"
|
||||
daemonize = "0.5.0"
|
||||
clap = { version = "4.1", features = ["derive"] }
|
||||
clap = { version = "4.2", features = ["derive"] }
|
||||
svi = "0.1.3"
|
||||
merge_config_files = "0.1.2"
|
||||
|
||||
@@ -4,7 +4,7 @@ use axum::{
|
||||
routing::{get, post},
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use helpers::{handle_anyhow_error, to_monitor_name};
|
||||
use helpers::handle_anyhow_error;
|
||||
use serde::Deserialize;
|
||||
use types::{Deployment, Log};
|
||||
|
||||
@@ -21,6 +21,12 @@ struct Container {
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct RenameContainerBody {
|
||||
curr_name: String,
|
||||
new_name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct GetLogQuery {
|
||||
tail: Option<u64>, // default is 1000 if not passed
|
||||
@@ -67,20 +73,26 @@ pub fn router() -> Router {
|
||||
)
|
||||
.route(
|
||||
"/start",
|
||||
post(|Json(container): Json<Container>| async move {
|
||||
Json(docker::start_container(&to_monitor_name(&container.name)).await)
|
||||
post(|container: Json<Container>| async move {
|
||||
Json(docker::start_container(&container.name).await)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/stop",
|
||||
post(|Json(container): Json<Container>| async move {
|
||||
Json(docker::stop_container(&to_monitor_name(&container.name)).await)
|
||||
post(|container: Json<Container>| async move {
|
||||
Json(docker::stop_container(&container.name).await)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/remove",
|
||||
post(|Json(container): Json<Container>| async move {
|
||||
Json(docker::stop_and_remove_container(&to_monitor_name(&container.name)).await)
|
||||
post(|container: Json<Container>| async move {
|
||||
Json(docker::stop_and_remove_container(&container.name).await)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/rename",
|
||||
post(|body: Json<RenameContainerBody>| async move {
|
||||
Json(docker::rename_container(&body.curr_name, &body.new_name).await)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
|
||||
@@ -3,7 +3,8 @@ use std::sync::Arc;
|
||||
use axum::Extension;
|
||||
use clap::Parser;
|
||||
use dotenv::dotenv;
|
||||
use helpers::{parse_comma_seperated_list, parse_config_files};
|
||||
use helpers::parse_comma_seperated_list;
|
||||
use merge_config_files::parse_config_paths;
|
||||
use serde::Deserialize;
|
||||
use types::PeripheryConfig;
|
||||
|
||||
@@ -25,14 +26,18 @@ pub struct Args {
|
||||
#[arg(long, default_value = "~/.monitor/periphery.log.err")]
|
||||
pub stderr: String,
|
||||
|
||||
/// Sets the path of a config file to use. can use multiple times
|
||||
/// Sets the path of a config file or directory to use. can use multiple times
|
||||
#[arg(short, long)]
|
||||
pub config_path: Option<Vec<String>>,
|
||||
|
||||
/// Sets the keywords to match directory periphery config file names on. can use multiple times. default "periphery" and "config"
|
||||
#[arg(long)]
|
||||
pub config_keyword: Option<Vec<String>>,
|
||||
|
||||
#[arg(short, long)]
|
||||
pub merge_nested_config: bool,
|
||||
|
||||
#[arg(short, long)]
|
||||
#[arg(long)]
|
||||
pub home_dir: Option<String>,
|
||||
|
||||
#[arg(short, long)]
|
||||
@@ -43,6 +48,8 @@ pub struct Args {
|
||||
struct Env {
|
||||
#[serde(default = "default_config_path")]
|
||||
config_paths: String,
|
||||
#[serde(default)]
|
||||
config_keywords: String,
|
||||
}
|
||||
|
||||
pub fn load() -> (Args, u16, PeripheryConfigExtension, HomeDirExtension) {
|
||||
@@ -63,9 +70,19 @@ pub fn load() -> (Args, u16, PeripheryConfigExtension, HomeDirExtension) {
|
||||
)
|
||||
.into_iter()
|
||||
.map(|p| p.replace("~", &home_dir))
|
||||
.collect();
|
||||
let config = parse_config_files::<PeripheryConfig>(
|
||||
&config_paths,
|
||||
.collect::<Vec<_>>();
|
||||
println!("{config_paths:?}");
|
||||
let env_match_keywords = parse_comma_seperated_list(env.config_keywords)
|
||||
.expect("failed to parse environemt CONFIG_KEYWORDS into comma seperated list");
|
||||
let match_keywords = args
|
||||
.config_keyword
|
||||
.as_ref()
|
||||
.unwrap_or(&env_match_keywords)
|
||||
.into_iter()
|
||||
.map(|kw| kw.as_str());
|
||||
let config = parse_config_paths::<PeripheryConfig>(
|
||||
config_paths.clone(),
|
||||
match_keywords,
|
||||
args.merge_nested_config,
|
||||
args.merge_nested_config,
|
||||
)
|
||||
@@ -117,7 +134,7 @@ fn default_config_path() -> String {
|
||||
fn get_home_dir(home_dir_arg: &Option<String>) -> String {
|
||||
match home_dir_arg {
|
||||
Some(home_dir) => home_dir.to_string(),
|
||||
None => std::env::var("$HOME")
|
||||
None => std::env::var("HOME")
|
||||
.expect("did not find $HOME env var, should pass home dir with arg --home-dir"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use types::{Build, DockerBuildArgs, EnvironmentVar, Log, Version};
|
||||
|
||||
use crate::helpers::run_monitor_command;
|
||||
|
||||
use super::docker_login;
|
||||
use super::{docker_login, parse_extra_args};
|
||||
|
||||
pub async fn prune_images() -> Log {
|
||||
let command = format!("docker image prune -a -f");
|
||||
@@ -32,6 +32,8 @@ pub async fn build(
|
||||
build_path,
|
||||
dockerfile_path,
|
||||
build_args,
|
||||
extra_args,
|
||||
use_buildx,
|
||||
} = docker_build_args
|
||||
.as_ref()
|
||||
.ok_or(anyhow!("build missing docker build args"))?;
|
||||
@@ -46,6 +48,8 @@ pub async fn build(
|
||||
None => "Dockerfile".to_owned(),
|
||||
};
|
||||
let build_args = parse_build_args(build_args);
|
||||
let extra_args = parse_extra_args(extra_args);
|
||||
let buildx = if *use_buildx { " buildx" } else { "" };
|
||||
let image_name = get_image_name(&name, docker_account, docker_organization);
|
||||
let image_tags = image_tags(&image_name, &version);
|
||||
let docker_push = if using_account {
|
||||
@@ -54,7 +58,7 @@ pub async fn build(
|
||||
String::new()
|
||||
};
|
||||
let command = format!(
|
||||
"cd {} && docker build {build_args}{image_tags} -f {dockerfile_path} .{docker_push}",
|
||||
"cd {} && docker{buildx} build{build_args}{extra_args}{image_tags} -f {dockerfile_path} .{docker_push}",
|
||||
build_dir.display()
|
||||
);
|
||||
if *skip_secret_interp {
|
||||
@@ -97,7 +101,7 @@ fn get_latest_image_name(image_name: &str) -> String {
|
||||
|
||||
fn image_tags(image_name: &str, version: &Version) -> String {
|
||||
format!(
|
||||
"-t {} -t {}",
|
||||
" -t {} -t {}",
|
||||
get_version_image_name(image_name, version),
|
||||
get_latest_image_name(image_name)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ use types::{
|
||||
Conversion, Deployment, DockerContainerStats, DockerRunArgs, EnvironmentVar, Log, RestartMode,
|
||||
};
|
||||
|
||||
use crate::helpers::run_monitor_command;
|
||||
use crate::helpers::{docker::parse_extra_args, run_monitor_command};
|
||||
|
||||
use super::docker_login;
|
||||
|
||||
@@ -69,6 +69,13 @@ pub async fn stop_and_remove_container(container_name: &str) -> Log {
|
||||
run_monitor_command("docker stop and remove", command).await
|
||||
}
|
||||
|
||||
pub async fn rename_container(curr_name: &str, new_name: &str) -> Log {
|
||||
let curr = to_monitor_name(curr_name);
|
||||
let new = to_monitor_name(new_name);
|
||||
let command = format!("docker rename {curr} {new}");
|
||||
run_monitor_command("docker rename", command).await
|
||||
}
|
||||
|
||||
pub async fn pull_image(image: &str) -> Log {
|
||||
let command = format!("docker pull {image}");
|
||||
run_monitor_command("docker pull", command).await
|
||||
@@ -190,12 +197,3 @@ fn parse_post_image(post_image: &Option<String>) -> String {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_extra_args(extra_args: &Vec<String>) -> String {
|
||||
let args = extra_args.join(" ");
|
||||
if args.len() > 0 {
|
||||
format!(" {args}")
|
||||
} else {
|
||||
args
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,3 +38,12 @@ pub async fn docker_login(
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_extra_args(extra_args: &Vec<String>) -> String {
|
||||
let args = extra_args.join(" ");
|
||||
if args.len() > 0 {
|
||||
format!(" {args}")
|
||||
} else {
|
||||
args
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +108,7 @@ pub async fn test_build(monitor: &MonitorClient) -> anyhow::Result<Update> {
|
||||
});
|
||||
build.docker_build_args = Some(DockerBuildArgs {
|
||||
build_path: "periphery".to_string(),
|
||||
dockerfile_path: None,
|
||||
build_args: Vec::new(),
|
||||
..Default::default()
|
||||
});
|
||||
let build = monitor.update_build(build).await?;
|
||||
println!("updated build.");
|
||||
|
||||
Reference in New Issue
Block a user