Health checks
Health checks can be enabled for applications which will make an http request once per minute and color the application icon based on the response and status code.
This commit is contained in:
parent
fcc69d2a91
commit
bbee8c6460
|
@ -86,16 +86,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-cors"
|
name = "actix-cors"
|
||||||
version = "0.5.4"
|
version = "0.6.0-beta.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36b133d8026a9f209a9aeeeacd028e7451bcca975f592881b305d37983f303d7"
|
checksum = "8debd30414af03e9411186aac95e0230b0bb1e91146f48015dfab5c049940223"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web 3.3.3",
|
"actix-service 2.0.2",
|
||||||
|
"actix-utils 3.0.0",
|
||||||
|
"actix-web 4.0.0-rc.2",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"tinyvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -461,6 +463,23 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-web-actors"
|
||||||
|
version = "4.0.0-beta.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbf2ef3eae6001ac2fa6690b2f8b152c00b5b8b2248e3e30f82dd2ec1e941345"
|
||||||
|
dependencies = [
|
||||||
|
"actix",
|
||||||
|
"actix-codec 0.4.2",
|
||||||
|
"actix-http 3.0.0-rc.1",
|
||||||
|
"actix-web 4.0.0-rc.2",
|
||||||
|
"bytes 1.1.0",
|
||||||
|
"bytestring",
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite 0.2.8",
|
||||||
|
"tokio 1.16.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-web-codegen"
|
name = "actix-web-codegen"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -852,6 +871,22 @@ version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536"
|
checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -996,6 +1031,15 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "firestorm"
|
name = "firestorm"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -1032,6 +1076,21 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||||
|
dependencies = [
|
||||||
|
"foreign-types-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-shared"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -1308,6 +1367,17 @@ dependencies = [
|
||||||
"itoa 1.0.1",
|
"itoa 1.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-body"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
|
||||||
|
dependencies = [
|
||||||
|
"bytes 1.1.0",
|
||||||
|
"http",
|
||||||
|
"pin-project-lite 0.2.8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.5.1"
|
version = "1.5.1"
|
||||||
|
@ -1320,6 +1390,43 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper"
|
||||||
|
version = "0.14.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55"
|
||||||
|
dependencies = [
|
||||||
|
"bytes 1.1.0",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"h2 0.3.11",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"httparse",
|
||||||
|
"httpdate",
|
||||||
|
"itoa 0.4.8",
|
||||||
|
"pin-project-lite 0.2.8",
|
||||||
|
"socket2 0.4.4",
|
||||||
|
"tokio 1.16.1",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
"want",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-tls"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||||
|
dependencies = [
|
||||||
|
"bytes 1.1.0",
|
||||||
|
"hyper",
|
||||||
|
"native-tls",
|
||||||
|
"tokio 1.16.1",
|
||||||
|
"tokio-native-tls",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
@ -1368,9 +1475,15 @@ dependencies = [
|
||||||
"socket2 0.3.19",
|
"socket2 0.3.19",
|
||||||
"widestring",
|
"widestring",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
"winreg",
|
"winreg 0.6.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipnet"
|
||||||
|
version = "2.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
|
@ -1677,6 +1790,24 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "native-tls"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"openssl",
|
||||||
|
"openssl-probe",
|
||||||
|
"openssl-sys",
|
||||||
|
"schannel",
|
||||||
|
"security-framework",
|
||||||
|
"security-framework-sys",
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "net2"
|
name = "net2"
|
||||||
version = "0.2.37"
|
version = "0.2.37"
|
||||||
|
@ -1780,6 +1911,39 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl"
|
||||||
|
version = "0.10.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"foreign-types",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"openssl-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-probe"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-sys"
|
||||||
|
version = "0.9.72"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ouroboros"
|
name = "ouroboros"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
|
@ -2099,6 +2263,51 @@ version = "0.6.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "remove_dir_all"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reqwest"
|
||||||
|
version = "0.11.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"bytes 1.1.0",
|
||||||
|
"encoding_rs",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"h2 0.3.11",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"hyper",
|
||||||
|
"hyper-tls",
|
||||||
|
"ipnet",
|
||||||
|
"js-sys",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"mime",
|
||||||
|
"native-tls",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite 0.2.8",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"tokio 1.16.1",
|
||||||
|
"tokio-native-tls",
|
||||||
|
"url",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
"winreg 0.7.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "resolv-conf"
|
name = "resolv-conf"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
@ -2222,6 +2431,16 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schannel"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -2324,6 +2543,29 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"core-foundation",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
"security-framework-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework-sys"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -2709,6 +2951,20 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"fastrand",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"remove_dir_all",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.30"
|
version = "1.0.30"
|
||||||
|
@ -2864,9 +3120,31 @@ dependencies = [
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pin-project-lite 0.2.8",
|
"pin-project-lite 0.2.8",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
|
"tokio-macros",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-native-tls"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
||||||
|
dependencies = [
|
||||||
|
"native-tls",
|
||||||
|
"tokio 1.16.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.22.0"
|
version = "0.22.0"
|
||||||
|
@ -2917,6 +3195,12 @@ dependencies = [
|
||||||
"tokio 1.16.1",
|
"tokio 1.16.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-service"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.30"
|
version = "0.1.30"
|
||||||
|
@ -3074,6 +3358,12 @@ dependencies = [
|
||||||
"trust-dns-proto",
|
"trust-dns-proto",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "try-lock"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
|
@ -3158,6 +3448,7 @@ dependencies = [
|
||||||
"actix-cors",
|
"actix-cors",
|
||||||
"actix-rt 2.6.0",
|
"actix-rt 2.6.0",
|
||||||
"actix-web 4.0.0-rc.2",
|
"actix-web 4.0.0-rc.2",
|
||||||
|
"actix-web-actors",
|
||||||
"actix-web-httpauth",
|
"actix-web-httpauth",
|
||||||
"base64",
|
"base64",
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
|
@ -3166,11 +3457,13 @@ dependencies = [
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
"rand 0.8.4",
|
"rand 0.8.4",
|
||||||
|
"reqwest",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
"tokio 1.16.1",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-actix-web",
|
"tracing-actix-web",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
@ -3207,6 +3500,16 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "want"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"try-lock",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.9.0+wasi-snapshot-preview1"
|
version = "0.9.0+wasi-snapshot-preview1"
|
||||||
|
@ -3244,6 +3547,18 @@ dependencies = [
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-futures"
|
||||||
|
version = "0.4.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.79"
|
version = "0.2.79"
|
||||||
|
@ -3360,6 +3675,15 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winreg"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ws2_32-sys"
|
name = "ws2_32-sys"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|
|
@ -11,7 +11,8 @@ tracing = "0.1.30"
|
||||||
tracing-unwrap = "0.9.2"
|
tracing-unwrap = "0.9.2"
|
||||||
tracing-subscriber = { version = "0.3.8", features = ["fmt"] }
|
tracing-subscriber = { version = "0.3.8", features = ["fmt"] }
|
||||||
actix = "0.12.0"
|
actix = "0.12.0"
|
||||||
actix-cors = "0.5.4"
|
actix-cors = "0.6.0-beta.10"
|
||||||
|
actix-web-actors = "4.0.0-beta.6"
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
serde = { version = "1.0.136", features= [ "derive" ] }
|
serde = { version = "1.0.136", features= [ "derive" ] }
|
||||||
serde_json = "1.0.78"
|
serde_json = "1.0.78"
|
||||||
|
@ -25,8 +26,10 @@ bcrypt = "0.10.1"
|
||||||
actix-web-httpauth = "0.6.0-beta.7"
|
actix-web-httpauth = "0.6.0-beta.7"
|
||||||
jsonwebtoken = "8.0.1"
|
jsonwebtoken = "8.0.1"
|
||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
|
tokio = { verison = "1", features=["full"] }
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
sqlx = { version = "^0.5", features=["sqlite", "migrate"] }
|
sqlx = { version = "^0.5", features=["sqlite", "migrate"] }
|
||||||
|
reqwest = "0.11.9"
|
||||||
|
|
||||||
[target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.jemallocator]
|
[target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.jemallocator]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
COPY target/x86_64-unknown-linux-musl/release/vade /app/vade
|
COPY target/x86_64-unknown-linux-musl/release/vade /app/vade
|
||||||
EXPOSE 8080
|
EXPOSE 8088
|
||||||
WORKDIR app
|
WORKDIR app
|
||||||
RUN touch data.db
|
RUN touch data.db
|
||||||
CMD ["./vade"]
|
CMD ["./vade"]
|
||||||
|
|
|
@ -6,7 +6,9 @@ CREATE TABLE application (
|
||||||
description TEXT,
|
description TEXT,
|
||||||
active Boolean NOT NULL DEFAULT 1,
|
active Boolean NOT NULL DEFAULT 1,
|
||||||
glyph TEXT,
|
glyph TEXT,
|
||||||
application_category_id INTEGER
|
application_category_id INTEGER,
|
||||||
|
alive Boolean NOT NULL DEFAULT 1,
|
||||||
|
enable_healthcheck Boolean NOT NULL DEFAULT 0
|
||||||
);
|
);
|
||||||
CREATE TABLE application_category (
|
CREATE TABLE application_category (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
|
|
@ -3,11 +3,50 @@ use tracing::instrument;
|
||||||
use crate::api::api_prelude::*;
|
use crate::api::api_prelude::*;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct ApiApplication {
|
||||||
|
pub id: i32,
|
||||||
|
pub app_name: String,
|
||||||
|
pub url: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub active: bool,
|
||||||
|
pub glyph: Option<String>,
|
||||||
|
pub application_category_id: Option<i32>,
|
||||||
|
pub enable_healthcheck: bool,
|
||||||
|
pub healthcheck_status: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<application::Model> for ApiApplication {
|
||||||
|
fn from(model: application::Model) -> Self {
|
||||||
|
Self {
|
||||||
|
id: model.id,
|
||||||
|
app_name: model.app_name,
|
||||||
|
url: model.url,
|
||||||
|
description: model.description,
|
||||||
|
active: model.active,
|
||||||
|
glyph: model.glyph,
|
||||||
|
application_category_id: model.application_category_id,
|
||||||
|
enable_healthcheck: model.enable_healthcheck,
|
||||||
|
healthcheck_status: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument]
|
#[instrument]
|
||||||
#[get("")]
|
#[get("")]
|
||||||
pub async fn list_applications(state: web::Data<AppState>) -> Result<HttpResponse> {
|
pub async fn list_applications(state: web::Data<AppState>) -> Result<HttpResponse> {
|
||||||
let apps: Vec<application::Model> = Application::find().all(&state.db).await.unwrap();
|
let apps: Vec<application::Model> = Application::find().all(&state.db).await.unwrap();
|
||||||
let count = apps.len();
|
let count = apps.len();
|
||||||
|
let mut apps = apps
|
||||||
|
.into_iter()
|
||||||
|
.map(|app| app.into())
|
||||||
|
.collect::<Vec<ApiApplication>>();
|
||||||
|
|
||||||
|
for app in apps.iter_mut() {
|
||||||
|
app.healthcheck_status = state.healthcheck_status.lock().await.get(&app.id).cloned();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(ListObjects::new(apps, count)))
|
Ok(HttpResponse::Ok().json(ListObjects::new(apps, count)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +64,7 @@ pub async fn new_application(
|
||||||
active: Set(data.0.active),
|
active: Set(data.0.active),
|
||||||
glyph: Set(data.0.glyph),
|
glyph: Set(data.0.glyph),
|
||||||
application_category_id: Set(data.0.application_category_id),
|
application_category_id: Set(data.0.application_category_id),
|
||||||
|
enable_healthcheck: Set(data.0.enable_healthcheck),
|
||||||
};
|
};
|
||||||
let app = model.insert(&state.db).await?;
|
let app = model.insert(&state.db).await?;
|
||||||
Ok(HttpResponse::Ok().json(app))
|
Ok(HttpResponse::Ok().json(app))
|
||||||
|
@ -65,6 +105,7 @@ pub async fn update_applications(
|
||||||
url: Set(data.url),
|
url: Set(data.url),
|
||||||
application_category_id: Set(data.application_category_id),
|
application_category_id: Set(data.application_category_id),
|
||||||
glyph: Set(data.glyph),
|
glyph: Set(data.glyph),
|
||||||
|
enable_healthcheck: Set(data.enable_healthcheck),
|
||||||
};
|
};
|
||||||
let model = ret.update(&state.db).await?;
|
let model = ret.update(&state.db).await?;
|
||||||
Ok(HttpResponse::Ok().json(model))
|
Ok(HttpResponse::Ok().json(model))
|
||||||
|
@ -168,6 +209,7 @@ mod tests {
|
||||||
description: Some("Some Application".into()),
|
description: Some("Some Application".into()),
|
||||||
active: true,
|
active: true,
|
||||||
application_category_id: None,
|
application_category_id: None,
|
||||||
|
enable_healthcheck: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = setup_state().await?;
|
let state = setup_state().await?;
|
||||||
|
|
|
@ -78,6 +78,8 @@ mod api_prelude {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test_prelude {
|
pub mod test_prelude {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub use super::ListObjects;
|
pub use super::ListObjects;
|
||||||
use crate::auth;
|
use crate::auth;
|
||||||
pub use crate::entity::*;
|
pub use crate::entity::*;
|
||||||
|
@ -96,6 +98,7 @@ pub mod test_prelude {
|
||||||
entity::prelude::*, entity::*, tests_cfg::*, DatabaseBackend, MockDatabase, MockExecResult,
|
entity::prelude::*, entity::*, tests_cfg::*, DatabaseBackend, MockDatabase, MockExecResult,
|
||||||
Transaction,
|
Transaction,
|
||||||
};
|
};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
/// Sets up a testing state with an in-memory database and creates the scheme.
|
/// Sets up a testing state with an in-memory database and creates the scheme.
|
||||||
pub async fn setup_state() -> Result<actix_web::web::Data<AppState>> {
|
pub async fn setup_state() -> Result<actix_web::web::Data<AppState>> {
|
||||||
|
@ -118,7 +121,10 @@ pub mod test_prelude {
|
||||||
|
|
||||||
auth::generate_secret(&db).await?;
|
auth::generate_secret(&db).await?;
|
||||||
|
|
||||||
Ok(actix_web::web::Data::new(AppState { db }))
|
Ok(actix_web::web::Data::new(AppState {
|
||||||
|
db,
|
||||||
|
healthcheck_status: Mutex::new(HashMap::new()),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub struct Model {
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
pub glyph: Option<String>,
|
pub glyph: Option<String>,
|
||||||
pub application_category_id: Option<i32>,
|
pub application_category_id: Option<i32>,
|
||||||
|
pub enable_healthcheck: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter)]
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
@ -50,6 +51,7 @@ impl Default for Model {
|
||||||
active: true,
|
active: true,
|
||||||
glyph: Default::default(),
|
glyph: Default::default(),
|
||||||
application_category_id: Default::default(),
|
application_category_id: Default::default(),
|
||||||
|
enable_healthcheck: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ impl actix_web::error::ResponseError for Error {
|
||||||
fn status_code(&self) -> actix_web::http::StatusCode {
|
fn status_code(&self) -> actix_web::http::StatusCode {
|
||||||
match self.code {
|
match self.code {
|
||||||
ErrorCode::UnAuthorized => StatusCode::UNAUTHORIZED,
|
ErrorCode::UnAuthorized => StatusCode::UNAUTHORIZED,
|
||||||
ErrorCode::NoSetup => StatusCode::UPGRADE_REQUIRED,
|
ErrorCode::NoSetup => StatusCode::OK,
|
||||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
use actix::*;
|
||||||
|
use rand::{self, rngs::ThreadRng, Rng};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub type ID = i64;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Session {
|
||||||
|
pub addr: Recipient<Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "ID")]
|
||||||
|
pub struct Connect {
|
||||||
|
pub addr: Recipient<Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
|
pub struct Disconnect {
|
||||||
|
pub id: ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message, Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
|
pub enum Event {
|
||||||
|
HealthcheckChange { app_id: i32, alive: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct EventBroker {
|
||||||
|
rng: ThreadRng,
|
||||||
|
sessions: HashMap<ID, Session>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Supervised for EventBroker {}
|
||||||
|
|
||||||
|
impl SystemService for EventBroker {}
|
||||||
|
|
||||||
|
impl Actor for EventBroker {
|
||||||
|
type Context = Context<Self>;
|
||||||
|
|
||||||
|
fn started(&mut self, _: &mut Self::Context) {
|
||||||
|
tracing::info!("EventBroker - Started");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stopped(&mut self, _: &mut Self::Context) {
|
||||||
|
tracing::error!("EventBroker - Shutdown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Connect> for EventBroker {
|
||||||
|
type Result = ID;
|
||||||
|
fn handle(&mut self, msg: Connect, _: &mut Context<Self>) -> Self::Result {
|
||||||
|
tracing::info!("Session connected");
|
||||||
|
let id = self.rng.gen::<ID>();
|
||||||
|
self.sessions.insert(id, Session { addr: msg.addr });
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Disconnect> for EventBroker {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
|
||||||
|
tracing::info!("Session disconnected");
|
||||||
|
self.sessions.remove(&msg.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Event> for EventBroker {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: Event, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
tracing::info!("Event received");
|
||||||
|
for (_, ses) in self.sessions.iter() {
|
||||||
|
let _ = ses.addr.do_send(msg.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,11 @@ axios.interceptors.request.use(function (config) {
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
axios.defaults.baseURL = process.env.NODE_ENV === 'development'
|
||||||
|
? 'http://localhost:8088'
|
||||||
|
: '';
|
||||||
|
|
||||||
axios.interceptors.response.use(function (response) {
|
axios.interceptors.response.use(function (response) {
|
||||||
return response;
|
return response;
|
||||||
}, function (error) {
|
}, function (error) {
|
||||||
|
|
58
src/main.rs
58
src/main.rs
|
@ -1,8 +1,10 @@
|
||||||
use std::path::Path;
|
use std::{collections::HashMap, path::Path};
|
||||||
|
|
||||||
|
use actix::SystemService;
|
||||||
use actix_web::{get, web, App, HttpResponse, HttpServer};
|
use actix_web::{get, web, App, HttpResponse, HttpServer};
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
use sea_orm::{Database, DatabaseConnection};
|
use sea_orm::{prelude::*, Database};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
use tracing::{info, instrument};
|
use tracing::{info, instrument};
|
||||||
use tracing_subscriber::prelude::*;
|
use tracing_subscriber::prelude::*;
|
||||||
|
|
||||||
|
@ -10,6 +12,8 @@ mod api;
|
||||||
mod auth;
|
mod auth;
|
||||||
mod entity;
|
mod entity;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod events;
|
||||||
|
mod socket_session;
|
||||||
|
|
||||||
#[cfg(all(target_env = "musl", target_pointer_width = "64"))]
|
#[cfg(all(target_env = "musl", target_pointer_width = "64"))]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
|
@ -18,32 +22,74 @@ static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub db: DatabaseConnection,
|
pub db: DatabaseConnection,
|
||||||
|
pub healthcheck_status: Mutex<HashMap<i32, bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_rt::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let subscriber = tracing_subscriber::registry().with(
|
let subscriber = tracing_subscriber::registry().with(
|
||||||
tracing_subscriber::fmt::Layer::new()
|
tracing_subscriber::fmt::Layer::new()
|
||||||
.pretty()
|
.pretty()
|
||||||
.with_writer(std::io::stdout)
|
.with_writer(std::io::stdout)
|
||||||
.with_ansi(true)
|
.with_ansi(true)
|
||||||
.with_filter(tracing_subscriber::filter::LevelFilter::TRACE),
|
.with_filter(tracing_subscriber::filter::LevelFilter::INFO),
|
||||||
);
|
);
|
||||||
tracing::subscriber::set_global_default(subscriber).expect("Unable to set a global collector");
|
tracing::subscriber::set_global_default(subscriber).expect("Unable to set a global collector");
|
||||||
|
|
||||||
let db = setup_database().await.unwrap();
|
let db = setup_database().await.unwrap();
|
||||||
let state = web::Data::new(AppState { db });
|
let state = web::Data::new(AppState {
|
||||||
|
db,
|
||||||
|
healthcheck_status: Mutex::new(HashMap::new()),
|
||||||
|
});
|
||||||
|
|
||||||
info!("Starting http server on 8080");
|
info!("Starting http server on 8080");
|
||||||
|
|
||||||
|
let st = state.clone();
|
||||||
|
actix_rt::spawn(async move {
|
||||||
|
loop {
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(60)).await;
|
||||||
|
let apps: Vec<entity::application::Model> = entity::application::Entity::find()
|
||||||
|
.filter(entity::application::Column::EnableHealthcheck.eq(true))
|
||||||
|
.all(&st.db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for app in apps {
|
||||||
|
match reqwest::get(app.url).await {
|
||||||
|
Ok(res) if res.status() == 200 => {
|
||||||
|
st.healthcheck_status.lock().await.insert(app.id, true);
|
||||||
|
let _ = events::EventBroker::from_registry()
|
||||||
|
.send(events::Event::HealthcheckChange {
|
||||||
|
app_id: app.id,
|
||||||
|
alive: true,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
st.healthcheck_status.lock().await.insert(app.id, false);
|
||||||
|
let _ = events::EventBroker::from_registry()
|
||||||
|
.send(events::Event::HealthcheckChange {
|
||||||
|
app_id: app.id,
|
||||||
|
alive: false,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
|
let cors = actix_cors::Cors::permissive();
|
||||||
App::new()
|
App::new()
|
||||||
.app_data(state.clone())
|
.app_data(state.clone())
|
||||||
.wrap(tracing_actix_web::TracingLogger::default())
|
.wrap(tracing_actix_web::TracingLogger::default())
|
||||||
|
.wrap(cors)
|
||||||
|
.service(web::resource("/events/{token}").to(socket_session::event_session_index))
|
||||||
.service(api::routes())
|
.service(api::routes())
|
||||||
.service(dist)
|
.service(dist)
|
||||||
})
|
})
|
||||||
.bind("0.0.0.0:8080")
|
.bind("0.0.0.0:8088")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
use super::events::{Connect, Disconnect, Event, EventBroker, ID};
|
||||||
|
use crate::auth::AuthClaims;
|
||||||
|
use actix::*;
|
||||||
|
use actix_web::{
|
||||||
|
web::{self, Data},
|
||||||
|
Error, HttpRequest, HttpResponse,
|
||||||
|
};
|
||||||
|
use actix_web_actors::ws;
|
||||||
|
use jsonwebtoken::{decode, DecodingKey, Validation};
|
||||||
|
|
||||||
|
pub struct EventSession {
|
||||||
|
id: ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Actor for EventSession {
|
||||||
|
type Context = ws::WebsocketContext<Self>;
|
||||||
|
|
||||||
|
fn started(&mut self, ctx: &mut Self::Context) {
|
||||||
|
let addr = ctx.address();
|
||||||
|
EventBroker::from_registry()
|
||||||
|
.send(Connect {
|
||||||
|
addr: addr.recipient(),
|
||||||
|
})
|
||||||
|
.into_actor(self)
|
||||||
|
.then(|res, act, ctx| {
|
||||||
|
match res {
|
||||||
|
Ok(res) => act.id = res,
|
||||||
|
// something is wrong. Burn it with fire.
|
||||||
|
_ => ctx.stop(),
|
||||||
|
}
|
||||||
|
fut::ready(())
|
||||||
|
})
|
||||||
|
.wait(ctx);
|
||||||
|
}
|
||||||
|
fn stopping(&mut self, _: &mut Self::Context) -> Running {
|
||||||
|
// notify suprivisor we are leavin
|
||||||
|
EventBroker::from_registry().do_send(Disconnect { id: self.id });
|
||||||
|
Running::Stop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Event> for EventSession {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: Event, ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
let data = serde_json::to_string(&msg).unwrap_or_default();
|
||||||
|
ctx.text(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for EventSession {
|
||||||
|
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
||||||
|
match msg {
|
||||||
|
Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
|
||||||
|
Ok(ws::Message::Text(text)) => ctx.text(text),
|
||||||
|
Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn event_session_index(
|
||||||
|
req: HttpRequest,
|
||||||
|
stream: web::Payload,
|
||||||
|
state: Data<crate::AppState>,
|
||||||
|
path: web::Path<String>,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let secret = crate::auth::get_secret(&state.db).await?;
|
||||||
|
|
||||||
|
let decoded = decode::<AuthClaims>(
|
||||||
|
&path,
|
||||||
|
&DecodingKey::from_secret(&secret),
|
||||||
|
&Validation::default(),
|
||||||
|
);
|
||||||
|
match decoded {
|
||||||
|
Ok(_) => ws::start(EventSession { id: 0 }, &req, stream),
|
||||||
|
Err(e) => Err(actix_web::error::ErrorUnauthorized(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,6 @@ label {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
color: #2c3e50;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<btn @click="delCategory" label="Delete" danger v-if="!!this.category.id" />
|
<btn @click="delCategory" label="Delete" danger v-if="!!this.category.id" />
|
||||||
<btn @click="close" label="Cancel" />
|
<btn @click="close" label="Cancel" />
|
||||||
<btn @click="submit" :label="saveLabel" />
|
<btn primary @click="submit" :label="saveLabel" />
|
||||||
</template>
|
</template>
|
||||||
</modal>
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -13,12 +13,13 @@
|
||||||
<text-field v-model="app.description" label="Description" />
|
<text-field v-model="app.description" label="Description" />
|
||||||
<text-field v-model="app.url" label="URL" />
|
<text-field v-model="app.url" label="URL" />
|
||||||
<icon-picker v-model="app.glyph" />
|
<icon-picker v-model="app.glyph" />
|
||||||
|
<switch-field v-model="app.enableHealthcheck" label="Health check" />
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<btn @click="delApp" label="Delete" danger v-if="!!this.app.id" />
|
<btn @click="delApp" label="Delete" danger v-if="!!this.app.id" />
|
||||||
<btn @click="close" label="Cancel" />
|
<btn @click="close" label="Cancel" />
|
||||||
<btn :label="saveLabel" type="button" @click="save" />
|
<btn primary :label="saveLabel" type="button" @click="save" />
|
||||||
</template>
|
</template>
|
||||||
</modal>
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
@ -29,9 +30,10 @@ import IconPicker from "./IconPicker.vue";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Btn from "./Button.vue";
|
import Btn from "./Button.vue";
|
||||||
import SelectField from "./SelectField.vue";
|
import SelectField from "./SelectField.vue";
|
||||||
|
import SwitchField from './Switch.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { TextField, Modal, IconPicker, Btn, SelectField },
|
components: { TextField, Modal, IconPicker, Btn, SelectField, SwitchField },
|
||||||
props: ["open", "mode", "data", "categories"],
|
props: ["open", "mode", "data", "categories"],
|
||||||
watch: {
|
watch: {
|
||||||
data: function (next) {
|
data: function (next) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="tile" @click="click">
|
<div :class="{tile: true, alive: appData.healthcheckStatus === true, dead: appData.healthcheckStatus === false}" @click="click">
|
||||||
<font-awesome-icon :icon="appData.glyph" size="2x" />
|
<font-awesome-icon :icon="appData.glyph" size="2x" />
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<div class="title">{{appData.appName}}</div>
|
<div class="title">{{appData.appName}}</div>
|
||||||
|
@ -43,6 +43,13 @@ svg {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tile.alive svg {
|
||||||
|
color: #009900;
|
||||||
|
}
|
||||||
|
.tile.dead svg {
|
||||||
|
color: #900;
|
||||||
|
}
|
||||||
.title {
|
.title {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<btn @click="delCategory" label="Delete" danger v-if="!!this.category.id" />
|
<btn @click="delCategory" label="Delete" danger v-if="!!this.category.id" />
|
||||||
<btn @click="close" label="Cancel" />
|
<btn @click="close" label="Cancel" />
|
||||||
<btn @click="submit" :label="saveLabel" />
|
<btn primary @click="submit" :label="saveLabel" />
|
||||||
</template>
|
</template>
|
||||||
</modal>
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<btn @click="delBookmark" label="Delete" danger v-if="!!this.bookmark.id" />
|
<btn @click="delBookmark" label="Delete" danger v-if="!!this.bookmark.id" />
|
||||||
<btn @click="close" label="Cancel" />
|
<btn @click="close" label="Cancel" />
|
||||||
<btn :label="saveLabel" type="button" @click="save" />
|
<btn primary :label="saveLabel" type="button" @click="save" />
|
||||||
</template>
|
</template>
|
||||||
</modal>
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<button :class="{danger: danger}">{{label}}</button>
|
<button :class="{danger: danger, primary: primary}">{{label}}</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -7,6 +7,7 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
label: String,
|
label: String,
|
||||||
danger: Boolean,
|
danger: Boolean,
|
||||||
|
primary: Boolean,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -16,26 +17,33 @@ button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
padding: 10px 18px;
|
padding: 10px 18px;
|
||||||
background: #333;
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
border: 2px solid #222;
|
border: none;
|
||||||
background: #333;
|
background: #21242B;
|
||||||
box-shadow: none;
|
box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.24) 0px 1px 2px;
|
||||||
}
|
|
||||||
|
|
||||||
button.danger {
|
|
||||||
color: #cc0000;
|
|
||||||
background-color: #200;
|
|
||||||
border: 2px solid #200;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
button:hover {
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
background: #444;
|
box-shadow: rgba(0, 0, 0, 0.42) 0px 1px 3px, rgba(0, 0, 0, 0.64) 0px 1px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.danger {
|
||||||
|
background: #41141B;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
button.danger:hover {
|
button.danger:hover {
|
||||||
background: #400;
|
background: #61141B;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.primary {
|
||||||
|
background: #21244B;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
button.primary:hover {
|
||||||
|
background: #21246B;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -14,10 +14,15 @@
|
||||||
}
|
}
|
||||||
label {
|
label {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
font-weight: bold;
|
font-weight: 500;
|
||||||
text-transform: uppercase;
|
line-height: 35px;
|
||||||
|
text-align: right;
|
||||||
|
color: #999;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
label::after {
|
||||||
|
content: ':'
|
||||||
|
}
|
||||||
.form-field + .form-field {
|
.form-field + .form-field {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
</form-field>
|
</form-field>
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
|
:class="{selected: modelValue === icon}"
|
||||||
:icon="icon"
|
:icon="icon"
|
||||||
size="2x"
|
size="2x"
|
||||||
v-for="icon in items"
|
v-for="icon in items"
|
||||||
|
@ -1039,7 +1040,7 @@ export default {
|
||||||
}
|
}
|
||||||
return icons
|
return icons
|
||||||
.filter((i) => i.includes(this.modelValue.toString().toLowerCase()))
|
.filter((i) => i.includes(this.modelValue.toString().toLowerCase()))
|
||||||
.slice(0, 21);
|
.slice(0, 18);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -1061,19 +1062,35 @@ export default {
|
||||||
|
|
||||||
input {
|
input {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #121212;
|
background-color: transparent;
|
||||||
outline: none;
|
outline: none;
|
||||||
display: block;
|
display: block;
|
||||||
border: 1px solid #000;
|
border: none;
|
||||||
|
border-bottom: 1px solid #555;
|
||||||
|
font-weight: bold;
|
||||||
padding: 8px 15px;
|
padding: 8px 15px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
transition: all 0.2s;
|
||||||
|
border-bottom: 1px solid #336699;
|
||||||
|
}
|
||||||
|
|
||||||
.icons {
|
.icons {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
height: 130px;
|
||||||
|
background: #21242b;
|
||||||
|
padding: 15px;
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 100%;
|
border-radius: 10px;
|
||||||
|
width: calc(100% - 30px);
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
grid-template-columns: repeat(7, 1fr);
|
grid-template-columns: repeat(6, 1fr);
|
||||||
|
justify-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
.icons .selected {
|
||||||
|
color: #336699;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -80,7 +80,7 @@ export default {
|
||||||
height 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
height 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), 0 6px 6px rgba(0, 0, 0, 0.16);
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), 0 6px 6px rgba(0, 0, 0, 0.16);
|
||||||
|
|
||||||
background: #333;
|
background: #282c33;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
@ -96,7 +96,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal .actions {
|
.modal .actions {
|
||||||
background: #3c3c3c;
|
background: #21242b;
|
||||||
border-top: 1px solid #2c2c2c;
|
border-top: 1px solid #2c2c2c;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<style>
|
<style>
|
||||||
.panel {
|
.panel {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background: #333;
|
background: #21242b;
|
||||||
padding: 25px;
|
padding: 25px;
|
||||||
box-shadow: rgb(0, 0, 0) 0px 20px 30px -10px;
|
box-shadow: rgb(0, 0, 0) 0px 20px 30px -10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -30,7 +30,8 @@ export default {
|
||||||
select {
|
select {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: block;
|
display: block;
|
||||||
color: #ccc;
|
color: #fff;
|
||||||
|
font-weight: bold;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
padding: 0.6em 1.4em 0.5em 0.8em;
|
padding: 0.6em 1.4em 0.5em 0.8em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -38,10 +39,12 @@ select {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: none;
|
border: none;
|
||||||
|
border-bottom: 1px solid #555;
|
||||||
|
overflow: hideden;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
background-color: #121212;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
select::-ms-expand {
|
select::-ms-expand {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<form-field>
|
||||||
|
<label>{{label}}</label>
|
||||||
|
<div :class="{switch: true, active: modelValue}" @click="handleClick">
|
||||||
|
<div class="indicator"></div>
|
||||||
|
</div>
|
||||||
|
</form-field>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FormField from './FormField.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ["label", "modelValue"],
|
||||||
|
components: { FormField },
|
||||||
|
methods: {
|
||||||
|
handleClick() {
|
||||||
|
this.$emit("update:modelValue", !this.modelValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.switch {
|
||||||
|
background: #21242b;
|
||||||
|
border-radius: 15px;
|
||||||
|
height: 36px;
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
.indicator {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
border-radius: 13px;
|
||||||
|
background: white;
|
||||||
|
position: relative;
|
||||||
|
top: 4px;
|
||||||
|
left: 5px;
|
||||||
|
background: #282c33;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch.active .indicator {
|
||||||
|
transition: all 0.2s;
|
||||||
|
top: 4px;
|
||||||
|
left: 45px;
|
||||||
|
background: #336699;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -29,11 +29,19 @@ export default {
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
input {
|
input {
|
||||||
|
transition: all 0.2s;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #121212;
|
background-color: transparent;
|
||||||
|
font-weight: bold;
|
||||||
outline: none;
|
outline: none;
|
||||||
border: 1px solid #000;
|
border: none;
|
||||||
|
border-bottom: 1px solid #555;
|
||||||
|
overflow: hidden;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 8px 15px;
|
padding: 8px 15px;
|
||||||
}
|
}
|
||||||
|
input:focus {
|
||||||
|
transition: all 0.2s;
|
||||||
|
border-bottom: 1px solid #336699;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dashboard">
|
<div :class="{dashboard: true, 'edit-mode': editMode}">
|
||||||
<div class="editmode-tiles">
|
<div class="editmode-tiles">
|
||||||
<new-item-tile title="New Application" v-if="editMode" @click="openNewApp" />
|
<new-item-tile title="New Application" v-if="editMode" @click="openNewApp" />
|
||||||
<new-item-tile title="New App Category" v-if="editMode" @click="openNewAppCat" />
|
<new-item-tile title="New App Category" v-if="editMode" @click="openNewAppCat" />
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container" v-for="cat in applicationCategories" :key="cat.id">
|
<div class="container" v-for="cat in applicationCategories" :key="cat.id">
|
||||||
<h1 @click="appCatClicked(cat)">
|
<h1 @click="appCatClicked(cat)" class="editable">
|
||||||
<font-awesome-icon v-if="cat.glyph" :icon="cat.glyph" />
|
<font-awesome-icon v-if="cat.glyph" :icon="cat.glyph" />
|
||||||
{{cat.categoryName}}
|
{{cat.categoryName}}
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container" v-if="bookmarks.length">
|
||||||
<h1 class="bookmark">BOOKMARKS</h1>
|
<h1 class="bookmark">BOOKMARKS</h1>
|
||||||
<div class="bookmark-container">
|
<div class="bookmark-container">
|
||||||
<div class="bookmark-category">
|
<div class="bookmark-category">
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="bookmark-category" v-for="cat in bookmarkCategories" :key="cat.id">
|
<div class="bookmark-category" v-for="cat in bookmarkCategories" :key="cat.id">
|
||||||
<h2 @click="bookmarkCatClicked(cat)">
|
<h2 @click="bookmarkCatClicked(cat)" class="editable">
|
||||||
<font-awesome-icon v-if="cat.glyph" :icon="cat.glyph" />
|
<font-awesome-icon v-if="cat.glyph" :icon="cat.glyph" />
|
||||||
{{cat.categoryName}}
|
{{cat.categoryName}}
|
||||||
</h2>
|
</h2>
|
||||||
|
@ -102,7 +102,23 @@ import BookmarkCategoryModal from "../components/BookmarkCategoryModal.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Dashboard",
|
name: "Dashboard",
|
||||||
|
unmounted() {
|
||||||
|
this.connection = null;
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
connect_ws() {
|
||||||
|
const token = localStorage.getItem("token")
|
||||||
|
this.connection = new WebSocket("ws://localhost:8088/events/" + encodeURIComponent(token));
|
||||||
|
this.connection.onmessage = (event) => {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
const { app_id, alive } = data.HealthcheckChange;
|
||||||
|
const idx = this.applications.findIndex((app) => app.id == app_id);
|
||||||
|
this.applications[idx].healthcheckStatus = alive;
|
||||||
|
}
|
||||||
|
this.connection.onclose = () => {
|
||||||
|
setTimeout(() => this.connect_ws(), 1000);
|
||||||
|
};
|
||||||
|
},
|
||||||
appsForCategory(catId) {
|
appsForCategory(catId) {
|
||||||
return this.applications.filter((i) => i.applicationCategoryId === catId);
|
return this.applications.filter((i) => i.applicationCategoryId === catId);
|
||||||
},
|
},
|
||||||
|
@ -179,12 +195,16 @@ export default {
|
||||||
this.bookmarkCategories = (
|
this.bookmarkCategories = (
|
||||||
await axios.get("/api/bookmark_categories")
|
await axios.get("/api/bookmark_categories")
|
||||||
).data.items;
|
).data.items;
|
||||||
|
|
||||||
this.editApp = {};
|
this.editApp = {};
|
||||||
this.newAppOpen = false;
|
this.editAppCat = {};
|
||||||
|
this.appOpen = false;
|
||||||
this.appCatOpen = false;
|
this.appCatOpen = false;
|
||||||
|
|
||||||
|
this.editBookmark = {};
|
||||||
|
this.editBookmarkCat = {};
|
||||||
this.bookmarkCatOpen = false;
|
this.bookmarkCatOpen = false;
|
||||||
this.bookmarkOpen = false;
|
this.bookmarkOpen = false;
|
||||||
this.editAppCat = {};
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -212,9 +232,11 @@ export default {
|
||||||
editBookmarkCat: {},
|
editBookmarkCat: {},
|
||||||
bookmarks: [],
|
bookmarks: [],
|
||||||
bookmarkCategories: [],
|
bookmarkCategories: [],
|
||||||
|
connection: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
this.connect_ws();
|
||||||
this.reload();
|
this.reload();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -237,6 +259,10 @@ h2 {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
.edit-mode .editable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
h2 svg {
|
h2 svg {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ export default {
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
background: #333;
|
background: #333;
|
||||||
font-size: 40px;
|
font-size: 20px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #121212;
|
background: #121212;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<panel>
|
<panel>
|
||||||
<h1>Setup a password</h1>
|
<h1>Setup a password</h1>
|
||||||
<text-field v-model="password" password />
|
<text-field v-model="password" password />
|
||||||
<btn label="Setup" @click="setup" />
|
<btn primary label="Setup" @click="setup" />
|
||||||
</panel>
|
</panel>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
Loading…
Reference in New Issue