Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed8352d857 | ||
|
|
41f64ca9fe | ||
|
|
738e49dc92 | ||
|
|
68a16257c6 | ||
|
|
5dcbae55f4 | ||
|
|
e0d963256c | ||
|
|
2bff4bbd15 | ||
|
|
a0eba89f34 | ||
|
|
4e78de9da2 | ||
|
|
06f49e24c2 | ||
|
|
e598ae45f7 | ||
|
|
ad7f469e06 | ||
|
|
cb57ff5cc2 | ||
|
|
72f070fbcb | ||
|
|
fb0a86d37c | ||
|
|
36402dfefa | ||
|
|
c61874dbdd | ||
|
|
20848f28d5 | ||
|
|
5d23966f00 | ||
|
|
294cc66e4a | ||
|
|
0fd2e7032a | ||
|
|
340b85043e | ||
|
|
1821ae3c2b | ||
|
|
c536cb2afa | ||
|
|
3f79b4c2f6 | ||
|
|
137ba9185e | ||
|
|
2f6a0913dc | ||
|
|
2c7336d440 | ||
|
|
5a022ff3f6 | ||
|
|
fe4855500e | ||
|
|
ce8f5cbe0c | ||
|
|
b6bebe33fe | ||
|
|
9cf8072c1e | ||
|
|
0ca00ca6a1 | ||
|
|
619c5242db | ||
|
|
ff07854590 | ||
|
|
ba99989c66 | ||
|
|
3c75933cf2 | ||
|
|
ea9ef7afd3 | ||
|
|
f5e633a781 | ||
|
|
90b94816dd | ||
|
|
11dcaef3ff | ||
|
|
3f3e7f71ec | ||
|
|
b6070572cf | ||
|
|
e818a02a2a | ||
|
|
d8c8e222c8 | ||
|
|
f12b9715bc | ||
|
|
31c65d3e0b | ||
|
|
fcd6741cf9 | ||
|
|
d663b9230f | ||
|
|
5f2d61ad7a | ||
|
|
ec80a2dc0b | ||
|
|
2cece3de59 | ||
|
|
79eb580802 | ||
|
|
b93e3327a7 | ||
|
|
f01030a47a | ||
|
|
3cbd907c09 | ||
|
|
8fae69b7a4 | ||
|
|
e45dd4240e | ||
|
|
16a8552e6c | ||
|
|
c953ec6152 | ||
|
|
a433f10240 | ||
|
|
b73b97b5e9 | ||
|
|
0607e0b228 | ||
|
|
3bd4f67ec7 | ||
|
|
845d5a5d51 | ||
|
|
0101da731f | ||
|
|
68592acd39 | ||
|
|
b86fc5edb3 | ||
|
|
32b54ddf11 | ||
|
|
f8a0741e6c | ||
|
|
652d231771 | ||
|
|
1c3c5f4fec | ||
|
|
e7006a4a44 | ||
|
|
0f81d54f25 | ||
|
|
f3121c24c6 | ||
|
|
7abd0f2cd4 | ||
|
|
46b342005a | ||
|
|
4e0349bd58 | ||
|
|
30a569233b | ||
|
|
44ba6b4d0f | ||
|
|
9065070eb2 | ||
|
|
fd951bfdd5 | ||
|
|
27d613b594 | ||
|
|
ba757b9aa4 | ||
|
|
c445463c0c | ||
|
|
4693177e4b | ||
|
|
30b135eba3 | ||
|
|
bda48e07f1 | ||
|
|
7c8ab64e34 | ||
|
|
ec7855bd02 | ||
|
|
d401eefd6b | ||
|
|
0ad759a384 | ||
|
|
d3d20b29c3 | ||
|
|
fbb570c0d5 | ||
|
|
2318082bb8 | ||
|
|
05dd5f7de2 | ||
|
|
841251d09e | ||
|
|
a4dd8f70f0 | ||
|
|
9d0d1329b6 | ||
|
|
7a929777b4 | ||
|
|
ce3ec99c9f | ||
|
|
4f7847275e | ||
|
|
9a5ddb22b3 | ||
|
|
b1322cbec3 | ||
|
|
637bc84136 | ||
|
|
3147f02b6a | ||
|
|
dabff74946 | ||
|
|
de99577b9d | ||
|
|
1846432601 | ||
|
|
6f307dc822 | ||
|
|
f43210e29e | ||
|
|
50b9be4eca | ||
|
|
1a38222974 | ||
|
|
cb9cd0a43c | ||
|
|
c2e2ae8dbd | ||
|
|
df790a92be | ||
|
|
e1f9242b34 | ||
|
|
9d2a52e639 | ||
|
|
5cbca9457e | ||
|
|
25c66cff43 | ||
|
|
75a2e0deee | ||
|
|
b857137f1a | ||
|
|
40141cdcb0 | ||
|
|
c5c1d6903e | ||
|
|
f15c8188b9 | ||
|
|
8f971cfb02 | ||
|
|
32c811ec5a | ||
|
|
4febc56c30 | ||
|
|
f22ee4a68b | ||
|
|
87bec0399f | ||
|
|
1f29255dd1 | ||
|
|
4a11638182 | ||
|
|
10743372f8 | ||
|
|
d4b11bbb76 | ||
|
|
ced2417c98 | ||
|
|
27415aeca6 | ||
|
|
9bd2e336a6 | ||
|
|
d5778ec173 | ||
|
|
74e80f086a | ||
|
|
b1c6191fa1 | ||
|
|
8e0245c844 | ||
|
|
613fbf8c39 | ||
|
|
1788cda5c2 | ||
|
|
155adf549e | ||
|
|
5463542532 | ||
|
|
f5a25bc0e5 | ||
|
|
993339d0d1 | ||
|
|
bc4fe897cf | ||
|
|
c773bdaabd | ||
|
|
9f9a2be1d8 | ||
|
|
1e4b0c2f20 | ||
|
|
56025f6d56 | ||
|
|
5188b90f32 | ||
|
|
7178f537a8 | ||
|
|
45ac589975 | ||
|
|
9925e10ffc | ||
|
|
e3fde8d564 | ||
|
|
8aee01e008 | ||
|
|
cd203ff067 | ||
|
|
254cb6d80f | ||
|
|
9b3ad54d0a | ||
|
|
4ae3bb5780 | ||
|
|
b8aa1ef765 | ||
|
|
5e5daef49a | ||
|
|
88c478c533 | ||
|
|
1ecec115e4 | ||
|
|
8ff597eedb | ||
|
|
69f74488e5 | ||
|
|
ded9a27f42 | ||
|
|
43284ae625 | ||
|
|
f7b1ef9741 | ||
|
|
a6d445818f | ||
|
|
122a84b9e2 | ||
|
|
9716e64c93 | ||
|
|
eb8cf1b6da | ||
|
|
c633a9ad8d | ||
|
|
923eda1307 | ||
|
|
997342a31c |
No files matched your search
@@ -0,0 +1 @@
|
||||
release_notes: false
|
||||
@@ -7,7 +7,7 @@ import { URLSearchParams } from "url";
|
||||
const API_TOKEN = process.env.POEDITOR_API;
|
||||
const PROJECT_ID = process.env.POEDITOR_PROJECT_ID;
|
||||
const LANGUAGES = (
|
||||
process.env.LANGUAGES || "ar,zh-tw,cs,en,fi,fr,de,pt-br,ru,es,tr,ja"
|
||||
process.env.LANGUAGES || "ar,zh-tw,cs,en,fi,fr,de,pt-br,ru,es,tr,ja,zh-cn"
|
||||
).split(",");
|
||||
const EXPORT_FORMAT = process.env.EXPORT_FORMAT || "key_value_json";
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
- name: Install client dependencies
|
||||
working-directory: client
|
||||
run: npm ci
|
||||
run: npm install
|
||||
|
||||
- name: Check client formatting
|
||||
working-directory: client
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
|
||||
- name: Install server dependencies
|
||||
working-directory: server
|
||||
run: npm ci
|
||||
run: npm install
|
||||
|
||||
- name: Check server formatting
|
||||
working-directory: server
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
languages:
|
||||
description: "Languages to synchronize (comma separated, e.g.: tr,en,es)"
|
||||
required: false
|
||||
default: "ar,zh-tw,cs,en,fi,fr,de,pt-br,ru,es,tr,ja"
|
||||
default: "ar,zh-tw,cs,en,fi,fr,de,pt-br,ru,es,tr,ja,zh-cn"
|
||||
format:
|
||||
description: "Export format (key_value_json or json)"
|
||||
required: false
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.vscode
|
||||
Generated
+12
-633
@@ -21,7 +21,7 @@
|
||||
"dayjs": "1.11.13",
|
||||
"flag-icons": "7.3.2",
|
||||
"html2canvas": "^1.4.1",
|
||||
"i18next": "^24.2.2",
|
||||
"i18next": "25.4.2",
|
||||
"joi": "17.13.3",
|
||||
"mui-color-input": "^6.0.0",
|
||||
"react": "18.3.1",
|
||||
@@ -46,9 +46,6 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.6",
|
||||
"prettier": "^3.3.3",
|
||||
"vite": "6.3.5"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-linux-arm64-musl": "4.41.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
@@ -283,9 +280,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
|
||||
"integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz",
|
||||
"integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -491,262 +488,6 @@
|
||||
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
|
||||
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
|
||||
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
|
||||
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
|
||||
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
|
||||
@@ -763,134 +504,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
|
||||
@@ -2063,123 +1676,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz",
|
||||
"integrity": "sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz",
|
||||
"integrity": "sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz",
|
||||
"integrity": "sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz",
|
||||
"integrity": "sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz",
|
||||
"integrity": "sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz",
|
||||
"integrity": "sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz",
|
||||
"integrity": "sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz",
|
||||
"integrity": "sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz",
|
||||
"integrity": "sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz",
|
||||
@@ -2193,71 +1689,6 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz",
|
||||
"integrity": "sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz",
|
||||
"integrity": "sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz",
|
||||
"integrity": "sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz",
|
||||
"integrity": "sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz",
|
||||
"integrity": "sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz",
|
||||
@@ -2284,45 +1715,6 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz",
|
||||
"integrity": "sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz",
|
||||
"integrity": "sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz",
|
||||
"integrity": "sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@sideway/address": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
|
||||
@@ -4342,14 +3734,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
||||
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4363,20 +3756,6 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
@@ -4690,9 +4069,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "24.2.3",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.3.tgz",
|
||||
"integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==",
|
||||
"version": "25.4.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.4.2.tgz",
|
||||
"integrity": "sha512-gD4T25a6ovNXsfXY1TwHXXXLnD/K2t99jyYMCSimSCBnBRJVQr5j+VAaU83RJCPzrTGhVQ6dqIga66xO2rtd5g==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -4709,7 +4088,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.10"
|
||||
"@babel/runtime": "^7.27.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
|
||||
+1
-4
@@ -26,7 +26,7 @@
|
||||
"dayjs": "1.11.13",
|
||||
"flag-icons": "7.3.2",
|
||||
"html2canvas": "^1.4.1",
|
||||
"i18next": "^24.2.2",
|
||||
"i18next": "25.4.2",
|
||||
"joi": "17.13.3",
|
||||
"mui-color-input": "^6.0.0",
|
||||
"react": "18.3.1",
|
||||
@@ -59,8 +59,5 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.6",
|
||||
"prettier": "^3.3.3",
|
||||
"vite": "6.3.5"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-linux-arm64-musl": "4.41.0"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Box, Stack, Tooltip, Typography } from "@mui/material";
|
||||
import { formatDateWithTz } from "../../../Utils/timeUtils";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, forwardRef } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
@@ -17,26 +17,32 @@ import PropTypes from "prop-types";
|
||||
* @returns {JSX.Element} The Bar component.
|
||||
*/
|
||||
|
||||
const Bar = ({ width, height, backgroundColor, borderRadius, children }) => {
|
||||
const theme = useTheme();
|
||||
const Bar = forwardRef(
|
||||
({ width, height, backgroundColor, borderRadius, children, ...otherProps }, ref) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box
|
||||
position="relative"
|
||||
width={width}
|
||||
height={height}
|
||||
backgroundColor={backgroundColor}
|
||||
sx={{
|
||||
borderRadius: borderRadius || theme.spacing(1.5),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Box
|
||||
ref={ref}
|
||||
position="relative"
|
||||
width={width}
|
||||
height={height}
|
||||
backgroundColor={backgroundColor}
|
||||
sx={{
|
||||
borderRadius: borderRadius || theme.spacing(1.5),
|
||||
}}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Bar.displayName = "Bar";
|
||||
|
||||
Bar.propTypes = {
|
||||
width: PropTypes.string.isRequired,
|
||||
width: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
|
||||
height: PropTypes.string.isRequired,
|
||||
backgroundColor: PropTypes.string.isRequired,
|
||||
borderRadius: PropTypes.string,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export const tooltipDateFormatLookup = (dateRange) => {
|
||||
const dateFormatLookup = {
|
||||
recent: "ddd. MMMM D, YYYY, hh:mm A",
|
||||
day: "ddd. MMMM D, YYYY, hh:mm A",
|
||||
week: "ddd. MMMM D, YYYY, hh:mm A",
|
||||
month: "ddd. MMMM D, YYYY",
|
||||
|
||||
@@ -65,7 +65,7 @@ const Check = ({ text, noHighlightText, variant = "info", outlined = false }) =>
|
||||
};
|
||||
|
||||
Check.propTypes = {
|
||||
text: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
|
||||
text: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
|
||||
noHighlightText: PropTypes.string,
|
||||
variant: PropTypes.oneOf(["info", "error", "success"]),
|
||||
outlined: PropTypes.bool,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useId } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Modal, Stack, Typography } from "@mui/material";
|
||||
|
||||
const GenericDialog = ({ title, description, open, onClose, theme, children }) => {
|
||||
const GenericDialog = ({ title, description, open, onClose, theme, children, width }) => {
|
||||
const titleId = useId();
|
||||
const descriptionId = useId();
|
||||
const ariaDescribedBy = description?.length > 0 ? descriptionId : "";
|
||||
@@ -16,6 +16,7 @@ const GenericDialog = ({ title, description, open, onClose, theme, children }) =
|
||||
>
|
||||
<Stack
|
||||
gap={theme.spacing(2)}
|
||||
width={width}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
@@ -39,6 +40,7 @@ const GenericDialog = ({ title, description, open, onClose, theme, children }) =
|
||||
fontSize={16}
|
||||
color={theme.palette.primary.contrastText}
|
||||
fontWeight={600}
|
||||
marginBottom={theme.spacing(4)}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
@@ -46,6 +48,7 @@ const GenericDialog = ({ title, description, open, onClose, theme, children }) =
|
||||
<Typography
|
||||
id={descriptionId}
|
||||
color={theme.palette.primary.contrastTextTertiary}
|
||||
marginBottom={theme.spacing(4)}
|
||||
>
|
||||
{description}
|
||||
</Typography>
|
||||
@@ -64,6 +67,7 @@ GenericDialog.propTypes = {
|
||||
theme: PropTypes.object.isRequired,
|
||||
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node])
|
||||
.isRequired,
|
||||
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]),
|
||||
};
|
||||
|
||||
export { GenericDialog };
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useTheme } from "@emotion/react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Background from "../../assets/Images/background-grid.svg?react";
|
||||
import SkeletonDark from "../../assets/Images/create-placeholder-dark.svg?react";
|
||||
import OutputAnimation from "../../assets/Animations/output.gif";
|
||||
import DarkmodeOutput from "../../assets/Animations/darkmodeOutput.gif";
|
||||
import { useSelector } from "react-redux";
|
||||
@@ -13,7 +12,7 @@ const FallbackBackground = () => {
|
||||
<Box
|
||||
component="img"
|
||||
src={mode === "light" ? OutputAnimation : DarkmodeOutput}
|
||||
Background="transparent"
|
||||
background="transparent"
|
||||
alt="Loading animation"
|
||||
sx={{
|
||||
zIndex: 1,
|
||||
|
||||
@@ -74,7 +74,7 @@ const StarPrompt = ({ repoUrl = "https://github.com/bluewave-labs/checkmate" })
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
color: theme.palette.text.secondary,
|
||||
color: theme.palette.primary.contrastTextTertiary,
|
||||
fontSize: "0.938rem",
|
||||
lineHeight: 1.5,
|
||||
mb: 1,
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
TableHead,
|
||||
TableRow,
|
||||
} from "@mui/material";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import SkeletonLayout from "./skeleton";
|
||||
import PropTypes from "prop-types";
|
||||
import { useTheme } from "@emotion/react";
|
||||
@@ -41,6 +42,7 @@ const DataTable = ({
|
||||
data = [],
|
||||
config = {
|
||||
emptyView: "No data",
|
||||
tooltipContent: null,
|
||||
onRowClick: () => {},
|
||||
},
|
||||
}) => {
|
||||
@@ -101,24 +103,54 @@ const DataTable = ({
|
||||
data.map((row) => {
|
||||
const key = row.id || row._id || Math.random();
|
||||
return (
|
||||
<TableRow
|
||||
<Tooltip
|
||||
key={key}
|
||||
sx={config?.rowSX ?? {}}
|
||||
onClick={config?.onRowClick ? () => config.onRowClick(row) : null}
|
||||
followCursor
|
||||
enterDelay={500}
|
||||
enterNextDelay={500}
|
||||
title={
|
||||
typeof config.tooltipContent === "function"
|
||||
? config.tooltipContent(row)
|
||||
: config.tooltipContent
|
||||
}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
background: "unset",
|
||||
},
|
||||
},
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: ({ popper }) => {
|
||||
return [popper.width / 2 + 20, -popper.height / 8];
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
>
|
||||
{headers.map((header, index) => {
|
||||
return (
|
||||
<TableCell
|
||||
align={index === 0 ? "left" : "center"}
|
||||
key={header.id}
|
||||
onClick={header.onClick ? (e) => header.onClick(e, row) : null}
|
||||
sx={header.getCellSx ? header.getCellSx(row) : {}}
|
||||
>
|
||||
{header.render(row)}
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
<TableRow
|
||||
sx={config?.rowSX ?? {}}
|
||||
onClick={config?.onRowClick ? () => config.onRowClick(row) : null}
|
||||
>
|
||||
{headers.map((header, index) => {
|
||||
return (
|
||||
<TableCell
|
||||
align={index === 0 ? "left" : "center"}
|
||||
key={header.id}
|
||||
onClick={header.onClick ? (e) => header.onClick(e, row) : null}
|
||||
sx={header.getCellSx ? header.getCellSx(row) : {}}
|
||||
>
|
||||
{header.render(row)}
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
</Tooltip>
|
||||
);
|
||||
})
|
||||
)}
|
||||
|
||||
@@ -14,25 +14,25 @@ const useGetInviteToken = () => {
|
||||
const clearToken = () => {
|
||||
setToken(undefined);
|
||||
};
|
||||
|
||||
const fetchToken = async (email, role) => {
|
||||
const response = await networkService.requestInvitationToken({ email, role });
|
||||
const token = response?.data?.data?.token;
|
||||
if (typeof token === "undefined") {
|
||||
throw new Error(t("inviteNoTokenFound"));
|
||||
}
|
||||
return token;
|
||||
};
|
||||
const getInviteToken = async ({ email, role }) => {
|
||||
try {
|
||||
const response = await networkService.requestInvitationToken({
|
||||
email,
|
||||
role,
|
||||
});
|
||||
const token = response?.data?.data?.token;
|
||||
if (typeof token === "undefined") {
|
||||
throw new Error(t("inviteNoTokenFound"));
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
const token = await fetchToken(email, role);
|
||||
let inviteLink = token;
|
||||
|
||||
if (typeof CLIENT_HOST !== "undefined") {
|
||||
inviteLink = `${CLIENT_HOST}/register/${token}`;
|
||||
}
|
||||
|
||||
setToken(inviteLink);
|
||||
return token;
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
} finally {
|
||||
@@ -40,7 +40,26 @@ const useGetInviteToken = () => {
|
||||
}
|
||||
};
|
||||
|
||||
return [getInviteToken, clearToken, isLoading, error, token];
|
||||
const addTeamMember = async (formData, role) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const token = await fetchToken(formData.email, role);
|
||||
const toSubmit = {
|
||||
...formData,
|
||||
inviteToken: token,
|
||||
};
|
||||
delete toSubmit.confirm;
|
||||
const responseRegister = await networkService.registerUser(toSubmit);
|
||||
return responseRegister;
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return [getInviteToken, clearToken, isLoading, error, token, addTeamMember];
|
||||
};
|
||||
|
||||
export { useGetInviteToken };
|
||||
@@ -15,9 +15,6 @@ const useFetchLogs = () => {
|
||||
setIsLoading(true);
|
||||
const response = await networkService.getLogs();
|
||||
setLogs(response.data.data);
|
||||
createToast({
|
||||
body: t("logsPage.toast.fetchLogsSuccess"),
|
||||
});
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
createToast({
|
||||
|
||||
@@ -369,6 +369,8 @@ const useUpdateMonitor = () => {
|
||||
setIsLoading(true);
|
||||
const updatedFields = {
|
||||
name: monitor.name,
|
||||
statusWindowSize: monitor.statusWindowSize,
|
||||
statusWindowThreshold: monitor.statusWindowThreshold,
|
||||
description: monitor.description,
|
||||
interval: monitor.interval,
|
||||
notifications: monitor.notifications,
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { useState } from "react";
|
||||
import Button from "@mui/material/Button";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Proptypes from "prop-types";
|
||||
|
||||
const AddMemberMenu = ({ handleInviteOpen, handleIsRegisterOpen }) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const handleClick = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="accent"
|
||||
endIcon={<ArrowDropDownIcon sx={{ color: theme.palette.secondary.light }} />}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{t("teamPanel.addTeamMember.addMemberMenu")}
|
||||
</Button>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
sx={{
|
||||
"& .MuiPaper-root": {
|
||||
minWidth: anchorEl?.offsetWidth || "auto",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleClose();
|
||||
handleInviteOpen();
|
||||
}}
|
||||
>
|
||||
{t("teamPanel.inviteTeamMember")}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleClose();
|
||||
handleIsRegisterOpen(true);
|
||||
}}
|
||||
>
|
||||
{t("teamPanel.register")}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
AddMemberMenu.propTypes = {
|
||||
handleInviteOpen: Proptypes.func.isRequired,
|
||||
handleIsRegisterOpen: Proptypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default AddMemberMenu;
|
||||
@@ -0,0 +1,48 @@
|
||||
import { useState } from "react";
|
||||
import { newOrChangedCredentials } from "../../../../../Validation/validation";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const useAddTeamMember = () => {
|
||||
const { t } = useTranslation();
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
const clearErrors = () => setErrors({});
|
||||
|
||||
const validateFields = (name, value, formData) => {
|
||||
const { error } = newOrChangedCredentials.validate(
|
||||
{ [name]: value },
|
||||
{ abortEarly: false, context: { password: formData.password } }
|
||||
);
|
||||
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
[name]: error?.details?.[0]?.message || "",
|
||||
}));
|
||||
};
|
||||
|
||||
const validateForm = (formData, role) => {
|
||||
const { error } = newOrChangedCredentials.validate(formData, {
|
||||
abortEarly: false,
|
||||
context: { password: formData.password },
|
||||
});
|
||||
const formErrors = {};
|
||||
if (error) {
|
||||
for (const err of error.details) {
|
||||
formErrors[err.path[0]] = err.message;
|
||||
}
|
||||
}
|
||||
if (!role[0] || role.length === 0) {
|
||||
formErrors.role = t(
|
||||
"teamPanel.registerTeamMember.auth.common.inputs.role.errors.empty"
|
||||
);
|
||||
}
|
||||
if (Object.keys(formErrors).length > 0) {
|
||||
setErrors(formErrors);
|
||||
return false;
|
||||
}
|
||||
setErrors({});
|
||||
return true;
|
||||
};
|
||||
|
||||
return { errors, setErrors, clearErrors, validateFields, validateForm };
|
||||
};
|
||||
export default useAddTeamMember;
|
||||
@@ -0,0 +1,213 @@
|
||||
import { Button, Stack } from "@mui/material";
|
||||
import { GenericDialog } from "../../../../Components/Dialog/genericDialog";
|
||||
import TextInput from "../../../../Components/Inputs/TextInput";
|
||||
import Select from "../../../../Components/Inputs/Select";
|
||||
import { useGetInviteToken } from "../../../../Hooks/inviteHooks";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createToast } from "../../../../Utils/toastUtils";
|
||||
import { useState } from "react";
|
||||
import PasswordTooltip from "../../../Auth/components/PasswordTooltip";
|
||||
import useAddTeamMember from "./hooks/useAddTeamMember";
|
||||
import usePasswordFeedback from "../../../Auth/hooks/usePasswordFeedback";
|
||||
import { PasswordEndAdornment } from "../../../../Components/Inputs/TextInput/Adornments";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const INITIAL_FORM_STATE = {
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
password: "",
|
||||
confirm: "",
|
||||
teamId: "",
|
||||
};
|
||||
|
||||
const INITIAL_ROLE_STATE = ["user"];
|
||||
const AddTeamMember = ({ handleIsRegisterOpen, isRegisterOpen, onMemberAdded }) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const { errors, setErrors, clearErrors, validateFields, validateForm } =
|
||||
useAddTeamMember();
|
||||
const { feedback, handlePasswordFeedback } = usePasswordFeedback();
|
||||
const [getInviteToken, clearToken, isLoading, error, token, addTeamMember] =
|
||||
useGetInviteToken();
|
||||
const [form, setForm] = useState(INITIAL_FORM_STATE);
|
||||
const [role, setRole] = useState(INITIAL_ROLE_STATE);
|
||||
const [isLoadingSubmit, setIsLoadingSubmit] = useState(false);
|
||||
const closeAddMemberModal = () => {
|
||||
handleIsRegisterOpen(false);
|
||||
setForm(INITIAL_FORM_STATE);
|
||||
setRole(INITIAL_ROLE_STATE);
|
||||
clearErrors();
|
||||
clearToken();
|
||||
};
|
||||
|
||||
const onChange = (e) => {
|
||||
let { name, value } = e.target;
|
||||
if (name === "email") value = value.toLowerCase();
|
||||
const updatedForm = { ...form, [name]: value };
|
||||
validateFields(name, value, updatedForm);
|
||||
setForm(updatedForm);
|
||||
|
||||
if (name === "password" || name === "confirm") {
|
||||
handlePasswordFeedback(updatedForm, name, value, form, errors, setErrors);
|
||||
}
|
||||
};
|
||||
|
||||
const onsubmitAddMember = async (event) => {
|
||||
event.preventDefault();
|
||||
if (!validateForm(form, role)) return;
|
||||
try {
|
||||
setIsLoadingSubmit(true);
|
||||
await addTeamMember(form, role);
|
||||
createToast({
|
||||
body: t("teamPanel.registerToast.success"),
|
||||
});
|
||||
onMemberAdded();
|
||||
closeAddMemberModal();
|
||||
} catch (error) {
|
||||
const errorMsg = error.response?.data?.msg || error.message || "unknownError";
|
||||
createToast({
|
||||
type: "error",
|
||||
body: t(errorMsg),
|
||||
});
|
||||
} finally {
|
||||
setIsLoadingSubmit(false);
|
||||
}
|
||||
};
|
||||
const tErr = (key) => (key ? t([`teamPanel.registerTeamMember.${key}`, key]) : "");
|
||||
return (
|
||||
<>
|
||||
<GenericDialog
|
||||
title={t("teamPanel.addTeamMember.title")}
|
||||
description={t("teamPanel.addTeamMember.description")}
|
||||
open={isRegisterOpen}
|
||||
onClose={closeAddMemberModal}
|
||||
theme={theme}
|
||||
width={{ sm: "55%", md: "50%", lg: "40%", xl: "30%" }}
|
||||
>
|
||||
<TextInput
|
||||
name="firstName"
|
||||
label={t("auth.common.inputs.firstName.label")}
|
||||
isRequired={true}
|
||||
gap={theme.spacing(4)}
|
||||
placeholder={t("auth.common.inputs.firstName.placeholder")}
|
||||
value={form.firstName}
|
||||
onChange={onChange}
|
||||
error={errors.firstName ? true : false}
|
||||
helperText={errors.firstName ? tErr(errors.firstName) : null}
|
||||
sx={{ mb: errors.firstName ? theme.spacing(15) : theme.spacing(5) }}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
name="lastName"
|
||||
label={t("auth.common.inputs.lastName.label")}
|
||||
isRequired={true}
|
||||
gap={theme.spacing(4)}
|
||||
placeholder={t("auth.common.inputs.lastName.placeholder")}
|
||||
value={form.lastName}
|
||||
onChange={onChange}
|
||||
error={errors.lastName ? true : false}
|
||||
helperText={errors.lastName ? tErr(errors.lastName) : null}
|
||||
sx={{ mb: errors.lastName ? theme.spacing(15) : theme.spacing(5) }}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
type="email"
|
||||
label={t("auth.common.inputs.email.label")}
|
||||
name="email"
|
||||
gap={theme.spacing(4)}
|
||||
isRequired={true}
|
||||
id="input-team-member"
|
||||
placeholder={t("teamPanel.email")}
|
||||
value={form.email}
|
||||
onChange={onChange}
|
||||
error={errors.email ? true : false}
|
||||
helperText={errors.email ? tErr(errors.email) : null}
|
||||
sx={{ mb: errors.email ? theme.spacing(15) : theme.spacing(5) }}
|
||||
/>
|
||||
|
||||
<Select
|
||||
label={t("teamPanel.role")}
|
||||
id="team-member-role"
|
||||
name="role"
|
||||
required={true}
|
||||
placeholder={t("teamPanel.selectRole")}
|
||||
isHidden={true}
|
||||
value={role[0] || ""}
|
||||
sx={{ mb: theme.spacing(5) }}
|
||||
onChange={(event) => setRole([event.target.value])}
|
||||
items={[
|
||||
{ _id: "admin", name: t("roles.admin") },
|
||||
{ _id: "user", name: t("roles.teamMember") },
|
||||
]}
|
||||
/>
|
||||
|
||||
<PasswordTooltip
|
||||
feedback={feedback}
|
||||
form={form}
|
||||
>
|
||||
<TextInput
|
||||
type="password"
|
||||
id="register-password-input"
|
||||
name="password"
|
||||
label={t("auth.common.inputs.password.label")}
|
||||
isRequired={true}
|
||||
placeholder="••••••••••"
|
||||
value={form.password}
|
||||
onChange={onChange}
|
||||
error={errors.password && errors.password[0] ? true : false}
|
||||
endAdornment={<PasswordEndAdornment />}
|
||||
sx={{ mb: theme.spacing(5) }}
|
||||
/>
|
||||
</PasswordTooltip>
|
||||
<TextInput
|
||||
type="password"
|
||||
id="register-confirm-input"
|
||||
name="confirm"
|
||||
label={t("auth.common.inputs.passwordConfirm.label")}
|
||||
gap={theme.spacing(4)}
|
||||
isRequired={true}
|
||||
placeholder={t("auth.common.inputs.passwordConfirm.placeholder")}
|
||||
autoComplete="current-password"
|
||||
value={form.confirm}
|
||||
onChange={onChange}
|
||||
error={errors.confirm && errors.confirm[0] ? true : false}
|
||||
endAdornment={<PasswordEndAdornment />}
|
||||
sx={{ mb: theme.spacing(5) }}
|
||||
/>
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={theme.spacing(10)}
|
||||
mt={theme.spacing(8)}
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={closeAddMemberModal}
|
||||
disabled={isLoadingSubmit}
|
||||
>
|
||||
{t("teamPanel.cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="accent"
|
||||
onClick={onsubmitAddMember}
|
||||
disabled={isLoadingSubmit}
|
||||
>
|
||||
{t("teamPanel.addTeamMember.addButton")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</GenericDialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
AddTeamMember.propTypes = {
|
||||
handleIsRegisterOpen: PropTypes.func.isRequired,
|
||||
isRegisterOpen: PropTypes.bool.isRequired,
|
||||
onMemberAdded: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default AddTeamMember;
|
||||
@@ -9,10 +9,12 @@ import { networkService } from "../../../main";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import Select from "../../../Components/Inputs/Select";
|
||||
import { GenericDialog } from "../../../Components/Dialog/genericDialog";
|
||||
import AddTeamMember from "../components/AddTeamMember";
|
||||
import DataTable from "../../../Components/Table";
|
||||
import { useGetInviteToken } from "../../../Hooks/inviteHooks";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useIsSuperAdmin } from "../../../Hooks/useIsAdmin";
|
||||
import AddMemberMenu from "./AddMemberMenu";
|
||||
/**
|
||||
* TeamPanel component manages the organization and team members,
|
||||
* providing functionalities like renaming the organization, managing team members,
|
||||
@@ -65,7 +67,10 @@ const TeamPanel = () => {
|
||||
render: (row) => row.role,
|
||||
},
|
||||
];
|
||||
|
||||
const [refreshTrigger, setRefreshTrigger] = useState(false);
|
||||
const refreshTeamList = () => {
|
||||
setRefreshTrigger((prev) => !prev);
|
||||
};
|
||||
useEffect(() => {
|
||||
const fetchTeam = async () => {
|
||||
try {
|
||||
@@ -79,7 +84,7 @@ const TeamPanel = () => {
|
||||
};
|
||||
|
||||
fetchTeam();
|
||||
}, []);
|
||||
}, [refreshTrigger]);
|
||||
|
||||
useEffect(() => {
|
||||
const ROLE_MAP = {
|
||||
@@ -109,6 +114,10 @@ const TeamPanel = () => {
|
||||
setIsDisabled(Object.keys(errors).length !== 0 || toInvite.email === "");
|
||||
}, [errors, toInvite.email]);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isRegisterOpen, setIsRegisterOpen] = useState(false);
|
||||
const handleIsRegisterOpen = (open) => {
|
||||
setIsRegisterOpen(open);
|
||||
};
|
||||
|
||||
const handleChange = (event) => {
|
||||
const { value } = event.target;
|
||||
@@ -138,7 +147,6 @@ const TeamPanel = () => {
|
||||
const handleGetToken = async () => {
|
||||
await getInviteToken({ email: toInvite.email, role: toInvite.role });
|
||||
};
|
||||
|
||||
const handleInviteMember = async () => {
|
||||
if (!toInvite.email) {
|
||||
setErrors((prev) => ({ ...prev, email: "Email is required." }));
|
||||
@@ -239,13 +247,16 @@ const TeamPanel = () => {
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Stack>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="accent"
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
{t("teamPanel.inviteTeamMember")}
|
||||
</Button>
|
||||
|
||||
<AddTeamMember
|
||||
handleIsRegisterOpen={handleIsRegisterOpen}
|
||||
isRegisterOpen={isRegisterOpen}
|
||||
onMemberAdded={refreshTeamList}
|
||||
/>
|
||||
<AddMemberMenu
|
||||
handleInviteOpen={() => setIsOpen(true)}
|
||||
handleIsRegisterOpen={handleIsRegisterOpen}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<DataTable
|
||||
@@ -264,7 +275,6 @@ const TeamPanel = () => {
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<GenericDialog
|
||||
title={t("teamPanel.inviteNewTeamMember")}
|
||||
description={t("teamPanel.inviteDescription")}
|
||||
|
||||
@@ -93,14 +93,14 @@ const PasswordTooltip = ({ feedback, form, children }) => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<span style={{ display: "inline-block", width: "100%" }}>{children}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
PasswordTooltip.propTypes = {
|
||||
feedback: PropTypes.shape({
|
||||
length: PropTypes.string.isRequired,
|
||||
length: PropTypes.string,
|
||||
special: PropTypes.string,
|
||||
number: PropTypes.string,
|
||||
uppercase: PropTypes.string,
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { useState } from "react";
|
||||
import { newOrChangedCredentials } from "../../../Validation/validation";
|
||||
|
||||
const usePasswordFeedback = () => {
|
||||
const [feedback, setFeedback] = useState({});
|
||||
const getFeedbackStatus = (form, errors, field, criteria) => {
|
||||
const fieldErrors = errors?.[field];
|
||||
const isFieldEmpty = form?.[field]?.length === 0;
|
||||
const hasError = fieldErrors?.includes(criteria) || fieldErrors?.includes("empty");
|
||||
const isCorrect = !isFieldEmpty && !hasError;
|
||||
|
||||
if (isCorrect) {
|
||||
return "success";
|
||||
} else if (hasError) {
|
||||
return "error";
|
||||
} else {
|
||||
return "info";
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasswordFeedback = (updatedForm, name, value, form, errors, setErrors) => {
|
||||
const validateValue = { [name]: value };
|
||||
const validateOptions = { abortEarly: false, context: { password: form.password } };
|
||||
if (name === "password" && form.confirm.length > 0) {
|
||||
validateValue.confirm = form.confirm;
|
||||
validateOptions.context = { password: value };
|
||||
} else if (name === "confirm") {
|
||||
validateValue.password = form.password;
|
||||
}
|
||||
const { error } = newOrChangedCredentials.validate(validateValue, validateOptions);
|
||||
|
||||
const pwdErrors = error?.details.map((error) => ({
|
||||
path: error.path[0],
|
||||
type: error.type,
|
||||
}));
|
||||
|
||||
const errorsByPath =
|
||||
pwdErrors &&
|
||||
pwdErrors.reduce((acc, { path, type }) => {
|
||||
if (!acc[path]) {
|
||||
acc[path] = [];
|
||||
}
|
||||
acc[path].push(type);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const oldErrors = { ...errors };
|
||||
if (name === "password") {
|
||||
oldErrors.password = undefined;
|
||||
} else if (name === "confirm") {
|
||||
oldErrors.confirm = undefined;
|
||||
}
|
||||
const newErrors = { ...oldErrors, ...errorsByPath };
|
||||
|
||||
setErrors(newErrors);
|
||||
|
||||
const newFeedback = {
|
||||
length: getFeedbackStatus(updatedForm, errorsByPath, "password", "string.min"),
|
||||
special: getFeedbackStatus(updatedForm, errorsByPath, "password", "special"),
|
||||
number: getFeedbackStatus(updatedForm, errorsByPath, "password", "number"),
|
||||
uppercase: getFeedbackStatus(updatedForm, errorsByPath, "password", "uppercase"),
|
||||
lowercase: getFeedbackStatus(updatedForm, errorsByPath, "password", "lowercase"),
|
||||
confirm: getFeedbackStatus(updatedForm, errorsByPath, "confirm", "different"),
|
||||
};
|
||||
|
||||
setFeedback(newFeedback);
|
||||
};
|
||||
return { feedback, handlePasswordFeedback, getFeedbackStatus };
|
||||
};
|
||||
|
||||
export default usePasswordFeedback;
|
||||
@@ -1,5 +1,10 @@
|
||||
//Components
|
||||
import Table from "../../../../Components/Table";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import DataTable from "../../../../Components/Table";
|
||||
import Table from "@mui/material/Table";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import TableCell from "@mui/material/TableCell";
|
||||
import TableSkeleton from "../../../../Components/Table/skeleton";
|
||||
import Pagination from "../../../../Components/Table/TablePagination";
|
||||
import { StatusLabel } from "../../../../Components/Label";
|
||||
@@ -16,7 +21,68 @@ import { useTranslation } from "react-i18next";
|
||||
import { useFetchChecksTeam } from "../../../../Hooks/checkHooks";
|
||||
import { useFetchChecksByMonitor } from "../../../../Hooks/checkHooks";
|
||||
import { useResolveIncident } from "../../../../Hooks/checkHooks";
|
||||
import { Button, Typography } from "@mui/material";
|
||||
import { Button, Typography, useTheme } from "@mui/material";
|
||||
import { lighten } from "@mui/material/styles";
|
||||
|
||||
const GetTooltip = (row) => {
|
||||
const theme = useTheme();
|
||||
const phases = row?.timings?.phases;
|
||||
|
||||
const phaseKeyFormattingMap = {
|
||||
firstByte: "first byte",
|
||||
};
|
||||
return (
|
||||
<Stack
|
||||
backgroundColor={lighten(theme.palette.primary.main, 0.1)}
|
||||
border={`1px solid ${theme.palette.primary.lowContrast}`}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
py={theme.spacing(2)}
|
||||
px={theme.spacing(4)}
|
||||
>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color={theme.palette.primary.contrastText}
|
||||
>{`Status code: ${row?.statusCode}`}</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color={theme.palette.primary.contrastText}
|
||||
>{`Response time: ${row?.responseTime} ms`}</Typography>
|
||||
{phases && (
|
||||
<>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color={theme.palette.primary.contrastText}
|
||||
>{`Request timing: `}</Typography>
|
||||
<Table
|
||||
size="small"
|
||||
sx={{ ml: theme.spacing(2), mt: theme.spacing(2) }}
|
||||
>
|
||||
<TableBody>
|
||||
{Object.keys(phases)?.map((phaseKey) => (
|
||||
<TableRow key={phaseKey}>
|
||||
<TableCell sx={{ border: "none", p: 0 }}>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="success"
|
||||
>
|
||||
{`${phaseKeyFormattingMap[phaseKey] || phaseKey}:`}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell sx={{ border: "none", p: 0 }}>
|
||||
<Typography
|
||||
color={theme.palette.primary.contrastText}
|
||||
variant="body2"
|
||||
>{`${phases[phaseKey]} ms`}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const IncidentTable = ({
|
||||
isLoading,
|
||||
@@ -167,9 +233,10 @@ const IncidentTable = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table
|
||||
<DataTable
|
||||
headers={headers}
|
||||
data={checks}
|
||||
config={{ tooltipContent: GetTooltip }}
|
||||
/>
|
||||
<Pagination
|
||||
paginationLabel={t("incidentsTablePaginationLabel")}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import ConfigBox from "../../../../Components/ConfigBox";
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import { CustomThreshold } from "../Components/CustomThreshold";
|
||||
import { capitalizeFirstLetter } from "../../../../Utils/stringUtils";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import PropTypes from "prop-types";
|
||||
const CustomAlertsSection = ({
|
||||
errors,
|
||||
onChange,
|
||||
infrastructureMonitor,
|
||||
handleCheckboxChange,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const METRICS = ["cpu", "memory", "disk", "temperature"];
|
||||
const METRIC_PREFIX = "usage_";
|
||||
const hasAlertError = (errors) => {
|
||||
return Object.keys(errors).filter((k) => k.startsWith(METRIC_PREFIX)).length > 0;
|
||||
};
|
||||
const getAlertError = (errors) => {
|
||||
const errorKey = Object.keys(errors).find((key) => key.startsWith(METRIC_PREFIX));
|
||||
return errorKey ? errors[errorKey] : null;
|
||||
};
|
||||
return (
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("infrastructureCustomizeAlerts")}
|
||||
</Typography>
|
||||
<Typography component="p">
|
||||
{t("infrastructureAlertNotificationDescription")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
{METRICS.map((metric) => {
|
||||
return (
|
||||
<CustomThreshold
|
||||
key={metric}
|
||||
infrastructureMonitor={infrastructureMonitor}
|
||||
errors={errors}
|
||||
checkboxId={metric}
|
||||
checkboxName={metric}
|
||||
checkboxLabel={
|
||||
metric !== "cpu" ? capitalizeFirstLetter(metric) : metric.toUpperCase()
|
||||
}
|
||||
onCheckboxChange={handleCheckboxChange}
|
||||
isChecked={infrastructureMonitor[metric]}
|
||||
fieldId={METRIC_PREFIX + metric}
|
||||
fieldName={METRIC_PREFIX + metric}
|
||||
fieldValue={String(infrastructureMonitor[METRIC_PREFIX + metric])}
|
||||
onFieldChange={onChange}
|
||||
alertUnit={metric == "temperature" ? "°C" : "%"}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{hasAlertError(errors) && (
|
||||
<Typography
|
||||
component="span"
|
||||
className="input-error"
|
||||
color={theme.palette.error.main}
|
||||
mt={theme.spacing(2)}
|
||||
sx={{
|
||||
opacity: 0.8,
|
||||
}}
|
||||
>
|
||||
{getAlertError(errors)}
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
);
|
||||
};
|
||||
|
||||
CustomAlertsSection.propTypes = {
|
||||
errors: PropTypes.object.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
infrastructureMonitor: PropTypes.object.isRequired,
|
||||
handleCheckboxChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default CustomAlertsSection;
|
||||
@@ -0,0 +1,78 @@
|
||||
import { useState } from "react";
|
||||
import { Box, Button } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import PauseCircleOutlineIcon from "@mui/icons-material/PauseCircleOutline";
|
||||
import PlayCircleOutlineRoundedIcon from "@mui/icons-material/PlayCircleOutlineRounded";
|
||||
import Dialog from "../../../../Components/Dialog";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const MonitorActionButtons = ({ monitor, isBusy, handlePause, handleRemove }) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Box
|
||||
alignSelf="flex-end"
|
||||
ml="auto"
|
||||
>
|
||||
<Button
|
||||
onClick={handlePause}
|
||||
loading={isBusy}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
sx={{
|
||||
pl: theme.spacing(4),
|
||||
pr: theme.spacing(6),
|
||||
"& svg": {
|
||||
mr: theme.spacing(2),
|
||||
"& path": {
|
||||
stroke: theme.palette.primary.contrastTextTertiary,
|
||||
strokeWidth: 0.1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{monitor?.isActive ? (
|
||||
<>
|
||||
<PauseCircleOutlineIcon />
|
||||
{t("pause")}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PlayCircleOutlineRoundedIcon />
|
||||
{t("resume")}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
loading={isBusy}
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={() => setIsOpen(true)}
|
||||
sx={{ ml: theme.spacing(6) }}
|
||||
>
|
||||
{t("remove")}
|
||||
</Button>
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
theme={theme}
|
||||
title={t("deleteDialogTitle")}
|
||||
description={t("deleteDialogDescription")}
|
||||
onCancel={() => setIsOpen(false)}
|
||||
confirmationButtonLabel={t("delete")}
|
||||
onConfirm={handleRemove}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
MonitorActionButtons.propTypes = {
|
||||
monitor: PropTypes.object.isRequired,
|
||||
isBusy: PropTypes.bool.isRequired,
|
||||
handlePause: PropTypes.func.isRequired,
|
||||
handleRemove: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default MonitorActionButtons;
|
||||
@@ -0,0 +1,73 @@
|
||||
import { Box, Stack, Tooltip, Typography } from "@mui/material";
|
||||
import { useMonitorUtils } from "../../../../Hooks/useMonitorUtils";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import PulseDot from "../../../../Components/Animated/PulseDot";
|
||||
import PropTypes from "prop-types";
|
||||
const MonitorStatusHeader = ({ monitor, infrastructureMonitor }) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const { statusColor, pagespeedStatusMsg, determineState } = useMonitorUtils();
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
height="fit-content"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Tooltip
|
||||
title={pagespeedStatusMsg[determineState(monitor)]}
|
||||
disableInteractive
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: { offset: [0, -8] },
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<PulseDot color={statusColor[determineState(monitor)]} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="monitorUrl"
|
||||
>
|
||||
{infrastructureMonitor.url?.replace(/^https?:\/\//, "") || "..."}
|
||||
</Typography>
|
||||
<Typography
|
||||
position="relative"
|
||||
variant="body2"
|
||||
ml={theme.spacing(6)}
|
||||
mt={theme.spacing(1)}
|
||||
sx={{
|
||||
"&:before": {
|
||||
position: "absolute",
|
||||
content: `""`,
|
||||
width: theme.spacing(2),
|
||||
height: theme.spacing(2),
|
||||
borderRadius: "50%",
|
||||
backgroundColor: theme.palette.primary.contrastTextTertiary,
|
||||
opacity: 0.8,
|
||||
left: theme.spacing(-5),
|
||||
top: "50%",
|
||||
transform: "translateY(-50%)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t("editing")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
MonitorStatusHeader.propTypes = {
|
||||
monitor: PropTypes.object.isRequired,
|
||||
infrastructureMonitor: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default MonitorStatusHeader;
|
||||
@@ -0,0 +1,90 @@
|
||||
import { useState } from "react";
|
||||
const useInfrastructureMonitorForm = () => {
|
||||
const [infrastructureMonitor, setInfrastructureMonitor] = useState({
|
||||
url: "",
|
||||
name: "",
|
||||
notifications: [],
|
||||
notify_email: false,
|
||||
interval: 0.25,
|
||||
cpu: false,
|
||||
usage_cpu: "",
|
||||
memory: false,
|
||||
usage_memory: "",
|
||||
disk: false,
|
||||
usage_disk: "",
|
||||
temperature: false,
|
||||
usage_temperature: "",
|
||||
secret: "",
|
||||
});
|
||||
|
||||
const onChangeForm = (name, value) => {
|
||||
setInfrastructureMonitor({
|
||||
...infrastructureMonitor,
|
||||
[name]: value,
|
||||
});
|
||||
};
|
||||
const handleCheckboxChange = (event) => {
|
||||
setInfrastructureMonitor({
|
||||
...infrastructureMonitor,
|
||||
[event.target.name]: event.target.checked,
|
||||
});
|
||||
};
|
||||
const initializeInfrastructureMonitorForCreate = (globalSettings) => {
|
||||
const gt = globalSettings?.data?.settings?.globalThresholds || {};
|
||||
setInfrastructureMonitor({
|
||||
url: "",
|
||||
name: "",
|
||||
notifications: [],
|
||||
interval: 0.25,
|
||||
cpu: gt.cpu !== undefined,
|
||||
usage_cpu: gt.cpu !== undefined ? gt.cpu.toString() : "",
|
||||
memory: gt.memory !== undefined,
|
||||
usage_memory: gt.memory !== undefined ? gt.memory.toString() : "",
|
||||
disk: gt.disk !== undefined,
|
||||
usage_disk: gt.disk !== undefined ? gt.disk.toString() : "",
|
||||
temperature: gt.temperature !== undefined,
|
||||
usage_temperature: gt.temperature !== undefined ? gt.temperature.toString() : "",
|
||||
secret: "",
|
||||
});
|
||||
};
|
||||
|
||||
const initializeInfrastructureMonitorForUpdate = (monitor) => {
|
||||
const MS_PER_MINUTE = 60000;
|
||||
const { thresholds = {} } = monitor;
|
||||
setInfrastructureMonitor({
|
||||
url: monitor.url.replace(/^https?:\/\//, ""),
|
||||
name: monitor.name || "",
|
||||
notifications: monitor.notifications || [],
|
||||
interval: monitor.interval / MS_PER_MINUTE,
|
||||
cpu: thresholds.usage_cpu !== undefined,
|
||||
usage_cpu:
|
||||
thresholds.usage_cpu !== undefined ? (thresholds.usage_cpu * 100).toString() : "",
|
||||
memory: thresholds.usage_memory !== undefined,
|
||||
usage_memory:
|
||||
thresholds.usage_memory !== undefined
|
||||
? (thresholds.usage_memory * 100).toString()
|
||||
: "",
|
||||
disk: thresholds.usage_disk !== undefined,
|
||||
usage_disk:
|
||||
thresholds.usage_disk !== undefined
|
||||
? (thresholds.usage_disk * 100).toString()
|
||||
: "",
|
||||
temperature: thresholds.usage_temperature !== undefined,
|
||||
usage_temperature:
|
||||
thresholds.usage_temperature !== undefined
|
||||
? (thresholds.usage_temperature * 100).toString()
|
||||
: "",
|
||||
secret: monitor.secret || "",
|
||||
});
|
||||
};
|
||||
return {
|
||||
infrastructureMonitor,
|
||||
setInfrastructureMonitor,
|
||||
onChangeForm,
|
||||
handleCheckboxChange,
|
||||
initializeInfrastructureMonitorForCreate,
|
||||
initializeInfrastructureMonitorForUpdate,
|
||||
};
|
||||
};
|
||||
|
||||
export default useInfrastructureMonitorForm;
|
||||
@@ -0,0 +1,80 @@
|
||||
import { useCreateMonitor, useUpdateMonitor } from "../../../../Hooks/monitorHooks";
|
||||
const useInfrastructureSubmit = () => {
|
||||
const [createMonitor, isCreating] = useCreateMonitor();
|
||||
const [updateMonitor, isUpdating] = useUpdateMonitor();
|
||||
const buildForm = (infrastructureMonitor, https) => {
|
||||
const MS_PER_MINUTE = 60000;
|
||||
|
||||
let form = {
|
||||
url: `http${https ? "s" : ""}://` + infrastructureMonitor.url,
|
||||
name:
|
||||
infrastructureMonitor.name === ""
|
||||
? infrastructureMonitor.url
|
||||
: infrastructureMonitor.name,
|
||||
interval: infrastructureMonitor.interval * MS_PER_MINUTE,
|
||||
cpu: infrastructureMonitor.cpu,
|
||||
...(infrastructureMonitor.cpu
|
||||
? { usage_cpu: infrastructureMonitor.usage_cpu }
|
||||
: {}),
|
||||
memory: infrastructureMonitor.memory,
|
||||
...(infrastructureMonitor.memory
|
||||
? { usage_memory: infrastructureMonitor.usage_memory }
|
||||
: {}),
|
||||
disk: infrastructureMonitor.disk,
|
||||
...(infrastructureMonitor.disk
|
||||
? { usage_disk: infrastructureMonitor.usage_disk }
|
||||
: {}),
|
||||
temperature: infrastructureMonitor.temperature,
|
||||
...(infrastructureMonitor.temperature
|
||||
? { usage_temperature: infrastructureMonitor.usage_temperature }
|
||||
: {}),
|
||||
secret: infrastructureMonitor.secret,
|
||||
};
|
||||
return form;
|
||||
};
|
||||
const submitInfrastructureForm = async (
|
||||
infrastructureMonitor,
|
||||
form,
|
||||
isCreate,
|
||||
monitorId
|
||||
) => {
|
||||
const {
|
||||
cpu,
|
||||
usage_cpu,
|
||||
memory,
|
||||
usage_memory,
|
||||
disk,
|
||||
usage_disk,
|
||||
temperature,
|
||||
usage_temperature,
|
||||
...rest
|
||||
} = form;
|
||||
|
||||
const thresholds = {
|
||||
...(cpu ? { usage_cpu: usage_cpu / 100 } : {}),
|
||||
...(memory ? { usage_memory: usage_memory / 100 } : {}),
|
||||
...(disk ? { usage_disk: usage_disk / 100 } : {}),
|
||||
...(temperature ? { usage_temperature: usage_temperature / 100 } : {}),
|
||||
};
|
||||
|
||||
const finalForm = {
|
||||
...(isCreate ? {} : { _id: monitorId }),
|
||||
...rest,
|
||||
description: form.name,
|
||||
type: "hardware",
|
||||
notifications: infrastructureMonitor.notifications,
|
||||
thresholds,
|
||||
};
|
||||
// Handle create or update
|
||||
isCreate
|
||||
? await createMonitor({ monitor: finalForm, redirect: "/infrastructure" })
|
||||
: await updateMonitor({ monitor: finalForm, redirect: "/infrastructure" });
|
||||
};
|
||||
return {
|
||||
buildForm,
|
||||
submitInfrastructureForm,
|
||||
isCreating,
|
||||
isUpdating,
|
||||
};
|
||||
};
|
||||
export default useInfrastructureSubmit;
|
||||
@@ -0,0 +1,37 @@
|
||||
import { useState } from "react";
|
||||
import { infrastructureMonitorValidation } from "../../../../Validation/validation";
|
||||
import { createToast } from "../../../../Utils/toastUtils";
|
||||
const useValidateInfrastructureForm = () => {
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
const validateField = (name, value) => {
|
||||
const { error } = infrastructureMonitorValidation.validate(
|
||||
{ [name]: value },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
...(error ? { [name]: error.details[0].message } : { [name]: undefined }),
|
||||
}));
|
||||
};
|
||||
|
||||
const validateForm = (form) => {
|
||||
const { error } = infrastructureMonitorValidation.validate(form, {
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
const newErrors = {};
|
||||
error.details.forEach((err) => {
|
||||
newErrors[err.path[0]] = err.message;
|
||||
});
|
||||
console.log(newErrors);
|
||||
setErrors(newErrors);
|
||||
createToast({ body: "Please check the form for errors." });
|
||||
return error;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return { errors, validateField, validateForm };
|
||||
};
|
||||
export default useValidateInfrastructureForm;
|
||||
@@ -1,40 +1,33 @@
|
||||
//Components
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import ConfigBox from "../../../Components/ConfigBox";
|
||||
import Dialog from "../../../Components/Dialog";
|
||||
import FieldWrapper from "../../../Components/Inputs/FieldWrapper";
|
||||
import Link from "../../../Components/Link";
|
||||
import PauseCircleOutlineIcon from "@mui/icons-material/PauseCircleOutline";
|
||||
import PlayCircleOutlineRoundedIcon from "@mui/icons-material/PlayCircleOutlineRounded";
|
||||
import PulseDot from "../../../Components/Animated/PulseDot";
|
||||
import Select from "../../../Components/Inputs/Select";
|
||||
import TextInput from "../../../Components/Inputs/TextInput";
|
||||
import { Box, Stack, Tooltip, Typography, Button, ButtonGroup } from "@mui/material";
|
||||
import { CustomThreshold } from "./Components/CustomThreshold";
|
||||
import { Box, Stack, Typography, Button, ButtonGroup } from "@mui/material";
|
||||
import { HttpAdornment } from "../../../Components/Inputs/TextInput/Adornments";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import MonitorStatusHeader from "./Components/MonitorStatusHeader";
|
||||
import MonitorActionButtons from "./Components/MonitorActionButtons";
|
||||
import CustomAlertsSection from "./Components/CustomAlertsSection";
|
||||
// Utils
|
||||
import NotificationsConfig from "../../../Components/NotificationConfig";
|
||||
import { capitalizeFirstLetter } from "../../../Utils/stringUtils";
|
||||
import { infrastructureMonitorValidation } from "../../../Validation/validation";
|
||||
import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications";
|
||||
import { useMonitorUtils } from "../../../Hooks/useMonitorUtils";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useCreateMonitor,
|
||||
useDeleteMonitor,
|
||||
useFetchGlobalSettings,
|
||||
useFetchHardwareMonitorById,
|
||||
usePauseMonitor,
|
||||
useUpdateMonitor,
|
||||
} from "../../../Hooks/monitorHooks";
|
||||
import useInfrastructureMonitorForm from "./hooks/useInfrastructureMonitorForm";
|
||||
import useValidateInfrastructureForm from "./hooks/useValidateInfrastructureForm";
|
||||
import useInfrastructureSubmit from "./hooks/useInfrastructureSubmit";
|
||||
|
||||
const CreateInfrastructureMonitor = () => {
|
||||
const { user } = useSelector((state) => state.auth);
|
||||
const { monitorId } = useParams();
|
||||
const isCreate = typeof monitorId === "undefined";
|
||||
|
||||
@@ -42,39 +35,29 @@ const CreateInfrastructureMonitor = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// State
|
||||
const [errors, setErrors] = useState({});
|
||||
const [https, setHttps] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [updateTrigger, setUpdateTrigger] = useState(false);
|
||||
const [infrastructureMonitor, setInfrastructureMonitor] = useState({
|
||||
url: "",
|
||||
name: "",
|
||||
notifications: [],
|
||||
notify_email: false,
|
||||
interval: 0.25,
|
||||
cpu: false,
|
||||
usage_cpu: "",
|
||||
memory: false,
|
||||
usage_memory: "",
|
||||
disk: false,
|
||||
usage_disk: "",
|
||||
temperature: false,
|
||||
usage_temperature: "",
|
||||
secret: "",
|
||||
});
|
||||
|
||||
// Fetch monitor details if editing
|
||||
const { statusColor, pagespeedStatusMsg, determineState } = useMonitorUtils();
|
||||
const [monitor, isLoading] = useFetchHardwareMonitorById({
|
||||
monitorId,
|
||||
updateTrigger,
|
||||
});
|
||||
const [createMonitor, isCreating] = useCreateMonitor();
|
||||
const [deleteMonitor, isDeleting] = useDeleteMonitor();
|
||||
const [globalSettings, globalSettingsLoading] = useFetchGlobalSettings();
|
||||
const [notifications, notificationsAreLoading] = useGetNotificationsByTeamId();
|
||||
const [pauseMonitor, isPausing] = usePauseMonitor();
|
||||
const [updateMonitor, isUpdating] = useUpdateMonitor();
|
||||
const {
|
||||
infrastructureMonitor,
|
||||
setInfrastructureMonitor,
|
||||
onChangeForm,
|
||||
handleCheckboxChange,
|
||||
initializeInfrastructureMonitorForCreate,
|
||||
initializeInfrastructureMonitorForUpdate,
|
||||
} = useInfrastructureMonitorForm();
|
||||
const { errors, validateField, validateForm } = useValidateInfrastructureForm();
|
||||
const { buildForm, submitInfrastructureForm, isCreating, isUpdating } =
|
||||
useInfrastructureSubmit();
|
||||
|
||||
const FREQUENCIES = [
|
||||
{ _id: 0.25, name: t("time.fifteenSeconds") },
|
||||
@@ -93,157 +76,27 @@ const CreateInfrastructureMonitor = () => {
|
||||
{ name: "Configure", path: `/infrastructure/configure/${monitorId}` },
|
||||
]),
|
||||
];
|
||||
const METRICS = ["cpu", "memory", "disk", "temperature"];
|
||||
const METRIC_PREFIX = "usage_";
|
||||
const MS_PER_MINUTE = 60000;
|
||||
|
||||
const hasAlertError = (errors) => {
|
||||
return Object.keys(errors).filter((k) => k.startsWith(METRIC_PREFIX)).length > 0;
|
||||
};
|
||||
|
||||
const getAlertError = (errors) => {
|
||||
const errorKey = Object.keys(errors).find((key) => key.startsWith(METRIC_PREFIX));
|
||||
return errorKey ? errors[errorKey] : null;
|
||||
};
|
||||
|
||||
// Populate form fields if editing
|
||||
useEffect(() => {
|
||||
if (isCreate) {
|
||||
if (globalSettingsLoading) return;
|
||||
|
||||
const gt = globalSettings?.data?.settings?.globalThresholds || {};
|
||||
|
||||
setHttps(false);
|
||||
|
||||
setInfrastructureMonitor({
|
||||
url: "",
|
||||
name: "",
|
||||
notifications: [],
|
||||
interval: 0.25,
|
||||
cpu: gt.cpu !== undefined,
|
||||
usage_cpu: gt.cpu !== undefined ? gt.cpu.toString() : "",
|
||||
memory: gt.memory !== undefined,
|
||||
usage_memory: gt.memory !== undefined ? gt.memory.toString() : "",
|
||||
disk: gt.disk !== undefined,
|
||||
usage_disk: gt.disk !== undefined ? gt.disk.toString() : "",
|
||||
temperature: gt.temperature !== undefined,
|
||||
usage_temperature: gt.temperature !== undefined ? gt.temperature.toString() : "",
|
||||
secret: "",
|
||||
});
|
||||
initializeInfrastructureMonitorForCreate(globalSettings);
|
||||
} else if (monitor) {
|
||||
const { thresholds = {} } = monitor;
|
||||
|
||||
setHttps(monitor.url.startsWith("https"));
|
||||
|
||||
setInfrastructureMonitor({
|
||||
url: monitor.url.replace(/^https?:\/\//, ""),
|
||||
name: monitor.name || "",
|
||||
notifications: monitor.notifications || [],
|
||||
interval: monitor.interval / MS_PER_MINUTE,
|
||||
cpu: thresholds.usage_cpu !== undefined,
|
||||
usage_cpu:
|
||||
thresholds.usage_cpu !== undefined
|
||||
? (thresholds.usage_cpu * 100).toString()
|
||||
: "",
|
||||
memory: thresholds.usage_memory !== undefined,
|
||||
usage_memory:
|
||||
thresholds.usage_memory !== undefined
|
||||
? (thresholds.usage_memory * 100).toString()
|
||||
: "",
|
||||
disk: thresholds.usage_disk !== undefined,
|
||||
usage_disk:
|
||||
thresholds.usage_disk !== undefined
|
||||
? (thresholds.usage_disk * 100).toString()
|
||||
: "",
|
||||
temperature: thresholds.usage_temperature !== undefined,
|
||||
usage_temperature:
|
||||
thresholds.usage_temperature !== undefined
|
||||
? (thresholds.usage_temperature * 100).toString()
|
||||
: "",
|
||||
secret: monitor.secret || "",
|
||||
});
|
||||
initializeInfrastructureMonitorForUpdate(monitor);
|
||||
}
|
||||
}, [isCreate, monitor, globalSettings, globalSettingsLoading]);
|
||||
|
||||
// Handlers
|
||||
const onSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
// Build the form
|
||||
let form = {
|
||||
url: `http${https ? "s" : ""}://` + infrastructureMonitor.url,
|
||||
name:
|
||||
infrastructureMonitor.name === ""
|
||||
? infrastructureMonitor.url
|
||||
: infrastructureMonitor.name,
|
||||
interval: infrastructureMonitor.interval * MS_PER_MINUTE,
|
||||
cpu: infrastructureMonitor.cpu,
|
||||
...(infrastructureMonitor.cpu
|
||||
? { usage_cpu: infrastructureMonitor.usage_cpu }
|
||||
: {}),
|
||||
memory: infrastructureMonitor.memory,
|
||||
...(infrastructureMonitor.memory
|
||||
? { usage_memory: infrastructureMonitor.usage_memory }
|
||||
: {}),
|
||||
disk: infrastructureMonitor.disk,
|
||||
...(infrastructureMonitor.disk
|
||||
? { usage_disk: infrastructureMonitor.usage_disk }
|
||||
: {}),
|
||||
temperature: infrastructureMonitor.temperature,
|
||||
...(infrastructureMonitor.temperature
|
||||
? { usage_temperature: infrastructureMonitor.usage_temperature }
|
||||
: {}),
|
||||
secret: infrastructureMonitor.secret,
|
||||
};
|
||||
|
||||
const { error } = infrastructureMonitorValidation.validate(form, {
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
const form = buildForm(infrastructureMonitor, https);
|
||||
const error = validateForm(form);
|
||||
if (error) {
|
||||
const newErrors = {};
|
||||
error.details.forEach((err) => {
|
||||
newErrors[err.path[0]] = err.message;
|
||||
});
|
||||
console.log(newErrors);
|
||||
setErrors(newErrors);
|
||||
createToast({ body: "Please check the form for errors." });
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the thresholds for the form
|
||||
const {
|
||||
cpu,
|
||||
usage_cpu,
|
||||
memory,
|
||||
usage_memory,
|
||||
disk,
|
||||
usage_disk,
|
||||
temperature,
|
||||
usage_temperature,
|
||||
...rest
|
||||
} = form;
|
||||
|
||||
const thresholds = {
|
||||
...(cpu ? { usage_cpu: usage_cpu / 100 } : {}),
|
||||
...(memory ? { usage_memory: usage_memory / 100 } : {}),
|
||||
...(disk ? { usage_disk: usage_disk / 100 } : {}),
|
||||
...(temperature ? { usage_temperature: usage_temperature / 100 } : {}),
|
||||
};
|
||||
|
||||
form = {
|
||||
...(isCreate ? {} : { _id: monitorId }),
|
||||
...rest,
|
||||
description: form.name,
|
||||
type: "hardware",
|
||||
notifications: infrastructureMonitor.notifications,
|
||||
thresholds,
|
||||
};
|
||||
|
||||
// Handle create or update
|
||||
isCreate
|
||||
? await createMonitor({ monitor: form, redirect: "/infrastructure" })
|
||||
: await updateMonitor({ monitor: form, redirect: "/infrastructure" });
|
||||
submitInfrastructureForm(infrastructureMonitor, form, isCreate, monitorId);
|
||||
};
|
||||
|
||||
const triggerUpdate = () => {
|
||||
@@ -252,28 +105,8 @@ const CreateInfrastructureMonitor = () => {
|
||||
|
||||
const onChange = (event) => {
|
||||
const { value, name } = event.target;
|
||||
setInfrastructureMonitor({
|
||||
...infrastructureMonitor,
|
||||
[name]: value,
|
||||
});
|
||||
|
||||
const { error } = infrastructureMonitorValidation.validate(
|
||||
{ [name]: value },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
...(error ? { [name]: error.details[0].message } : { [name]: undefined }),
|
||||
}));
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (event) => {
|
||||
const { name } = event.target;
|
||||
const { checked } = event.target;
|
||||
setInfrastructureMonitor({
|
||||
...infrastructureMonitor,
|
||||
[name]: checked,
|
||||
});
|
||||
onChangeForm(name, value);
|
||||
validateField(name, value);
|
||||
};
|
||||
|
||||
const handlePause = async () => {
|
||||
@@ -336,105 +169,19 @@ const CreateInfrastructureMonitor = () => {
|
||||
)}
|
||||
</Typography>
|
||||
{!isCreate && (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
height="fit-content"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Tooltip
|
||||
title={pagespeedStatusMsg[determineState(monitor)]}
|
||||
disableInteractive
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: { offset: [0, -8] },
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<PulseDot color={statusColor[determineState(monitor)]} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="monitorUrl"
|
||||
>
|
||||
{infrastructureMonitor.url?.replace(/^https?:\/\//, "") || "..."}
|
||||
</Typography>
|
||||
<Typography
|
||||
position="relative"
|
||||
variant="body2"
|
||||
ml={theme.spacing(6)}
|
||||
mt={theme.spacing(1)}
|
||||
sx={{
|
||||
"&:before": {
|
||||
position: "absolute",
|
||||
content: `""`,
|
||||
width: theme.spacing(2),
|
||||
height: theme.spacing(2),
|
||||
borderRadius: "50%",
|
||||
backgroundColor: theme.palette.primary.contrastTextTertiary,
|
||||
opacity: 0.8,
|
||||
left: theme.spacing(-5),
|
||||
top: "50%",
|
||||
transform: "translateY(-50%)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t("editing")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<MonitorStatusHeader
|
||||
monitor={monitor}
|
||||
infrastructureMonitor={infrastructureMonitor}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
{!isCreate && (
|
||||
<Box
|
||||
alignSelf="flex-end"
|
||||
ml="auto"
|
||||
>
|
||||
<Button
|
||||
onClick={handlePause}
|
||||
loading={isBusy}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
sx={{
|
||||
pl: theme.spacing(4),
|
||||
pr: theme.spacing(6),
|
||||
"& svg": {
|
||||
mr: theme.spacing(2),
|
||||
"& path": {
|
||||
stroke: theme.palette.primary.contrastTextTertiary,
|
||||
strokeWidth: 0.1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{monitor?.isActive ? (
|
||||
<>
|
||||
<PauseCircleOutlineIcon />
|
||||
{t("pause")}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PlayCircleOutlineRoundedIcon />
|
||||
{t("resume")}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
loading={isBusy}
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={() => setIsOpen(true)}
|
||||
sx={{ ml: theme.spacing(6) }}
|
||||
>
|
||||
{t("remove")}
|
||||
</Button>
|
||||
</Box>
|
||||
<MonitorActionButtons
|
||||
monitor={monitor}
|
||||
isBusy={isBusy}
|
||||
handlePause={handlePause}
|
||||
handleRemove={handleRemove}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
<ConfigBox>
|
||||
@@ -534,58 +281,12 @@ const CreateInfrastructureMonitor = () => {
|
||||
setNotifications={infrastructureMonitor.notifications}
|
||||
/>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("infrastructureCustomizeAlerts")}
|
||||
</Typography>
|
||||
<Typography component="p">
|
||||
{t("infrastructureAlertNotificationDescription")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
{METRICS.map((metric) => {
|
||||
return (
|
||||
<CustomThreshold
|
||||
key={metric}
|
||||
infrastructureMonitor={infrastructureMonitor}
|
||||
errors={errors}
|
||||
checkboxId={metric}
|
||||
checkboxName={metric}
|
||||
checkboxLabel={
|
||||
metric !== "cpu"
|
||||
? capitalizeFirstLetter(metric)
|
||||
: metric.toUpperCase()
|
||||
}
|
||||
onCheckboxChange={handleCheckboxChange}
|
||||
isChecked={infrastructureMonitor[metric]}
|
||||
fieldId={METRIC_PREFIX + metric}
|
||||
fieldName={METRIC_PREFIX + metric}
|
||||
fieldValue={String(infrastructureMonitor[METRIC_PREFIX + metric])}
|
||||
onFieldChange={onChange}
|
||||
alertUnit={metric == "temperature" ? "°C" : "%"}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{/* Error text */}
|
||||
{hasAlertError(errors) && (
|
||||
<Typography
|
||||
component="span"
|
||||
className="input-error"
|
||||
color={theme.palette.error.main}
|
||||
mt={theme.spacing(2)}
|
||||
sx={{
|
||||
opacity: 0.8,
|
||||
}}
|
||||
>
|
||||
{getAlertError(errors)}
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
<CustomAlertsSection
|
||||
errors={errors}
|
||||
onChange={onChange}
|
||||
infrastructureMonitor={infrastructureMonitor}
|
||||
handleCheckboxChange={handleCheckboxChange}
|
||||
/>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
@@ -620,17 +321,6 @@ const CreateInfrastructureMonitor = () => {
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
{!isCreate && (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
theme={theme}
|
||||
title={t("deleteDialogTitle")}
|
||||
description={t("deleteDialogDescription")}
|
||||
onCancel={() => setIsOpen(false)}
|
||||
confirmationButtonLabel={t("delete")}
|
||||
onConfirm={handleRemove}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { useState, useEffect } from "react";
|
||||
import { FormControl, InputLabel, Select, MenuItem, Box } from "@mui/material";
|
||||
import { Box } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import Select from "../../../../../Components/Inputs/Select";
|
||||
import NetworkStatBoxes from "./NetworkStatBoxes";
|
||||
import NetworkCharts from "./NetworkCharts";
|
||||
import MonitorTimeFrameHeader from "../../../../../Components/MonitorTimeFrameHeader";
|
||||
@@ -63,27 +64,17 @@ const Network = ({ net, checks, isLoading, dateRange, setDateRange }) => {
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
{availableInterfaces.length > 0 && (
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
size="small"
|
||||
<Select
|
||||
name="networkInterface"
|
||||
label={t("networkInterface")}
|
||||
value={selectedInterface}
|
||||
onChange={(e) => setSelectedInterface(e.target.value)}
|
||||
items={availableInterfaces.map((interfaceName) => ({
|
||||
_id: interfaceName,
|
||||
name: interfaceName,
|
||||
}))}
|
||||
sx={{ minWidth: 200 }}
|
||||
>
|
||||
<InputLabel>{t("networkInterface")}</InputLabel>
|
||||
<Select
|
||||
value={selectedInterface}
|
||||
onChange={(e) => setSelectedInterface(e.target.value)}
|
||||
label={t("networkInterface")}
|
||||
>
|
||||
{availableInterfaces.map((interfaceName) => (
|
||||
<MenuItem
|
||||
key={interfaceName}
|
||||
value={interfaceName}
|
||||
>
|
||||
{interfaceName}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
/>
|
||||
)}
|
||||
<MonitorTimeFrameHeader
|
||||
isLoading={isLoading}
|
||||
|
||||
@@ -121,6 +121,7 @@ const InfrastructureDetails = () => {
|
||||
<AreaChartBoxes
|
||||
shouldRender={!isLoading}
|
||||
monitor={monitor}
|
||||
dateRange={dateRange}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -51,34 +51,14 @@ const Logs = () => {
|
||||
];
|
||||
return (
|
||||
<Stack gap={theme.spacing(4)}>
|
||||
<Box
|
||||
sx={{
|
||||
position: "sticky",
|
||||
top: theme.spacing(17),
|
||||
backdropFilter: "blur(10px)",
|
||||
paddingY: theme.spacing(5),
|
||||
paddingLeft: theme.spacing(6),
|
||||
}}
|
||||
>
|
||||
<Typography variant="h2">{t("logsPage.description")}</Typography>
|
||||
</Box>
|
||||
<Divider
|
||||
color={theme.palette.accent.main}
|
||||
sx={{
|
||||
position: "sticky",
|
||||
top: theme.spacing(33),
|
||||
backdropFilter: "blur(10px)",
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(4)}
|
||||
mt={theme.spacing(10)}
|
||||
sx={{
|
||||
position: "sticky",
|
||||
top: theme.spacing(34),
|
||||
top: theme.spacing(17),
|
||||
backdropFilter: "blur(10px)",
|
||||
paddingTop: theme.spacing(4),
|
||||
paddingLeft: theme.spacing(6),
|
||||
|
||||
@@ -46,6 +46,11 @@ const JobTable = ({ jobs = [] }) => {
|
||||
content: t("queuePage.jobTable.urlHeader"),
|
||||
render: (row) => row.monitorUrl,
|
||||
},
|
||||
{
|
||||
id: "interval",
|
||||
content: t("queuePage.jobTable.intervalHeader"),
|
||||
render: (row) => `${row.monitorInterval} ms`,
|
||||
},
|
||||
{
|
||||
id: "type",
|
||||
content: t("queuePage.jobTable.typeHeader"),
|
||||
|
||||
@@ -29,8 +29,6 @@ const QueueDetails = () => {
|
||||
|
||||
return (
|
||||
<Stack gap={theme.spacing(4)}>
|
||||
<Typography variant="h2">{t("queuePage.metricsTable.title")}</Typography>
|
||||
<Divider color={theme.palette.accent.main} />
|
||||
<Stack
|
||||
gap={theme.spacing(20)}
|
||||
mt={theme.spacing(10)}
|
||||
|
||||
@@ -15,7 +15,7 @@ const Logs = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
// Local state
|
||||
const [value, setValue] = useState(2);
|
||||
const [value, setValue] = useState(0);
|
||||
|
||||
// Handlers
|
||||
const handleChange = (event, newValue) => {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import Select from "../../../../../Components/Inputs/Select";
|
||||
import PropTypes from "prop-types";
|
||||
const ConfigSelect = ({ configSelection, valueSelect, onChange, ...props }) => {
|
||||
const getValueById = (config, id) => {
|
||||
const item = config.find((config) => config._id === id);
|
||||
return item ? (item.value ? item.value : item.name) : null;
|
||||
};
|
||||
|
||||
const getIdByValue = (config, name) => {
|
||||
const item = config.find((config) => {
|
||||
if (config.value) {
|
||||
return config.value === name;
|
||||
} else {
|
||||
return config.name === name;
|
||||
}
|
||||
});
|
||||
return item ? item._id : null;
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
value={getIdByValue(configSelection, valueSelect)}
|
||||
onChange={(event) => {
|
||||
const newValue = getValueById(configSelection, event.target.value);
|
||||
onChange(newValue);
|
||||
}}
|
||||
items={configSelection}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
ConfigSelect.propTypes = {
|
||||
configSelection: PropTypes.array.isRequired,
|
||||
valueSelect: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
};
|
||||
export default ConfigSelect;
|
||||
@@ -0,0 +1,64 @@
|
||||
import { networkService } from "../../../../main";
|
||||
import dayjs from "dayjs";
|
||||
import {
|
||||
MS_PER_SECOND,
|
||||
MS_PER_MINUTE,
|
||||
MS_PER_HOUR,
|
||||
MS_PER_DAY,
|
||||
MS_PER_WEEK,
|
||||
} from "../../../../Utils/timeUtils";
|
||||
|
||||
const useMaintenanceActions = () => {
|
||||
const MS_LOOKUP = {
|
||||
seconds: MS_PER_SECOND,
|
||||
minutes: MS_PER_MINUTE,
|
||||
hours: MS_PER_HOUR,
|
||||
days: MS_PER_DAY,
|
||||
weeks: MS_PER_WEEK,
|
||||
};
|
||||
const REPEAT_LOOKUP = {
|
||||
none: 0,
|
||||
daily: MS_PER_DAY,
|
||||
weekly: MS_PER_DAY * 7,
|
||||
};
|
||||
const handleSubmitForm = async (maintenanceWindowId, form) => {
|
||||
const start = dayjs(form.startDate)
|
||||
.set("hour", form.startTime.hour())
|
||||
.set("minute", form.startTime.minute());
|
||||
|
||||
const MS_MULTIPLIER = MS_LOOKUP[form.durationUnit];
|
||||
const durationInMs = form.duration * MS_MULTIPLIER;
|
||||
const end = start.add(durationInMs);
|
||||
|
||||
const repeat = REPEAT_LOOKUP[form.repeat];
|
||||
|
||||
const submit = {
|
||||
monitors: form.monitors.map((monitor) => monitor._id),
|
||||
name: form.name,
|
||||
start: start.toISOString(),
|
||||
end: end.toISOString(),
|
||||
repeat,
|
||||
};
|
||||
|
||||
if (repeat === 0) {
|
||||
submit.expiry = end;
|
||||
}
|
||||
|
||||
const requestConfig = { maintenanceWindow: submit };
|
||||
|
||||
if (maintenanceWindowId !== undefined) {
|
||||
requestConfig.maintenanceWindowId = maintenanceWindowId;
|
||||
}
|
||||
const request =
|
||||
maintenanceWindowId === undefined
|
||||
? networkService.createMaintenanceWindow(requestConfig)
|
||||
: networkService.editMaintenanceWindow(requestConfig);
|
||||
return request;
|
||||
};
|
||||
|
||||
return {
|
||||
handleSubmitForm,
|
||||
};
|
||||
};
|
||||
|
||||
export default useMaintenanceActions;
|
||||
@@ -0,0 +1,74 @@
|
||||
import { networkService } from "../../../../main";
|
||||
import dayjs from "dayjs";
|
||||
import {
|
||||
MS_PER_SECOND,
|
||||
MS_PER_MINUTE,
|
||||
MS_PER_HOUR,
|
||||
MS_PER_DAY,
|
||||
MS_PER_WEEK,
|
||||
} from "../../../../Utils/timeUtils";
|
||||
const useMaintenanceData = () => {
|
||||
const REVERSE_REPEAT_LOOKUP = {
|
||||
0: "none",
|
||||
[MS_PER_DAY]: "daily",
|
||||
[MS_PER_WEEK]: "weekly",
|
||||
};
|
||||
const getDurationAndUnit = (durationInMs) => {
|
||||
if (durationInMs % MS_PER_DAY === 0) {
|
||||
return {
|
||||
duration: (durationInMs / MS_PER_DAY).toString(),
|
||||
durationUnit: "days",
|
||||
};
|
||||
} else if (durationInMs % MS_PER_HOUR === 0) {
|
||||
return {
|
||||
duration: (durationInMs / MS_PER_HOUR).toString(),
|
||||
durationUnit: "hours",
|
||||
};
|
||||
} else if (durationInMs % MS_PER_MINUTE === 0) {
|
||||
return {
|
||||
duration: (durationInMs / MS_PER_MINUTE).toString(),
|
||||
durationUnit: "minutes",
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
duration: (durationInMs / MS_PER_SECOND).toString(),
|
||||
durationUnit: "seconds",
|
||||
};
|
||||
}
|
||||
};
|
||||
const fetchMonitorsMaintenance = async () => {
|
||||
const response = await networkService.getMonitorsByTeamId({
|
||||
limit: null,
|
||||
types: ["http", "ping", "pagespeed", "port"],
|
||||
});
|
||||
const fetchedMonitors = response.data.data.monitors;
|
||||
return fetchedMonitors;
|
||||
};
|
||||
|
||||
const initializeMaintenanceForEdit = async (maintenanceWindowId, monitorList) => {
|
||||
const res = await networkService.getMaintenanceWindowById({
|
||||
maintenanceWindowId: maintenanceWindowId,
|
||||
});
|
||||
const maintenanceWindow = res.data.data;
|
||||
const { name, start, end, repeat, monitorId } = maintenanceWindow;
|
||||
const startTime = dayjs(start);
|
||||
const endTime = dayjs(end);
|
||||
const durationInMs = endTime.diff(startTime, "milliseconds").toString();
|
||||
const { duration, durationUnit } = getDurationAndUnit(durationInMs);
|
||||
const monitor = monitorList.find((monitor) => monitor._id === monitorId);
|
||||
const maintenanceWindowInformation = {
|
||||
name,
|
||||
repeat: REVERSE_REPEAT_LOOKUP[repeat],
|
||||
startDate: startTime,
|
||||
startTime,
|
||||
duration,
|
||||
durationUnit,
|
||||
monitors: monitor ? [monitor] : [],
|
||||
};
|
||||
return maintenanceWindowInformation;
|
||||
};
|
||||
|
||||
return { fetchMonitorsMaintenance, initializeMaintenanceForEdit };
|
||||
};
|
||||
|
||||
export default useMaintenanceData;
|
||||
@@ -10,71 +10,21 @@ import { MobileTimePicker } from "@mui/x-date-pickers/MobileTimePicker";
|
||||
import { maintenanceWindowValidation } from "../../../Validation/validation";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import MonitorList from "./Components/MonitorList";
|
||||
import Checkbox from "../../../Components/Inputs/Checkbox";
|
||||
import ConfigSelect from "./Components/ConfigSelect";
|
||||
import useMaintenanceData from "./hooks/useMaintenanceData";
|
||||
import useMaintenanceActions from "./hooks/useMaintenanceActions";
|
||||
|
||||
import dayjs from "dayjs";
|
||||
import Select from "../../../Components/Inputs/Select";
|
||||
import TextInput from "../../../Components/Inputs/TextInput";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import CalendarIcon from "../../../assets/icons/calendar.svg?react";
|
||||
import "./index.css";
|
||||
import Search from "../../../Components/Inputs/Search";
|
||||
import { networkService } from "../../../main";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
import {
|
||||
MS_PER_SECOND,
|
||||
MS_PER_MINUTE,
|
||||
MS_PER_HOUR,
|
||||
MS_PER_DAY,
|
||||
MS_PER_WEEK,
|
||||
} from "../../../Utils/timeUtils";
|
||||
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { buildErrors, hasValidationErrors } from "../../../Validation/error";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const getDurationAndUnit = (durationInMs) => {
|
||||
if (durationInMs % MS_PER_DAY === 0) {
|
||||
return {
|
||||
duration: (durationInMs / MS_PER_DAY).toString(),
|
||||
durationUnit: "days",
|
||||
};
|
||||
} else if (durationInMs % MS_PER_HOUR === 0) {
|
||||
return {
|
||||
duration: (durationInMs / MS_PER_HOUR).toString(),
|
||||
durationUnit: "hours",
|
||||
};
|
||||
} else if (durationInMs % MS_PER_MINUTE === 0) {
|
||||
return {
|
||||
duration: (durationInMs / MS_PER_MINUTE).toString(),
|
||||
durationUnit: "minutes",
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
duration: (durationInMs / MS_PER_SECOND).toString(),
|
||||
durationUnit: "seconds",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const MS_LOOKUP = {
|
||||
seconds: MS_PER_SECOND,
|
||||
minutes: MS_PER_MINUTE,
|
||||
hours: MS_PER_HOUR,
|
||||
days: MS_PER_DAY,
|
||||
weeks: MS_PER_WEEK,
|
||||
};
|
||||
|
||||
const REPEAT_LOOKUP = {
|
||||
none: 0,
|
||||
daily: MS_PER_DAY,
|
||||
weekly: MS_PER_DAY * 7,
|
||||
};
|
||||
|
||||
const REVERSE_REPEAT_LOOKUP = {
|
||||
0: "none",
|
||||
[MS_PER_DAY]: "daily",
|
||||
[MS_PER_WEEK]: "weekly",
|
||||
};
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const repeatConfig = [
|
||||
{ _id: 0, name: "Don't repeat", value: "none" },
|
||||
@@ -96,24 +46,10 @@ const durationConfig = [
|
||||
},
|
||||
];
|
||||
|
||||
const getValueById = (config, id) => {
|
||||
const item = config.find((config) => config._id === id);
|
||||
return item ? (item.value ? item.value : item.name) : null;
|
||||
};
|
||||
|
||||
const getIdByValue = (config, name) => {
|
||||
const item = config.find((config) => {
|
||||
if (config.value) {
|
||||
return config.value === name;
|
||||
} else {
|
||||
return config.name === name;
|
||||
}
|
||||
});
|
||||
return item ? item._id : null;
|
||||
};
|
||||
|
||||
const CreateMaintenance = () => {
|
||||
const { maintenanceWindowId } = useParams();
|
||||
const { handleSubmitForm } = useMaintenanceActions();
|
||||
const { fetchMonitorsMaintenance, initializeMaintenanceForEdit } = useMaintenanceData();
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
@@ -121,6 +57,7 @@ const CreateMaintenance = () => {
|
||||
const [monitors, setMonitors] = useState([]);
|
||||
const [search, setSearch] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [errors, setErrors] = useState({});
|
||||
const [form, setForm] = useState({
|
||||
repeat: "none",
|
||||
startDate: dayjs(),
|
||||
@@ -130,43 +67,35 @@ const CreateMaintenance = () => {
|
||||
name: "",
|
||||
monitors: [],
|
||||
});
|
||||
const [errors, setErrors] = useState({});
|
||||
const handleFormChange = (key, value) => {
|
||||
setForm((prev) => ({ ...prev, [key]: value }));
|
||||
const { error } = maintenanceWindowValidation.validate(
|
||||
{ [key]: value },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
setErrors((prev) => {
|
||||
return buildErrors(prev, key, error);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMonitors = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await networkService.getMonitorsByTeamId({
|
||||
limit: null,
|
||||
types: ["http", "ping", "pagespeed", "port"],
|
||||
});
|
||||
const monitors = response.data.data.monitors;
|
||||
setMonitors(monitors);
|
||||
const fetchedMonitors = await fetchMonitorsMaintenance();
|
||||
setMonitors(fetchedMonitors);
|
||||
|
||||
if (maintenanceWindowId === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await networkService.getMaintenanceWindowById({
|
||||
maintenanceWindowId: maintenanceWindowId,
|
||||
});
|
||||
const maintenanceWindow = res.data.data;
|
||||
const { name, start, end, repeat, monitorId } = maintenanceWindow;
|
||||
const startTime = dayjs(start);
|
||||
const endTime = dayjs(end);
|
||||
const durationInMs = endTime.diff(startTime, "milliseconds").toString();
|
||||
const { duration, durationUnit } = getDurationAndUnit(durationInMs);
|
||||
const monitor = monitors.find((monitor) => monitor._id === monitorId);
|
||||
setForm({
|
||||
...form,
|
||||
name,
|
||||
repeat: REVERSE_REPEAT_LOOKUP[repeat],
|
||||
startDate: startTime,
|
||||
startTime,
|
||||
duration,
|
||||
durationUnit,
|
||||
monitors: monitor ? [monitor] : [],
|
||||
});
|
||||
const maintenanceWindowInformation = await initializeMaintenanceForEdit(
|
||||
maintenanceWindowId,
|
||||
fetchedMonitors
|
||||
);
|
||||
setForm((prev) => ({
|
||||
...prev,
|
||||
...maintenanceWindowInformation,
|
||||
}));
|
||||
} catch (error) {
|
||||
createToast({ body: "Failed to fetch data" });
|
||||
logger.error("Failed to fetch monitors", error);
|
||||
@@ -177,90 +106,9 @@ const CreateMaintenance = () => {
|
||||
fetchMonitors();
|
||||
}, [user]);
|
||||
|
||||
const handleSearch = (value) => {
|
||||
setSearch(value);
|
||||
};
|
||||
|
||||
const handleSelectMonitors = (monitors) => {
|
||||
setForm({ ...form, monitors });
|
||||
const { error } = maintenanceWindowValidation.validate(
|
||||
{ monitors },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
setErrors((prev) => {
|
||||
return buildErrors(prev, "monitors", error);
|
||||
});
|
||||
};
|
||||
|
||||
const handleFormChange = (key, value) => {
|
||||
setForm({ ...form, [key]: value });
|
||||
const { error } = maintenanceWindowValidation.validate(
|
||||
{ [key]: value },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
setErrors((prev) => {
|
||||
return buildErrors(prev, key, error);
|
||||
});
|
||||
};
|
||||
|
||||
const handleTimeChange = (key, newTime) => {
|
||||
setForm({ ...form, [key]: newTime });
|
||||
const { error } = maintenanceWindowValidation.validate(
|
||||
{ [key]: newTime },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
setErrors((prev) => {
|
||||
return buildErrors(prev, key, error);
|
||||
});
|
||||
};
|
||||
|
||||
const handleMonitorsChange = (selected) => {
|
||||
setForm((prev) => ({ ...prev, monitors: selected }));
|
||||
const { error } = maintenanceWindowValidation.validate(
|
||||
{ monitors: selected },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
setErrors((prev) => {
|
||||
return buildErrors(prev, "monitors", error);
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (hasValidationErrors(form, maintenanceWindowValidation, setErrors)) return;
|
||||
// Build timestamp for maintenance window from startDate and startTime
|
||||
const start = dayjs(form.startDate)
|
||||
.set("hour", form.startTime.hour())
|
||||
.set("minute", form.startTime.minute());
|
||||
// Build end timestamp for maintenance window
|
||||
const MS_MULTIPLIER = MS_LOOKUP[form.durationUnit];
|
||||
const durationInMs = form.duration * MS_MULTIPLIER;
|
||||
const end = start.add(durationInMs);
|
||||
|
||||
// Get repeat value in milliseconds
|
||||
const repeat = REPEAT_LOOKUP[form.repeat];
|
||||
|
||||
const submit = {
|
||||
monitors: form.monitors.map((monitor) => monitor._id),
|
||||
name: form.name,
|
||||
start: start.toISOString(),
|
||||
end: end.toISOString(),
|
||||
repeat,
|
||||
};
|
||||
|
||||
if (repeat === 0) {
|
||||
submit.expiry = end;
|
||||
}
|
||||
|
||||
const requestConfig = { maintenanceWindow: submit };
|
||||
|
||||
if (maintenanceWindowId !== undefined) {
|
||||
requestConfig.maintenanceWindowId = maintenanceWindowId;
|
||||
}
|
||||
const request =
|
||||
maintenanceWindowId === undefined
|
||||
? networkService.createMaintenanceWindow(requestConfig)
|
||||
: networkService.editMaintenanceWindow(requestConfig);
|
||||
|
||||
const request = handleSubmitForm(maintenanceWindowId, form);
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await request;
|
||||
@@ -353,18 +201,13 @@ const CreateMaintenance = () => {
|
||||
error={errors["name"] ? true : false}
|
||||
helperText={errors["name"]}
|
||||
/>
|
||||
<Select
|
||||
<ConfigSelect
|
||||
id="repeat"
|
||||
name="maintenance-repeat"
|
||||
label={t("maintenanceRepeat")}
|
||||
value={getIdByValue(repeatConfig, form.repeat)}
|
||||
onChange={(event) => {
|
||||
handleFormChange(
|
||||
"repeat",
|
||||
getValueById(repeatConfig, event.target.value)
|
||||
);
|
||||
}}
|
||||
items={repeatConfig}
|
||||
valueSelect={form.repeat}
|
||||
configSelection={repeatConfig}
|
||||
onChange={(value) => handleFormChange("repeat", value)}
|
||||
/>
|
||||
<Stack>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
@@ -437,7 +280,7 @@ const CreateMaintenance = () => {
|
||||
}}
|
||||
sx={{}}
|
||||
onChange={(newDate) => {
|
||||
handleTimeChange("startDate", newDate);
|
||||
handleFormChange("startDate", newDate);
|
||||
}}
|
||||
error={errors["startDate"]}
|
||||
/>
|
||||
@@ -453,7 +296,7 @@ const CreateMaintenance = () => {
|
||||
>
|
||||
{t("startTime")}
|
||||
</Typography>
|
||||
<Typography>{t("timeZoneInfo")}</Typography>
|
||||
<Typography> {t("timeZoneInfo")} </Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(15)}>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
@@ -462,7 +305,7 @@ const CreateMaintenance = () => {
|
||||
label={t("startTime")}
|
||||
value={form.startTime}
|
||||
onChange={(newTime) => {
|
||||
handleTimeChange("startTime", newTime);
|
||||
handleFormChange("startTime", newTime);
|
||||
}}
|
||||
slotProps={{
|
||||
nextIconButton: { sx: { ml: theme.spacing(2) } },
|
||||
@@ -508,16 +351,11 @@ const CreateMaintenance = () => {
|
||||
error={errors["duration"] ? true : false}
|
||||
helperText={errors["duration"]}
|
||||
/>
|
||||
<Select
|
||||
<ConfigSelect
|
||||
id="durationUnit"
|
||||
value={getIdByValue(durationConfig, form.durationUnit)}
|
||||
items={durationConfig}
|
||||
onChange={(event) => {
|
||||
handleFormChange(
|
||||
"durationUnit",
|
||||
getValueById(durationConfig, event.target.value)
|
||||
);
|
||||
}}
|
||||
valueSelect={form.durationUnit}
|
||||
configSelection={durationConfig}
|
||||
onChange={(value) => handleFormChange("durationUnit", value)}
|
||||
error={errors["durationUnit"]}
|
||||
/>
|
||||
</Stack>
|
||||
@@ -545,7 +383,9 @@ const CreateMaintenance = () => {
|
||||
inputValue={search}
|
||||
value={form.monitors}
|
||||
handleInputChange={setSearch}
|
||||
handleChange={handleMonitorsChange}
|
||||
handleChange={(selected) => {
|
||||
handleFormChange("monitors", selected);
|
||||
}}
|
||||
error={errors["monitors"]}
|
||||
/>
|
||||
<MonitorList
|
||||
|
||||
@@ -89,8 +89,6 @@ const CreateNotifications = () => {
|
||||
error.details.forEach((err) => {
|
||||
newErrors[err.path[0]] = err.message;
|
||||
});
|
||||
console.log(JSON.stringify(newErrors));
|
||||
console.log(JSON.stringify(form, null, 2));
|
||||
createToast({ body: Object.values(newErrors)[0] });
|
||||
setErrors(newErrors);
|
||||
return;
|
||||
|
||||
@@ -9,7 +9,7 @@ export const NOTIFICATION_TYPES = [
|
||||
export const TITLE_MAP = {
|
||||
email: "createNotifications.emailSettings.title",
|
||||
slack: "createNotifications.slackSettings.title",
|
||||
pager_duty: "createNotifications.pagerDutySettings.title",
|
||||
pager_duty: "createNotifications.pagerdutySettings.title",
|
||||
webhook: "createNotifications.webhookSettings.title",
|
||||
discord: "createNotifications.discordSettings.title",
|
||||
};
|
||||
@@ -17,7 +17,7 @@ export const TITLE_MAP = {
|
||||
export const DESCRIPTION_MAP = {
|
||||
email: "createNotifications.emailSettings.description",
|
||||
slack: "createNotifications.slackSettings.description",
|
||||
pager_duty: "createNotifications.pagerDutySettings.description",
|
||||
pager_duty: "createNotifications.pagerdutySettings.description",
|
||||
webhook: "createNotifications.webhookSettings.description",
|
||||
discord: "createNotifications.discordSettings.description",
|
||||
};
|
||||
@@ -25,7 +25,7 @@ export const DESCRIPTION_MAP = {
|
||||
export const LABEL_MAP = {
|
||||
email: "createNotifications.emailSettings.emailLabel",
|
||||
slack: "createNotifications.slackSettings.webhookLabel",
|
||||
pager_duty: "createNotifications.pagerDutySettings.integrationKeyLabel",
|
||||
pager_duty: "createNotifications.pagerdutySettings.integrationKeyLabel",
|
||||
webhook: "createNotifications.webhookSettings.webhookLabel",
|
||||
discord: "createNotifications.discordSettings.webhookLabel",
|
||||
};
|
||||
@@ -33,7 +33,7 @@ export const LABEL_MAP = {
|
||||
export const PLACEHOLDER_MAP = {
|
||||
email: "createNotifications.emailSettings.emailPlaceholder",
|
||||
slack: "createNotifications.slackSettings.webhookPlaceholder",
|
||||
pager_duty: "createNotifications.pagerDutySettings.integrationKeyPlaceholder",
|
||||
pager_duty: "createNotifications.pagerdutySettings.integrationKeyPlaceholder",
|
||||
webhook: "createNotifications.webhookSettings.webhookPlaceholder",
|
||||
discord: "createNotifications.discordSettings.webhookPlaceholder",
|
||||
};
|
||||
@@ -152,7 +152,7 @@ const PageSpeedSetup = () => {
|
||||
});
|
||||
|
||||
const { error } = monitorValidation.validate(
|
||||
{ [name]: value },
|
||||
{ [name]: value, type: monitor.type },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
setErrors((prev) => ({
|
||||
|
||||
@@ -29,7 +29,7 @@ const PieChartLegend = ({ audits }) => {
|
||||
: theme.palette.tertiary.main;
|
||||
|
||||
// Find the position where the number ends and the unit begins
|
||||
const match = audit.displayValue.match(/(\d+\.?\d*)\s*([a-zA-Z]+)/);
|
||||
const match = audit?.displayValue?.match(/(\d+\.?\d*)\s*([a-zA-Z]+)/);
|
||||
let value;
|
||||
let unit;
|
||||
if (match) {
|
||||
|
||||
@@ -29,7 +29,7 @@ const SettingsURL = ({ HEADING_SX, handleChange, showURL = false }) => {
|
||||
<Select
|
||||
name="showURL"
|
||||
label={t("settingsPage.urlSettings.label")}
|
||||
value={showURL || ""}
|
||||
value={showURL === true}
|
||||
onChange={handleChange}
|
||||
items={[
|
||||
{ _id: true, name: t("settingsPage.urlSettings.selectEnabled") },
|
||||
|
||||
@@ -11,7 +11,12 @@ import PropTypes from "prop-types";
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const MonitorsList = ({ isLoading = false, shouldRender = true, monitors = [] }) => {
|
||||
const MonitorsList = ({
|
||||
isLoading = false,
|
||||
shouldRender = true,
|
||||
monitors = [],
|
||||
statusPage = {},
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const { determineState } = useMonitorUtils();
|
||||
|
||||
@@ -40,10 +45,12 @@ const MonitorsList = ({ isLoading = false, shouldRender = true, monitors = [] })
|
||||
alignItems="center"
|
||||
gap={theme.spacing(20)}
|
||||
>
|
||||
<Box flex={9}>
|
||||
<StatusPageBarChart checks={monitor?.checks?.slice().reverse()} />
|
||||
</Box>
|
||||
<Box flex={1}>
|
||||
{statusPage.showCharts !== false && (
|
||||
<Box flex={9}>
|
||||
<StatusPageBarChart checks={monitor?.checks?.slice().reverse()} />
|
||||
</Box>
|
||||
)}
|
||||
<Box flex={statusPage.showCharts !== false ? 1 : 10}>
|
||||
<StatusLabel
|
||||
status={status}
|
||||
text={status}
|
||||
@@ -60,6 +67,7 @@ const MonitorsList = ({ isLoading = false, shouldRender = true, monitors = [] })
|
||||
|
||||
MonitorsList.propTypes = {
|
||||
monitors: PropTypes.array.isRequired,
|
||||
statusPage: PropTypes.object,
|
||||
};
|
||||
|
||||
export default MonitorsList;
|
||||
@@ -154,7 +154,10 @@ const PublicStatus = () => {
|
||||
/>
|
||||
<Typography variant="h2">{t("statusPageStatusServiceStatus")}</Typography>
|
||||
<StatusBar monitors={monitors} />
|
||||
<MonitorsList monitors={monitors} />
|
||||
<MonitorsList
|
||||
monitors={monitors}
|
||||
statusPage={statusPage}
|
||||
/>
|
||||
{link}
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -44,20 +44,6 @@ import {
|
||||
useFetchMonitorGames,
|
||||
} from "../../../Hooks/monitorHooks";
|
||||
|
||||
/**
|
||||
* Parses a URL string and returns a URL object.
|
||||
*
|
||||
* @param {string} url - The URL string to parse.
|
||||
* @returns {URL} - The parsed URL object if valid, otherwise an empty string.
|
||||
*/
|
||||
const parseUrl = (url) => {
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create page renders monitor creation or configuration views.
|
||||
* @component
|
||||
@@ -69,6 +55,8 @@ const UptimeCreate = ({ isClone = false }) => {
|
||||
// States
|
||||
const [monitor, setMonitor] = useState({
|
||||
type: "http",
|
||||
statusWindowSize: 5,
|
||||
statusWindowThreshold: 60,
|
||||
matchMethod: "equal",
|
||||
expectedValue: "",
|
||||
jsonPath: "",
|
||||
@@ -191,7 +179,10 @@ const UptimeCreate = ({ isClone = false }) => {
|
||||
? `http${https ? "s" : ""}://` + monitor.url
|
||||
: monitor.url,
|
||||
name: monitor.name || monitor.url.substring(0, 50),
|
||||
statusWindowSize: monitor.statusWindowSize,
|
||||
statusWindowThreshold: monitor.statusWindowThreshold,
|
||||
type: monitor.type,
|
||||
|
||||
port:
|
||||
monitor.type === "port" || monitor.type === "game" ? monitor.port : undefined,
|
||||
interval: monitor.interval,
|
||||
@@ -206,6 +197,8 @@ const UptimeCreate = ({ isClone = false }) => {
|
||||
_id: monitor._id,
|
||||
url: monitor.url,
|
||||
name: monitor.name || monitor.url.substring(0, 50),
|
||||
statusWindowSize: monitor.statusWindowSize,
|
||||
statusWindowThreshold: monitor.statusWindowThreshold,
|
||||
type: monitor.type,
|
||||
matchMethod: monitor.matchMethod,
|
||||
expectedValue: monitor.expectedValue,
|
||||
@@ -299,7 +292,7 @@ const UptimeCreate = ({ isClone = false }) => {
|
||||
|
||||
const isBusy = isLoading || isCreating || isDeleting || isUpdating || isPausing;
|
||||
const displayInterval = monitor?.interval / MS_PER_MINUTE || 1;
|
||||
const parsedUrl = parseUrl(monitor?.url);
|
||||
const parsedUrl = monitor?.url;
|
||||
const protocol = parsedUrl?.protocol?.replace(":", "") || "";
|
||||
|
||||
useEffect(() => {
|
||||
@@ -616,6 +609,39 @@ const UptimeCreate = ({ isClone = false }) => {
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("createMonitorPage.incidentConfigTitle")}
|
||||
</Typography>
|
||||
<Typography component="p">
|
||||
{t("createMonitorPage.incidentConfigDescription")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<TextInput
|
||||
name="statusWindowSize"
|
||||
label={t("createMonitorPage.incidentConfigStatusWindowLabel")}
|
||||
type="number"
|
||||
value={monitor.statusWindowSize}
|
||||
onChange={onChange}
|
||||
error={errors["statusWindowSize"] ? true : false}
|
||||
helperText={errors["statusWindowSize"]}
|
||||
/>
|
||||
<TextInput
|
||||
name="statusWindowThreshold"
|
||||
label={t("createMonitorPage.incidentConfigStatusWindowThresholdLabel")}
|
||||
type="number"
|
||||
value={monitor.statusWindowThreshold}
|
||||
onChange={onChange}
|
||||
error={errors["statusWindowThreshold"] ? true : false}
|
||||
helperText={errors["statusWindowThreshold"]}
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
|
||||
@@ -223,7 +223,7 @@ const Routes = () => {
|
||||
<Route
|
||||
exact
|
||||
path="/register/:token"
|
||||
element={<AuthRegister />}
|
||||
element={<AuthRegister superAdminExists={true} />}
|
||||
/>
|
||||
|
||||
<Route
|
||||
|
||||
@@ -564,6 +564,10 @@ const baseTheme = (palette) => ({
|
||||
"& .MuiSelect-icon": {
|
||||
color: theme.palette.primary.contrastTextSecondary, // Dropdown + color
|
||||
},
|
||||
"& .MuiSelect-select": {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.primary.main, // Background on hover
|
||||
},
|
||||
|
||||
@@ -115,15 +115,27 @@ const monitorValidation = joi.object({
|
||||
_id: joi.string(),
|
||||
userId: joi.string(),
|
||||
teamId: joi.string(),
|
||||
statusWindowSize: joi.number().min(1).max(20).default(5).messages({
|
||||
"number.base": "Status window size must be a number.",
|
||||
"number.min": "Status window size must be at least 1.",
|
||||
"number.max": "Status window size must be at most 20.",
|
||||
}),
|
||||
statusWindowThreshold: joi.number().min(1).max(100).default(60).messages({
|
||||
"number.base": "Incident percentage must be a number.",
|
||||
"number.min": "Incident percentage must be at least 1.",
|
||||
"number.max": "Incident percentage must be at most 100.",
|
||||
}),
|
||||
url: joi.when("type", {
|
||||
is: "docker",
|
||||
then: joi
|
||||
.string()
|
||||
.trim()
|
||||
.regex(/^[a-z0-9]{64}$/)
|
||||
.regex(
|
||||
/^(\/+)?([a-zA-Z0-9][a-zA-Z0-9_.-]*[a-zA-Z0-9]|[a-zA-Z0-9]+|[a-f0-9]{12,64})$/
|
||||
)
|
||||
.messages({
|
||||
"string.empty": "This field is required.",
|
||||
"string.pattern.base": "Please enter a valid 64-character Docker container ID.",
|
||||
"string.pattern.base": "Please enter a valid container name or ID.",
|
||||
}),
|
||||
otherwise: joi
|
||||
.string()
|
||||
@@ -150,6 +162,11 @@ const monitorValidation = joi.object({
|
||||
// can be replaced by a shortest alternative
|
||||
// (?![-_])(?:[-\\w\\u00a1-\\uffff]{0,63}[^-_]\\.)+
|
||||
"(?:" +
|
||||
// Single hostname without dots (like localhost)
|
||||
"[a-z0-9\\u00a1-\\uffff][a-z0-9\\u00a1-\\uffff_-]{0,62}" +
|
||||
"|" +
|
||||
// Domain with dots
|
||||
"(?:" +
|
||||
"(?:" +
|
||||
"[a-z0-9\\u00a1-\\uffff]" +
|
||||
"[a-z0-9\\u00a1-\\uffff_-]{0,62}" +
|
||||
@@ -159,6 +176,7 @@ const monitorValidation = joi.object({
|
||||
// TLD identifier name, may end with dot
|
||||
"(?:[a-z\\u00a1-\\uffff]{2,}\\.?)" +
|
||||
")" +
|
||||
")" +
|
||||
// port number (optional)
|
||||
"(?::\\d{2,5})?" +
|
||||
// resource path (optional)
|
||||
@@ -439,21 +457,35 @@ const notificationValidation = joi.object({
|
||||
}),
|
||||
|
||||
address: joi.when("type", {
|
||||
is: "email",
|
||||
then: joi
|
||||
.string()
|
||||
.email({ tlds: { allow: false } })
|
||||
.required()
|
||||
.messages({
|
||||
"string.empty": "E-mail address cannot be empty",
|
||||
"any.required": "E-mail address is required",
|
||||
"string.email": "Please enter a valid e-mail address",
|
||||
}),
|
||||
otherwise: joi.string().uri().required().messages({
|
||||
"string.empty": "Webhook URL cannot be empty",
|
||||
"any.required": "Webhook URL is required",
|
||||
"string.uri": "Please enter a valid Webhook URL",
|
||||
}),
|
||||
switch: [
|
||||
{
|
||||
is: "email",
|
||||
then: joi
|
||||
.string()
|
||||
.email({ tlds: { allow: false } })
|
||||
.required()
|
||||
.messages({
|
||||
"string.empty": "E-mail address cannot be empty",
|
||||
"any.required": "E-mail address is required",
|
||||
"string.email": "Please enter a valid e-mail address",
|
||||
}),
|
||||
},
|
||||
{
|
||||
is: "pager_duty",
|
||||
then: joi.string().required().messages({
|
||||
"string.empty": "PagerDuty routing key cannot be empty",
|
||||
"any.required": "PagerDuty routing key is required",
|
||||
}),
|
||||
},
|
||||
{
|
||||
is: joi.valid("webhook", "slack", "discord"),
|
||||
then: joi.string().uri().required().messages({
|
||||
"string.empty": "Webhook URL cannot be empty",
|
||||
"any.required": "Webhook URL is required",
|
||||
"string.uri": "Please enter a valid Webhook URL",
|
||||
}),
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -260,7 +260,6 @@
|
||||
"notifyEmails": "",
|
||||
"seperateEmails": "",
|
||||
"checkFrequency": "",
|
||||
"chooseGame": "",
|
||||
"matchMethod": "",
|
||||
"expectedValue": "",
|
||||
"deleteDialogTitle": "",
|
||||
@@ -569,7 +568,8 @@
|
||||
"password": {
|
||||
"incorrect": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
@@ -595,7 +595,8 @@
|
||||
},
|
||||
"toasts": {
|
||||
"success": ""
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "",
|
||||
@@ -847,6 +848,10 @@
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
},
|
||||
"globalThresholds": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
@@ -915,7 +920,12 @@
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
"oneWeek": "",
|
||||
"fourMinutes": "",
|
||||
"oneMinute": "",
|
||||
"twoMinutes": "",
|
||||
"fifteenSeconds": "",
|
||||
"thirtySeconds": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
@@ -967,5 +977,75 @@
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
"incidentsPageActionResolveAll": "",
|
||||
"matchMethodOptions": {
|
||||
"equal": "",
|
||||
"equalPlaceholder": "",
|
||||
"include": "",
|
||||
"includePlaceholder": "",
|
||||
"regex": "",
|
||||
"regexPlaceholder": "",
|
||||
"text": ""
|
||||
},
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"http": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"ping": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"port": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"game": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
}
|
||||
},
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": ""
|
||||
},
|
||||
"bytesPerSecond": "",
|
||||
"bytesReceived": "",
|
||||
"bytesSent": "",
|
||||
"chooseGame": "",
|
||||
"createMonitorPage": {
|
||||
"incidentConfigDescription": "",
|
||||
"incidentConfigStatusWindowLabel": "",
|
||||
"incidentConfigStatusWindowThresholdLabel": "",
|
||||
"incidentConfigTitle": ""
|
||||
},
|
||||
"dataRate": "",
|
||||
"dataReceived": "",
|
||||
"dataSent": "",
|
||||
"details": "",
|
||||
"drops": "",
|
||||
"errors": "",
|
||||
"errorsIn": "",
|
||||
"errorsOut": "",
|
||||
"gameServerMonitoring": "",
|
||||
"gameServerMonitoringDescription": "",
|
||||
"network": "",
|
||||
"networkDrops": "",
|
||||
"networkErrors": "",
|
||||
"networkInterface": "",
|
||||
"noNetworkStatsAvailable": "",
|
||||
"packetsPerSecond": "",
|
||||
"packetsReceived": "",
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
}
|
||||
@@ -260,7 +260,6 @@
|
||||
"notifyEmails": "",
|
||||
"seperateEmails": "",
|
||||
"checkFrequency": "",
|
||||
"chooseGame": "",
|
||||
"matchMethod": "",
|
||||
"expectedValue": "",
|
||||
"deleteDialogTitle": "",
|
||||
@@ -569,7 +568,8 @@
|
||||
"password": {
|
||||
"incorrect": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
@@ -595,7 +595,8 @@
|
||||
},
|
||||
"toasts": {
|
||||
"success": "Vítejte! Váš účet byl úspěšně vytvořen."
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "Zapomenuté heslo",
|
||||
@@ -847,6 +848,10 @@
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
},
|
||||
"globalThresholds": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
@@ -915,7 +920,12 @@
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
"oneWeek": "",
|
||||
"fourMinutes": "",
|
||||
"oneMinute": "",
|
||||
"twoMinutes": "",
|
||||
"fifteenSeconds": "",
|
||||
"thirtySeconds": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
@@ -967,5 +977,75 @@
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
"incidentsPageActionResolveAll": "",
|
||||
"matchMethodOptions": {
|
||||
"equal": "",
|
||||
"equalPlaceholder": "",
|
||||
"include": "",
|
||||
"includePlaceholder": "",
|
||||
"regex": "",
|
||||
"regexPlaceholder": "",
|
||||
"text": ""
|
||||
},
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"http": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"ping": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"port": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"game": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
}
|
||||
},
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": ""
|
||||
},
|
||||
"bytesPerSecond": "",
|
||||
"bytesReceived": "",
|
||||
"bytesSent": "",
|
||||
"chooseGame": "",
|
||||
"createMonitorPage": {
|
||||
"incidentConfigDescription": "",
|
||||
"incidentConfigStatusWindowLabel": "",
|
||||
"incidentConfigStatusWindowThresholdLabel": "",
|
||||
"incidentConfigTitle": ""
|
||||
},
|
||||
"dataRate": "",
|
||||
"dataReceived": "",
|
||||
"dataSent": "",
|
||||
"details": "",
|
||||
"drops": "",
|
||||
"errors": "",
|
||||
"errorsIn": "",
|
||||
"errorsOut": "",
|
||||
"gameServerMonitoring": "",
|
||||
"gameServerMonitoringDescription": "",
|
||||
"network": "",
|
||||
"networkDrops": "",
|
||||
"networkErrors": "",
|
||||
"networkInterface": "",
|
||||
"noNetworkStatsAvailable": "",
|
||||
"packetsPerSecond": "",
|
||||
"packetsReceived": "",
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
}
|
||||
+325
-220
@@ -51,14 +51,14 @@
|
||||
"label": "Slack",
|
||||
"description": "Um Slack-Benachrichtigungen zu aktivieren, erstelle eine Slack-App und aktiviere eingehende Webhooks. Danach gib einfach die Webhook-URL hier ein.",
|
||||
"webhookLabel": "Webhook URL",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookPlaceholder": "https://hooks.slack.com/services/...",
|
||||
"webhookRequired": "Slack webhook URL wird benötigt"
|
||||
},
|
||||
"discord": {
|
||||
"label": "Discord",
|
||||
"description": "Um Daten von Checkmate an einen Discord-Kanal über Discord-Benachrichtigungen mithilfe von Webhooks zu senden, kannst Du die eingehenden Webhooks von Discord verwenden.",
|
||||
"webhookLabel": "Discord Webhook URL",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookPlaceholder": "https://discord.com/api/webhooks/...",
|
||||
"webhookRequired": "Discord webhook URL wird benötigt"
|
||||
},
|
||||
"telegram": {
|
||||
@@ -78,33 +78,37 @@
|
||||
"urlRequired": "Webhook URL wird benötigt"
|
||||
},
|
||||
"testNotificationDevelop": "Testbenachrichtigung 2",
|
||||
"integrationButton": "",
|
||||
"testSuccess": "",
|
||||
"testFailed": "",
|
||||
"unsupportedType": "",
|
||||
"networkError": "",
|
||||
"integrationButton": "Benachrichtigungsintegration",
|
||||
"testSuccess": "Testbenachrichtigung erfolgreich gesendet!",
|
||||
"testFailed": "Senden der Testbenachrichtigung fehlgeschlagen",
|
||||
"unsupportedType": "Nicht unterstützter Benachrichtigungstyp",
|
||||
"networkError": "Netzwerkfehler aufgetreten",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"actionButton": ""
|
||||
"title": "Ein Benachrichtigungskanal wird verwendet, um:",
|
||||
"checks": [
|
||||
"Teams über Ausfallzeiten oder Leistungsprobleme informieren",
|
||||
"Informieren Sie Techniker über Vorfälle",
|
||||
"Informieren Sie Administratoren über Systemänderungen"
|
||||
],
|
||||
"actionButton": "Lassen Sie uns Ihren ersten Benachrichtigungskanal erstellen!"
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
"createButton": "Benachrichtigungskanal erstellen",
|
||||
"createTitle": "Benachrichtigungskanal",
|
||||
"create": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Benachrichtigung erfolgreich erstellt",
|
||||
"failed": "Benachrichtigung konnte nicht erstellt werden"
|
||||
},
|
||||
"fetch": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Benachrichtigungen erfolgreich abgerufen",
|
||||
"failed": "Abrufen der Benachrichtigungen fehlgeschlagen"
|
||||
},
|
||||
"delete": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Benachrichtigung erfolgreich gelöscht",
|
||||
"failed": "Benachrichtigung konnte nicht gelöscht werden"
|
||||
},
|
||||
"edit": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Benachrichtigung erfolgreich aktualisiert",
|
||||
"failed": "Benachrichtigung konnte nicht aktualisiert werden"
|
||||
},
|
||||
"test": {
|
||||
"success": "Test Benachrichtigung erfolgreich gesendet",
|
||||
@@ -227,7 +231,7 @@
|
||||
"createMaintenance": "Erstelle Wartungsfenster",
|
||||
"editMaintenance": "Ändere Wartungsfenster",
|
||||
"maintenanceWindowName": "Name des Wartungsfensters",
|
||||
"friendlyNameInput": "",
|
||||
"friendlyNameInput": "Freundlicher Name",
|
||||
"friendlyNamePlaceholder": "Wartung um __ : __ für ___ Minuten",
|
||||
"maintenanceRepeat": "Wartungsfenster wiederholen",
|
||||
"maintenance": "Wartungsfenster",
|
||||
@@ -247,7 +251,7 @@
|
||||
"edit": "Ändern",
|
||||
"createA": "Erstelle ein",
|
||||
"remove": "Löschen",
|
||||
"maintenanceWindowDescription": "",
|
||||
"maintenanceWindowDescription": "Pings werden während dieses Zeitraums nicht gesendet",
|
||||
"startTime": "Anfangszeit",
|
||||
"timeZoneInfo": "Alle Daten und Zeiten sind in der Zeitzone GMT+0.",
|
||||
"monitorsToApply": "Monitore, auf die das Wartungsfenster angewendet werden soll",
|
||||
@@ -260,79 +264,78 @@
|
||||
"notifyEmails": "Benachrichtige auch per E-Mail an mehrere Adressen (kommt bald)",
|
||||
"seperateEmails": "Du kannst mehrere E-Mails mit einem Komma trennen",
|
||||
"checkFrequency": "Überprüfungsfrequenz",
|
||||
"chooseGame": "",
|
||||
"matchMethod": "",
|
||||
"matchMethod": "Übereinstimmungsmethode",
|
||||
"expectedValue": "Erwarteter Wert",
|
||||
"deleteDialogTitle": "Möchtest du diesen Monitor wirklich löschen?",
|
||||
"deleteDialogDescription": "Sobald dieser Monitor gelöscht ist, kann er nicht wiederhergestellt werden.",
|
||||
"pageSpeedMonitor": "",
|
||||
"shown": "",
|
||||
"pageSpeedMonitor": "PageSpeed-Monitor",
|
||||
"shown": "Gezeigt",
|
||||
"ago": "vor",
|
||||
"companyName": "",
|
||||
"pageSpeedDetailsPerformanceReport": "",
|
||||
"pageSpeedDetailsPerformanceReportCalculator": "",
|
||||
"checkingEvery": "",
|
||||
"statusPageCreateSettings": "",
|
||||
"basicInformation": "",
|
||||
"statusPageCreateBasicInfoDescription": "",
|
||||
"statusPageCreateSelectTimeZoneDescription": "",
|
||||
"statusPageCreateAppearanceDescription": "",
|
||||
"statusPageCreateSettingsCheckboxLabel": "",
|
||||
"statusPageCreateBasicInfoStatusPageAddress": "",
|
||||
"statusPageCreateTabsContent": "",
|
||||
"statusPageCreateTabsContentDescription": "",
|
||||
"statusPageCreateTabsContentFeaturesDescription": "",
|
||||
"showCharts": "",
|
||||
"showUptimePercentage": "",
|
||||
"companyName": "Name der Firma",
|
||||
"pageSpeedDetailsPerformanceReport": "Die Werte sind geschätzt und können variieren.",
|
||||
"pageSpeedDetailsPerformanceReportCalculator": "Rechner öffnen",
|
||||
"checkingEvery": "Überprüfen alle",
|
||||
"statusPageCreateSettings": "Wenn Ihre Statusseite fertig ist, können Sie sie als veröffentlicht markieren.",
|
||||
"basicInformation": "Grundlegende Informationen",
|
||||
"statusPageCreateBasicInfoDescription": "Definieren Sie den Firmennamen und die Subdomäne, auf die Ihre Statusseite verweist.",
|
||||
"statusPageCreateSelectTimeZoneDescription": "Wählen Sie die Zeitzone aus, in der Ihre Statusseite angezeigt wird.",
|
||||
"statusPageCreateAppearanceDescription": "Definieren Sie das Standard-Erscheinungsbild Ihrer öffentlichen Statusseite.",
|
||||
"statusPageCreateSettingsCheckboxLabel": "Veröffentlicht und für die Öffentlichkeit sichtbar",
|
||||
"statusPageCreateBasicInfoStatusPageAddress": "Ihre Statusseitenadresse",
|
||||
"statusPageCreateTabsContent": "Statusseitenserver",
|
||||
"statusPageCreateTabsContentDescription": "Sie können Ihrer Statusseite beliebig viele Server hinzufügen, die Sie überwachen. Sie die Reihenfolge der Server ändern.",
|
||||
"statusPageCreateTabsContentFeaturesDescription": "Mehr Details auf der Statusseite anzeigen",
|
||||
"showCharts": "Diagramme anzeigen",
|
||||
"showUptimePercentage": "Verfügbarkeitsprozentsatz anzeigen",
|
||||
"removeLogo": "Entferne Logo",
|
||||
"statusPageStatus": "",
|
||||
"statusPageStatusContactAdmin": "",
|
||||
"statusPageStatusNotPublic": "",
|
||||
"statusPageStatusNoPage": "",
|
||||
"statusPageStatusServiceStatus": "",
|
||||
"deleteStatusPage": "",
|
||||
"deleteStatusPageConfirm": "",
|
||||
"deleteStatusPageDescription": "",
|
||||
"uptimeCreate": "",
|
||||
"uptimeCreateJsonPath": "",
|
||||
"uptimeCreateJsonPathQuery": "",
|
||||
"maintenanceTableActionMenuDialogTitle": "",
|
||||
"infrastructureEditYour": "",
|
||||
"infrastructureEditMonitor": "",
|
||||
"infrastructureMonitorCreated": "",
|
||||
"infrastructureMonitorUpdated": "",
|
||||
"errorInvalidTypeId": "",
|
||||
"errorInvalidFieldId": "",
|
||||
"inviteNoTokenFound": "",
|
||||
"pageSpeedWarning": "",
|
||||
"statusPageStatus": "Eine öffentliche Statusseite ist nicht eingerichtet.",
|
||||
"statusPageStatusContactAdmin": "Bitte wenden Sie sich an Ihren Administrator",
|
||||
"statusPageStatusNotPublic": "Diese Statusseite ist nicht öffentlich.",
|
||||
"statusPageStatusNoPage": "Hier gibt es keine Statusseite.",
|
||||
"statusPageStatusServiceStatus": "Dienststatus",
|
||||
"deleteStatusPage": "Möchten Sie diese Statusseite löschen?",
|
||||
"deleteStatusPageConfirm": "Ja, Statusseite löschen",
|
||||
"deleteStatusPageDescription": "Nach dem Löschen kann Ihre Statusseite nicht mehr abgerufen werden.",
|
||||
"uptimeCreate": "Der erwartete Wert wird zum Abgleich mit dem Antwortergebnis verwendet und die Übereinstimmung bestimmt den Status.",
|
||||
"uptimeCreateJsonPath": "Dieser Ausdruck wird anhand der JSON-Antwortdaten ausgewertet und das Ergebnis wird zum Abgleich mit dem erwarteten Wert verwendet. Siehe",
|
||||
"uptimeCreateJsonPathQuery": "für die Dokumentation der Abfragesprache.",
|
||||
"maintenanceTableActionMenuDialogTitle": "Möchten Sie dieses Wartungsfenster wirklich entfernen?",
|
||||
"infrastructureEditYour": "Bearbeiten Sie Ihre",
|
||||
"infrastructureEditMonitor": "Infrastrukturmonitor speichern",
|
||||
"infrastructureMonitorCreated": "Infrastrukturmonitor erfolgreich erstellt!",
|
||||
"infrastructureMonitorUpdated": "Infrastrukturmonitor erfolgreich aktualisiert!",
|
||||
"errorInvalidTypeId": "Ungültiger Benachrichtigungstyp angegeben",
|
||||
"errorInvalidFieldId": "Ungültige Feld-ID angegeben",
|
||||
"inviteNoTokenFound": "Kein Einladungstoken gefunden",
|
||||
"pageSpeedWarning": "Warnung: Sie haben noch keinen Google PageSpeed API-Schlüssel hinzugefügt. Ohne diesen funktioniert der PageSpeed-Monitor nicht.",
|
||||
"pageSpeedLearnMoreLink": "Hier klicken",
|
||||
"pageSpeedAddApiKey": "um einen API key einzutragen",
|
||||
"update": "Update",
|
||||
"invalidFileFormat": "Nicht unterstützes Dateiformat",
|
||||
"invalidFileSize": "Dateigröße zu groß!",
|
||||
"ClickUpload": "",
|
||||
"ClickUpload": "Zum Hochladen klicken",
|
||||
"DragandDrop": "drag and drop",
|
||||
"MaxSize": "Maximalgröße",
|
||||
"SupportedFormats": "Unterstützte Formate",
|
||||
"FirstName": "Vorname",
|
||||
"LastName": "Zuname",
|
||||
"EmailDescriptionText": "",
|
||||
"EmailDescriptionText": "Dies ist Ihre aktuelle E-Mail-Adresse – sie kann nicht geändert werden.",
|
||||
"YourPhoto": "Profilphoto",
|
||||
"PhotoDescriptionText": "",
|
||||
"PhotoDescriptionText": "Dieses Foto wird auf Ihrer Profilseite angezeigt.",
|
||||
"save": "Speichern",
|
||||
"DeleteDescriptionText": "",
|
||||
"DeleteAccountWarning": "",
|
||||
"DeleteDescriptionText": "Dadurch werden das Konto und alle zugehörigen Daten vom Server entfernt. Dieser Vorgang kann nicht rückgängig gemacht werden.",
|
||||
"DeleteAccountWarning": "Wenn Sie Ihr Konto löschen, können Sie sich nicht mehr anmelden und alle Ihre Daten werden gelöscht. Dieser Vorgang kann nicht rückgängig gemacht werden.",
|
||||
"DeleteWarningTitle": "Account wirklich löschen?",
|
||||
"bulkImport": {
|
||||
"title": "",
|
||||
"selectFileTips": "",
|
||||
"selectFileDescription": "",
|
||||
"title": "Massenimport",
|
||||
"selectFileTips": "Wählen Sie die CSV-Datei zum Hochladen aus",
|
||||
"selectFileDescription": "Sie können unsere <template>Vorlage</template> oder <sample>Beispiel</sample> herunterladen.",
|
||||
"selectFile": "Datei auswählen",
|
||||
"parsingFailed": "",
|
||||
"parsingFailed": "Parsen fehlgeschlagen",
|
||||
"uploadSuccess": "Monitore erfolgreich erstellt!",
|
||||
"validationFailed": "Prüfung fehlgeschlagen",
|
||||
"noFileSelected": "Keine Datei ausgewählt",
|
||||
"fallbackPage": "",
|
||||
"fallbackPage": "Importieren Sie eine Datei, um eine Liste von Servern gleichzeitig hochzuladen",
|
||||
"invalidFileType": "Ungültiger Dateityp",
|
||||
"uploadFailed": "Upload fehlgeschlagen"
|
||||
},
|
||||
@@ -347,8 +350,8 @@
|
||||
"createNew": "Neu anlegen",
|
||||
"greeting": {
|
||||
"prepend": "Hallo",
|
||||
"append": "",
|
||||
"overview": ""
|
||||
"append": "Der Nachmittag gehört Ihnen – machen wir ihn episch!",
|
||||
"overview": "Hier ist eine Übersicht Ihrer {{type}}-Monitore."
|
||||
},
|
||||
"roles": {
|
||||
"superAdmin": "Super admin",
|
||||
@@ -364,7 +367,7 @@
|
||||
},
|
||||
"inviteTeamMember": "Team Mirglied einladen",
|
||||
"inviteNewTeamMember": "Neues Team Mirglied einladen",
|
||||
"inviteDescription": "",
|
||||
"inviteDescription": "Wenn Sie ein neues Teammitglied hinzufügen, erhält dieses Zugriff auf alle Monitore.",
|
||||
"email": "Email",
|
||||
"selectRole": "Rolle auswählen",
|
||||
"inviteLink": "Einladungslink",
|
||||
@@ -381,17 +384,17 @@
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "Pausiert",
|
||||
"resumed": "",
|
||||
"resumed": "Wiederaufgenommen",
|
||||
"active": "Aktiv"
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "Uptime",
|
||||
"pagespeed": "",
|
||||
"infrastructure": "",
|
||||
"incidents": "",
|
||||
"pagespeed": "Pagespeed",
|
||||
"infrastructure": "Infrastruktur",
|
||||
"incidents": "Vorfälle",
|
||||
"statusPages": "Statusseite",
|
||||
"maintenance": "Wartung",
|
||||
"integrations": "",
|
||||
"integrations": "Integrationen",
|
||||
"settings": "Einstellungen",
|
||||
"support": "Support",
|
||||
"discussions": "Diskussionen",
|
||||
@@ -404,12 +407,12 @@
|
||||
"notifications": "Benachrichtigungen",
|
||||
"logs": "Logs"
|
||||
},
|
||||
"settingsEmailUser": "",
|
||||
"state": "",
|
||||
"settingsEmailUser": "E-Mail-Benutzer – Benutzername zur Authentifizierung, überschreibt die E-Mail-Adresse, falls angegeben",
|
||||
"state": "Zustand",
|
||||
"statusBreadCrumbsStatusPages": "Statusseite",
|
||||
"statusBreadCrumbsDetails": "Details",
|
||||
"commonSaving": "Speichere...",
|
||||
"navControls": "",
|
||||
"navControls": "Steuerungen",
|
||||
"incidentsPageTitle": "Ereignisse",
|
||||
"passwordPanel": {
|
||||
"passwordChangedSuccess": "Dein Passwort wurde erfolgreich geändert.",
|
||||
@@ -430,22 +433,22 @@
|
||||
"settingsTestEmailUnknownError": "Unbekannter Fehler",
|
||||
"statusMsg": {
|
||||
"paused": "Monitoring pausiert",
|
||||
"up": "",
|
||||
"down": "",
|
||||
"pending": ""
|
||||
"up": "Ihre Site ist online.",
|
||||
"down": "Ihre Site ist nicht erreichbar.",
|
||||
"pending": "Ausstehend..."
|
||||
},
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": "",
|
||||
"http": "Geben Sie die zu überwachende URL oder IP ein (z. B. https://example.com/ oder 192.168.1.100) und fügen Sie einen eindeutigen Anzeigenamen hinzu, der auf dem Dashboard angezeigt wird.",
|
||||
"ping": "Geben Sie die IP-Adresse oder den Hostnamen ein, der angepingt werden soll (z. B. 192.168.1.100 oder example.com), und fügen Sie einen eindeutigen Anzeigenamen hinzu, der auf dem Dashboard angezeigt wird.",
|
||||
"docker": "Geben Sie die Docker-ID Ihres Containers ein. Docker-IDs müssen die vollständige 64-Zeichen-ID sein. Sie können docker inspect <short_id> ausführen, um die vollständige Container-ID abzurufen.",
|
||||
"port": "Geben Sie die URL oder IP des Servers, die Portnummer und einen eindeutigen Anzeigenamen ein, der auf dem Dashboard angezeigt wird.",
|
||||
"game": ""
|
||||
},
|
||||
"common": {
|
||||
"appName": "Checkmate",
|
||||
"monitoringAgentName": "Capture",
|
||||
"buttons": {
|
||||
"toggleTheme": ""
|
||||
"toggleTheme": "Schaltet hell und dunkel um"
|
||||
},
|
||||
"toasts": {
|
||||
"networkError": "Netzwerkfehler",
|
||||
@@ -465,7 +468,7 @@
|
||||
"placeholder": "jordan.ellis@domain.com",
|
||||
"errors": {
|
||||
"empty": "Email adresse eingeben um fortzufahren",
|
||||
"invalid": ""
|
||||
"invalid": "Bitte überprüfen Sie die Gültigkeit der eingegebenen E-Mail-Adresse erneut"
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
@@ -492,7 +495,7 @@
|
||||
"highlighted": "einen Kleinbuchstaben enthalten"
|
||||
},
|
||||
"match": {
|
||||
"beginning": "",
|
||||
"beginning": "Passwort und Kennwort bestätigen",
|
||||
"highlighted": "muß übereinstimmen"
|
||||
}
|
||||
},
|
||||
@@ -544,7 +547,7 @@
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
"min": "Mindestens eine Rolle ist erforderlich"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -569,7 +572,8 @@
|
||||
"password": {
|
||||
"incorrect": "Das Passwort stimmt nicht mit dem gespeicherten überein"
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
@@ -577,25 +581,26 @@
|
||||
"user": "Anmelden"
|
||||
},
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepOne": "Geben Sie Ihre persönlichen Daten ein",
|
||||
"stepTwo": "Email eingeben",
|
||||
"stepThree": "Passwort eingeben"
|
||||
},
|
||||
"description": {
|
||||
"superAdmin": "Super admin account anlegen um zu beginnen",
|
||||
"user": ""
|
||||
"user": "Melden Sie sich als Benutzer an und bitten Sie den Superadministrator um Zugriff auf Ihre Monitore"
|
||||
},
|
||||
"gettingStartedButton": {
|
||||
"superAdmin": "Super admin account anlegen",
|
||||
"user": "Normalen Benutzer anlegen"
|
||||
},
|
||||
"termsAndPolicies": "",
|
||||
"termsAndPolicies": "Indem Sie ein Konto erstellen, stimmen Sie unseren <a1>Servicebedingungen</a1> und <a2>Datenschutzrichtlinien</a2> zu.",
|
||||
"links": {
|
||||
"login": "Du hast bereits einen Benutzer? <a>Anmelden</a>"
|
||||
},
|
||||
"toasts": {
|
||||
"success": "Willkommen! Benutzer erfolgreich angelegt"
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "Passwort vergessen?",
|
||||
@@ -624,7 +629,7 @@
|
||||
"emailNotFound": "Email nicht gefunden.",
|
||||
"redirect": "Umleitung in <seconds/>...",
|
||||
"success": "Passwort erfolgreich zurückgesetzt.",
|
||||
"error": ""
|
||||
"error": "Kennwort konnte nicht zurückgesetzt werden. Bitte versuchen Sie es später erneut oder wenden Sie sich an den Support."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -635,7 +640,7 @@
|
||||
"stillUnreachable": "Server immer noch nicht erreichbar. Bitte später erneut versuchen."
|
||||
},
|
||||
"alertBox": "Server Verbindungsfehler",
|
||||
"description": "",
|
||||
"description": "Wir können keine Verbindung zum Server herstellen. Bitte überprüfen Sie Ihre Internetverbindung oder Ihre Bereitstellungskonfiguration, falls das Problem weiterhin besteht.",
|
||||
"retryButton": {
|
||||
"default": "Verbindung erneut versuchen",
|
||||
"processing": "Verbinde..."
|
||||
@@ -643,16 +648,16 @@
|
||||
}
|
||||
},
|
||||
"createNotifications": {
|
||||
"title": "",
|
||||
"title": "Benachrichtigungskanal erstellen",
|
||||
"nameSettings": {
|
||||
"title": "Name",
|
||||
"description": "",
|
||||
"description": "Ein beschreibender Name für Ihre Integration.",
|
||||
"nameLabel": "Name",
|
||||
"namePlaceholder": "z.B. Slack Benachrichtigung"
|
||||
},
|
||||
"typeSettings": {
|
||||
"title": "Typ",
|
||||
"description": "",
|
||||
"description": "Wählen Sie den Typ des Benachrichtigungskanals aus, den Sie erstellen möchten.",
|
||||
"typeLabel": "Typ"
|
||||
},
|
||||
"emailSettings": {
|
||||
@@ -665,7 +670,7 @@
|
||||
"title": "Slack",
|
||||
"description": "Slack webhook hier konfigurieren",
|
||||
"webhookLabel": "Slack webhook URL",
|
||||
"webhookPlaceholder": ""
|
||||
"webhookPlaceholder": "https://hooks.slack.com/services/..."
|
||||
},
|
||||
"pagerdutySettings": {
|
||||
"title": "PagerDuty",
|
||||
@@ -685,38 +690,38 @@
|
||||
"webhookLabel": "Webhook URL",
|
||||
"webhookPlaceholder": "https://your-server.com/webhook"
|
||||
},
|
||||
"testNotification": "",
|
||||
"dialogDeleteTitle": "",
|
||||
"dialogDeleteConfirm": ""
|
||||
"testNotification": "Testbenachrichtigung",
|
||||
"dialogDeleteTitle": "Möchten Sie diese Benachrichtigung wirklich löschen?",
|
||||
"dialogDeleteConfirm": "Löschen"
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "Benachrichtigungen",
|
||||
"description": ""
|
||||
"description": "Wählen Sie die Benachrichtigungskanäle aus, die Sie verwenden möchten"
|
||||
},
|
||||
"monitorStatus": {
|
||||
"checkingEvery": "",
|
||||
"withCaptureAgent": "",
|
||||
"checkingEvery": "Überprüfung alle {{interval}}",
|
||||
"withCaptureAgent": "mit Capture-Agent {{version}}",
|
||||
"up": "hoch",
|
||||
"down": "runter",
|
||||
"paused": "pausiert"
|
||||
},
|
||||
"advancedMatching": "",
|
||||
"advancedMatching": "Erweiterter Abgleich",
|
||||
"sendTestNotifications": "Test Benachrichtigung senden",
|
||||
"selectAll": "Alle auswählen",
|
||||
"showAdminLoginLink": "",
|
||||
"showAdminLoginLink": "Link „Administrator? Hier anmelden“ auf der Statusseite anzeigen",
|
||||
"logsPage": {
|
||||
"title": "Logs",
|
||||
"description": "",
|
||||
"description": "Systemprotokolle – letzte 1000 Zeilen",
|
||||
"tabs": {
|
||||
"queue": "Warteschlange",
|
||||
"logs": "Server logs",
|
||||
"diagnostics": ""
|
||||
"diagnostics": "Diagnose"
|
||||
},
|
||||
"toast": {
|
||||
"fetchLogsSuccess": ""
|
||||
"fetchLogsSuccess": "Protokolle erfolgreich abgerufen"
|
||||
},
|
||||
"logLevelSelect": {
|
||||
"title": "",
|
||||
"title": "Protokollebene",
|
||||
"values": {
|
||||
"all": "Alle",
|
||||
"info": "Info",
|
||||
@@ -736,7 +741,7 @@
|
||||
"urlHeader": "URL",
|
||||
"typeHeader": "Typ",
|
||||
"activeHeader": "Aktiv",
|
||||
"lockedAtHeader": "",
|
||||
"lockedAtHeader": "Gesperrt bei",
|
||||
"runCountHeader": "Anzahl Läufe",
|
||||
"failCountHeader": "Anzahl Fehler",
|
||||
"lastRunHeader": "Letzter Lauf um",
|
||||
@@ -744,8 +749,8 @@
|
||||
"lastRunTookHeader": "Letzter Lauf dauerte"
|
||||
},
|
||||
"metricsTable": {
|
||||
"title": "",
|
||||
"metricHeader": "",
|
||||
"title": "Warteschlangenmetriken",
|
||||
"metricHeader": "Metrisch",
|
||||
"valueHeader": "Wert"
|
||||
},
|
||||
"failedJobTable": {
|
||||
@@ -766,9 +771,9 @@
|
||||
"title": "Export/Import",
|
||||
"import": "Monitore importieren",
|
||||
"export": "Monitore exportieren",
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"details": ""
|
||||
"deleteSuccess": "Monitor erfolgreich gelöscht",
|
||||
"deleteFailed": "Monitor konnte nicht gelöscht werden",
|
||||
"details": "Details"
|
||||
},
|
||||
"settingsPage": {
|
||||
"aboutSettings": {
|
||||
@@ -783,43 +788,43 @@
|
||||
},
|
||||
"emailSettings": {
|
||||
"buttonSendTestEmail": "Test-Email senden",
|
||||
"description": "",
|
||||
"descriptionTransport": "",
|
||||
"labelAddress": "",
|
||||
"labelConnectionHost": "",
|
||||
"labelHost": "",
|
||||
"description": "Konfigurieren Sie die E-Mail-Einstellungen für Ihr System. Diese werden zum Senden von Benachrichtigungen und Warnungen verwendet.",
|
||||
"descriptionTransport": "Dies erstellt einen SMTP-Transport für NodeMailer",
|
||||
"labelAddress": "E-Mail-Adresse - Wird zur Authentifizierung verwendet",
|
||||
"labelConnectionHost": "E-Mail-Verbindungshost – Hostname zur Verwendung in der HELO/EHLO-Begrüßung",
|
||||
"labelHost": "E-Mail-Host – Hostname oder IP-Adresse, mit der eine Verbindung hergestellt werden soll",
|
||||
"labelIgnoreTLS": "STARTTLS abschalten: TLS nicht nutzen, selbst wenn der Server es unterstützt",
|
||||
"labelPassword": "Email password - Password für Anmeldung",
|
||||
"labelPasswordSet": "Passwort erstellt. Reset drücken um es zu ändern.",
|
||||
"labelPool": "",
|
||||
"labelPool": "Verbindungspooling aktivieren: Vorhandene Verbindungen wiederverwenden, um die Leistung zu verbessern",
|
||||
"labelPort": "Email port - Port zum verbinden",
|
||||
"labelRejectUnauthorized": "Ungültige Zertifikate ablehnen. Verbindungen mit self-signed oder nicht vertrauenswürdigen Zertifikaten ablehnen.",
|
||||
"labelRequireTLS": "",
|
||||
"labelRequireTLS": "STARTTLS erzwingen: TLS-Upgrade erforderlich, schlägt fehl, wenn nicht unterstützt",
|
||||
"labelSecure": "SSL verwenden (empfohlen): Verbindung mit SSL/TLS verschlüsseln",
|
||||
"labelTLSServername": "",
|
||||
"labelUser": "",
|
||||
"linkTransport": "",
|
||||
"labelTLSServername": "TLS-Servername – Optionaler Hostname für die TLS-Validierung, wenn der Host eine IP ist",
|
||||
"labelUser": "E-Mail-Benutzer – Benutzername zur Authentifizierung, überschreibt die E-Mail-Adresse, falls angegeben",
|
||||
"linkTransport": "Spezifikationen finden Sie hier",
|
||||
"placeholderUser": "Leer lassen, wenn nicht benötigt",
|
||||
"title": "Email",
|
||||
"toastEmailRequiredFieldsError": ""
|
||||
"toastEmailRequiredFieldsError": "E-Mail-Adresse, Host, Port und Passwort sind erforderlich"
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "",
|
||||
"description": "Geben Sie Ihren Google PageSpeed API-Schlüssel ein, um die Google PageSpeed-Überwachung zu aktivieren. Klicken Sie auf „Zurücksetzen“, um den Schlüssel zu aktualisieren.",
|
||||
"labelApiKeySet": "API key erstellt. Reset drücken um ihn zu ändern.",
|
||||
"labelApiKey": "PageSpeed API key",
|
||||
"title": "Google PageSpeed API key"
|
||||
},
|
||||
"saveButtonLabel": "Speichern",
|
||||
"statsSettings": {
|
||||
"clearAllStatsButton": "",
|
||||
"clearAllStatsDescription": "",
|
||||
"clearAllStatsDialogConfirm": "",
|
||||
"clearAllStatsDialogDescription": "",
|
||||
"clearAllStatsDialogTitle": "",
|
||||
"description": "",
|
||||
"labelTTL": "",
|
||||
"clearAllStatsButton": "Alle Statistiken löschen",
|
||||
"clearAllStatsDescription": "Alle Statistiken löschen. Dies ist irreversibel.",
|
||||
"clearAllStatsDialogConfirm": "Ja, alle Statistiken löschen",
|
||||
"clearAllStatsDialogDescription": "Nach dem Entfernen können der Überwachungsverlauf und die Statistiken nicht mehr abgerufen werden.",
|
||||
"clearAllStatsDialogTitle": "Möchten Sie alle Statistiken löschen?",
|
||||
"description": "Legen Sie fest, wie lange Sie historische Daten aufbewahren möchten. Sie können auch alle vorhandenen Daten löschen.",
|
||||
"labelTTL": "Die Tage, an denen Sie den Überwachungsverlauf beibehalten möchten.",
|
||||
"labelTTLOptional": "0 für unendlich",
|
||||
"title": ""
|
||||
"title": "Monitorverlauf"
|
||||
},
|
||||
"systemResetSettings": {
|
||||
"buttonRemoveAllMonitors": "Alle Monitore löschen",
|
||||
@@ -830,142 +835,242 @@
|
||||
"title": "System zurücksetzen"
|
||||
},
|
||||
"timezoneSettings": {
|
||||
"description": "",
|
||||
"description": "Wählen Sie die Zeitzone aus, die zum Anzeigen von Datum und Uhrzeit in der gesamten Anwendung verwendet wird.",
|
||||
"label": "Zeitzone",
|
||||
"title": "Zeitzone"
|
||||
},
|
||||
"title": "Einstellungen",
|
||||
"uiSettings": {
|
||||
"description": "",
|
||||
"description": "Wechseln Sie zwischen hellem und dunklem Modus oder ändern Sie die Sprache der Benutzeroberfläche.",
|
||||
"labelLanguage": "Sprache",
|
||||
"labelTheme": "",
|
||||
"title": ""
|
||||
"labelTheme": "Themenmodus",
|
||||
"title": "Aussehen"
|
||||
},
|
||||
"urlSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"description": "Zeigt die IP-Adresse oder URL des Monitors auf der öffentlichen Statusseite an. Wenn diese Option deaktiviert ist, wird zum Schutz vertraulicher Informationen nur der Monitorname angezeigt.",
|
||||
"label": "IP/URL auf der Statusseite anzeigen",
|
||||
"selectDisabled": "Inaktiv",
|
||||
"selectEnabled": "Aktiv",
|
||||
"title": ""
|
||||
"title": "Überwachen Sie IP/URL auf der Statusseite"
|
||||
},
|
||||
"globalThresholds": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": "Speichern"
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "",
|
||||
"incidentsOptionsHeaderFilterResolved": "Gelöst",
|
||||
"settingsSave": "Speichern",
|
||||
"statusPageCreateAppearanceTitle": "",
|
||||
"statusPageCreateAppearanceTitle": "Aussehen",
|
||||
"confirmPassword": "Passwort bestätigen",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "Demo Monitor konnte nicht hinzugefügt werden",
|
||||
"successAddDemoMonitors": "Demo Monitor erfolgreich hinzugefügt"
|
||||
},
|
||||
"settingsAppearance": "",
|
||||
"settingsDisplayTimezone": "",
|
||||
"settingsGeneralSettings": "",
|
||||
"incidentsOptionsHeaderTotalIncidents": "",
|
||||
"settingsAppearance": "Aussehen",
|
||||
"settingsDisplayTimezone": "Zeitzone anzeigen",
|
||||
"settingsGeneralSettings": "Allgemeine Einstellungen",
|
||||
"incidentsOptionsHeaderTotalIncidents": "Gesamtanzahl der Vorfälle",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"createSuccess": "",
|
||||
"updateSuccess": "",
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"deleteSuccess": "Statusseite erfolgreich gelöscht",
|
||||
"deleteFailed": "Löschen der Statusseite fehlgeschlagen",
|
||||
"createSuccess": "Statusseite erfolgreich erstellt",
|
||||
"updateSuccess": "Statusseite erfolgreich aktualisiert",
|
||||
"generalSettings": "Allgemeine Einstellungen",
|
||||
"contents": "Inhalt",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Überwachen und zeigen Sie den Zustand Ihrer Dienste in Echtzeit an.",
|
||||
"Verfolgen Sie mehrere Dienste und teilen Sie deren Status.",
|
||||
"Informieren Sie Nutzer über Ausfälle und Leistung."
|
||||
],
|
||||
"title": "Eine Statusseite wird verwendet, um:",
|
||||
"actionButton": "Lassen Sie uns Ihre erste Statusseite erstellen!"
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "",
|
||||
"incidentsTableResolvedAt": "",
|
||||
"incidentsTableActionResolve": "",
|
||||
"testNotificationsDisabled": "Für diesen Monitor sind keine Benachrichtigungen eingerichtet. Sie müssen eine hinzufügen, indem Sie auf die Schaltfläche „Konfigurieren“ klicken.",
|
||||
"incidentsTableResolvedAt": "Gelöst am",
|
||||
"incidentsTableActionResolve": "Lösen",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "",
|
||||
"failureResolveAll": "",
|
||||
"failureResolveMonitor": ""
|
||||
"failureResolveOne": "Der Vorfall konnte nicht gelöst werden.",
|
||||
"failureResolveAll": "Es konnten nicht alle Vorfälle gelöst werden.",
|
||||
"failureResolveMonitor": "Monitorvorfälle konnten nicht behoben werden."
|
||||
},
|
||||
"checkFormError": "",
|
||||
"checkFormError": "Bitte überprüfen Sie das Formular auf Fehler.",
|
||||
"diagnosticsPage": {
|
||||
"diagnosticDescription": "",
|
||||
"statsDescription": "",
|
||||
"diagnosticDescription": "Systemdiagnose",
|
||||
"statsDescription": "Systemstatistiken",
|
||||
"gauges": {
|
||||
"heapAllocationTitle": "",
|
||||
"heapAllocationSubtitle": "",
|
||||
"heapUsageTitle": "",
|
||||
"heapUsageSubtitle": "",
|
||||
"heapUtilizationTitle": "",
|
||||
"heapUtilizationSubtitle": "",
|
||||
"instantCpuUsageTitle": "",
|
||||
"instantCpuUsageSubtitle": ""
|
||||
"heapAllocationTitle": "Heap-Zuweisung",
|
||||
"heapAllocationSubtitle": "% des verfügbaren Speichers",
|
||||
"heapUsageTitle": "Heap-Nutzung",
|
||||
"heapUsageSubtitle": "% des verfügbaren Speichers",
|
||||
"heapUtilizationTitle": "Heap-Auslastung",
|
||||
"heapUtilizationSubtitle": "% der zugewiesenen",
|
||||
"instantCpuUsageTitle": "Sofortige CPU-Auslastung",
|
||||
"instantCpuUsageSubtitle": "% der von der CPU genutzten 1s"
|
||||
},
|
||||
"stats": {
|
||||
"eventLoopDelayTitle": "",
|
||||
"uptimeTitle": "",
|
||||
"usedHeapSizeTitle": "",
|
||||
"totalHeapSizeTitle": "",
|
||||
"osMemoryLimitTitle": ""
|
||||
"eventLoopDelayTitle": "Ereignisschleifenverzögerung",
|
||||
"uptimeTitle": "Betriebszeit",
|
||||
"usedHeapSizeTitle": "Verwendete Heap-Größe",
|
||||
"totalHeapSizeTitle": "Gesamtgröße des Heapspeichers",
|
||||
"osMemoryLimitTitle": "Betriebssystem-Speicherlimit"
|
||||
}
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "",
|
||||
"pageSpeedLighthouseAPI": "Verwenden Sie die Lighthouse PageSpeed API, um Ihre Website zu überwachen",
|
||||
"time": {
|
||||
"threeMinutes": "",
|
||||
"fiveMinutes": "",
|
||||
"tenMinutes": "",
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
"threeMinutes": "3 Minuten",
|
||||
"fiveMinutes": "5 Minuten",
|
||||
"tenMinutes": "10 Minuten",
|
||||
"twentyMinutes": "20 Minuten",
|
||||
"oneHour": "1 Stunde",
|
||||
"oneDay": "1 Tag",
|
||||
"oneWeek": "1 Woche",
|
||||
"fourMinutes": "",
|
||||
"oneMinute": "",
|
||||
"twoMinutes": "",
|
||||
"fifteenSeconds": "",
|
||||
"thirtySeconds": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
"noOptionsFound": "Keine {{unit}} gefunden"
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Verfolgen Sie die Leistung Ihrer Server",
|
||||
"Identifizieren Sie Engpässe und optimieren Sie die Nutzung",
|
||||
"Sorgen Sie für Zuverlässigkeit durch Echtzeitüberwachung"
|
||||
],
|
||||
"title": "Ein Infrastrukturmonitor wird verwendet, um:",
|
||||
"actionButton": "Lassen Sie uns Ihren ersten Infrastrukturmonitor erstellen!"
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Markieren Sie Ihre Wartungszeiträume.",
|
||||
"Beseitigen Sie Missverständnisse.",
|
||||
"Beenden Sie das Senden von Warnmeldungen in Wartungsfenstern."
|
||||
],
|
||||
"title": "Ein Wartungsfenster wird verwendet, um:",
|
||||
"actionButton": "Lassen Sie uns Ihr erstes Wartungsfenster erstellen!"
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Berichten Sie über die Benutzerfreundlichkeit einer Seite.",
|
||||
"Helfen Sie bei der Analyse der Webseitengeschwindigkeit.",
|
||||
"Geben Sie Verbesserungsvorschläge für die Seite."
|
||||
],
|
||||
"title": "Ein PageSpeed-Monitor wird verwendet, um:",
|
||||
"actionButton": "Lassen Sie uns Ihren ersten PageSpeed-Monitor erstellen!"
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Prüfen Sie, ob Websites oder Server online und reaktionsfähig sind.",
|
||||
"Benachrichtigen Sie Ihre Teams über Ausfallzeiten oder Leistungsprobleme.",
|
||||
"Überwachen Sie HTTP-Endpunkte, Pings, Container und Ports.",
|
||||
"Verfolgen Sie historische Verfügbarkeits- und Zuverlässigkeitstrends."
|
||||
],
|
||||
"title": "Ein Uptime-Monitor wird verwendet, um:",
|
||||
"actionButton": "Lassen Sie uns Ihren ersten Uptime-Monitor erstellen!"
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
"email": "E-Mail",
|
||||
"firstName": "Vorname",
|
||||
"lastName": "Nachname",
|
||||
"role": "Rollen",
|
||||
"save": "Speichern"
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
"actionHeader": "Aktion",
|
||||
"roleHeader": "Rolle"
|
||||
},
|
||||
"title": "",
|
||||
"title": "Benutzer bearbeiten",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
"successUserUpdate": "Benutzer erfolgreich aktualisiert",
|
||||
"validationErrors": "Validierungsfehler"
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
"incidentsPageActionResolveMonitor": "Monitor-Vorfälle beheben",
|
||||
"incidentsPageActionResolveAll": "Alle Vorfälle lösen",
|
||||
"matchMethodOptions": {
|
||||
"equal": "",
|
||||
"equalPlaceholder": "",
|
||||
"include": "",
|
||||
"includePlaceholder": "",
|
||||
"regex": "",
|
||||
"regexPlaceholder": "",
|
||||
"text": ""
|
||||
},
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"http": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"ping": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"port": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"game": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
}
|
||||
},
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": ""
|
||||
},
|
||||
"bytesPerSecond": "",
|
||||
"bytesReceived": "",
|
||||
"bytesSent": "",
|
||||
"chooseGame": "",
|
||||
"createMonitorPage": {
|
||||
"incidentConfigDescription": "",
|
||||
"incidentConfigStatusWindowLabel": "",
|
||||
"incidentConfigStatusWindowThresholdLabel": "",
|
||||
"incidentConfigTitle": ""
|
||||
},
|
||||
"dataRate": "",
|
||||
"dataReceived": "",
|
||||
"dataSent": "",
|
||||
"details": "",
|
||||
"drops": "",
|
||||
"errors": "",
|
||||
"errorsIn": "",
|
||||
"errorsOut": "",
|
||||
"gameServerMonitoring": "",
|
||||
"gameServerMonitoringDescription": "",
|
||||
"network": "",
|
||||
"networkDrops": "",
|
||||
"networkErrors": "",
|
||||
"networkInterface": "",
|
||||
"noNetworkStatsAvailable": "",
|
||||
"packetsPerSecond": "",
|
||||
"packetsReceived": "",
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
}
|
||||
+930
-878
File diff suppressed because it is too large.
Load diff
@@ -260,7 +260,6 @@
|
||||
"notifyEmails": "",
|
||||
"seperateEmails": "",
|
||||
"checkFrequency": "",
|
||||
"chooseGame": "",
|
||||
"matchMethod": "",
|
||||
"expectedValue": "",
|
||||
"deleteDialogTitle": "",
|
||||
@@ -569,7 +568,8 @@
|
||||
"password": {
|
||||
"incorrect": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
@@ -595,7 +595,8 @@
|
||||
},
|
||||
"toasts": {
|
||||
"success": ""
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "",
|
||||
@@ -847,6 +848,10 @@
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
},
|
||||
"globalThresholds": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
@@ -915,7 +920,12 @@
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
"oneWeek": "",
|
||||
"fourMinutes": "",
|
||||
"oneMinute": "",
|
||||
"twoMinutes": "",
|
||||
"fifteenSeconds": "",
|
||||
"thirtySeconds": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
@@ -967,5 +977,75 @@
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
"incidentsPageActionResolveAll": "",
|
||||
"matchMethodOptions": {
|
||||
"equal": "",
|
||||
"equalPlaceholder": "",
|
||||
"include": "",
|
||||
"includePlaceholder": "",
|
||||
"regex": "",
|
||||
"regexPlaceholder": "",
|
||||
"text": ""
|
||||
},
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"http": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"ping": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"port": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"game": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
}
|
||||
},
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": ""
|
||||
},
|
||||
"bytesPerSecond": "",
|
||||
"bytesReceived": "",
|
||||
"bytesSent": "",
|
||||
"chooseGame": "",
|
||||
"createMonitorPage": {
|
||||
"incidentConfigDescription": "",
|
||||
"incidentConfigStatusWindowLabel": "",
|
||||
"incidentConfigStatusWindowThresholdLabel": "",
|
||||
"incidentConfigTitle": ""
|
||||
},
|
||||
"dataRate": "",
|
||||
"dataReceived": "",
|
||||
"dataSent": "",
|
||||
"details": "",
|
||||
"drops": "",
|
||||
"errors": "",
|
||||
"errorsIn": "",
|
||||
"errorsOut": "",
|
||||
"gameServerMonitoring": "",
|
||||
"gameServerMonitoringDescription": "",
|
||||
"network": "",
|
||||
"networkDrops": "",
|
||||
"networkErrors": "",
|
||||
"networkInterface": "",
|
||||
"noNetworkStatsAvailable": "",
|
||||
"packetsPerSecond": "",
|
||||
"packetsReceived": "",
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
}
|
||||
@@ -260,7 +260,6 @@
|
||||
"notifyEmails": "",
|
||||
"seperateEmails": "",
|
||||
"checkFrequency": "",
|
||||
"chooseGame": "",
|
||||
"matchMethod": "",
|
||||
"expectedValue": "",
|
||||
"deleteDialogTitle": "",
|
||||
@@ -569,7 +568,8 @@
|
||||
"password": {
|
||||
"incorrect": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
@@ -595,7 +595,8 @@
|
||||
},
|
||||
"toasts": {
|
||||
"success": ""
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "",
|
||||
@@ -847,6 +848,10 @@
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
},
|
||||
"globalThresholds": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
@@ -915,7 +920,12 @@
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
"oneWeek": "",
|
||||
"fourMinutes": "",
|
||||
"oneMinute": "",
|
||||
"twoMinutes": "",
|
||||
"fifteenSeconds": "",
|
||||
"thirtySeconds": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
@@ -967,5 +977,75 @@
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
"incidentsPageActionResolveAll": "",
|
||||
"matchMethodOptions": {
|
||||
"equal": "",
|
||||
"equalPlaceholder": "",
|
||||
"include": "",
|
||||
"includePlaceholder": "",
|
||||
"regex": "",
|
||||
"regexPlaceholder": "",
|
||||
"text": ""
|
||||
},
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"http": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"ping": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"port": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"game": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
}
|
||||
},
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": ""
|
||||
},
|
||||
"bytesPerSecond": "",
|
||||
"bytesReceived": "",
|
||||
"bytesSent": "",
|
||||
"chooseGame": "",
|
||||
"createMonitorPage": {
|
||||
"incidentConfigDescription": "",
|
||||
"incidentConfigStatusWindowLabel": "",
|
||||
"incidentConfigStatusWindowThresholdLabel": "",
|
||||
"incidentConfigTitle": ""
|
||||
},
|
||||
"dataRate": "",
|
||||
"dataReceived": "",
|
||||
"dataSent": "",
|
||||
"details": "",
|
||||
"drops": "",
|
||||
"errors": "",
|
||||
"errorsIn": "",
|
||||
"errorsOut": "",
|
||||
"gameServerMonitoring": "",
|
||||
"gameServerMonitoringDescription": "",
|
||||
"network": "",
|
||||
"networkDrops": "",
|
||||
"networkErrors": "",
|
||||
"networkInterface": "",
|
||||
"noNetworkStatsAvailable": "",
|
||||
"packetsPerSecond": "",
|
||||
"packetsReceived": "",
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
}
|
||||
+130
-29
@@ -90,7 +90,7 @@
|
||||
"Informez les ingénieurs lorsque des incidents se produisent",
|
||||
"Tenez les administrateurs informés des changements apportés au système"
|
||||
],
|
||||
"actionButton": ""
|
||||
"actionButton": "Créez votre premier canal de notification !"
|
||||
},
|
||||
"createButton": "Créer un canal de notification",
|
||||
"createTitle": "Canal de notification",
|
||||
@@ -264,7 +264,6 @@
|
||||
"notifyEmails": "Envoyer également une notification par email à plusieurs adresses (bientôt).",
|
||||
"seperateEmails": "Vous pouvez ajouter plusieurs adresses emails en les séparant par une virgule",
|
||||
"checkFrequency": "Vérifier la fréquence",
|
||||
"chooseGame": "",
|
||||
"matchMethod": "Méthode de rapprochement",
|
||||
"expectedValue": "Valeur attendue",
|
||||
"deleteDialogTitle": "Voulez-vous vraiment supprimer ce moniteur ?",
|
||||
@@ -548,7 +547,7 @@
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
"min": "Au moins un rôle est requis"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -573,7 +572,8 @@
|
||||
"password": {
|
||||
"incorrect": "Le mot de passe fourni est erroné"
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcome": "Bon retour sur Checkmate !"
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
@@ -599,7 +599,8 @@
|
||||
},
|
||||
"toasts": {
|
||||
"success": "Bienvenue ! Votre compte a été créé avec succès."
|
||||
}
|
||||
},
|
||||
"welcome": "Bienvenue sur Checkmate !"
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "Mot de passe oublié ?",
|
||||
@@ -851,6 +852,10 @@
|
||||
"selectDisabled": "Désactivé",
|
||||
"selectEnabled": "Activé",
|
||||
"title": "IP/URL du moniteur sur la page de statut"
|
||||
},
|
||||
"globalThresholds": {
|
||||
"title": "Seuils globaux",
|
||||
"description": "Configurez des seuils globaux pour le CPU, la mémoire, le disque et la température. Si une valeur est fournie, elle sera automatiquement activée pour la surveillance."
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
@@ -876,9 +881,13 @@
|
||||
"generalSettings": "Paramètres généraux",
|
||||
"contents": "Contenus",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Surveillez et affichez l’état de santé de vos services en temps réel",
|
||||
"Suivez plusieurs services et partagez leur statut",
|
||||
"Tenez les utilisateurs informés des pannes et des performances"
|
||||
],
|
||||
"title": "Une page de status est utilisée pour :",
|
||||
"actionButton": "Créer votre première page de status !"
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "Il n'y aucune notification paramétrée pour ce moniteur. Vous pouvez en ajouter une en cliquant sur le bouton 'Configurer'",
|
||||
@@ -919,57 +928,149 @@
|
||||
"twentyMinutes": "20 minutes",
|
||||
"oneHour": "1 heure",
|
||||
"oneDay": "1 jour",
|
||||
"oneWeek": "1 semaine"
|
||||
"oneWeek": "1 semaine",
|
||||
"fourMinutes": "4 minutes",
|
||||
"oneMinute": "1 minute",
|
||||
"twoMinutes": "2 minutes",
|
||||
"fifteenSeconds": "15 secondes",
|
||||
"thirtySeconds": "30 secondes"
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
"noOptionsFound": "Aucune {{unit}} trouvée"
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Suivez les performances de vos serveurs",
|
||||
"Identifiez les points de blocage et optimisez l’utilisation",
|
||||
"Assurez la fiabilité grâce à la surveillance en temps réel"
|
||||
],
|
||||
"title": "Une surveillance d'infrastructure est utilisé pour :",
|
||||
"actionButton": "Créer votre premier moniteur d'infrastructure !"
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Marquez vos périodes de maintenance",
|
||||
"Éliminez tout malentendu",
|
||||
"Arrêtez d’envoyer des alertes pendant les fenêtres de maintenance"
|
||||
],
|
||||
"title": "Une fenêtre de maintenance est utilisé pour :",
|
||||
"actionButton": "Créer votre première fenêtre de maintenance !"
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
"Rapport sur l’expérience utilisateur d’une page",
|
||||
"Aider à analyser la vitesse de la page web",
|
||||
"Donner des suggestions sur la façon dont la page peut être améliorée"
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": [
|
||||
"Vérifiez si les sites web ou serveurs sont en ligne et réactifs",
|
||||
"Alertez les équipes en cas de panne ou de problème de performance",
|
||||
"Surveillez les points de terminaison HTTP, les pings, les conteneurs et les ports",
|
||||
"Suivez l’historique de la disponibilité et les tendances de fiabilité"
|
||||
],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
"email": "Email",
|
||||
"firstName": "Prénom",
|
||||
"lastName": "Nom",
|
||||
"role": "Rôles",
|
||||
"save": "Enregistrer"
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
"actionHeader": "Action",
|
||||
"roleHeader": "Rôle"
|
||||
},
|
||||
"title": "",
|
||||
"title": "Modifier l'utilisateur",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"successUserUpdate": "L'utilisateur à bien été modifié",
|
||||
"validationErrors": ""
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
"incidentsPageActionResolveMonitor": "Résoudres les incidents de surveillance",
|
||||
"incidentsPageActionResolveAll": "Résoudre tous les incidents",
|
||||
"matchMethodOptions": {
|
||||
"equal": "Égal",
|
||||
"equalPlaceholder": "",
|
||||
"include": "Inclure",
|
||||
"includePlaceholder": "ok",
|
||||
"regex": "Regex",
|
||||
"regexPlaceholder": "",
|
||||
"text": ""
|
||||
},
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "ID du conteneur",
|
||||
"namePlaceholder": "Mon conteneur",
|
||||
"placeholder": "abcd1234"
|
||||
},
|
||||
"http": {
|
||||
"label": "URL à surveiller",
|
||||
"namePlaceholder": "Google",
|
||||
"placeholder": "google.com"
|
||||
},
|
||||
"ping": {
|
||||
"label": "Adresse IP à surveiller",
|
||||
"namePlaceholder": "Google",
|
||||
"placeholder": "1.1.1.1"
|
||||
},
|
||||
"port": {
|
||||
"label": "URL à surveiller",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": "localhost"
|
||||
},
|
||||
"game": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
}
|
||||
},
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": "Chemin JSON"
|
||||
},
|
||||
"bytesPerSecond": "",
|
||||
"bytesReceived": "",
|
||||
"bytesSent": "",
|
||||
"chooseGame": "",
|
||||
"createMonitorPage": {
|
||||
"incidentConfigDescription": "",
|
||||
"incidentConfigStatusWindowLabel": "",
|
||||
"incidentConfigStatusWindowThresholdLabel": "",
|
||||
"incidentConfigTitle": ""
|
||||
},
|
||||
"dataRate": "",
|
||||
"dataReceived": "",
|
||||
"dataSent": "",
|
||||
"details": "",
|
||||
"drops": "",
|
||||
"errors": "",
|
||||
"errorsIn": "",
|
||||
"errorsOut": "",
|
||||
"gameServerMonitoring": "",
|
||||
"gameServerMonitoringDescription": "",
|
||||
"network": "",
|
||||
"networkDrops": "",
|
||||
"networkErrors": "",
|
||||
"networkInterface": "",
|
||||
"noNetworkStatsAvailable": "",
|
||||
"packetsPerSecond": "",
|
||||
"packetsReceived": "",
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
}
|
||||
@@ -264,7 +264,6 @@
|
||||
"notifyEmails": "複数のアドレスにメールでも通知(近日公開)",
|
||||
"seperateEmails": "複数のメールはカンマで区切ることができます",
|
||||
"checkFrequency": "チェック頻度",
|
||||
"chooseGame": "",
|
||||
"matchMethod": "マッチ方法",
|
||||
"expectedValue": "期待値",
|
||||
"deleteDialogTitle": "本当にこのモニターを削除しますか?",
|
||||
@@ -573,7 +572,8 @@
|
||||
"password": {
|
||||
"incorrect": "入力されたパスワードが記録と一致しません"
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
@@ -599,7 +599,8 @@
|
||||
},
|
||||
"toasts": {
|
||||
"success": "ようこそ!アカウントが正常に作成されました。"
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "パスワードを忘れましたか?",
|
||||
@@ -851,6 +852,10 @@
|
||||
"selectDisabled": "無効",
|
||||
"selectEnabled": "有効",
|
||||
"title": "ステータスページのモニターIP/URL"
|
||||
},
|
||||
"globalThresholds": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
@@ -919,7 +924,12 @@
|
||||
"twentyMinutes": "20分",
|
||||
"oneHour": "1時間",
|
||||
"oneDay": "1日",
|
||||
"oneWeek": "1週間"
|
||||
"oneWeek": "1週間",
|
||||
"fourMinutes": "",
|
||||
"oneMinute": "",
|
||||
"twoMinutes": "",
|
||||
"fifteenSeconds": "",
|
||||
"thirtySeconds": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
@@ -971,5 +981,75 @@
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
"incidentsPageActionResolveAll": "",
|
||||
"matchMethodOptions": {
|
||||
"equal": "",
|
||||
"equalPlaceholder": "",
|
||||
"include": "",
|
||||
"includePlaceholder": "",
|
||||
"regex": "",
|
||||
"regexPlaceholder": "",
|
||||
"text": ""
|
||||
},
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"http": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"ping": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"port": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"game": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
}
|
||||
},
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": ""
|
||||
},
|
||||
"bytesPerSecond": "",
|
||||
"bytesReceived": "",
|
||||
"bytesSent": "",
|
||||
"chooseGame": "",
|
||||
"createMonitorPage": {
|
||||
"incidentConfigDescription": "",
|
||||
"incidentConfigStatusWindowLabel": "",
|
||||
"incidentConfigStatusWindowThresholdLabel": "",
|
||||
"incidentConfigTitle": ""
|
||||
},
|
||||
"dataRate": "",
|
||||
"dataReceived": "",
|
||||
"dataSent": "",
|
||||
"details": "",
|
||||
"drops": "",
|
||||
"errors": "",
|
||||
"errorsIn": "",
|
||||
"errorsOut": "",
|
||||
"gameServerMonitoring": "",
|
||||
"gameServerMonitoringDescription": "",
|
||||
"network": "",
|
||||
"networkDrops": "",
|
||||
"networkErrors": "",
|
||||
"networkInterface": "",
|
||||
"noNetworkStatsAvailable": "",
|
||||
"packetsPerSecond": "",
|
||||
"packetsReceived": "",
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
}
|
||||
+400
-295
@@ -84,31 +84,35 @@
|
||||
"unsupportedType": "Tipo de notificação não suportado",
|
||||
"networkError": "Ocorreu um erro de rede",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"actionButton": ""
|
||||
"title": "Um canal de notificação é usado para:",
|
||||
"checks": [
|
||||
"Alerte as equipes sobre tempo de inatividade ou problemas de desempenho",
|
||||
"Informe os engenheiros sobre incidentes",
|
||||
"Mantenha os administradores informados sobre as mudanças no sistema"
|
||||
],
|
||||
"actionButton": "Vamos criar seu primeiro canal de notificação!"
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
"createButton": "Criar canal de notificação",
|
||||
"createTitle": "Canal de notificação",
|
||||
"create": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Notificação criada com sucesso",
|
||||
"failed": "Falha ao criar notificação"
|
||||
},
|
||||
"fetch": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Notificações obtidas com sucesso",
|
||||
"failed": "Falha ao buscar notificações"
|
||||
},
|
||||
"delete": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Notificação excluída com sucesso",
|
||||
"failed": "Falha ao excluir a notificação"
|
||||
},
|
||||
"edit": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Notificação atualizada com sucesso",
|
||||
"failed": "Falha ao atualizar a notificação"
|
||||
},
|
||||
"test": {
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"success": "Notificação de teste enviada com sucesso",
|
||||
"failed": "Falha ao enviar notificação de teste"
|
||||
}
|
||||
},
|
||||
"testLocale": "testLocale",
|
||||
@@ -260,7 +264,6 @@
|
||||
"notifyEmails": "Também notificar por e-mail para vários endereços (em breve)",
|
||||
"seperateEmails": "Você pode separar vários e-mails com uma vírgula",
|
||||
"checkFrequency": "Verifique a frequência",
|
||||
"chooseGame": "",
|
||||
"matchMethod": "Método de correspondência",
|
||||
"expectedValue": "Valor esperado",
|
||||
"deleteDialogTitle": "Você realmente deseja excluir este monitor?",
|
||||
@@ -333,8 +336,8 @@
|
||||
"validationFailed": "Falha na validação",
|
||||
"noFileSelected": "Nenhum arquivo selecionado",
|
||||
"fallbackPage": "Importe um arquivo para enviar uma lista de servidores em massa",
|
||||
"invalidFileType": "",
|
||||
"uploadFailed": ""
|
||||
"invalidFileType": "Tipo de arquivo inválido",
|
||||
"uploadFailed": "Falha no upload"
|
||||
},
|
||||
"DeleteAccountTitle": "Remover conta",
|
||||
"DeleteAccountButton": "Remover conta",
|
||||
@@ -401,8 +404,8 @@
|
||||
"password": "Senha",
|
||||
"team": "Equipe",
|
||||
"logOut": "Sair",
|
||||
"notifications": "",
|
||||
"logs": ""
|
||||
"notifications": "Notificações",
|
||||
"logs": "Registros"
|
||||
},
|
||||
"settingsEmailUser": "Usuário de e-mail - Nome de usuário para autenticação, substitui o endereço de e-mail, se especificado",
|
||||
"state": "Estado",
|
||||
@@ -477,41 +480,41 @@
|
||||
},
|
||||
"special": {
|
||||
"beginning": "Deve conter pelo menos",
|
||||
"highlighted": ""
|
||||
"highlighted": "um caractere especial"
|
||||
},
|
||||
"number": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
"beginning": "Deve ter pelo menos",
|
||||
"highlighted": "um número"
|
||||
},
|
||||
"uppercase": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
"beginning": "Deve ter pelo menos",
|
||||
"highlighted": "uma letra maiúscula"
|
||||
},
|
||||
"lowercase": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
"beginning": "Deve ter pelo menos",
|
||||
"highlighted": "uma letra minúscula"
|
||||
},
|
||||
"match": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
"beginning": "Senha e confirmação de senha",
|
||||
"highlighted": "devem combinar"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"empty": "Por favor, digite sua senha",
|
||||
"length": "A senha deve ter pelo menos 8 caracteres",
|
||||
"uppercase": "A senha deve conter pelo menos 1 letra maiúscula",
|
||||
"lowercase": "",
|
||||
"number": "",
|
||||
"special": "",
|
||||
"incorrect": ""
|
||||
"lowercase": "Senha deve ter pelo menos uma letra minúscula",
|
||||
"number": "Senha deve ter pelo menos um número",
|
||||
"special": "Senha deve ter pelo menos um caractere especial",
|
||||
"incorrect": "A senha que você forneceu não está registrada"
|
||||
}
|
||||
},
|
||||
"passwordConfirm": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"label": "Confirme a senha",
|
||||
"placeholder": "Informe a senha novamente para confirmar",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"different": ""
|
||||
"empty": "Por favor, informe a senha novamente para confirmação (cuidado com erros de digitação)",
|
||||
"different": "As senhas informadas não combinam, então uma deve estar errada"
|
||||
}
|
||||
},
|
||||
"firstName": {
|
||||
@@ -539,12 +542,12 @@
|
||||
"fields": {
|
||||
"password": {
|
||||
"errors": {
|
||||
"incorrect": ""
|
||||
"incorrect": "A senha que você forneceu não corresponde aos nossos registros"
|
||||
}
|
||||
},
|
||||
"role": {
|
||||
"errors": {
|
||||
"min": ""
|
||||
"min": "Pelo menos uma função é necessária"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -558,8 +561,8 @@
|
||||
"links": {
|
||||
"forgotPassword": "Esqueceu sua senha?",
|
||||
"register": "Não tem uma conta?",
|
||||
"forgotPasswordLink": "",
|
||||
"registerLink": ""
|
||||
"forgotPasswordLink": "Redefinir senha",
|
||||
"registerLink": "Registre-se aqui"
|
||||
},
|
||||
"toasts": {
|
||||
"success": "Bem-vindo de volta! Você está autenticado.",
|
||||
@@ -567,9 +570,10 @@
|
||||
},
|
||||
"errors": {
|
||||
"password": {
|
||||
"incorrect": ""
|
||||
"incorrect": "A senha que você forneceu não corresponde aos nossos registros"
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
@@ -577,42 +581,43 @@
|
||||
"user": "Se inscrever"
|
||||
},
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": "",
|
||||
"stepThree": ""
|
||||
"stepOne": "Informe seus dados pessoais",
|
||||
"stepTwo": "Informe seu email",
|
||||
"stepThree": "Crie sua senha"
|
||||
},
|
||||
"description": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
"superAdmin": "Crie sua conta de superadministrador para começar",
|
||||
"user": "Cadastre-se como usuário e peça ao superadministrador acesso aos seus monitores"
|
||||
},
|
||||
"gettingStartedButton": {
|
||||
"superAdmin": "",
|
||||
"user": ""
|
||||
"superAdmin": "Criar conta de superadministrador",
|
||||
"user": "Cadastre-se como usuário normal"
|
||||
},
|
||||
"termsAndPolicies": "",
|
||||
"termsAndPolicies": "Ao criar uma conta, você concorda com nossos <a1>Termos de Serviço</a1> e <a2>Política de Privacidade</a2>.",
|
||||
"links": {
|
||||
"login": ""
|
||||
"login": "Já tem uma conta? <a>Entrar</a>"
|
||||
},
|
||||
"toasts": {
|
||||
"success": ""
|
||||
}
|
||||
"success": "Bem-vindo! Sua conta foi criada com sucesso."
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "",
|
||||
"heading": "Esqueceu sua senha?",
|
||||
"subheadings": {
|
||||
"stepOne": "",
|
||||
"stepTwo": "",
|
||||
"stepThree": "",
|
||||
"stepFour": ""
|
||||
"stepOne": "Não se preocupe, enviaremos instruções de redefinição.",
|
||||
"stepTwo": "Enviamos um link de redefinição de senha para <email/>",
|
||||
"stepThree": "Sua nova senha deve ser diferente das senhas usadas anteriormente.",
|
||||
"stepFour": "Sua senha foi redefinida com sucesso. Clique abaixo para fazer login magicamente."
|
||||
},
|
||||
"buttons": {
|
||||
"openEmail": "",
|
||||
"resetPassword": ""
|
||||
"openEmail": "Abra o aplicativo de e-mail",
|
||||
"resetPassword": "Redefinir senha"
|
||||
},
|
||||
"imageAlts": {
|
||||
"passwordKey": "",
|
||||
"email": "",
|
||||
"lock": "",
|
||||
"passwordKey": "Ícone de chave de senha",
|
||||
"email": "Ícone de e-mail",
|
||||
"lock": "Ícone de cadeado",
|
||||
"passwordConfirm": "Ícone de confirmação de senha"
|
||||
},
|
||||
"links": {
|
||||
@@ -620,352 +625,452 @@
|
||||
"resend": "Não recebeu o email? <a>Clique para reenviar</a>"
|
||||
},
|
||||
"toasts": {
|
||||
"sent": "",
|
||||
"emailNotFound": "",
|
||||
"redirect": "",
|
||||
"success": "",
|
||||
"error": ""
|
||||
"sent": "Instruções enviadas para <email/>.",
|
||||
"emailNotFound": "E-mail não encontrado.",
|
||||
"redirect": "Redirecionando em <segundos/>...",
|
||||
"success": "Sua senha foi redefinida com sucesso.",
|
||||
"error": "Não foi possível redefinir a senha. Tente novamente mais tarde ou entre em contato com o suporte."
|
||||
}
|
||||
}
|
||||
},
|
||||
"errorPages": {
|
||||
"serverUnreachable": {
|
||||
"toasts": {
|
||||
"reconnected": "",
|
||||
"stillUnreachable": ""
|
||||
"reconnected": "Reconectado ao servidor com sucesso.",
|
||||
"stillUnreachable": "O servidor ainda está inacessível. Tente novamente mais tarde."
|
||||
},
|
||||
"alertBox": "",
|
||||
"description": "",
|
||||
"alertBox": "Erro de conexão do servidor",
|
||||
"description": "Não conseguimos conectar ao servidor. Verifique sua conexão com a internet ou sua configuração de implantação se o problema persistir.",
|
||||
"retryButton": {
|
||||
"default": "",
|
||||
"processing": ""
|
||||
"default": "Tentar conectar novamente",
|
||||
"processing": "Conectando..."
|
||||
}
|
||||
}
|
||||
},
|
||||
"createNotifications": {
|
||||
"title": "",
|
||||
"title": "Criar canal de notificação",
|
||||
"nameSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"nameLabel": "",
|
||||
"namePlaceholder": ""
|
||||
"title": "Nome",
|
||||
"description": "Um nome descritivo para sua integração.",
|
||||
"nameLabel": "Nome",
|
||||
"namePlaceholder": "por exemplo, notificações do Slack"
|
||||
},
|
||||
"typeSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"typeLabel": ""
|
||||
"title": "Tipo",
|
||||
"description": "Selecione o tipo de canal de notificação que você deseja criar.",
|
||||
"typeLabel": "Tipo"
|
||||
},
|
||||
"emailSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"emailLabel": "",
|
||||
"emailPlaceholder": ""
|
||||
"title": "E-mail",
|
||||
"description": "Endereços de e-mail de destino.",
|
||||
"emailLabel": "Endereço de email",
|
||||
"emailPlaceholder": "por exemplo john@example.com"
|
||||
},
|
||||
"slackSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
"title": "Folga",
|
||||
"description": "Configure seu webhook do Slack aqui",
|
||||
"webhookLabel": "URL do webhook do Slack",
|
||||
"webhookPlaceholder": "https://hooks.slack.com/services/..."
|
||||
},
|
||||
"pagerdutySettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"integrationKeyLabel": "",
|
||||
"integrationKeyPlaceholder": ""
|
||||
"title": "PagerDuty",
|
||||
"description": "Configure sua integração com o PagerDuty aqui",
|
||||
"integrationKeyLabel": "Chave de integração",
|
||||
"integrationKeyPlaceholder": "1234567890"
|
||||
},
|
||||
"discordSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
"title": "Discórdia",
|
||||
"description": "Configure seu webhook do Discord aqui",
|
||||
"webhookLabel": "URL do webhook do Discord",
|
||||
"webhookPlaceholder": "https://seu-servidor.com/webhook"
|
||||
},
|
||||
"webhookSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
"title": "Webhook",
|
||||
"description": "Configure seu webhook aqui",
|
||||
"webhookLabel": "URL do webhook",
|
||||
"webhookPlaceholder": "https://seu-servidor.com/webhook"
|
||||
},
|
||||
"testNotification": "",
|
||||
"dialogDeleteTitle": "",
|
||||
"dialogDeleteConfirm": ""
|
||||
"testNotification": "Notificação de teste",
|
||||
"dialogDeleteTitle": "Tem certeza de que deseja excluir esta notificação?",
|
||||
"dialogDeleteConfirm": "Excluir"
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
"title": "Notificações",
|
||||
"description": "Selecione os canais de notificação que deseja usar"
|
||||
},
|
||||
"monitorStatus": {
|
||||
"checkingEvery": "",
|
||||
"withCaptureAgent": "",
|
||||
"up": "",
|
||||
"down": "",
|
||||
"paused": ""
|
||||
"checkingEvery": "Verificando a cada {{intervalo}}",
|
||||
"withCaptureAgent": "com agente de captura {{versão}}",
|
||||
"up": "acima",
|
||||
"down": "abaixo",
|
||||
"paused": "pausado"
|
||||
},
|
||||
"advancedMatching": "",
|
||||
"sendTestNotifications": "",
|
||||
"selectAll": "",
|
||||
"showAdminLoginLink": "",
|
||||
"advancedMatching": "Correspondência avançada",
|
||||
"sendTestNotifications": "Enviar notificações de teste",
|
||||
"selectAll": "Selecionar tudo",
|
||||
"showAdminLoginLink": "Mostrar o link \"Administrador? Efetue login aqui\" na página de status",
|
||||
"logsPage": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "Registros",
|
||||
"description": "Logs do sistema - últimas 1000 linhas",
|
||||
"tabs": {
|
||||
"queue": "",
|
||||
"logs": "",
|
||||
"diagnostics": ""
|
||||
"queue": "Fila de tarefas",
|
||||
"logs": "Logs do servidor",
|
||||
"diagnostics": "Diagnóstico"
|
||||
},
|
||||
"toast": {
|
||||
"fetchLogsSuccess": ""
|
||||
"fetchLogsSuccess": "Logs obtidos com sucesso"
|
||||
},
|
||||
"logLevelSelect": {
|
||||
"title": "",
|
||||
"title": "Nível de log",
|
||||
"values": {
|
||||
"all": "",
|
||||
"info": "",
|
||||
"warn": "",
|
||||
"error": "",
|
||||
"debug": ""
|
||||
"all": "Todos",
|
||||
"info": "Informações",
|
||||
"warn": "Avisar",
|
||||
"error": "Erro",
|
||||
"debug": "Depurar"
|
||||
}
|
||||
}
|
||||
},
|
||||
"queuePage": {
|
||||
"title": "",
|
||||
"refreshButton": "",
|
||||
"flushButton": "",
|
||||
"title": "Fila",
|
||||
"refreshButton": "Atualizar",
|
||||
"flushButton": "Fila de descarga",
|
||||
"jobTable": {
|
||||
"title": "",
|
||||
"idHeader": "",
|
||||
"urlHeader": "",
|
||||
"typeHeader": "",
|
||||
"activeHeader": "",
|
||||
"lockedAtHeader": "",
|
||||
"runCountHeader": "",
|
||||
"failCountHeader": "",
|
||||
"lastRunHeader": "",
|
||||
"lastFinishedAtHeader": "",
|
||||
"lastRunTookHeader": ""
|
||||
"title": "Empregos atualmente na fila",
|
||||
"idHeader": "ID do monitor",
|
||||
"urlHeader": "URL",
|
||||
"typeHeader": "Tipo",
|
||||
"activeHeader": "Ativo",
|
||||
"lockedAtHeader": "Trancado em",
|
||||
"runCountHeader": "Contagem de corridas",
|
||||
"failCountHeader": "Contagem de falhas",
|
||||
"lastRunHeader": "Última corrida em",
|
||||
"lastFinishedAtHeader": "Última conclusão em",
|
||||
"lastRunTookHeader": "A última corrida aconteceu"
|
||||
},
|
||||
"metricsTable": {
|
||||
"title": "",
|
||||
"metricHeader": "",
|
||||
"valueHeader": ""
|
||||
"title": "Métricas de fila",
|
||||
"metricHeader": "Métrica",
|
||||
"valueHeader": "Valor"
|
||||
},
|
||||
"failedJobTable": {
|
||||
"title": "",
|
||||
"monitorIdHeader": "",
|
||||
"monitorUrlHeader": "",
|
||||
"failCountHeader": "",
|
||||
"failedAtHeader": "",
|
||||
"failReasonHeader": ""
|
||||
"title": "Trabalhos com falha",
|
||||
"monitorIdHeader": "ID do monitor",
|
||||
"monitorUrlHeader": "URL do monitor",
|
||||
"failCountHeader": "Contagem de falhas",
|
||||
"failedAtHeader": "Última falha em",
|
||||
"failReasonHeader": "Motivo da falha"
|
||||
}
|
||||
},
|
||||
"export": {
|
||||
"title": "",
|
||||
"success": "",
|
||||
"failed": ""
|
||||
"title": "Monitores de Exportação",
|
||||
"success": "Monitores exportados com sucesso!",
|
||||
"failed": "Falha ao exportar monitores"
|
||||
},
|
||||
"monitorActions": {
|
||||
"title": "",
|
||||
"import": "",
|
||||
"export": "",
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"details": ""
|
||||
"title": "Exportação/Importação",
|
||||
"import": "Monitores de importação",
|
||||
"export": "Monitores de Exportação",
|
||||
"deleteSuccess": "Monitor excluído com sucesso",
|
||||
"deleteFailed": "Falha ao excluir o monitor",
|
||||
"details": "Detalhes"
|
||||
},
|
||||
"settingsPage": {
|
||||
"aboutSettings": {
|
||||
"labelDevelopedBy": "",
|
||||
"labelVersion": "",
|
||||
"title": ""
|
||||
"labelDevelopedBy": "Desenvolvido pela Bluewave Labs",
|
||||
"labelVersion": "Versão",
|
||||
"title": "Sobre"
|
||||
},
|
||||
"demoMonitorsSettings": {
|
||||
"buttonAddMonitors": "",
|
||||
"description": "",
|
||||
"title": ""
|
||||
"buttonAddMonitors": "Adicionar monitores de demonstração",
|
||||
"description": "Adicione monitores de amostra para fins de demonstração.",
|
||||
"title": "Monitores de demonstração"
|
||||
},
|
||||
"emailSettings": {
|
||||
"buttonSendTestEmail": "",
|
||||
"description": "",
|
||||
"descriptionTransport": "",
|
||||
"labelAddress": "",
|
||||
"labelConnectionHost": "",
|
||||
"labelHost": "",
|
||||
"labelIgnoreTLS": "",
|
||||
"labelPassword": "",
|
||||
"labelPasswordSet": "",
|
||||
"labelPool": "",
|
||||
"labelPort": "",
|
||||
"labelRejectUnauthorized": "",
|
||||
"labelRequireTLS": "",
|
||||
"labelSecure": "",
|
||||
"labelTLSServername": "",
|
||||
"labelUser": "",
|
||||
"linkTransport": "",
|
||||
"placeholderUser": "",
|
||||
"title": "",
|
||||
"toastEmailRequiredFieldsError": ""
|
||||
"buttonSendTestEmail": "Enviar e-mail de teste",
|
||||
"description": "Configure as configurações de e-mail do seu sistema. Elas são usadas para enviar notificações e alertas.",
|
||||
"descriptionTransport": "Isso cria um transporte SMTP para o NodeMailer",
|
||||
"labelAddress": "Endereço de e-mail - Usado para autenticação",
|
||||
"labelConnectionHost": "Host de conexão de e-mail - Nome do host a ser usado na saudação HELO/EHLO",
|
||||
"labelHost": "Host de e-mail - Nome do host ou endereço IP para conectar",
|
||||
"labelIgnoreTLS": "Desabilitar STARTTLS: Não use TLS mesmo que o servidor suporte",
|
||||
"labelPassword": "Senha de e-mail - Senha para autenticação",
|
||||
"labelPasswordSet": "A senha foi definida. Clique em Redefinir para alterá-la.",
|
||||
"labelPool": "Habilitar pool de conexões: Reutilize conexões existentes para melhorar o desempenho",
|
||||
"labelPort": "Porta de e-mail - Porta para conectar",
|
||||
"labelRejectUnauthorized": "Rejeitar certificados inválidos: rejeitar conexões com certificados autoassinados ou não confiáveis",
|
||||
"labelRequireTLS": "Forçar STARTTLS: requer atualização de TLS, falha se não for suportado",
|
||||
"labelSecure": "Usar SSL (recomendado): criptografar a conexão usando SSL/TLS",
|
||||
"labelTLSServername": "Nome do servidor TLS - Nome do host opcional para validação TLS quando o host é um IP",
|
||||
"labelUser": "Usuário de e-mail - Nome de usuário para autenticação, substitui o endereço de e-mail, se especificado",
|
||||
"linkTransport": "Veja as especificações aqui",
|
||||
"placeholderUser": "Deixe em branco se não for necessário",
|
||||
"title": "E-mail",
|
||||
"toastEmailRequiredFieldsError": "Endereço de e-mail, host, porta e senha são obrigatórios"
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "",
|
||||
"labelApiKeySet": "",
|
||||
"labelApiKey": "",
|
||||
"title": ""
|
||||
"description": "Insira sua chave da API do Google PageSpeed para ativar o monitoramento do Google PageSpeed. Clique em Redefinir para atualizar a chave.",
|
||||
"labelApiKeySet": "A chave de API está definida. Clique em Redefinir para alterá-la.",
|
||||
"labelApiKey": "Chave da API do PageSpeed",
|
||||
"title": "Chave da API do Google PageSpeed"
|
||||
},
|
||||
"saveButtonLabel": "",
|
||||
"saveButtonLabel": "Salvar",
|
||||
"statsSettings": {
|
||||
"clearAllStatsButton": "",
|
||||
"clearAllStatsDescription": "",
|
||||
"clearAllStatsDialogConfirm": "",
|
||||
"clearAllStatsDialogDescription": "",
|
||||
"clearAllStatsDialogTitle": "",
|
||||
"description": "",
|
||||
"labelTTL": "",
|
||||
"labelTTLOptional": "",
|
||||
"title": ""
|
||||
"clearAllStatsButton": "Limpar todas as estatísticas",
|
||||
"clearAllStatsDescription": "Limpe todas as estatísticas. Isso é irreversível.",
|
||||
"clearAllStatsDialogConfirm": "Sim, limpar todas as estatísticas",
|
||||
"clearAllStatsDialogDescription": "Uma vez removido, o histórico de monitoramento e as estatísticas não podem ser recuperados.",
|
||||
"clearAllStatsDialogTitle": "Você quer limpar todas as estatísticas?",
|
||||
"description": "Defina por quanto tempo você deseja manter os dados históricos. Você também pode limpar todos os dados existentes.",
|
||||
"labelTTL": "Os dias em que você deseja continuar monitorando o histórico.",
|
||||
"labelTTLOptional": "0 para infinito",
|
||||
"title": "Histórico do monitor"
|
||||
},
|
||||
"systemResetSettings": {
|
||||
"buttonRemoveAllMonitors": "",
|
||||
"description": "",
|
||||
"dialogConfirm": "",
|
||||
"dialogDescription": "",
|
||||
"dialogTitle": "",
|
||||
"title": ""
|
||||
"buttonRemoveAllMonitors": "Remover todos os monitores",
|
||||
"description": "Remova todos os monitores do seu sistema.",
|
||||
"dialogConfirm": "Sim, remova todos os monitores",
|
||||
"dialogDescription": "Uma vez removidos, os monitores não podem ser recuperados.",
|
||||
"dialogTitle": "Você deseja remover todos os monitores?",
|
||||
"title": "Reinicialização do sistema"
|
||||
},
|
||||
"timezoneSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"title": ""
|
||||
"description": "Selecione o fuso horário usado para exibir datas e horas em todo o aplicativo.",
|
||||
"label": "Exibir fuso horário",
|
||||
"title": "Exibir fuso horário"
|
||||
},
|
||||
"title": "",
|
||||
"title": "Configurações",
|
||||
"uiSettings": {
|
||||
"description": "",
|
||||
"labelLanguage": "",
|
||||
"labelTheme": "",
|
||||
"title": ""
|
||||
"description": "Alterne entre o modo claro e escuro ou altere o idioma da interface do usuário.",
|
||||
"labelLanguage": "Linguagem",
|
||||
"labelTheme": "Modo de tema",
|
||||
"title": "Aparência"
|
||||
},
|
||||
"urlSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
"description": "Exiba o endereço IP ou URL do monitor na página de status pública. Se estiver desabilitado, apenas o nome do monitor será exibido para proteger informações confidenciais.",
|
||||
"label": "Exibir IP/URL na página de status",
|
||||
"selectDisabled": "Desabilitado",
|
||||
"selectEnabled": "Habilitado",
|
||||
"title": "Monitorar IP/URL na página de status"
|
||||
},
|
||||
"globalThresholds": {
|
||||
"title": "Limites Globais",
|
||||
"description": "Configurar limites globais da CPU, Memória, Disco e Temperatura. Se um valor for fornecido, o monitoramento será ativado automaticamente"
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": ""
|
||||
"buttonSave": "Salvar"
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "",
|
||||
"settingsSave": "",
|
||||
"statusPageCreateAppearanceTitle": "",
|
||||
"confirmPassword": "",
|
||||
"incidentsOptionsHeaderFilterResolved": "Resolvido",
|
||||
"settingsSave": "Salvar",
|
||||
"statusPageCreateAppearanceTitle": "Aparência",
|
||||
"confirmPassword": "Confirme sua senha",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "",
|
||||
"successAddDemoMonitors": ""
|
||||
"failureAddDemoMonitors": "Falha ao adicionar monitores de demonstração",
|
||||
"successAddDemoMonitors": "Monitores de demonstração adicionados com sucesso"
|
||||
},
|
||||
"settingsAppearance": "",
|
||||
"settingsDisplayTimezone": "",
|
||||
"settingsGeneralSettings": "",
|
||||
"incidentsOptionsHeaderTotalIncidents": "",
|
||||
"settingsAppearance": "Aparência",
|
||||
"settingsDisplayTimezone": "Exibir fuso horário",
|
||||
"settingsGeneralSettings": "Configurações gerais",
|
||||
"incidentsOptionsHeaderTotalIncidents": "Total de incidentes",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"createSuccess": "",
|
||||
"updateSuccess": "",
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"deleteSuccess": "Página de status excluída com sucesso",
|
||||
"deleteFailed": "Falha ao excluir a página de status",
|
||||
"createSuccess": "Página de status criada com sucesso",
|
||||
"updateSuccess": "Página de status atualizada com sucesso",
|
||||
"generalSettings": "Configurações gerais",
|
||||
"contents": "Conteúdo",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Monitore e exiba a integridade dos seus serviços em tempo real",
|
||||
"Acompanhe vários serviços e compartilhe seus status",
|
||||
"Mantenha os usuários informados sobre interrupções e desempenho"
|
||||
],
|
||||
"title": "Uma página de status é usada para:",
|
||||
"actionButton": "Vamos criar sua primeira página de status!"
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "",
|
||||
"incidentsTableResolvedAt": "",
|
||||
"incidentsTableActionResolve": "",
|
||||
"testNotificationsDisabled": "Não há notificações configuradas para este monitor. Você precisa adicionar uma clicando no botão \"Configurar\".",
|
||||
"incidentsTableResolvedAt": "Resolvido em",
|
||||
"incidentsTableActionResolve": "Resolver",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "",
|
||||
"failureResolveAll": "",
|
||||
"failureResolveMonitor": ""
|
||||
"failureResolveOne": "Falha ao resolver o incidente.",
|
||||
"failureResolveAll": "Falha ao resolver todos os incidentes.",
|
||||
"failureResolveMonitor": "Falha ao resolver incidentes do monitor."
|
||||
},
|
||||
"checkFormError": "",
|
||||
"checkFormError": "Verifique se há erros no formulário.",
|
||||
"diagnosticsPage": {
|
||||
"diagnosticDescription": "",
|
||||
"statsDescription": "",
|
||||
"diagnosticDescription": "Diagnóstico do sistema",
|
||||
"statsDescription": "Estatísticas do sistema",
|
||||
"gauges": {
|
||||
"heapAllocationTitle": "",
|
||||
"heapAllocationSubtitle": "",
|
||||
"heapUsageTitle": "",
|
||||
"heapUsageSubtitle": "",
|
||||
"heapUtilizationTitle": "",
|
||||
"heapUtilizationSubtitle": "",
|
||||
"instantCpuUsageTitle": "",
|
||||
"instantCpuUsageSubtitle": ""
|
||||
"heapAllocationTitle": "Alocação de heap",
|
||||
"heapAllocationSubtitle": "% de memória disponível",
|
||||
"heapUsageTitle": "Uso de heap",
|
||||
"heapUsageSubtitle": "% de memória disponível",
|
||||
"heapUtilizationTitle": "Utilização de heap",
|
||||
"heapUtilizationSubtitle": "% do alocado",
|
||||
"instantCpuUsageTitle": "Uso instantâneo da CPU",
|
||||
"instantCpuUsageSubtitle": "% de 1s usados pela CPU"
|
||||
},
|
||||
"stats": {
|
||||
"eventLoopDelayTitle": "",
|
||||
"uptimeTitle": "",
|
||||
"usedHeapSizeTitle": "",
|
||||
"totalHeapSizeTitle": "",
|
||||
"osMemoryLimitTitle": ""
|
||||
"eventLoopDelayTitle": "Atraso do loop de eventos",
|
||||
"uptimeTitle": "Tempo de atividade",
|
||||
"usedHeapSizeTitle": "Tamanho de heap usado",
|
||||
"totalHeapSizeTitle": "Tamanho total do heap",
|
||||
"osMemoryLimitTitle": "Limite de memória do sistema operacional"
|
||||
}
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "",
|
||||
"pageSpeedLighthouseAPI": "Use a API Lighthouse PageSpeed para monitorar seu site",
|
||||
"time": {
|
||||
"threeMinutes": "",
|
||||
"fiveMinutes": "",
|
||||
"tenMinutes": "",
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
"threeMinutes": "3 minutos",
|
||||
"fiveMinutes": "5 minutos",
|
||||
"tenMinutes": "10 minutos",
|
||||
"twentyMinutes": "20 minutos",
|
||||
"oneHour": "1 hora",
|
||||
"oneDay": "1 dia",
|
||||
"oneWeek": "1 semana",
|
||||
"fourMinutes": "4 minutos",
|
||||
"oneMinute": "1 minuto",
|
||||
"twoMinutes": "2 minutos",
|
||||
"fifteenSeconds": "",
|
||||
"thirtySeconds": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
"noOptionsFound": "Nenhuma {{unidade}} encontrada"
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Acompanhe o desempenho dos seus servidores",
|
||||
"Identifique gargalos e otimize o uso",
|
||||
"Garanta a confiabilidade com monitoramento em tempo real"
|
||||
],
|
||||
"title": "Um monitor de infraestrutura é usado para:",
|
||||
"actionButton": "Vamos criar seu primeiro monitor de infraestrutura!"
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Marque seus períodos de manutenção",
|
||||
"Elimine qualquer mal-entendido",
|
||||
"Pare de enviar alertas em janelas de manutenção"
|
||||
],
|
||||
"title": "Uma janela de manutenção é usada para:",
|
||||
"actionButton": "Vamos criar sua primeira janela de manutenção!"
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Relatar a experiência do usuário em uma página",
|
||||
"Ajudar a analisar a velocidade da página",
|
||||
"Dar sugestões sobre como a página pode ser melhorada"
|
||||
],
|
||||
"title": "Um monitor PageSpeed é usado para:",
|
||||
"actionButton": "Vamos criar seu primeiro monitor PageSpeed!"
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"Verifique se sites ou servidores estão online e responsivos",
|
||||
"Alerte as equipes sobre tempo de inatividade ou problemas de desempenho",
|
||||
"Monitore endpoints HTTP, pings, contêineres e portas",
|
||||
"Acompanhe o histórico de tempo de atividade e tendências de confiabilidade"
|
||||
],
|
||||
"title": "Um monitor de tempo de atividade é usado para:",
|
||||
"actionButton": "Vamos criar seu primeiro monitor de tempo de atividade!"
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
"email": "E-mail",
|
||||
"firstName": "Primeiro nome",
|
||||
"lastName": "Sobrenome",
|
||||
"role": "Funções",
|
||||
"save": "Salvar"
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
"actionHeader": "Ação",
|
||||
"roleHeader": "Papel"
|
||||
},
|
||||
"title": "",
|
||||
"title": "Editar usuário",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
"successUserUpdate": "Usuário atualizado com sucesso",
|
||||
"validationErrors": "Erros de validação"
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
"incidentsPageActionResolveMonitor": "Resolver incidentes de monitoramento",
|
||||
"incidentsPageActionResolveAll": "Resolver todos os incidentes",
|
||||
"matchMethodOptions": {
|
||||
"equal": "",
|
||||
"equalPlaceholder": "",
|
||||
"include": "",
|
||||
"includePlaceholder": "",
|
||||
"regex": "Regex",
|
||||
"regexPlaceholder": "",
|
||||
"text": ""
|
||||
},
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "",
|
||||
"namePlaceholder": "Meu Contêiner",
|
||||
"placeholder": "abcd1234"
|
||||
},
|
||||
"http": {
|
||||
"label": "URL para monitorar",
|
||||
"namePlaceholder": "Google",
|
||||
"placeholder": "google.com"
|
||||
},
|
||||
"ping": {
|
||||
"label": "Endereço IP para monitorar",
|
||||
"namePlaceholder": "Google",
|
||||
"placeholder": "1.1.1.1"
|
||||
},
|
||||
"port": {
|
||||
"label": "URL para monitorar",
|
||||
"namePlaceholder": "Localhost:5173",
|
||||
"placeholder": "localhost"
|
||||
},
|
||||
"game": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
}
|
||||
},
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": ""
|
||||
},
|
||||
"bytesPerSecond": "",
|
||||
"bytesReceived": "",
|
||||
"bytesSent": "",
|
||||
"chooseGame": "",
|
||||
"createMonitorPage": {
|
||||
"incidentConfigDescription": "",
|
||||
"incidentConfigStatusWindowLabel": "",
|
||||
"incidentConfigStatusWindowThresholdLabel": "",
|
||||
"incidentConfigTitle": ""
|
||||
},
|
||||
"dataRate": "",
|
||||
"dataReceived": "",
|
||||
"dataSent": "",
|
||||
"details": "",
|
||||
"drops": "",
|
||||
"errors": "",
|
||||
"errorsIn": "",
|
||||
"errorsOut": "",
|
||||
"gameServerMonitoring": "",
|
||||
"gameServerMonitoringDescription": "",
|
||||
"network": "",
|
||||
"networkDrops": "",
|
||||
"networkErrors": "",
|
||||
"networkInterface": "",
|
||||
"noNetworkStatsAvailable": "",
|
||||
"packetsPerSecond": "",
|
||||
"packetsReceived": "",
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
}
|
||||
+100
-20
@@ -260,7 +260,6 @@
|
||||
"notifyEmails": "Также уведомлять по электронной почте на несколько адресов (скоро)",
|
||||
"seperateEmails": "Вы можете разделить несколько адресов электронной почты запятой.",
|
||||
"checkFrequency": "Проверить частоту",
|
||||
"chooseGame": "",
|
||||
"matchMethod": "Метод сопоставления",
|
||||
"expectedValue": "Ожидаемое значение",
|
||||
"deleteDialogTitle": "Вы действительно хотите удалить этот монитор?",
|
||||
@@ -427,7 +426,7 @@
|
||||
"settingsTestEmailSuccess": "",
|
||||
"settingsTestEmailFailed": "",
|
||||
"settingsTestEmailFailedWithReason": "",
|
||||
"settingsTestEmailUnknownError": "",
|
||||
"settingsTestEmailUnknownError": "Неизвестная ошибка",
|
||||
"statusMsg": {
|
||||
"paused": "",
|
||||
"up": "",
|
||||
@@ -450,46 +449,46 @@
|
||||
"toasts": {
|
||||
"networkError": "",
|
||||
"checkConnection": "",
|
||||
"unknownError": ""
|
||||
"unknownError": "Неизвестная ошибка"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"common": {
|
||||
"navigation": {
|
||||
"continue": "",
|
||||
"back": ""
|
||||
"back": "Назад"
|
||||
},
|
||||
"inputs": {
|
||||
"email": {
|
||||
"label": "",
|
||||
"placeholder": "",
|
||||
"errors": {
|
||||
"empty": "",
|
||||
"empty": "Для продолжения введите свой адрес электронной почты",
|
||||
"invalid": ""
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"label": "",
|
||||
"label": "Пароль",
|
||||
"rules": {
|
||||
"length": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
"beginning": "Должен быть как минимум",
|
||||
"highlighted": "8 символов в длину"
|
||||
},
|
||||
"special": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
"beginning": "Должен содержать как минимум",
|
||||
"highlighted": "один спецсимвол"
|
||||
},
|
||||
"number": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
"beginning": "Должен содержать как минимум",
|
||||
"highlighted": "одно число"
|
||||
},
|
||||
"uppercase": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
"beginning": "Должен содержать как минимум",
|
||||
"highlighted": "одну заглавную букву"
|
||||
},
|
||||
"lowercase": {
|
||||
"beginning": "",
|
||||
"highlighted": ""
|
||||
"beginning": "Должен содержать как минимум",
|
||||
"highlighted": "одну прописную букву"
|
||||
},
|
||||
"match": {
|
||||
"beginning": "",
|
||||
@@ -569,7 +568,8 @@
|
||||
"password": {
|
||||
"incorrect": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
@@ -595,7 +595,8 @@
|
||||
},
|
||||
"toasts": {
|
||||
"success": ""
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "",
|
||||
@@ -847,6 +848,10 @@
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
},
|
||||
"globalThresholds": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
@@ -915,7 +920,12 @@
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
"oneWeek": "",
|
||||
"fourMinutes": "",
|
||||
"oneMinute": "",
|
||||
"twoMinutes": "",
|
||||
"fifteenSeconds": "",
|
||||
"thirtySeconds": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
@@ -967,5 +977,75 @@
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
"incidentsPageActionResolveAll": "",
|
||||
"matchMethodOptions": {
|
||||
"equal": "",
|
||||
"equalPlaceholder": "",
|
||||
"include": "",
|
||||
"includePlaceholder": "",
|
||||
"regex": "",
|
||||
"regexPlaceholder": "",
|
||||
"text": ""
|
||||
},
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"http": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"ping": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"port": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"game": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
}
|
||||
},
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": ""
|
||||
},
|
||||
"bytesPerSecond": "",
|
||||
"bytesReceived": "",
|
||||
"bytesSent": "",
|
||||
"chooseGame": "",
|
||||
"createMonitorPage": {
|
||||
"incidentConfigDescription": "",
|
||||
"incidentConfigStatusWindowLabel": "",
|
||||
"incidentConfigStatusWindowThresholdLabel": "",
|
||||
"incidentConfigTitle": ""
|
||||
},
|
||||
"dataRate": "",
|
||||
"dataReceived": "",
|
||||
"dataSent": "",
|
||||
"details": "",
|
||||
"drops": "",
|
||||
"errors": "",
|
||||
"errorsIn": "",
|
||||
"errorsOut": "",
|
||||
"gameServerMonitoring": "",
|
||||
"gameServerMonitoringDescription": "",
|
||||
"network": "",
|
||||
"networkDrops": "",
|
||||
"networkErrors": "",
|
||||
"networkInterface": "",
|
||||
"noNetworkStatsAvailable": "",
|
||||
"packetsPerSecond": "",
|
||||
"packetsReceived": "",
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
}
|
||||
@@ -260,7 +260,6 @@
|
||||
"notifyEmails": "Ayrıca birden fazla eposta adresine bildirim gönderebilirsiniz (yakında geliyor)",
|
||||
"seperateEmails": "Birden fazla epostayı virgülle ayırabilirsiniz",
|
||||
"checkFrequency": "Frekansı denetle",
|
||||
"chooseGame": "Oyun seç",
|
||||
"matchMethod": "Eşleşme yöntemi",
|
||||
"expectedValue": "Beklenen değer",
|
||||
"deleteDialogTitle": "Gerçekten bu monitörü silmek istiyor musunuz?",
|
||||
@@ -569,7 +568,8 @@
|
||||
"password": {
|
||||
"incorrect": "Giriş yaptığınız parola sistemdekiyle uyuşmuyor"
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
@@ -595,7 +595,8 @@
|
||||
},
|
||||
"toasts": {
|
||||
"success": "Hoşgeldiniz! Hesabınız başarıyla oluşturuldu."
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "Parolanızı mı kaybettiniz?",
|
||||
@@ -847,6 +848,10 @@
|
||||
"selectDisabled": "Kapalı",
|
||||
"selectEnabled": "Etkin",
|
||||
"title": ""
|
||||
},
|
||||
"globalThresholds": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
@@ -915,7 +920,12 @@
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "1 saat",
|
||||
"oneDay": "1 gün",
|
||||
"oneWeek": "1 hafta"
|
||||
"oneWeek": "1 hafta",
|
||||
"fourMinutes": "",
|
||||
"oneMinute": "",
|
||||
"twoMinutes": "",
|
||||
"fifteenSeconds": "",
|
||||
"thirtySeconds": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
@@ -967,5 +977,75 @@
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
"incidentsPageActionResolveAll": "",
|
||||
"matchMethodOptions": {
|
||||
"equal": "",
|
||||
"equalPlaceholder": "",
|
||||
"include": "",
|
||||
"includePlaceholder": "",
|
||||
"regex": "",
|
||||
"regexPlaceholder": "",
|
||||
"text": ""
|
||||
},
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"http": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"ping": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"port": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"game": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
}
|
||||
},
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": ""
|
||||
},
|
||||
"bytesPerSecond": "",
|
||||
"bytesReceived": "",
|
||||
"bytesSent": "",
|
||||
"chooseGame": "",
|
||||
"createMonitorPage": {
|
||||
"incidentConfigDescription": "",
|
||||
"incidentConfigStatusWindowLabel": "",
|
||||
"incidentConfigStatusWindowThresholdLabel": "",
|
||||
"incidentConfigTitle": ""
|
||||
},
|
||||
"dataRate": "",
|
||||
"dataReceived": "",
|
||||
"dataSent": "",
|
||||
"details": "",
|
||||
"drops": "",
|
||||
"errors": "",
|
||||
"errorsIn": "",
|
||||
"errorsOut": "",
|
||||
"gameServerMonitoring": "",
|
||||
"gameServerMonitoringDescription": "",
|
||||
"network": "",
|
||||
"networkDrops": "",
|
||||
"networkErrors": "",
|
||||
"networkInterface": "",
|
||||
"noNetworkStatsAvailable": "",
|
||||
"packetsPerSecond": "",
|
||||
"packetsReceived": "",
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
}
|
||||
+284
-200
@@ -3,81 +3,81 @@
|
||||
"title": "名稱",
|
||||
"distributedStatusHeaderText": "實時、真實設備覆蓋",
|
||||
"distributedStatusSubHeaderText": "由全球數百萬個設備提供支持,您可以按全球區域、國家或城市查看系統效能",
|
||||
"settingsDisabled": "",
|
||||
"settingsSuccessSaved": "",
|
||||
"settingsFailedToSave": "",
|
||||
"settingsStatsCleared": "",
|
||||
"settingsFailedToClearStats": "",
|
||||
"settingsMonitorsDeleted": "",
|
||||
"settingsFailedToDeleteMonitors": "",
|
||||
"starPromptTitle": "",
|
||||
"starPromptDescription": "",
|
||||
"https": "",
|
||||
"http": "",
|
||||
"monitor": "",
|
||||
"aboutus": "",
|
||||
"now": "",
|
||||
"delete": "",
|
||||
"configure": "",
|
||||
"responseTime": "",
|
||||
"ms": "",
|
||||
"bar": "",
|
||||
"area": "",
|
||||
"country": "",
|
||||
"city": "",
|
||||
"response": "",
|
||||
"settingsDisabled": "已停用",
|
||||
"settingsSuccessSaved": "設定已成功儲存",
|
||||
"settingsFailedToSave": "無法儲存設定",
|
||||
"settingsStatsCleared": "統計數據已成功清除",
|
||||
"settingsFailedToClearStats": "無法清除統計數據",
|
||||
"settingsMonitorsDeleted": "已成功刪除所有監視器",
|
||||
"settingsFailedToDeleteMonitors": "無法刪除所有監視器",
|
||||
"starPromptTitle": "星標Checkmate",
|
||||
"starPromptDescription": "查看最新版本發布及在 GitHub 上幫助社區成長",
|
||||
"https": "HTTPS",
|
||||
"http": "HTTP",
|
||||
"monitor": "監控",
|
||||
"aboutus": "關於我們",
|
||||
"now": "現在",
|
||||
"delete": "刪除",
|
||||
"configure": "設定",
|
||||
"responseTime": "回應時間",
|
||||
"ms": "毫秒",
|
||||
"bar": "條",
|
||||
"area": "區域",
|
||||
"country": "國家",
|
||||
"city": "城市",
|
||||
"response": "回應",
|
||||
"monitorStatusUp": "",
|
||||
"monitorStatusDown": "",
|
||||
"webhookSendSuccess": "",
|
||||
"webhookSendSuccess": "Webhook 通知發送成功",
|
||||
"webhookSendError": "",
|
||||
"webhookUnsupportedPlatform": "",
|
||||
"distributedRightCategoryTitle": "",
|
||||
"distributedStatusServerMonitors": "",
|
||||
"distributedStatusServerMonitorsDescription": "",
|
||||
"distributedUptimeCreateSelectURL": "",
|
||||
"distributedUptimeCreateChecks": "",
|
||||
"distributedUptimeCreateChecksDescription": "",
|
||||
"distributedUptimeCreateIncidentNotification": "",
|
||||
"distributedUptimeCreateIncidentDescription": "",
|
||||
"distributedUptimeCreateAdvancedSettings": "",
|
||||
"distributedUptimeDetailsNoMonitorHistory": "",
|
||||
"distributedUptimeDetailsStatusHeaderUptime": "",
|
||||
"distributedUptimeDetailsStatusHeaderLastUpdate": "",
|
||||
"webhookUnsupportedPlatform": "不支援的平台: {platform}",
|
||||
"distributedRightCategoryTitle": "監控",
|
||||
"distributedStatusServerMonitors": "伺服器監控",
|
||||
"distributedStatusServerMonitorsDescription": "監控相關伺服器狀態",
|
||||
"distributedUptimeCreateSelectURL": "你可以在這裡選擇主機的 URL,同時選擇監視器類型。",
|
||||
"distributedUptimeCreateChecks": "要執行的檢查項目",
|
||||
"distributedUptimeCreateChecksDescription": "你可以在新增網站後,隨時新增或移除檢查項目。",
|
||||
"distributedUptimeCreateIncidentNotification": "事故通知",
|
||||
"distributedUptimeCreateIncidentDescription": "事故發生時,向用戶發送通知。",
|
||||
"distributedUptimeCreateAdvancedSettings": "進階設定",
|
||||
"distributedUptimeDetailsNoMonitorHistory": "此監控尚無檢查記錄。",
|
||||
"distributedUptimeDetailsStatusHeaderUptime": "正常運行時間:",
|
||||
"distributedUptimeDetailsStatusHeaderLastUpdate": "最後更新",
|
||||
"notifications": {
|
||||
"enableNotifications": "",
|
||||
"testNotification": "",
|
||||
"addOrEditNotifications": "",
|
||||
"testNotification": "測試通知",
|
||||
"addOrEditNotifications": "新增或編輯通知",
|
||||
"slack": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookRequired": ""
|
||||
"label": "Slack",
|
||||
"description": "要啟用 Slack 通知,請建立一個 Slack 應用程式並啟用傳入 Webhook。完成後,請在此提供 Webhook URL 即可。",
|
||||
"webhookLabel": "Webhook URL",
|
||||
"webhookPlaceholder": "https://hooks.slack.com/services/...",
|
||||
"webhookRequired": "Slack webhook URL is required"
|
||||
},
|
||||
"discord": {
|
||||
"label": "",
|
||||
"label": "Discord",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookLabel": "Discord Webhook URL",
|
||||
"webhookPlaceholder": "https://discord.com/api/webhooks/...",
|
||||
"webhookRequired": ""
|
||||
},
|
||||
"telegram": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"tokenLabel": "",
|
||||
"tokenPlaceholder": "",
|
||||
"chatIdLabel": "",
|
||||
"chatIdPlaceholder": "",
|
||||
"label": "Telegram",
|
||||
"description": "要啟用 Telegram 通知,請使用 BotFather 用於創建和管理 Telegram 的官方機器來建立一個 Telegram 機器人。接著,取得 API 金鑰和聊天室 ID,並在此處填寫。",
|
||||
"tokenLabel": "你的機器人金鑰",
|
||||
"tokenPlaceholder": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
|
||||
"chatIdLabel": "你的聊天 ID",
|
||||
"chatIdPlaceholder": "-1001234567890",
|
||||
"fieldsRequired": ""
|
||||
},
|
||||
"webhook": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"urlLabel": "",
|
||||
"urlPlaceholder": "",
|
||||
"label": "Webhooks",
|
||||
"description": "你可以設定自訂 webhook,在事件發生時接收通知。",
|
||||
"urlLabel": "Webhook URL",
|
||||
"urlPlaceholder": "https://your-server.com/webhook",
|
||||
"urlRequired": ""
|
||||
},
|
||||
"testNotificationDevelop": "",
|
||||
"testNotificationDevelop": "測試通知 2",
|
||||
"integrationButton": "",
|
||||
"testSuccess": "",
|
||||
"testFailed": "",
|
||||
@@ -85,8 +85,12 @@
|
||||
"networkError": "",
|
||||
"fallback": {
|
||||
"title": "",
|
||||
"checks": [""],
|
||||
"actionButton": ""
|
||||
"checks": [
|
||||
"提醒團隊有關停機或性能問題",
|
||||
"讓工程師知道事件發生時的情況",
|
||||
"讓管理員了解系統變更情況"
|
||||
],
|
||||
"actionButton": "讓我們建立您的第一個通知頻道!"
|
||||
},
|
||||
"createButton": "",
|
||||
"createTitle": "",
|
||||
@@ -111,35 +115,35 @@
|
||||
"failed": ""
|
||||
}
|
||||
},
|
||||
"testLocale": "",
|
||||
"add": "",
|
||||
"testLocale": "測試語言環境",
|
||||
"add": "新增",
|
||||
"monitors": "",
|
||||
"distributedUptimeStatusCreateStatusPage": "",
|
||||
"distributedUptimeStatusCreateStatusPageAccess": "",
|
||||
"distributedUptimeStatusCreateStatusPageReady": "",
|
||||
"distributedUptimeStatusBasicInfoHeader": "",
|
||||
"distributedUptimeStatusBasicInfoHeader": "基本資料",
|
||||
"distributedUptimeStatusBasicInfoDescription": "",
|
||||
"distributedUptimeStatusLogoHeader": "",
|
||||
"distributedUptimeStatusLogoHeader": "標誌",
|
||||
"distributedUptimeStatusLogoDescription": "",
|
||||
"distributedUptimeStatusLogoUploadButton": "",
|
||||
"distributedUptimeStatusLogoUploadButton": "上傳標誌",
|
||||
"distributedUptimeStatusStandardMonitorsHeader": "",
|
||||
"distributedUptimeStatusStandardMonitorsDescription": "",
|
||||
"distributedUptimeStatusCreateYour": "",
|
||||
"distributedUptimeStatusEditYour": "",
|
||||
"distributedUptimeStatusPublishedLabel": "",
|
||||
"distributedUptimeStatusCompanyNameLabel": "",
|
||||
"distributedUptimeStatusCompanyNameLabel": "公司名稱",
|
||||
"distributedUptimeStatusPageAddressLabel": "",
|
||||
"distributedUptimeStatus30Days": "",
|
||||
"distributedUptimeStatus60Days": "",
|
||||
"distributedUptimeStatus90Days": "",
|
||||
"distributedUptimeStatus30Days": "30日",
|
||||
"distributedUptimeStatus60Days": "60日",
|
||||
"distributedUptimeStatus90Days": "90日",
|
||||
"distributedUptimeStatusPageNotSetUp": "",
|
||||
"distributedUptimeStatusContactAdmin": "",
|
||||
"distributedUptimeStatusPageNotPublic": "",
|
||||
"distributedUptimeStatusPageDeleteDialog": "",
|
||||
"distributedUptimeStatusPageDeleteConfirm": "",
|
||||
"distributedUptimeStatusPageDeleteDescription": "",
|
||||
"distributedUptimeStatusDevices": "",
|
||||
"distributedUptimeStatusUpt": "",
|
||||
"distributedUptimeStatusDevices": "設備",
|
||||
"distributedUptimeStatusUpt": "UPT",
|
||||
"distributedUptimeStatusUptBurned": "",
|
||||
"distributedUptimeStatusUptLogo": "",
|
||||
"incidentsTableNoIncidents": "",
|
||||
@@ -169,53 +173,53 @@
|
||||
"infrastructureServerUrlLabel": "",
|
||||
"infrastructureDisplayNameLabel": "",
|
||||
"infrastructureAuthorizationSecretLabel": "",
|
||||
"gb": "",
|
||||
"mb": "",
|
||||
"mem": "",
|
||||
"gb": "GB",
|
||||
"mb": "MB",
|
||||
"mem": "Mem",
|
||||
"memoryUsage": "",
|
||||
"cpu": "",
|
||||
"cpuUsage": "",
|
||||
"cpuTemperature": "",
|
||||
"cpu": "CPU",
|
||||
"cpuUsage": "CPU 使用量",
|
||||
"cpuTemperature": "CPU 溫度",
|
||||
"diskUsage": "",
|
||||
"used": "",
|
||||
"total": "",
|
||||
"cores": "",
|
||||
"cores": "核",
|
||||
"frequency": "",
|
||||
"status": "",
|
||||
"cpuPhysical": "",
|
||||
"cpuLogical": "",
|
||||
"cpuFrequency": "",
|
||||
"avgCpuTemperature": "",
|
||||
"cpuPhysical": "CPU (實體核心)",
|
||||
"cpuLogical": "CPU (邏輯核心)",
|
||||
"cpuFrequency": "CPU 頻率",
|
||||
"avgCpuTemperature": "平均 CPU 溫度",
|
||||
"memory": "",
|
||||
"disk": "",
|
||||
"uptime": "",
|
||||
"os": "",
|
||||
"os": "OS",
|
||||
"host": "",
|
||||
"actions": "",
|
||||
"actions": "行動",
|
||||
"integrations": "",
|
||||
"integrationsPrism": "",
|
||||
"integrationsSlack": "",
|
||||
"integrationsSlack": "Slack",
|
||||
"integrationsSlackInfo": "",
|
||||
"integrationsDiscord": "",
|
||||
"integrationsDiscordInfo": "",
|
||||
"integrationsZapier": "",
|
||||
"integrationsZapierInfo": "",
|
||||
"commonSave": "",
|
||||
"commonSave": "儲存",
|
||||
"createYour": "",
|
||||
"createMonitor": "",
|
||||
"pause": "",
|
||||
"resume": "",
|
||||
"editing": "",
|
||||
"url": "",
|
||||
"access": "",
|
||||
"timezone": "",
|
||||
"url": "UR:",
|
||||
"access": "存取",
|
||||
"timezone": "時區",
|
||||
"features": "",
|
||||
"administrator": "",
|
||||
"administrator": "系統管理員?",
|
||||
"loginHere": "",
|
||||
"displayName": "",
|
||||
"urlMonitor": "",
|
||||
"portToMonitor": "",
|
||||
"websiteMonitoring": "",
|
||||
"websiteMonitoring": "網站監控",
|
||||
"websiteMonitoringDescription": "",
|
||||
"pingMonitoring": "",
|
||||
"pingMonitoringDescription": "",
|
||||
@@ -232,15 +236,15 @@
|
||||
"maintenanceRepeat": "",
|
||||
"maintenance": "",
|
||||
"duration": "",
|
||||
"addMonitors": "",
|
||||
"addMonitors": "新增監視器",
|
||||
"window": "",
|
||||
"cancel": "",
|
||||
"cancel": "取消",
|
||||
"message": "",
|
||||
"low": "",
|
||||
"high": "",
|
||||
"low": "低",
|
||||
"high": "高",
|
||||
"statusCode": "",
|
||||
"date&Time": "",
|
||||
"type": "",
|
||||
"date&Time": "日期與時間",
|
||||
"type": "類型",
|
||||
"statusPageName": "",
|
||||
"publicURL": "",
|
||||
"repeat": "",
|
||||
@@ -259,21 +263,20 @@
|
||||
"notifySMS": "",
|
||||
"notifyEmails": "",
|
||||
"seperateEmails": "",
|
||||
"checkFrequency": "",
|
||||
"chooseGame": "",
|
||||
"checkFrequency": "檢查頻率",
|
||||
"matchMethod": "",
|
||||
"expectedValue": "",
|
||||
"deleteDialogTitle": "",
|
||||
"deleteDialogDescription": "",
|
||||
"deleteDialogTitle": "您確定要刪除此監視器嗎?",
|
||||
"deleteDialogDescription": "一旦刪除,該監視器無法恢復。",
|
||||
"pageSpeedMonitor": "",
|
||||
"shown": "",
|
||||
"ago": "",
|
||||
"companyName": "",
|
||||
"companyName": "公司名稱",
|
||||
"pageSpeedDetailsPerformanceReport": "",
|
||||
"pageSpeedDetailsPerformanceReportCalculator": "",
|
||||
"checkingEvery": "",
|
||||
"statusPageCreateSettings": "",
|
||||
"basicInformation": "",
|
||||
"basicInformation": "基本資料",
|
||||
"statusPageCreateBasicInfoDescription": "",
|
||||
"statusPageCreateSelectTimeZoneDescription": "",
|
||||
"statusPageCreateAppearanceDescription": "",
|
||||
@@ -307,24 +310,24 @@
|
||||
"pageSpeedWarning": "",
|
||||
"pageSpeedLearnMoreLink": "",
|
||||
"pageSpeedAddApiKey": "",
|
||||
"update": "",
|
||||
"update": "更新",
|
||||
"invalidFileFormat": "",
|
||||
"invalidFileSize": "",
|
||||
"ClickUpload": "",
|
||||
"ClickUpload": "點擊上傳",
|
||||
"DragandDrop": "",
|
||||
"MaxSize": "",
|
||||
"SupportedFormats": "",
|
||||
"FirstName": "",
|
||||
"LastName": "",
|
||||
"EmailDescriptionText": "",
|
||||
"YourPhoto": "",
|
||||
"YourPhoto": "頭像",
|
||||
"PhotoDescriptionText": "",
|
||||
"save": "",
|
||||
"DeleteDescriptionText": "",
|
||||
"DeleteAccountWarning": "",
|
||||
"DeleteDescriptionText": "這將刪除帳號以及所有相關的伺服器資料,此操作不可逆轉。",
|
||||
"DeleteAccountWarning": "刪除您的帳號表示您將無法再次登入,且所有資料都會被移除。此操作不可逆轉。",
|
||||
"DeleteWarningTitle": "",
|
||||
"bulkImport": {
|
||||
"title": "",
|
||||
"title": "批量匯入",
|
||||
"selectFileTips": "",
|
||||
"selectFileDescription": "",
|
||||
"selectFile": "",
|
||||
@@ -336,8 +339,8 @@
|
||||
"invalidFileType": "",
|
||||
"uploadFailed": ""
|
||||
},
|
||||
"DeleteAccountTitle": "",
|
||||
"DeleteAccountButton": "",
|
||||
"DeleteAccountTitle": "刪除帳戶",
|
||||
"DeleteAccountButton": "刪除帳戶",
|
||||
"publicLink": "",
|
||||
"maskedPageSpeedKeyPlaceholder": "",
|
||||
"reset": "",
|
||||
@@ -352,14 +355,14 @@
|
||||
},
|
||||
"roles": {
|
||||
"superAdmin": "",
|
||||
"admin": "",
|
||||
"admin": "管理員",
|
||||
"teamMember": "",
|
||||
"demoUser": ""
|
||||
"demoUser": "示範用戶"
|
||||
},
|
||||
"teamPanel": {
|
||||
"teamMembers": "",
|
||||
"filter": {
|
||||
"all": "",
|
||||
"all": "全部",
|
||||
"member": ""
|
||||
},
|
||||
"inviteTeamMember": "",
|
||||
@@ -368,7 +371,7 @@
|
||||
"email": "",
|
||||
"selectRole": "",
|
||||
"inviteLink": "",
|
||||
"cancel": "",
|
||||
"cancel": "取消",
|
||||
"noMembers": "",
|
||||
"getToken": "",
|
||||
"emailToken": "",
|
||||
@@ -382,7 +385,7 @@
|
||||
"monitorState": {
|
||||
"paused": "",
|
||||
"resumed": "",
|
||||
"active": ""
|
||||
"active": "啟用中"
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "",
|
||||
@@ -396,7 +399,7 @@
|
||||
"support": "",
|
||||
"discussions": "",
|
||||
"docs": "",
|
||||
"changelog": "",
|
||||
"changelog": "版本變更記錄",
|
||||
"profile": "",
|
||||
"password": "",
|
||||
"team": "",
|
||||
@@ -408,17 +411,17 @@
|
||||
"state": "",
|
||||
"statusBreadCrumbsStatusPages": "",
|
||||
"statusBreadCrumbsDetails": "",
|
||||
"commonSaving": "",
|
||||
"commonSaving": "儲存中...",
|
||||
"navControls": "",
|
||||
"incidentsPageTitle": "",
|
||||
"passwordPanel": {
|
||||
"passwordChangedSuccess": "",
|
||||
"passwordInputIncorrect": "",
|
||||
"currentPassword": "",
|
||||
"currentPassword": "當前密碼",
|
||||
"enterCurrentPassword": "",
|
||||
"newPassword": "",
|
||||
"enterNewPassword": "",
|
||||
"confirmNewPassword": "",
|
||||
"confirmNewPassword": "確認新密碼",
|
||||
"passwordRequirements": "",
|
||||
"saving": ""
|
||||
},
|
||||
@@ -442,22 +445,22 @@
|
||||
"game": ""
|
||||
},
|
||||
"common": {
|
||||
"appName": "",
|
||||
"appName": "Checkmate",
|
||||
"monitoringAgentName": "",
|
||||
"buttons": {
|
||||
"toggleTheme": ""
|
||||
},
|
||||
"toasts": {
|
||||
"networkError": "",
|
||||
"checkConnection": "",
|
||||
"checkConnection": "請檢查您的連線",
|
||||
"unknownError": ""
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"common": {
|
||||
"navigation": {
|
||||
"continue": "",
|
||||
"back": ""
|
||||
"continue": "繼續",
|
||||
"back": "返回"
|
||||
},
|
||||
"inputs": {
|
||||
"email": {
|
||||
@@ -472,23 +475,23 @@
|
||||
"label": "",
|
||||
"rules": {
|
||||
"length": {
|
||||
"beginning": "",
|
||||
"beginning": "必須至少",
|
||||
"highlighted": ""
|
||||
},
|
||||
"special": {
|
||||
"beginning": "",
|
||||
"beginning": "必須至少包含",
|
||||
"highlighted": ""
|
||||
},
|
||||
"number": {
|
||||
"beginning": "",
|
||||
"beginning": "必須至少包含",
|
||||
"highlighted": ""
|
||||
},
|
||||
"uppercase": {
|
||||
"beginning": "",
|
||||
"beginning": "必須至少包含",
|
||||
"highlighted": ""
|
||||
},
|
||||
"lowercase": {
|
||||
"beginning": "",
|
||||
"beginning": "必須至少包含",
|
||||
"highlighted": ""
|
||||
},
|
||||
"match": {
|
||||
@@ -562,14 +565,15 @@
|
||||
"registerLink": ""
|
||||
},
|
||||
"toasts": {
|
||||
"success": "",
|
||||
"success": "歡迎回來!您已成功登入。",
|
||||
"incorrectPassword": ""
|
||||
},
|
||||
"errors": {
|
||||
"password": {
|
||||
"incorrect": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"registration": {
|
||||
"heading": {
|
||||
@@ -594,8 +598,9 @@
|
||||
"login": ""
|
||||
},
|
||||
"toasts": {
|
||||
"success": ""
|
||||
}
|
||||
"success": "歡迎!您的帳號已成功建立。"
|
||||
},
|
||||
"welcome": ""
|
||||
},
|
||||
"forgotPassword": {
|
||||
"heading": "",
|
||||
@@ -623,7 +628,7 @@
|
||||
"sent": "",
|
||||
"emailNotFound": "",
|
||||
"redirect": "",
|
||||
"success": "",
|
||||
"success": "您的密碼已成功重設。",
|
||||
"error": ""
|
||||
}
|
||||
}
|
||||
@@ -634,10 +639,10 @@
|
||||
"reconnected": "",
|
||||
"stillUnreachable": ""
|
||||
},
|
||||
"alertBox": "",
|
||||
"description": "",
|
||||
"alertBox": "伺服器連線錯誤",
|
||||
"description": "我們無法連接到伺服器。請檢查您的網路連線,若問題持續,請確認您的部署設定是否正確。",
|
||||
"retryButton": {
|
||||
"default": "",
|
||||
"default": "重新嘗試連線",
|
||||
"processing": ""
|
||||
}
|
||||
}
|
||||
@@ -645,42 +650,42 @@
|
||||
"createNotifications": {
|
||||
"title": "",
|
||||
"nameSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "姓名",
|
||||
"description": "為您的整合設定描述名稱。",
|
||||
"nameLabel": "",
|
||||
"namePlaceholder": ""
|
||||
},
|
||||
"typeSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"typeLabel": ""
|
||||
"title": "類型",
|
||||
"description": "請選擇您想要建立的通知頻道類型。",
|
||||
"typeLabel": "類型"
|
||||
},
|
||||
"emailSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "電郵",
|
||||
"description": "目的地電子郵件位址。",
|
||||
"emailLabel": "",
|
||||
"emailPlaceholder": ""
|
||||
},
|
||||
"slackSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "Slack",
|
||||
"description": "在此配置您的 Slack webhook",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"pagerdutySettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "PagerDuty",
|
||||
"description": "在此配置您的 PagerDuty 整合",
|
||||
"integrationKeyLabel": "",
|
||||
"integrationKeyPlaceholder": ""
|
||||
},
|
||||
"discordSettings": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "Discord",
|
||||
"description": "在此配置您的 Discord webhook",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
},
|
||||
"webhookSettings": {
|
||||
"title": "",
|
||||
"title": "Webhook",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookPlaceholder": ""
|
||||
@@ -690,8 +695,8 @@
|
||||
"dialogDeleteConfirm": ""
|
||||
},
|
||||
"notificationConfig": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
"title": "通知",
|
||||
"description": "請選擇您想要使用的通知頻道"
|
||||
},
|
||||
"monitorStatus": {
|
||||
"checkingEvery": "",
|
||||
@@ -700,7 +705,7 @@
|
||||
"down": "",
|
||||
"paused": ""
|
||||
},
|
||||
"advancedMatching": "",
|
||||
"advancedMatching": "進階匹配",
|
||||
"sendTestNotifications": "",
|
||||
"selectAll": "",
|
||||
"showAdminLoginLink": "",
|
||||
@@ -718,11 +723,11 @@
|
||||
"logLevelSelect": {
|
||||
"title": "",
|
||||
"values": {
|
||||
"all": "",
|
||||
"all": "全部",
|
||||
"info": "",
|
||||
"warn": "",
|
||||
"error": "",
|
||||
"debug": ""
|
||||
"debug": "除錯"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -734,8 +739,8 @@
|
||||
"title": "",
|
||||
"idHeader": "",
|
||||
"urlHeader": "",
|
||||
"typeHeader": "",
|
||||
"activeHeader": "",
|
||||
"typeHeader": "類型",
|
||||
"activeHeader": "啟用中",
|
||||
"lockedAtHeader": "",
|
||||
"runCountHeader": "",
|
||||
"failCountHeader": "",
|
||||
@@ -774,7 +779,7 @@
|
||||
"aboutSettings": {
|
||||
"labelDevelopedBy": "",
|
||||
"labelVersion": "",
|
||||
"title": ""
|
||||
"title": "關於"
|
||||
},
|
||||
"demoMonitorsSettings": {
|
||||
"buttonAddMonitors": "",
|
||||
@@ -782,7 +787,7 @@
|
||||
"title": ""
|
||||
},
|
||||
"emailSettings": {
|
||||
"buttonSendTestEmail": "",
|
||||
"buttonSendTestEmail": "發送測試電子郵件",
|
||||
"description": "",
|
||||
"descriptionTransport": "",
|
||||
"labelAddress": "",
|
||||
@@ -800,25 +805,25 @@
|
||||
"labelUser": "",
|
||||
"linkTransport": "",
|
||||
"placeholderUser": "",
|
||||
"title": "",
|
||||
"title": "電郵",
|
||||
"toastEmailRequiredFieldsError": ""
|
||||
},
|
||||
"pageSpeedSettings": {
|
||||
"description": "",
|
||||
"labelApiKeySet": "",
|
||||
"labelApiKey": "",
|
||||
"title": ""
|
||||
"title": "Google PageSpeed API 金鑰"
|
||||
},
|
||||
"saveButtonLabel": "",
|
||||
"statsSettings": {
|
||||
"clearAllStatsButton": "",
|
||||
"clearAllStatsDescription": "",
|
||||
"clearAllStatsDialogConfirm": "",
|
||||
"clearAllStatsDialogDescription": "",
|
||||
"clearAllStatsDialogTitle": "",
|
||||
"clearAllStatsButton": "清除所有統計數據",
|
||||
"clearAllStatsDescription": "清除所有統計數據,此操作不可逆轉。",
|
||||
"clearAllStatsDialogConfirm": "是,請清除所有統計數據",
|
||||
"clearAllStatsDialogDescription": "一旦刪除,監控歷史和統計數據將無法復原。",
|
||||
"clearAllStatsDialogTitle": "您是否要清除所有統計數據?",
|
||||
"description": "",
|
||||
"labelTTL": "",
|
||||
"labelTTLOptional": "",
|
||||
"labelTTLOptional": "0 表示無限",
|
||||
"title": ""
|
||||
},
|
||||
"systemResetSettings": {
|
||||
@@ -827,19 +832,19 @@
|
||||
"dialogConfirm": "",
|
||||
"dialogDescription": "",
|
||||
"dialogTitle": "",
|
||||
"title": ""
|
||||
"title": "系統重設"
|
||||
},
|
||||
"timezoneSettings": {
|
||||
"description": "",
|
||||
"label": "",
|
||||
"title": ""
|
||||
"title": "顯示時區"
|
||||
},
|
||||
"title": "",
|
||||
"title": "設定",
|
||||
"uiSettings": {
|
||||
"description": "",
|
||||
"labelLanguage": "",
|
||||
"labelTheme": "",
|
||||
"title": ""
|
||||
"title": "外觀"
|
||||
},
|
||||
"urlSettings": {
|
||||
"description": "",
|
||||
@@ -847,15 +852,19 @@
|
||||
"selectDisabled": "",
|
||||
"selectEnabled": "",
|
||||
"title": ""
|
||||
},
|
||||
"globalThresholds": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"statusPageCreate": {
|
||||
"buttonSave": ""
|
||||
"buttonSave": "儲存"
|
||||
},
|
||||
"incidentsOptionsHeaderFilterResolved": "",
|
||||
"settingsSave": "",
|
||||
"statusPageCreateAppearanceTitle": "",
|
||||
"confirmPassword": "",
|
||||
"confirmPassword": "確認密碼",
|
||||
"monitorHooks": {
|
||||
"failureAddDemoMonitors": "",
|
||||
"successAddDemoMonitors": ""
|
||||
@@ -866,15 +875,15 @@
|
||||
"incidentsOptionsHeaderTotalIncidents": "",
|
||||
"statusPage": {
|
||||
"deleteSuccess": "",
|
||||
"deleteFailed": "",
|
||||
"deleteFailed": "刪除狀態頁面失敗",
|
||||
"createSuccess": "",
|
||||
"updateSuccess": "",
|
||||
"generalSettings": "",
|
||||
"contents": "",
|
||||
"contents": "內容",
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"actionButton": "讓我們建立您的第一個狀態頁面!"
|
||||
}
|
||||
},
|
||||
"testNotificationsDisabled": "",
|
||||
@@ -909,63 +918,138 @@
|
||||
},
|
||||
"pageSpeedLighthouseAPI": "",
|
||||
"time": {
|
||||
"threeMinutes": "",
|
||||
"fiveMinutes": "",
|
||||
"tenMinutes": "",
|
||||
"twentyMinutes": "",
|
||||
"oneHour": "",
|
||||
"oneDay": "",
|
||||
"oneWeek": ""
|
||||
"threeMinutes": "3分鐘",
|
||||
"fiveMinutes": "5分鐘",
|
||||
"tenMinutes": "10分鐘",
|
||||
"twentyMinutes": "20分鐘",
|
||||
"oneHour": "1小時",
|
||||
"oneDay": "1日",
|
||||
"oneWeek": "1星期",
|
||||
"fourMinutes": "",
|
||||
"oneMinute": "",
|
||||
"twoMinutes": "",
|
||||
"fifteenSeconds": "",
|
||||
"thirtySeconds": ""
|
||||
},
|
||||
"general": {
|
||||
"noOptionsFound": ""
|
||||
},
|
||||
"infrastructureMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": ["追蹤伺服器的效能", "識別瓶頸並優化資源使用", "透過即時監控確保可靠性"],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"actionButton": "讓我們建立您的第一個基礎設施監視器!"
|
||||
}
|
||||
},
|
||||
"maintenanceWindow": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"checks": ["標記您的維護期間", "消除任何誤解", "在維護時段停止發送警報"],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"actionButton": "讓我們建立您的第一個維護時段!"
|
||||
}
|
||||
},
|
||||
"pageSpeed": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"actionButton": "讓我們建立您的第一個 PageSpeed 監視器!"
|
||||
}
|
||||
},
|
||||
"uptimeMonitor": {
|
||||
"fallback": {
|
||||
"checks": [""],
|
||||
"title": "",
|
||||
"actionButton": ""
|
||||
"actionButton": "讓我們建立您的第一個正常運行時間監視器!"
|
||||
}
|
||||
},
|
||||
"editUserPage": {
|
||||
"form": {
|
||||
"email": "",
|
||||
"firstName": "",
|
||||
"lastName": "",
|
||||
"role": "",
|
||||
"save": ""
|
||||
"email": "電郵",
|
||||
"firstName": "名",
|
||||
"lastName": "姓",
|
||||
"role": "角色",
|
||||
"save": "儲存"
|
||||
},
|
||||
"table": {
|
||||
"actionHeader": "",
|
||||
"roleHeader": ""
|
||||
"actionHeader": "行動",
|
||||
"roleHeader": "角色"
|
||||
},
|
||||
"title": "",
|
||||
"title": "編輯用戶",
|
||||
"toast": {
|
||||
"successUserUpdate": "",
|
||||
"validationErrors": ""
|
||||
}
|
||||
},
|
||||
"incidentsPageActionResolveMonitor": "",
|
||||
"incidentsPageActionResolveAll": ""
|
||||
"incidentsPageActionResolveMonitor": "解決監視器事故",
|
||||
"incidentsPageActionResolveAll": "解決所有事故",
|
||||
"matchMethodOptions": {
|
||||
"equal": "",
|
||||
"equalPlaceholder": "",
|
||||
"include": "",
|
||||
"includePlaceholder": "",
|
||||
"regex": "",
|
||||
"regexPlaceholder": "",
|
||||
"text": ""
|
||||
},
|
||||
"monitorType": {
|
||||
"docker": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"http": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"ping": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"port": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
},
|
||||
"game": {
|
||||
"label": "",
|
||||
"namePlaceholder": "",
|
||||
"placeholder": ""
|
||||
}
|
||||
},
|
||||
"uptimeAdvancedMatching": {
|
||||
"jsonPath": ""
|
||||
},
|
||||
"bytesPerSecond": "",
|
||||
"bytesReceived": "",
|
||||
"bytesSent": "",
|
||||
"chooseGame": "",
|
||||
"createMonitorPage": {
|
||||
"incidentConfigDescription": "",
|
||||
"incidentConfigStatusWindowLabel": "",
|
||||
"incidentConfigStatusWindowThresholdLabel": "",
|
||||
"incidentConfigTitle": ""
|
||||
},
|
||||
"dataRate": "",
|
||||
"dataReceived": "",
|
||||
"dataSent": "",
|
||||
"details": "",
|
||||
"drops": "",
|
||||
"errors": "",
|
||||
"errorsIn": "",
|
||||
"errorsOut": "",
|
||||
"gameServerMonitoring": "",
|
||||
"gameServerMonitoringDescription": "",
|
||||
"network": "",
|
||||
"networkDrops": "",
|
||||
"networkErrors": "",
|
||||
"networkInterface": "",
|
||||
"noNetworkStatsAvailable": "",
|
||||
"packetsPerSecond": "",
|
||||
"packetsReceived": "",
|
||||
"packetsReceivedRate": "",
|
||||
"packetsSent": "",
|
||||
"rate": "",
|
||||
"selectInterface": ""
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { execSync } from "child_process";
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, process.cwd(), "");
|
||||
let version = "3.0-beta";
|
||||
let version = "3.1.7";
|
||||
|
||||
return {
|
||||
base: "/",
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
FROM node:20-alpine AS build
|
||||
FROM node:20-slim AS build
|
||||
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
make g++ \
|
||||
gcc \
|
||||
libc-dev \
|
||||
linux-headers \
|
||||
libusb-dev \
|
||||
eudev-dev
|
||||
|
||||
COPY ./client/package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
FROM node:20-alpine
|
||||
FROM node:20-slim
|
||||
|
||||
# Install ping
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y iputils-ping \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
FROM node:20-alpine AS build
|
||||
FROM node:20-slim AS build
|
||||
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
make g++ \
|
||||
gcc \
|
||||
libc-dev \
|
||||
linux-headers \
|
||||
libusb-dev \
|
||||
eudev-dev
|
||||
|
||||
|
||||
COPY ./client/package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
FROM node:20-alpine
|
||||
FROM node:20-slim
|
||||
|
||||
# Install ping
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y iputils-ping \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ services:
|
||||
server:
|
||||
image: ghcr.io/bluewave-labs/checkmate-backend-mono-multiarch:latest
|
||||
restart: always
|
||||
pull_policy: always
|
||||
ports:
|
||||
- "52345:52345"
|
||||
environment:
|
||||
@@ -15,9 +16,8 @@ services:
|
||||
- mongodb
|
||||
mongodb:
|
||||
image: mongo:4.4.18
|
||||
container_name: checkmate-mongodb
|
||||
restart: always
|
||||
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
|
||||
command: ["mongod", "--quiet", "--bind_ip_all"]
|
||||
ports:
|
||||
- "27017:27017"
|
||||
volumes:
|
||||
|
||||
@@ -1,25 +1,42 @@
|
||||
FROM node:20-alpine AS frontend-build
|
||||
# ---------------------
|
||||
# Frontend build stage
|
||||
# ---------------------
|
||||
FROM node:24-slim AS frontend-build
|
||||
|
||||
# Install ping
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y iputils-ping \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app/client
|
||||
|
||||
COPY client/package*.json ./
|
||||
RUN npm ci
|
||||
COPY client/package.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
RUN npm install esbuild@0.25.5 --build-from-source
|
||||
|
||||
COPY client ./
|
||||
|
||||
RUN npm run build
|
||||
|
||||
FROM node:20-alpine AS backend
|
||||
# ---------------------
|
||||
# Backend stage
|
||||
# ---------------------
|
||||
FROM node:24-slim AS backend
|
||||
|
||||
WORKDIR /app/server
|
||||
|
||||
COPY server/package.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
COPY server ./
|
||||
|
||||
COPY --from=frontend-build /app/client/dist ./public
|
||||
|
||||
RUN npm ci
|
||||
|
||||
RUN chmod +x ./scripts/inject-vars.sh
|
||||
|
||||
EXPOSE 52345
|
||||
|
||||
CMD ./scripts/inject-vars.sh && node ./src/index.js
|
||||
CMD ["sh", "-c", "./scripts/inject-vars.sh && node ./src/index.js"]
|
||||
@@ -5,7 +5,7 @@ cd "$(dirname "$0")"
|
||||
cd ../..
|
||||
|
||||
# Define service names and their corresponding Dockerfiles in parallel arrays
|
||||
services=("mono_mongo" "mono_redis" "mono_server")
|
||||
services=("mono_mongo" "mono_server")
|
||||
dockerfiles=(
|
||||
"./docker/dist-mono/mongoDB.Dockerfile"
|
||||
"./docker/dist-mono/server.Dockerfile"
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
FROM node:20-alpine AS frontend-build
|
||||
FROM node:20-slim AS frontend-build
|
||||
|
||||
# Install ping
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y iputils-ping \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app/client
|
||||
|
||||
@@ -8,7 +13,7 @@ RUN npm install
|
||||
COPY client ./
|
||||
RUN npm run build
|
||||
|
||||
FROM node:20-alpine AS app
|
||||
FROM node:20-slim AS app
|
||||
|
||||
WORKDIR /app/server
|
||||
|
||||
|
||||
Vendored
+1
-10
@@ -1,18 +1,9 @@
|
||||
FROM node:20-alpine AS build
|
||||
FROM node:20-slim AS build
|
||||
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
make g++ \
|
||||
gcc \
|
||||
libc-dev \
|
||||
linux-headers \
|
||||
libusb-dev \
|
||||
eudev-dev
|
||||
|
||||
COPY ./client/package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
Vendored
+6
-1
@@ -1,4 +1,9 @@
|
||||
FROM node:20-alpine
|
||||
FROM node:20-slim
|
||||
|
||||
# Install ping
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y iputils-ping \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
FROM node:20-alpine AS build
|
||||
FROM node:20-slim AS build
|
||||
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
make g++ \
|
||||
gcc \
|
||||
libc-dev \
|
||||
linux-headers \
|
||||
libusb-dev \
|
||||
eudev-dev
|
||||
|
||||
COPY ./client/package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
FROM node:20-alpine
|
||||
FROM node:20-slim
|
||||
|
||||
# Install ping
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y iputils-ping \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV NODE_OPTIONS="--max-old-space-size=2048"
|
||||
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
FROM node:20-alpine AS build
|
||||
FROM node:20-slim AS build
|
||||
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
make g++ \
|
||||
gcc \
|
||||
libc-dev \
|
||||
linux-headers \
|
||||
libusb-dev \
|
||||
eudev-dev
|
||||
|
||||
|
||||
COPY ./client/package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
FROM node:20-alpine
|
||||
FROM node:20-slim
|
||||
|
||||
# Install ping
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y iputils-ping \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV NODE_OPTIONS="--max-old-space-size=2048"
|
||||
|
||||
|
||||
Generated
+416
-292
File diff suppressed because it is too large.
Load diff
+2
-1
@@ -28,6 +28,7 @@
|
||||
"express": "^4.19.2",
|
||||
"express-rate-limit": "8.0.1",
|
||||
"gamedig": "^5.3.1",
|
||||
"got": "14.4.7",
|
||||
"handlebars": "^4.7.8",
|
||||
"helmet": "^8.0.0",
|
||||
"ioredis": "^5.4.2",
|
||||
@@ -45,7 +46,7 @@
|
||||
"ping": "0.4.4",
|
||||
"sharp": "0.33.5",
|
||||
"ssl-checker": "2.0.10",
|
||||
"super-simple-scheduler": "1.3.0",
|
||||
"super-simple-scheduler": "1.4.1",
|
||||
"swagger-ui-express": "5.0.1",
|
||||
"winston": "^3.13.0"
|
||||
},
|
||||
|
||||
@@ -19,8 +19,10 @@ import MaintenanceWindowService from "../service/business/maintenanceWindowServi
|
||||
import MonitorService from "../service/business/monitorService.js";
|
||||
import papaparse from "papaparse";
|
||||
import axios from "axios";
|
||||
import got from "got";
|
||||
import ping from "ping";
|
||||
import http from "http";
|
||||
import https from "https";
|
||||
import Docker from "dockerode";
|
||||
import net from "net";
|
||||
import fs from "fs";
|
||||
@@ -32,6 +34,8 @@ import mjml2html from "mjml";
|
||||
import jwt from "jsonwebtoken";
|
||||
import crypto from "crypto";
|
||||
import { games } from "gamedig";
|
||||
import jmespath from "jmespath";
|
||||
import { GameDig } from "gamedig";
|
||||
|
||||
import { fileURLToPath } from "url";
|
||||
import { ObjectId } from "mongodb";
|
||||
@@ -43,8 +47,6 @@ import { ParseBoolean } from "../utils/utils.js";
|
||||
|
||||
// Models
|
||||
import Check from "../db/models/Check.js";
|
||||
import HardwareCheck from "../db/models/HardwareCheck.js";
|
||||
import PageSpeedCheck from "../db/models/PageSpeedCheck.js";
|
||||
import Monitor from "../db/models/Monitor.js";
|
||||
import User from "../db/models/User.js";
|
||||
import InviteToken from "../db/models/InviteToken.js";
|
||||
@@ -52,7 +54,6 @@ import StatusPage from "../db/models/StatusPage.js";
|
||||
import Team from "../db/models/Team.js";
|
||||
import MaintenanceWindow from "../db/models/MaintenanceWindow.js";
|
||||
import MonitorStats from "../db/models/MonitorStats.js";
|
||||
import NetworkCheck from "../db/models/NetworkCheck.js";
|
||||
import Notification from "../db/models/Notification.js";
|
||||
import RecoveryToken from "../db/models/RecoveryToken.js";
|
||||
import AppSettings from "../db/models/AppSettings.js";
|
||||
@@ -61,12 +62,9 @@ import InviteModule from "../db/mongo/modules/inviteModule.js";
|
||||
import CheckModule from "../db/mongo/modules/checkModule.js";
|
||||
import StatusPageModule from "../db/mongo/modules/statusPageModule.js";
|
||||
import UserModule from "../db/mongo/modules/userModule.js";
|
||||
import HardwareCheckModule from "../db/mongo/modules/hardwareCheckModule.js";
|
||||
import MaintenanceWindowModule from "../db/mongo/modules/maintenanceWindowModule.js";
|
||||
import MonitorModule from "../db/mongo/modules/monitorModule.js";
|
||||
import NetworkCheckModule from "../db/mongo/modules/networkCheckModule.js";
|
||||
import NotificationModule from "../db/mongo/modules/notificationModule.js";
|
||||
import PageSpeedCheckModule from "../db/mongo/modules/pageSpeedCheckModule.js";
|
||||
import RecoveryModule from "../db/mongo/modules/recoveryModule.js";
|
||||
import SettingsModule from "../db/mongo/modules/settingsModule.js";
|
||||
|
||||
@@ -80,18 +78,15 @@ export const initializeServices = async ({ logger, envSettings, settingsService
|
||||
const stringService = new StringService(translationService);
|
||||
|
||||
// Create DB
|
||||
const checkModule = new CheckModule({ logger, Check, HardwareCheck, PageSpeedCheck, Monitor, User });
|
||||
const checkModule = new CheckModule({ logger, Check, Monitor, User });
|
||||
const inviteModule = new InviteModule({ InviteToken, crypto, stringService });
|
||||
const statusPageModule = new StatusPageModule({ StatusPage, NormalizeData, stringService });
|
||||
const userModule = new UserModule({ User, Team, GenerateAvatarImage, ParseBoolean, stringService });
|
||||
const hardwareCheckModule = new HardwareCheckModule({ HardwareCheck, Monitor, logger });
|
||||
const maintenanceWindowModule = new MaintenanceWindowModule({ MaintenanceWindow });
|
||||
const monitorModule = new MonitorModule({
|
||||
Monitor,
|
||||
MonitorStats,
|
||||
Check,
|
||||
PageSpeedCheck,
|
||||
HardwareCheck,
|
||||
stringService,
|
||||
fs,
|
||||
path,
|
||||
@@ -100,9 +95,7 @@ export const initializeServices = async ({ logger, envSettings, settingsService
|
||||
NormalizeData,
|
||||
NormalizeDataUptimeDetails,
|
||||
});
|
||||
const networkCheckModule = new NetworkCheckModule({ NetworkCheck });
|
||||
const notificationModule = new NotificationModule({ Notification, Monitor });
|
||||
const pageSpeedCheckModule = new PageSpeedCheckModule({ PageSpeedCheck });
|
||||
const recoveryModule = new RecoveryModule({ User, RecoveryToken, crypto, stringService });
|
||||
const settingsModule = new SettingsModule({ AppSettings });
|
||||
|
||||
@@ -113,19 +106,29 @@ export const initializeServices = async ({ logger, envSettings, settingsService
|
||||
inviteModule,
|
||||
statusPageModule,
|
||||
userModule,
|
||||
hardwareCheckModule,
|
||||
maintenanceWindowModule,
|
||||
monitorModule,
|
||||
networkCheckModule,
|
||||
notificationModule,
|
||||
pageSpeedCheckModule,
|
||||
recoveryModule,
|
||||
settingsModule,
|
||||
});
|
||||
|
||||
await db.connect();
|
||||
|
||||
const networkService = new NetworkService(axios, ping, logger, http, Docker, net, stringService, settingsService);
|
||||
const networkService = new NetworkService({
|
||||
axios,
|
||||
got,
|
||||
https,
|
||||
jmespath,
|
||||
GameDig,
|
||||
ping,
|
||||
logger,
|
||||
http,
|
||||
Docker,
|
||||
net,
|
||||
stringService,
|
||||
settingsService,
|
||||
});
|
||||
const emailService = new EmailService(settingsService, fs, path, compile, mjml2html, nodemailer, logger);
|
||||
const bufferService = new BufferService({ db, logger, envSettings });
|
||||
const statusService = new StatusService({ db, logger, buffer: bufferService });
|
||||
|
||||
@@ -95,7 +95,7 @@ class StatusPageController extends BaseController {
|
||||
|
||||
deleteStatusPage = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await this.db.deleteStatusPage(req.params.url);
|
||||
await this.db.statusPageModule.deleteStatusPage(req.params.url);
|
||||
return res.success({
|
||||
msg: this.stringService.statusPageDelete,
|
||||
});
|
||||
|
||||
+166
-89
@@ -1,99 +1,176 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
const BaseCheckSchema = mongoose.Schema({
|
||||
/**
|
||||
* Reference to the associated Monitor document.
|
||||
*
|
||||
* @type {mongoose.Schema.Types.ObjectId}
|
||||
*/
|
||||
monitorId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Monitor",
|
||||
immutable: true,
|
||||
index: true,
|
||||
},
|
||||
|
||||
teamId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Team",
|
||||
immutable: true,
|
||||
index: true,
|
||||
},
|
||||
/**
|
||||
* Status of the check (true for up, false for down).
|
||||
*
|
||||
* @type {Boolean}
|
||||
*/
|
||||
status: {
|
||||
type: Boolean,
|
||||
index: true,
|
||||
},
|
||||
/**
|
||||
* Response time of the check in milliseconds.
|
||||
*
|
||||
* @type {Number}
|
||||
*/
|
||||
responseTime: {
|
||||
type: Number,
|
||||
},
|
||||
/**
|
||||
* HTTP status code received during the check.
|
||||
*
|
||||
* @type {Number}
|
||||
*/
|
||||
statusCode: {
|
||||
type: Number,
|
||||
index: true,
|
||||
},
|
||||
/**
|
||||
* Message or description of the check result.
|
||||
*
|
||||
* @type {String}
|
||||
*/
|
||||
message: {
|
||||
type: String,
|
||||
},
|
||||
/**
|
||||
* Expiry date of the check, auto-calculated to expire after 30 days.
|
||||
*
|
||||
* @type {Date}
|
||||
*/
|
||||
|
||||
expiry: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
expires: 60 * 60 * 24 * 30, // 30 days
|
||||
},
|
||||
/**
|
||||
* Acknowledgment of the check.
|
||||
*
|
||||
* @type {Boolean}
|
||||
*/
|
||||
ack: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* Resolution date of the check (when the check was resolved).
|
||||
*
|
||||
* @type {Date}
|
||||
*/
|
||||
ackAt: {
|
||||
type: Date,
|
||||
},
|
||||
const cpuSchema = mongoose.Schema({
|
||||
physical_core: { type: Number, default: 0 },
|
||||
logical_core: { type: Number, default: 0 },
|
||||
frequency: { type: Number, default: 0 },
|
||||
temperature: { type: [Number], default: [] },
|
||||
free_percent: { type: Number, default: 0 },
|
||||
usage_percent: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
/**
|
||||
* Check Schema for MongoDB collection.
|
||||
*
|
||||
* Represents a check associated with a monitor, storing information
|
||||
* about the status and response of a particular check event.
|
||||
*/
|
||||
const CheckSchema = mongoose.Schema({ ...BaseCheckSchema.obj }, { timestamps: true });
|
||||
const memorySchema = mongoose.Schema({
|
||||
total_bytes: { type: Number, default: 0 },
|
||||
available_bytes: { type: Number, default: 0 },
|
||||
used_bytes: { type: Number, default: 0 },
|
||||
usage_percent: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
const diskSchema = mongoose.Schema({
|
||||
read_speed_bytes: { type: Number, default: 0 },
|
||||
write_speed_bytes: { type: Number, default: 0 },
|
||||
total_bytes: { type: Number, default: 0 },
|
||||
free_bytes: { type: Number, default: 0 },
|
||||
usage_percent: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
const hostSchema = mongoose.Schema({
|
||||
os: { type: String, default: "" },
|
||||
platform: { type: String, default: "" },
|
||||
kernel_version: { type: String, default: "" },
|
||||
});
|
||||
|
||||
const errorSchema = mongoose.Schema({
|
||||
metric: { type: [String], default: [] },
|
||||
err: { type: String, default: "" },
|
||||
});
|
||||
|
||||
const captureSchema = mongoose.Schema({
|
||||
version: { type: String, default: "" },
|
||||
mode: { type: String, default: "" },
|
||||
});
|
||||
|
||||
const networkInterfaceSchema = mongoose.Schema({
|
||||
name: { type: String },
|
||||
bytes_sent: { type: Number, default: 0 },
|
||||
bytes_recv: { type: Number, default: 0 },
|
||||
packets_sent: { type: Number, default: 0 },
|
||||
packets_recv: { type: Number, default: 0 },
|
||||
err_in: { type: Number, default: 0 },
|
||||
err_out: { type: Number, default: 0 },
|
||||
drop_in: { type: Number, default: 0 },
|
||||
drop_out: { type: Number, default: 0 },
|
||||
fifo_in: { type: Number, default: 0 },
|
||||
fifo_out: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
const CheckSchema = new mongoose.Schema(
|
||||
{
|
||||
// Common fields
|
||||
monitorId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Monitor",
|
||||
immutable: true,
|
||||
index: true,
|
||||
},
|
||||
|
||||
teamId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Team",
|
||||
immutable: true,
|
||||
index: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: ["http", "ping", "pagespeed", "hardware", "docker", "port", "game"],
|
||||
required: true,
|
||||
index: true,
|
||||
},
|
||||
|
||||
status: {
|
||||
type: Boolean,
|
||||
index: true,
|
||||
},
|
||||
|
||||
responseTime: {
|
||||
type: Number,
|
||||
},
|
||||
|
||||
timings: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
|
||||
statusCode: {
|
||||
type: Number,
|
||||
index: true,
|
||||
},
|
||||
|
||||
message: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
expiry: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
expires: 60 * 60 * 24 * 30, // 30 days
|
||||
},
|
||||
|
||||
ack: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
ackAt: {
|
||||
type: Date,
|
||||
},
|
||||
|
||||
// Hardware fields
|
||||
cpu: {
|
||||
type: cpuSchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
memory: {
|
||||
type: memorySchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
disk: {
|
||||
type: [diskSchema],
|
||||
default: () => [],
|
||||
},
|
||||
host: {
|
||||
type: hostSchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
errors: {
|
||||
type: [errorSchema],
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
capture: {
|
||||
type: captureSchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
net: {
|
||||
type: [networkInterfaceSchema],
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
// PageSpeed fields
|
||||
accessibility: {
|
||||
type: Number,
|
||||
},
|
||||
bestPractices: {
|
||||
type: Number,
|
||||
},
|
||||
seo: {
|
||||
type: Number,
|
||||
},
|
||||
performance: {
|
||||
type: Number,
|
||||
},
|
||||
audits: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
CheckSchema.index({ updatedAt: 1 });
|
||||
CheckSchema.index({ monitorId: 1, updatedAt: 1 });
|
||||
CheckSchema.index({ monitorId: 1, updatedAt: -1 });
|
||||
CheckSchema.index({ teamId: 1, updatedAt: -1 });
|
||||
|
||||
export default mongoose.model("Check", CheckSchema);
|
||||
export { BaseCheckSchema };
|
||||
@@ -1,100 +0,0 @@
|
||||
import mongoose from "mongoose";
|
||||
import { BaseCheckSchema } from "./Check.js";
|
||||
const cpuSchema = mongoose.Schema({
|
||||
physical_core: { type: Number, default: 0 },
|
||||
logical_core: { type: Number, default: 0 },
|
||||
frequency: { type: Number, default: 0 },
|
||||
temperature: { type: [Number], default: [] },
|
||||
free_percent: { type: Number, default: 0 },
|
||||
usage_percent: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
const memorySchema = mongoose.Schema({
|
||||
total_bytes: { type: Number, default: 0 },
|
||||
available_bytes: { type: Number, default: 0 },
|
||||
used_bytes: { type: Number, default: 0 },
|
||||
usage_percent: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
const diskSchema = mongoose.Schema({
|
||||
read_speed_bytes: { type: Number, default: 0 },
|
||||
write_speed_bytes: { type: Number, default: 0 },
|
||||
total_bytes: { type: Number, default: 0 },
|
||||
free_bytes: { type: Number, default: 0 },
|
||||
usage_percent: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
const hostSchema = mongoose.Schema({
|
||||
os: { type: String, default: "" },
|
||||
platform: { type: String, default: "" },
|
||||
kernel_version: { type: String, default: "" },
|
||||
});
|
||||
|
||||
const errorSchema = mongoose.Schema({
|
||||
metric: { type: [String], default: [] },
|
||||
err: { type: String, default: "" },
|
||||
});
|
||||
|
||||
const captureSchema = mongoose.Schema({
|
||||
version: { type: String, default: "" },
|
||||
mode: { type: String, default: "" },
|
||||
});
|
||||
|
||||
const networkInterfaceSchema = mongoose.Schema({
|
||||
name: { type: String },
|
||||
bytes_sent: { type: Number, default: 0 },
|
||||
bytes_recv: { type: Number, default: 0 },
|
||||
packets_sent: { type: Number, default: 0 },
|
||||
packets_recv: { type: Number, default: 0 },
|
||||
err_in: { type: Number, default: 0 },
|
||||
err_out: { type: Number, default: 0 },
|
||||
drop_in: { type: Number, default: 0 },
|
||||
drop_out: { type: Number, default: 0 },
|
||||
fifo_in: { type: Number, default: 0 },
|
||||
fifo_out: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
const HardwareCheckSchema = mongoose.Schema(
|
||||
{
|
||||
...BaseCheckSchema.obj,
|
||||
cpu: {
|
||||
type: cpuSchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
memory: {
|
||||
type: memorySchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
disk: {
|
||||
type: [diskSchema],
|
||||
default: () => [],
|
||||
},
|
||||
host: {
|
||||
type: hostSchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
errors: {
|
||||
type: [errorSchema],
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
capture: {
|
||||
type: captureSchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
net: {
|
||||
type: [networkInterfaceSchema],
|
||||
default: () => [],
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
HardwareCheckSchema.index({ createdAt: 1 });
|
||||
HardwareCheckSchema.index({ monitorId: 1, createdAt: 1 });
|
||||
HardwareCheckSchema.index({ monitorId: 1, createdAt: -1 });
|
||||
|
||||
export default mongoose.model("HardwareCheck", HardwareCheckSchema);
|
||||
@@ -1,6 +1,4 @@
|
||||
import mongoose from "mongoose";
|
||||
import HardwareCheck from "./HardwareCheck.js";
|
||||
import PageSpeedCheck from "./PageSpeedCheck.js";
|
||||
import Check from "./Check.js";
|
||||
import MonitorStats from "./MonitorStats.js";
|
||||
import StatusPage from "./StatusPage.js";
|
||||
@@ -30,6 +28,18 @@ const MonitorSchema = mongoose.Schema(
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
},
|
||||
statusWindow: {
|
||||
type: [Boolean],
|
||||
default: [],
|
||||
},
|
||||
statusWindowSize: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
statusWindowThreshold: {
|
||||
type: Number,
|
||||
default: 60,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
@@ -133,13 +143,7 @@ MonitorSchema.pre("findOneAndDelete", async function (next) {
|
||||
throw new Error("Monitor not found");
|
||||
}
|
||||
|
||||
if (doc?.type === "pagespeed") {
|
||||
await PageSpeedCheck.deleteMany({ monitorId: doc._id });
|
||||
} else if (doc?.type === "hardware") {
|
||||
await HardwareCheck.deleteMany({ monitorId: doc._id });
|
||||
} else {
|
||||
await Check.deleteMany({ monitorId: doc._id });
|
||||
}
|
||||
await Check.deleteMany({ monitorId: doc._id });
|
||||
|
||||
// Deal with status pages
|
||||
await StatusPage.updateMany({ monitors: doc?._id }, { $pull: { monitors: doc?._id } });
|
||||
@@ -156,13 +160,7 @@ MonitorSchema.pre("deleteMany", async function (next) {
|
||||
const monitors = await this.model.find(filter).select(["_id", "type"]).lean();
|
||||
|
||||
for (const monitor of monitors) {
|
||||
if (monitor.type === "pagespeed") {
|
||||
await PageSpeedCheck.deleteMany({ monitorId: monitor._id });
|
||||
} else if (monitor.type === "hardware") {
|
||||
await HardwareCheck.deleteMany({ monitorId: monitor._id });
|
||||
} else {
|
||||
await Check.deleteMany({ monitorId: monitor._id });
|
||||
}
|
||||
await Check.deleteMany({ monitorId: monitor._id });
|
||||
await StatusPage.updateMany({ monitors: monitor._id }, { $pull: { monitors: monitor._id } });
|
||||
await MonitorStats.deleteMany({ monitorId: monitor._id.toString() });
|
||||
}
|
||||
|
||||
@@ -40,10 +40,6 @@ const MonitorStatsSchema = new mongoose.Schema(
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
uptBurnt: {
|
||||
type: mongoose.Schema.Types.Decimal128,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import mongoose from "mongoose";
|
||||
import { BaseCheckSchema } from "./Check.js";
|
||||
|
||||
const networkInterfaceSchema = mongoose.Schema({
|
||||
name: { type: String, required: true },
|
||||
bytes_sent: { type: Number, default: 0 },
|
||||
bytes_recv: { type: Number, default: 0 },
|
||||
packets_sent: { type: Number, default: 0 },
|
||||
packets_recv: { type: Number, default: 0 },
|
||||
err_in: { type: Number, default: 0 },
|
||||
err_out: { type: Number, default: 0 },
|
||||
drop_in: { type: Number, default: 0 },
|
||||
drop_out: { type: Number, default: 0 },
|
||||
fifo_in: { type: Number, default: 0 },
|
||||
fifo_out: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
const captureSchema = mongoose.Schema({
|
||||
version: { type: String, default: "" },
|
||||
mode: { type: String, default: "" },
|
||||
});
|
||||
|
||||
const NetworkCheckSchema = mongoose.Schema(
|
||||
{
|
||||
...BaseCheckSchema.obj,
|
||||
data: {
|
||||
type: [networkInterfaceSchema],
|
||||
default: () => [],
|
||||
},
|
||||
capture: {
|
||||
type: captureSchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
errors: {
|
||||
type: mongoose.Schema.Types.Mixed,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
NetworkCheckSchema.index({ createdAt: 1 });
|
||||
NetworkCheckSchema.index({ monitorId: 1, createdAt: 1 });
|
||||
NetworkCheckSchema.index({ monitorId: 1, createdAt: -1 });
|
||||
|
||||
export default mongoose.model("NetworkCheck", NetworkCheckSchema);
|
||||
@@ -1,83 +0,0 @@
|
||||
import mongoose from "mongoose";
|
||||
import { BaseCheckSchema } from "./Check.js";
|
||||
const AuditSchema = mongoose.Schema({
|
||||
id: { type: String, required: true },
|
||||
title: { type: String, required: true },
|
||||
description: { type: String, required: true },
|
||||
score: { type: Number, required: true },
|
||||
scoreDisplayMode: { type: String, required: true },
|
||||
displayValue: { type: String, required: true },
|
||||
numericValue: { type: Number, required: true },
|
||||
numericUnit: { type: String, required: true },
|
||||
});
|
||||
|
||||
const AuditsSchema = mongoose.Schema({
|
||||
cls: {
|
||||
type: AuditSchema,
|
||||
required: true,
|
||||
},
|
||||
si: {
|
||||
type: AuditSchema,
|
||||
required: true,
|
||||
},
|
||||
fcp: {
|
||||
type: AuditSchema,
|
||||
required: true,
|
||||
},
|
||||
lcp: {
|
||||
type: AuditSchema,
|
||||
required: true,
|
||||
},
|
||||
tbt: {
|
||||
type: AuditSchema,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Mongoose schema for storing metrics from Google Lighthouse.
|
||||
* @typedef {Object} PageSpeedCheck
|
||||
* @property {mongoose.Schema.Types.ObjectId} monitorId - Reference to the Monitor model.
|
||||
* @property {number} accessibility - Accessibility score.
|
||||
* @property {number} bestPractices - Best practices score.
|
||||
* @property {number} seo - SEO score.
|
||||
* @property {number} performance - Performance score.
|
||||
*/
|
||||
|
||||
const PageSpeedCheck = mongoose.Schema(
|
||||
{
|
||||
...BaseCheckSchema.obj,
|
||||
accessibility: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
bestPractices: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
seo: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
performance: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
audits: {
|
||||
type: AuditsSchema,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
/**
|
||||
* Mongoose model for storing metrics from Google Lighthouse.
|
||||
* @typedef {mongoose.Model<PageSpeedCheck>} LighthouseMetricsModel
|
||||
*/
|
||||
|
||||
PageSpeedCheck.index({ createdAt: 1 });
|
||||
PageSpeedCheck.index({ monitorId: 1, createdAt: 1 });
|
||||
PageSpeedCheck.index({ monitorId: 1, createdAt: -1 });
|
||||
|
||||
export default mongoose.model("PageSpeedCheck", PageSpeedCheck);
|
||||
@@ -1,6 +1,6 @@
|
||||
import mongoose from "mongoose";
|
||||
import AppSettings from "../models/AppSettings.js";
|
||||
|
||||
import { runMigrations } from "./migration/index.js";
|
||||
class MongoDB {
|
||||
static SERVICE_NAME = "MongoDB";
|
||||
|
||||
@@ -65,6 +65,8 @@ class MongoDB {
|
||||
service: this.SERVICE_NAME,
|
||||
method: "connect",
|
||||
});
|
||||
|
||||
await runMigrations();
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import Monitor from "../../models/Monitor.js";
|
||||
async function migrateStatusWindowThreshold() {
|
||||
try {
|
||||
const monitors = await Monitor.find({ statusWindowThreshold: { $lt: 1 } });
|
||||
for (const monitor of monitors) {
|
||||
monitor.statusWindowThreshold = monitor.statusWindowThreshold * 100;
|
||||
await monitor.save();
|
||||
console.log(`Migrated monitor ${monitor._id}: statusWindowThreshold set to ${monitor.statusWindowThreshold}`);
|
||||
}
|
||||
console.log("StatusWindowThreshold migration complete.");
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error("Migration error:", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export { migrateStatusWindowThreshold };
|
||||
@@ -0,0 +1,7 @@
|
||||
import { migrateStatusWindowThreshold } from "./0001_migrateStatusWindowThreshold.js";
|
||||
|
||||
const runMigrations = async () => {
|
||||
await migrateStatusWindowThreshold();
|
||||
};
|
||||
|
||||
export { runMigrations };
|
||||
@@ -31,7 +31,7 @@ class CheckModule {
|
||||
}
|
||||
};
|
||||
|
||||
getChecksByMonitor = async ({ monitorId, type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status }) => {
|
||||
getChecksByMonitor = async ({ monitorId, sortOrder, dateRange, filter, ack, page, rowsPerPage, status }) => {
|
||||
try {
|
||||
status = status === "true" ? true : status === "false" ? false : undefined;
|
||||
page = parseInt(page);
|
||||
@@ -79,19 +79,7 @@ class CheckModule {
|
||||
skip = page * rowsPerPage;
|
||||
}
|
||||
|
||||
const checkModels = {
|
||||
http: this.Check,
|
||||
ping: this.Check,
|
||||
docker: this.Check,
|
||||
port: this.Check,
|
||||
pagespeed: this.PageSpeedCheck,
|
||||
hardware: this.HardwareCheck,
|
||||
game: this.Check,
|
||||
};
|
||||
|
||||
const Model = checkModels[type];
|
||||
|
||||
const checks = await Model.aggregate([
|
||||
const checks = await this.Check.aggregate([
|
||||
{ $match: matchStage },
|
||||
{ $sort: { createdAt: sortOrder } },
|
||||
{
|
||||
@@ -166,18 +154,6 @@ class CheckModule {
|
||||
|
||||
const aggregatePipeline = [
|
||||
{ $match: matchStage },
|
||||
{
|
||||
$unionWith: {
|
||||
coll: "hardwarechecks",
|
||||
pipeline: [{ $match: matchStage }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$unionWith: {
|
||||
coll: "pagespeedchecks",
|
||||
pipeline: [{ $match: matchStage }],
|
||||
},
|
||||
},
|
||||
|
||||
{ $sort: { createdAt: sortOrder } },
|
||||
{
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
const SERVICE_NAME = "hardwareCheckModule";
|
||||
|
||||
class HardwareCheckModule {
|
||||
constructor({ HardwareCheck, Monitor, logger }) {
|
||||
this.HardwareCheck = HardwareCheck;
|
||||
this.Monitor = Monitor;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
createHardwareChecks = async (hardwareChecks) => {
|
||||
try {
|
||||
await this.HardwareCheck.insertMany(hardwareChecks, { ordered: false });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createHardwareChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default HardwareCheckModule;
|
||||
@@ -11,25 +11,10 @@ import {
|
||||
const SERVICE_NAME = "monitorModule";
|
||||
|
||||
class MonitorModule {
|
||||
constructor({
|
||||
Monitor,
|
||||
MonitorStats,
|
||||
Check,
|
||||
PageSpeedCheck,
|
||||
HardwareCheck,
|
||||
stringService,
|
||||
fs,
|
||||
path,
|
||||
fileURLToPath,
|
||||
ObjectId,
|
||||
NormalizeData,
|
||||
NormalizeDataUptimeDetails,
|
||||
}) {
|
||||
constructor({ Monitor, MonitorStats, Check, stringService, fs, path, fileURLToPath, ObjectId, NormalizeData, NormalizeDataUptimeDetails }) {
|
||||
this.Monitor = Monitor;
|
||||
this.MonitorStats = MonitorStats;
|
||||
this.Check = Check;
|
||||
this.PageSpeedCheck = PageSpeedCheck;
|
||||
this.HardwareCheck = HardwareCheck;
|
||||
this.stringService = stringService;
|
||||
this.fs = fs;
|
||||
this.path = path;
|
||||
@@ -38,15 +23,6 @@ class MonitorModule {
|
||||
this.NormalizeData = NormalizeData;
|
||||
this.NormalizeDataUptimeDetails = NormalizeDataUptimeDetails;
|
||||
|
||||
this.CHECK_MODEL_LOOKUP = {
|
||||
http: Check,
|
||||
ping: Check,
|
||||
docker: Check,
|
||||
port: Check,
|
||||
pagespeed: PageSpeedCheck,
|
||||
hardware: HardwareCheck,
|
||||
};
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
@@ -146,19 +122,18 @@ class MonitorModule {
|
||||
};
|
||||
|
||||
//Helper
|
||||
getMonitorChecks = async (monitorId, model, dateRange, sortOrder) => {
|
||||
getMonitorChecks = async (monitorId, dateRange, sortOrder) => {
|
||||
const indexSpec = {
|
||||
monitorId: 1,
|
||||
createdAt: sortOrder, // This will be 1 or -1
|
||||
updatedAt: sortOrder, // This will be 1 or -1
|
||||
};
|
||||
|
||||
const [checksAll, checksForDateRange] = await Promise.all([
|
||||
model.find({ monitorId }).sort({ createdAt: sortOrder }).hint(indexSpec).lean(),
|
||||
model
|
||||
.find({
|
||||
monitorId,
|
||||
createdAt: { $gte: dateRange.start, $lte: dateRange.end },
|
||||
})
|
||||
this.Check.find({ monitorId }).sort({ createdAt: sortOrder }).hint(indexSpec).lean(),
|
||||
this.Check.find({
|
||||
monitorId,
|
||||
createdAt: { $gte: dateRange.start, $lte: dateRange.end },
|
||||
})
|
||||
.hint(indexSpec)
|
||||
.lean(),
|
||||
]);
|
||||
@@ -295,9 +270,8 @@ class MonitorModule {
|
||||
const sort = sortOrder === "asc" ? 1 : -1;
|
||||
|
||||
// Get Checks for monitor in date range requested
|
||||
const model = this.CHECK_MODEL_LOOKUP[monitor.type];
|
||||
const dates = this.getDateRange(dateRange);
|
||||
const { checksAll, checksForDateRange } = await this.getMonitorChecks(monitorId, model, dates, sort);
|
||||
const { checksAll, checksForDateRange } = await this.getMonitorChecks(monitorId, dates, sort);
|
||||
|
||||
// Build monitor stats
|
||||
const monitorStats = {
|
||||
@@ -339,7 +313,7 @@ class MonitorModule {
|
||||
};
|
||||
const dateString = formatLookup[dateRange];
|
||||
|
||||
const hardwareStats = await this.HardwareCheck.aggregate(buildHardwareDetailsPipeline(monitor, dates, dateString));
|
||||
const hardwareStats = await this.Check.aggregate(buildHardwareDetailsPipeline(monitor, dates, dateString));
|
||||
|
||||
const stats = hardwareStats[0];
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ const buildUptimeDetailsPipeline = (monitorId, dates, dateString) => {
|
||||
{
|
||||
$match: {
|
||||
monitorId: new ObjectId(monitorId),
|
||||
createdAt: { $gte: dates.start, $lte: dates.end },
|
||||
updatedAt: { $gte: dates.start, $lte: dates.end },
|
||||
},
|
||||
},
|
||||
{
|
||||
$sort: {
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -173,6 +173,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
{
|
||||
$match: {
|
||||
monitorId: monitor._id,
|
||||
type: "hardware",
|
||||
createdAt: { $gte: dates.start, $lte: dates.end },
|
||||
},
|
||||
},
|
||||
@@ -218,14 +219,14 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
{
|
||||
$project: {
|
||||
diskCount: {
|
||||
$size: "$disk",
|
||||
$size: { $ifNull: ["$disk", []] },
|
||||
},
|
||||
netCount: { $size: "$net" },
|
||||
netCount: { $size: { $ifNull: ["$net", []] } },
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "hardwarechecks",
|
||||
from: "checks",
|
||||
let: {
|
||||
diskCount: "$diskCount",
|
||||
netCount: "$netCount",
|
||||
@@ -381,7 +382,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
},
|
||||
net: {
|
||||
$map: {
|
||||
input: { $range: [0, { $size: { $arrayElemAt: ["$net", 0] } }] },
|
||||
input: { $range: [0, { $size: { $ifNull: [{ $arrayElemAt: ["$net", 0] }, []] } }] },
|
||||
as: "netIndex",
|
||||
in: {
|
||||
name: {
|
||||
@@ -409,7 +410,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
$arrayElemAt: [
|
||||
{
|
||||
$map: {
|
||||
input: { $arrayElemAt: ["$net", { $subtract: [{ $size: "$net" }, 1] }] },
|
||||
input: {
|
||||
$arrayElemAt: [{ $ifNull: ["$net", []] }, { $subtract: [{ $size: { $ifNull: ["$net", []] } }, 1] }],
|
||||
},
|
||||
as: "iface",
|
||||
in: "$$iface.bytes_sent",
|
||||
},
|
||||
@@ -418,7 +421,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
],
|
||||
},
|
||||
tFirst: { $arrayElemAt: ["$updatedAts", 0] },
|
||||
tLast: { $arrayElemAt: ["$updatedAts", { $subtract: [{ $size: "$updatedAts" }, 1] }] },
|
||||
tLast: {
|
||||
$arrayElemAt: [{ $ifNull: ["$updatedAts", []] }, { $subtract: [{ $size: { $ifNull: ["$updatedAts", []] } }, 1] }],
|
||||
},
|
||||
},
|
||||
in: {
|
||||
$cond: [
|
||||
@@ -444,7 +449,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
$arrayElemAt: [
|
||||
{
|
||||
$map: {
|
||||
input: { $arrayElemAt: ["$net", { $subtract: [{ $size: "$net" }, 1] }] },
|
||||
input: {
|
||||
$arrayElemAt: [{ $ifNull: ["$net", []] }, { $subtract: [{ $size: { $ifNull: ["$net", []] } }, 1] }],
|
||||
},
|
||||
as: "iface",
|
||||
in: "$$iface.bytes_recv",
|
||||
},
|
||||
@@ -453,7 +460,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
],
|
||||
},
|
||||
tFirst: { $arrayElemAt: ["$updatedAts", 0] },
|
||||
tLast: { $arrayElemAt: ["$updatedAts", { $subtract: [{ $size: "$updatedAts" }, 1] }] },
|
||||
tLast: {
|
||||
$arrayElemAt: [{ $ifNull: ["$updatedAts", []] }, { $subtract: [{ $size: { $ifNull: ["$updatedAts", []] } }, 1] }],
|
||||
},
|
||||
},
|
||||
in: {
|
||||
$cond: [
|
||||
@@ -479,7 +488,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
$arrayElemAt: [
|
||||
{
|
||||
$map: {
|
||||
input: { $arrayElemAt: ["$net", { $subtract: [{ $size: "$net" }, 1] }] },
|
||||
input: {
|
||||
$arrayElemAt: [{ $ifNull: ["$net", []] }, { $subtract: [{ $size: { $ifNull: ["$net", []] } }, 1] }],
|
||||
},
|
||||
as: "iface",
|
||||
in: "$$iface.packets_sent",
|
||||
},
|
||||
@@ -488,7 +499,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
],
|
||||
},
|
||||
tFirst: { $arrayElemAt: ["$updatedAts", 0] },
|
||||
tLast: { $arrayElemAt: ["$updatedAts", { $subtract: [{ $size: "$updatedAts" }, 1] }] },
|
||||
tLast: {
|
||||
$arrayElemAt: [{ $ifNull: ["$updatedAts", []] }, { $subtract: [{ $size: { $ifNull: ["$updatedAts", []] } }, 1] }],
|
||||
},
|
||||
},
|
||||
in: {
|
||||
$cond: [
|
||||
@@ -522,9 +535,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
$map: {
|
||||
input: {
|
||||
$arrayElemAt: [
|
||||
"$net",
|
||||
{ $ifNull: ["$net", []] },
|
||||
{
|
||||
$subtract: [{ $size: "$net" }, 1],
|
||||
$subtract: [{ $size: { $ifNull: ["$net", []] } }, 1],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -538,9 +551,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
tFirst: { $arrayElemAt: ["$updatedAts", 0] },
|
||||
tLast: {
|
||||
$arrayElemAt: [
|
||||
"$updatedAts",
|
||||
{ $ifNull: ["$updatedAts", []] },
|
||||
{
|
||||
$subtract: [{ $size: "$updatedAts" }, 1],
|
||||
$subtract: [{ $size: { $ifNull: ["$updatedAts", []] } }, 1],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -566,7 +579,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
$arrayElemAt: [
|
||||
{
|
||||
$map: {
|
||||
input: { $arrayElemAt: ["$net", { $subtract: [{ $size: "$net" }, 1] }] },
|
||||
input: {
|
||||
$arrayElemAt: [{ $ifNull: ["$net", []] }, { $subtract: [{ $size: { $ifNull: ["$net", []] } }, 1] }],
|
||||
},
|
||||
as: "iface",
|
||||
in: "$$iface.err_in",
|
||||
},
|
||||
@@ -575,7 +590,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
],
|
||||
},
|
||||
tFirst: { $arrayElemAt: ["$updatedAts", 0] },
|
||||
tLast: { $arrayElemAt: ["$updatedAts", { $subtract: [{ $size: "$updatedAts" }, 1] }] },
|
||||
tLast: {
|
||||
$arrayElemAt: [{ $ifNull: ["$updatedAts", []] }, { $subtract: [{ $size: { $ifNull: ["$updatedAts", []] } }, 1] }],
|
||||
},
|
||||
},
|
||||
in: {
|
||||
$cond: [
|
||||
@@ -609,9 +626,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
$map: {
|
||||
input: {
|
||||
$arrayElemAt: [
|
||||
"$net",
|
||||
{ $ifNull: ["$net", []] },
|
||||
{
|
||||
$subtract: [{ $size: "$net" }, 1],
|
||||
$subtract: [{ $size: { $ifNull: ["$net", []] } }, 1],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -625,9 +642,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
tFirst: { $arrayElemAt: ["$updatedAts", 0] },
|
||||
tLast: {
|
||||
$arrayElemAt: [
|
||||
"$updatedAts",
|
||||
{ $ifNull: ["$updatedAts", []] },
|
||||
{
|
||||
$subtract: [{ $size: "$updatedAts" }, 1],
|
||||
$subtract: [{ $size: { $ifNull: ["$updatedAts", []] } }, 1],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -664,9 +681,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
$map: {
|
||||
input: {
|
||||
$arrayElemAt: [
|
||||
"$net",
|
||||
{ $ifNull: ["$net", []] },
|
||||
{
|
||||
$subtract: [{ $size: "$net" }, 1],
|
||||
$subtract: [{ $size: { $ifNull: ["$net", []] } }, 1],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -680,9 +697,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
tFirst: { $arrayElemAt: ["$updatedAts", 0] },
|
||||
tLast: {
|
||||
$arrayElemAt: [
|
||||
"$updatedAts",
|
||||
{ $ifNull: ["$updatedAts", []] },
|
||||
{
|
||||
$subtract: [{ $size: "$updatedAts" }, 1],
|
||||
$subtract: [{ $size: { $ifNull: ["$updatedAts", []] } }, 1],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -719,9 +736,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
$map: {
|
||||
input: {
|
||||
$arrayElemAt: [
|
||||
"$net",
|
||||
{ $ifNull: ["$net", []] },
|
||||
{
|
||||
$subtract: [{ $size: "$net" }, 1],
|
||||
$subtract: [{ $size: { $ifNull: ["$net", []] } }, 1],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -735,9 +752,9 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
tFirst: { $arrayElemAt: ["$updatedAts", 0] },
|
||||
tLast: {
|
||||
$arrayElemAt: [
|
||||
"$updatedAts",
|
||||
{ $ifNull: ["$updatedAts", []] },
|
||||
{
|
||||
$subtract: [{ $size: "$updatedAts" }, 1],
|
||||
$subtract: [{ $size: { $ifNull: ["$updatedAts", []] } }, 1],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -970,12 +987,7 @@ const buildMonitorsWithChecksByTeamIdPipeline = ({ matchStage, filter, page, row
|
||||
|
||||
// Add checks
|
||||
if (limit) {
|
||||
let checksCollection = "checks";
|
||||
if (type === "pagespeed") {
|
||||
checksCollection = "pagespeedchecks";
|
||||
} else if (type === "hardware") {
|
||||
checksCollection = "hardwarechecks";
|
||||
}
|
||||
const checksCollection = "checks";
|
||||
monitorsPipeline.push({
|
||||
$lookup: {
|
||||
from: checksCollection,
|
||||
@@ -1040,12 +1052,8 @@ const buildFilteredMonitorsByTeamIdPipeline = ({ matchStage, filter, page, rowsP
|
||||
|
||||
// Add checks
|
||||
if (limit) {
|
||||
let checksCollection = "checks";
|
||||
if (type === "pagespeed") {
|
||||
checksCollection = "pagespeedchecks";
|
||||
} else if (type === "hardware") {
|
||||
checksCollection = "hardwarechecks";
|
||||
}
|
||||
const checksCollection = "checks";
|
||||
|
||||
pipeline.push({
|
||||
$lookup: {
|
||||
from: checksCollection,
|
||||
@@ -1159,46 +1167,6 @@ const buildGetMonitorsByTeamIdPipeline = (req) => {
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(limit
|
||||
? [
|
||||
{
|
||||
$lookup: {
|
||||
from: "pagespeedchecks",
|
||||
let: { monitorId: "$_id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
...(limit ? [{ $limit: limit }] : []),
|
||||
],
|
||||
as: "pagespeedchecks",
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(limit
|
||||
? [
|
||||
{
|
||||
$lookup: {
|
||||
from: "hardwarechecks",
|
||||
let: { monitorId: "$_id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
...(limit ? [{ $limit: limit }] : []),
|
||||
],
|
||||
as: "hardwarechecks",
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
{
|
||||
$addFields: {
|
||||
@@ -1209,14 +1177,6 @@ const buildGetMonitorsByTeamIdPipeline = (req) => {
|
||||
case: { $in: ["$type", ["http", "ping", "docker", "port", "game"]] },
|
||||
then: "$standardchecks",
|
||||
},
|
||||
{
|
||||
case: { $eq: ["$type", "pagespeed"] },
|
||||
then: "$pagespeedchecks",
|
||||
},
|
||||
{
|
||||
case: { $eq: ["$type", "hardware"] },
|
||||
then: "$hardwarechecks",
|
||||
},
|
||||
],
|
||||
default: [],
|
||||
},
|
||||
@@ -1226,8 +1186,6 @@ const buildGetMonitorsByTeamIdPipeline = (req) => {
|
||||
{
|
||||
$project: {
|
||||
standardchecks: 0,
|
||||
pagespeedchecks: 0,
|
||||
hardwarechecks: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
const SERVICE_NAME = "networkCheckModule";
|
||||
|
||||
class NetworkCheckModule {
|
||||
constructor({ NetworkCheck }) {
|
||||
this.NetworkCheck = NetworkCheck;
|
||||
}
|
||||
createNetworkCheck = async (networkCheckData) => {
|
||||
try {
|
||||
const networkCheck = await new this.NetworkCheck(networkCheckData);
|
||||
await networkCheck.save();
|
||||
return networkCheck;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createNetworkCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getNetworkChecksByMonitorId = async (monitorId, limit = 100) => {
|
||||
try {
|
||||
const networkChecks = await this.NetworkCheck.find({ monitorId }).sort({ createdAt: -1 }).limit(limit);
|
||||
return networkChecks;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNetworkChecksByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default NetworkCheckModule;
|
||||
@@ -1,31 +0,0 @@
|
||||
// import PageSpeedCheck from "../../models/PageSpeedCheck.js";
|
||||
const SERVICE_NAME = "pageSpeedCheckModule";
|
||||
|
||||
class PageSpeedCheckModule {
|
||||
constructor({ PageSpeedCheck }) {
|
||||
this.PageSpeedCheck = PageSpeedCheck;
|
||||
}
|
||||
|
||||
createPageSpeedChecks = async (pageSpeedChecks) => {
|
||||
try {
|
||||
await this.PageSpeedCheck.insertMany(pageSpeedChecks, { ordered: false });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createPageSpeedCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
deletePageSpeedChecksByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await this.PageSpeedCheck.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deletePageSpeedChecksByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
export default PageSpeedCheckModule;
|
||||
@@ -162,6 +162,14 @@ class StatusPageModule {
|
||||
as: "monitors.checks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "monitorstats",
|
||||
localField: "monitors._id",
|
||||
foreignField: "monitorId",
|
||||
as: "monitors.stats",
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
"monitors.orderIndex": {
|
||||
@@ -181,6 +189,9 @@ class StatusPageModule {
|
||||
},
|
||||
},
|
||||
},
|
||||
"monitors.uptimePercentage": {
|
||||
$arrayElemAt: ["$monitors.stats.uptimePercentage", 0],
|
||||
},
|
||||
},
|
||||
},
|
||||
{ $match: { "monitors.orderIndex": { $ne: -1 } } },
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ const startApp = async () => {
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const openApiSpec = JSON.parse(fs.readFileSync(path.join(__dirname, "../openapi.json"), "utf8"));
|
||||
const frontendPath = path.join(__dirname, "public");
|
||||
const frontendPath = path.join(__dirname, "..", "public");
|
||||
|
||||
// Create services
|
||||
const settingsService = new SettingsService(AppSettings);
|
||||
|
||||
Loaded 100 of 108 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user