diff --git a/client/package-lock.json b/client/package-lock.json
index 4050adfbb..f58f56f44 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -45,7 +45,7 @@
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.6",
"prettier": "^3.3.3",
- "vite": "^5.4.19"
+ "vite": "6.3.5"
},
"optionalDependencies": {
"@rollup/rollup-linux-arm64-musl": "4.41.0"
@@ -492,9 +492,9 @@
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
- "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "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"
],
@@ -504,13 +504,13 @@
"aix"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
- "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
+ "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
"cpu": [
"arm"
],
@@ -520,13 +520,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
- "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "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"
],
@@ -536,13 +536,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
- "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "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"
],
@@ -552,13 +552,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
- "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "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"
],
@@ -568,13 +568,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
- "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "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"
],
@@ -584,13 +584,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
- "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "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"
],
@@ -600,13 +600,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
- "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "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"
],
@@ -616,13 +616,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
- "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
+ "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
"cpu": [
"arm"
],
@@ -632,13 +632,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
- "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "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"
],
@@ -648,13 +648,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
- "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "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"
],
@@ -664,13 +664,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
- "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
+ "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
"cpu": [
"loong64"
],
@@ -680,13 +680,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
- "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "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"
],
@@ -696,13 +696,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
- "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "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"
],
@@ -712,13 +712,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
- "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "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"
],
@@ -728,13 +728,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
- "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
+ "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
"cpu": [
"s390x"
],
@@ -744,13 +744,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
- "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
+ "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
"cpu": [
"x64"
],
@@ -760,13 +760,29 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "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.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
- "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "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"
],
@@ -776,13 +792,29 @@
"netbsd"
],
"engines": {
- "node": ">=12"
+ "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.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
- "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "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"
],
@@ -792,13 +824,13 @@
"openbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
- "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "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"
],
@@ -808,13 +840,13 @@
"sunos"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
- "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "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"
],
@@ -824,13 +856,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
- "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
+ "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
"cpu": [
"ia32"
],
@@ -840,13 +872,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
- "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "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"
],
@@ -856,7 +888,7 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@eslint-community/eslint-utils": {
@@ -3040,9 +3072,9 @@
}
},
"node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3172,23 +3204,6 @@
],
"license": "CC-BY-4.0"
},
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -3259,6 +3274,15 @@
"node": ">=10"
}
},
+ "node_modules/cosmiconfig/node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -3820,41 +3844,43 @@
}
},
"node_modules/esbuild": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
- "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
+ "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
- "node": ">=12"
+ "node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.21.5",
- "@esbuild/android-arm": "0.21.5",
- "@esbuild/android-arm64": "0.21.5",
- "@esbuild/android-x64": "0.21.5",
- "@esbuild/darwin-arm64": "0.21.5",
- "@esbuild/darwin-x64": "0.21.5",
- "@esbuild/freebsd-arm64": "0.21.5",
- "@esbuild/freebsd-x64": "0.21.5",
- "@esbuild/linux-arm": "0.21.5",
- "@esbuild/linux-arm64": "0.21.5",
- "@esbuild/linux-ia32": "0.21.5",
- "@esbuild/linux-loong64": "0.21.5",
- "@esbuild/linux-mips64el": "0.21.5",
- "@esbuild/linux-ppc64": "0.21.5",
- "@esbuild/linux-riscv64": "0.21.5",
- "@esbuild/linux-s390x": "0.21.5",
- "@esbuild/linux-x64": "0.21.5",
- "@esbuild/netbsd-x64": "0.21.5",
- "@esbuild/openbsd-x64": "0.21.5",
- "@esbuild/sunos-x64": "0.21.5",
- "@esbuild/win32-arm64": "0.21.5",
- "@esbuild/win32-ia32": "0.21.5",
- "@esbuild/win32-x64": "0.21.5"
+ "@esbuild/aix-ppc64": "0.25.5",
+ "@esbuild/android-arm": "0.25.5",
+ "@esbuild/android-arm64": "0.25.5",
+ "@esbuild/android-x64": "0.25.5",
+ "@esbuild/darwin-arm64": "0.25.5",
+ "@esbuild/darwin-x64": "0.25.5",
+ "@esbuild/freebsd-arm64": "0.25.5",
+ "@esbuild/freebsd-x64": "0.25.5",
+ "@esbuild/linux-arm": "0.25.5",
+ "@esbuild/linux-arm64": "0.25.5",
+ "@esbuild/linux-ia32": "0.25.5",
+ "@esbuild/linux-loong64": "0.25.5",
+ "@esbuild/linux-mips64el": "0.25.5",
+ "@esbuild/linux-ppc64": "0.25.5",
+ "@esbuild/linux-riscv64": "0.25.5",
+ "@esbuild/linux-s390x": "0.25.5",
+ "@esbuild/linux-x64": "0.25.5",
+ "@esbuild/netbsd-arm64": "0.25.5",
+ "@esbuild/netbsd-x64": "0.25.5",
+ "@esbuild/openbsd-arm64": "0.25.5",
+ "@esbuild/openbsd-x64": "0.25.5",
+ "@esbuild/sunos-x64": "0.25.5",
+ "@esbuild/win32-arm64": "0.25.5",
+ "@esbuild/win32-ia32": "0.25.5",
+ "@esbuild/win32-x64": "0.25.5"
}
},
"node_modules/escalade": {
@@ -4052,6 +4078,23 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
"node_modules/eslint/node_modules/globals": {
"version": "13.24.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
@@ -4184,6 +4227,20 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/fdir": {
+ "version": "6.4.6",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -6713,6 +6770,22 @@
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"license": "MIT"
},
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -6923,20 +6996,23 @@
}
},
"node_modules/vite": {
- "version": "5.4.19",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
- "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
+ "version": "6.3.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
+ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
"license": "MIT",
"dependencies": {
- "esbuild": "^0.21.3",
- "postcss": "^8.4.43",
- "rollup": "^4.20.0"
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
- "node": "^18.0.0 || >=20.0.0"
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -6945,19 +7021,25 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
- "@types/node": "^18.0.0 || >=20.0.0",
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
- "terser": "^5.4.0"
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
+ "jiti": {
+ "optional": true
+ },
"less": {
"optional": true
},
@@ -6978,6 +7060,12 @@
},
"terser": {
"optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
}
}
},
@@ -7133,12 +7221,17 @@
"license": "ISC"
},
"node_modules/yaml": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
+ "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
"license": "ISC",
+ "optional": true,
+ "peer": true,
+ "bin": {
+ "yaml": "bin.mjs"
+ },
"engines": {
- "node": ">= 6"
+ "node": ">= 14.6"
}
},
"node_modules/yocto-queue": {
diff --git a/client/package.json b/client/package.json
index 83dfe8407..611ba5093 100644
--- a/client/package.json
+++ b/client/package.json
@@ -58,7 +58,7 @@
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.6",
"prettier": "^3.3.3",
- "vite": "^5.4.19"
+ "vite": "6.3.5"
},
"optionalDependencies": {
"@rollup/rollup-linux-arm64-musl": "4.41.0"
diff --git a/client/src/Components/Layouts/AppLayout/index.jsx b/client/src/Components/Layouts/AppLayout/index.jsx
index 564012d72..d13f6d879 100644
--- a/client/src/Components/Layouts/AppLayout/index.jsx
+++ b/client/src/Components/Layouts/AppLayout/index.jsx
@@ -2,16 +2,17 @@ import Box from "@mui/material/Box";
import PropTypes from "prop-types";
import { useTheme } from "@emotion/react";
import BackgroundSVG from "../../../assets/Images/background.svg";
+import { useSelector } from "react-redux";
const AppLayout = ({ children }) => {
const theme = useTheme();
-
+ const ui = useSelector((state) => state.ui);
return (
{
+const Link = ({ level, label, url, external = true }) => {
const theme = useTheme();
const levelConfig = {
@@ -49,11 +51,12 @@ const Link = ({ level, label, url }) => {
const { sx, color } = levelConfig[level];
return (
{label}
@@ -64,6 +67,7 @@ Link.propTypes = {
url: PropTypes.string.isRequired,
level: PropTypes.oneOf(["primary", "secondary", "tertiary", "error"]),
label: PropTypes.string.isRequired,
+ external: PropTypes.bool,
};
export default Link;
diff --git a/client/src/Components/MonitorActions/index.jsx b/client/src/Components/MonitorActions/index.jsx
new file mode 100644
index 000000000..ac5d89a47
--- /dev/null
+++ b/client/src/Components/MonitorActions/index.jsx
@@ -0,0 +1,116 @@
+import * as React from "react";
+import Button from "@mui/material/Button";
+import ButtonGroup from "@mui/material/ButtonGroup";
+import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
+import ClickAwayListener from "@mui/material/ClickAwayListener";
+import Grow from "@mui/material/Grow";
+import Paper from "@mui/material/Paper";
+import Popper from "@mui/material/Popper";
+import MenuItem from "@mui/material/MenuItem";
+import MenuList from "@mui/material/MenuList";
+import { useNavigate } from "react-router-dom";
+import { useTranslation } from "react-i18next";
+import { createToast } from "../../Utils/toastUtils";
+import { useExportMonitors } from "../../Hooks/monitorHooks";
+
+const MonitorActions = ({ isLoading }) => {
+ const [open, setOpen] = React.useState(false);
+ const anchorRef = React.useRef(null);
+ const [selectedIndex, setSelectedIndex] = React.useState(0);
+ const navigate = useNavigate();
+ const { t } = useTranslation();
+ const [exportMonitors, isExporting] = useExportMonitors();
+
+ const options = [t("monitorActions.import"), t("monitorActions.export")];
+
+ const handleClick = async () => {
+ if (selectedIndex === 0) {
+ // Import
+ navigate("/uptime/bulk-import");
+ } else {
+ // Export
+ const [success, error] = await exportMonitors();
+ if (!success) {
+ createToast({ body: error || t("export.failed") });
+ }
+ }
+ };
+
+ const handleMenuItemClick = (event, index) => {
+ setSelectedIndex(index);
+ setOpen(false);
+ };
+
+ const handleToggle = () => {
+ setOpen((prevOpen) => !prevOpen);
+ };
+
+ const handleClose = (event) => {
+ if (anchorRef.current && anchorRef.current.contains(event.target)) {
+ return;
+ }
+ setOpen(false);
+ };
+
+ return (
+
+
+
+
+
+
+ {({ TransitionProps, placement }) => (
+
+
+
+
+
+
+
+ )}
+
+
+ );
+};
+
+export default MonitorActions;
diff --git a/client/src/Components/MonitorCreateHeader/index.jsx b/client/src/Components/MonitorCreateHeader/index.jsx
index b630cb259..b3b50681c 100644
--- a/client/src/Components/MonitorCreateHeader/index.jsx
+++ b/client/src/Components/MonitorCreateHeader/index.jsx
@@ -3,6 +3,7 @@ import { useNavigate } from "react-router-dom";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import { useTheme } from "@emotion/react";
+import MonitorActions from "../MonitorActions";
const CreateMonitorHeader = ({ isAdmin, label, isLoading = true, path, bulkPath }) => {
const navigate = useNavigate();
@@ -28,18 +29,7 @@ const CreateMonitorHeader = ({ isAdmin, label, isLoading = true, path, bulkPath
>
{label || t("createNew")}
- {bulkPath && (
-
- )}
+ {/* {bulkPath && } */}
);
};
diff --git a/client/src/Components/Sidebar/index.jsx b/client/src/Components/Sidebar/index.jsx
index 06a140c54..e9f0d3ad7 100644
--- a/client/src/Components/Sidebar/index.jsx
+++ b/client/src/Components/Sidebar/index.jsx
@@ -8,7 +8,6 @@ import {
ListItemButton,
ListItemIcon,
ListItemText,
- ListSubheader,
Menu,
MenuItem,
Stack,
@@ -39,6 +38,7 @@ import Docs from "../../assets/icons/docs.svg?react";
import StatusPages from "../../assets/icons/status-pages.svg?react";
import Discussions from "../../assets/icons/discussions.svg?react";
import Notifications from "../../assets/icons/notifications.svg?react";
+import Logs from "../../assets/icons/logs.svg?react";
import "./index.css";
@@ -64,6 +64,7 @@ const getMenu = (t) => [
{ name: t("menu.statusPages"), path: "status", icon: },
{ name: t("menu.maintenance"), path: "maintenance", icon: },
+ { name: t("menu.logs"), path: "logs", icon: },
{
name: t("menu.settings"),
diff --git a/client/src/Components/StatusBoxes/index.jsx b/client/src/Components/StatusBoxes/index.jsx
index 40ea2d182..d97bb6211 100644
--- a/client/src/Components/StatusBoxes/index.jsx
+++ b/client/src/Components/StatusBoxes/index.jsx
@@ -4,7 +4,7 @@ import SkeletonLayout from "./skeleton";
// Utils
import { useTheme } from "@mui/material/styles";
import PropTypes from "prop-types";
-const StatusBoxes = ({ shouldRender, flexWrap = "nowrap", children }) => {
+const StatusBoxes = ({ shouldRender = true, flexWrap = "nowrap", children }) => {
const theme = useTheme();
if (!shouldRender) {
return (
diff --git a/client/src/Components/Table/TableUtils.js b/client/src/Components/Table/TableUtils.js
new file mode 100644
index 000000000..a9f95a5b7
--- /dev/null
+++ b/client/src/Components/Table/TableUtils.js
@@ -0,0 +1,11 @@
+export const createHeaderFactory = (getCellSx = () => {}) => {
+ return ({ id, content, onClick = () => {}, render = () => {} }) => {
+ return {
+ id,
+ content,
+ onClick,
+ getCellSx,
+ render,
+ };
+ };
+};
diff --git a/client/src/Components/TextLink/index.jsx b/client/src/Components/TextLink/index.jsx
index a10839e84..83ae251a5 100644
--- a/client/src/Components/TextLink/index.jsx
+++ b/client/src/Components/TextLink/index.jsx
@@ -5,7 +5,7 @@ import PropTypes from "prop-types";
import { useTheme } from "@emotion/react";
import { Link as RouterLink } from "react-router-dom";
-const TextLink = ({ text, linkText, href }) => {
+const TextLink = ({ text, linkText, href, target = "_self" }) => {
const theme = useTheme();
return (
@@ -18,6 +18,7 @@ const TextLink = ({ text, linkText, href }) => {
color="accent"
to={href}
component={RouterLink}
+ target={target}
>
{linkText}
@@ -29,6 +30,7 @@ TextLink.propTypes = {
text: PropTypes.string,
linkText: PropTypes.string,
href: PropTypes.string,
+ target: PropTypes.string,
};
export default TextLink;
diff --git a/client/src/Hooks/logHooks.js b/client/src/Hooks/logHooks.js
new file mode 100644
index 000000000..000e4d0f0
--- /dev/null
+++ b/client/src/Hooks/logHooks.js
@@ -0,0 +1,35 @@
+import { useState, useEffect } from "react";
+import { networkService } from "../main";
+import { createToast } from "../Utils/toastUtils";
+import { useTranslation } from "react-i18next";
+
+const useFetchLogs = () => {
+ const { t } = useTranslation();
+ const [logs, setLogs] = useState(undefined);
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(undefined);
+
+ useEffect(() => {
+ const fetchLogs = async () => {
+ try {
+ setIsLoading(true);
+ const response = await networkService.getLogs();
+ setLogs(response.data.data);
+ createToast({
+ body: t("logsPage.toast.fetchLogsSuccess"),
+ });
+ } catch (error) {
+ setError(error);
+ createToast({
+ message: error.message,
+ });
+ } finally {
+ setIsLoading(false);
+ }
+ };
+ fetchLogs();
+ }, [t]);
+ return [logs, isLoading, error];
+};
+
+export { useFetchLogs };
diff --git a/client/src/Hooks/monitorHooks.js b/client/src/Hooks/monitorHooks.js
index 7e46438e0..013e7fc25 100644
--- a/client/src/Hooks/monitorHooks.js
+++ b/client/src/Hooks/monitorHooks.js
@@ -453,6 +453,39 @@ const useCreateBulkMonitors = () => {
return [createBulkMonitors, isLoading];
};
+const useExportMonitors = () => {
+ const [isLoading, setIsLoading] = useState(false);
+ const { t } = useTranslation();
+
+ const exportMonitors = async () => {
+ setIsLoading(true);
+ try {
+ const response = await networkService.exportMonitors();
+
+ // Create a download link
+ const url = window.URL.createObjectURL(response.data);
+ const link = document.createElement("a");
+ link.href = url;
+ link.setAttribute("download", "monitors.csv");
+ document.body.appendChild(link);
+ link.click();
+ link.remove();
+ window.URL.revokeObjectURL(url);
+
+ createToast({ body: t("export.success") });
+ return [true, null];
+ } catch (err) {
+ const errorMessage = err?.response?.data?.msg || err.message;
+ createToast({ body: errorMessage || t("export.failed") });
+ return [false, errorMessage];
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return [exportMonitors, isLoading];
+};
+
export {
useFetchMonitorsWithSummary,
useFetchMonitorsWithChecks,
@@ -469,4 +502,5 @@ export {
useDeleteAllMonitors,
useDeleteMonitorStats,
useCreateBulkMonitors,
+ useExportMonitors,
};
diff --git a/client/src/Hooks/queueHooks.js b/client/src/Hooks/queueHooks.js
new file mode 100644
index 000000000..f7a25b3d3
--- /dev/null
+++ b/client/src/Hooks/queueHooks.js
@@ -0,0 +1,57 @@
+import { useState, useEffect } from "react";
+import { networkService } from "../main";
+import { createToast } from "../Utils/toastUtils";
+
+const useFetchQueueData = (trigger) => {
+ const [jobs, setJobs] = useState(undefined);
+ const [metrics, setMetrics] = useState(undefined);
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(undefined);
+
+ useEffect(() => {
+ const fetchJobs = async () => {
+ try {
+ setIsLoading(true);
+ const response = await networkService.getQueueData();
+ if (response.status === 200) {
+ setJobs(response.data.data.jobs);
+ setMetrics(response.data.data.metrics);
+ }
+ } catch (error) {
+ setError(error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchJobs();
+ }, [trigger]);
+
+ return [jobs, metrics, isLoading, error];
+};
+
+const useFlushQueue = () => {
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(undefined);
+
+ const flushQueue = async (trigger, setTrigger) => {
+ try {
+ setIsLoading(true);
+ await networkService.flushQueue();
+ createToast({
+ body: "Queue flushed",
+ });
+ } catch (error) {
+ setError(error);
+ createToast({
+ body: error.message,
+ });
+ } finally {
+ setIsLoading(false);
+ setTrigger(!trigger);
+ }
+ };
+ return [flushQueue, isLoading, error];
+};
+
+export { useFetchQueueData, useFlushQueue };
diff --git a/client/src/Hooks/useNotifications.js b/client/src/Hooks/useNotifications.js
index 69e03fd5e..cc6c3e875 100644
--- a/client/src/Hooks/useNotifications.js
+++ b/client/src/Hooks/useNotifications.js
@@ -161,7 +161,7 @@ const useTestNotification = () => {
} catch (error) {
setError(error);
createToast({
- body: t("notifications.test.failed"),
+ body: error?.response?.data?.msg || t("notifications.test.failed"),
});
} finally {
setIsLoading(false);
diff --git a/client/src/Pages/Logs/Logs/index.jsx b/client/src/Pages/Logs/Logs/index.jsx
new file mode 100644
index 000000000..6c5e1a301
--- /dev/null
+++ b/client/src/Pages/Logs/Logs/index.jsx
@@ -0,0 +1,97 @@
+import Stack from "@mui/material/Stack";
+import Box from "@mui/material/Box";
+import Select from "../../../Components/Inputs/Select";
+import Typography from "@mui/material/Typography";
+
+import { useFetchLogs } from "../../../Hooks/logHooks";
+import { useTheme } from "@emotion/react";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+
+const formatLog = (theme, log, idx) => {
+ const levelColors = {
+ info: theme.palette.success.main,
+ warn: theme.palette.warning.main,
+ error: theme.palette.error.main,
+ debug: theme.palette.accent.main,
+ };
+
+ const color = levelColors[log.level] || theme.palette.primary.contrastText;
+
+ return (
+
+ [{log.timestamp}]{" "}
+ {log.level.toUpperCase()}
+ {": "}
+ {`(${log.service})`}
+ {`(${log.method})`}
+ {": "}
+ {log.message}
+
+
+ );
+};
+
+const Logs = () => {
+ // Local state
+ const [logLevel, setLogLevel] = useState("all");
+
+ // Hooks
+ const theme = useTheme();
+ const { t } = useTranslation();
+ const [logs, isLoading, error] = useFetchLogs();
+ // Setup
+ const LOG_LEVELS = [
+ { _id: "all", name: t("logsPage.logLevelSelect.values.all") },
+ { _id: "info", name: t("logsPage.logLevelSelect.values.info") },
+ { _id: "warn", name: t("logsPage.logLevelSelect.values.warn") },
+ { _id: "error", name: t("logsPage.logLevelSelect.values.error") },
+ { _id: "debug", name: t("logsPage.logLevelSelect.values.debug") },
+ ];
+ return (
+
+
+ {t("logsPage.description")}
+
+
+ {t("logsPage.logLevelSelect.title")}
+
+
+
+
+ {logs
+ ?.filter((log) => {
+ if (logLevel === "all") return true;
+ return log.level === logLevel;
+ })
+ .reverse()
+ .map((log, idx) => formatLog(theme, log, idx))}
+
+
+
+ );
+};
+
+export default Logs;
diff --git a/client/src/Pages/Logs/Queue/components/FailedJobTable/index.jsx b/client/src/Pages/Logs/Queue/components/FailedJobTable/index.jsx
new file mode 100644
index 000000000..94fc2f5c3
--- /dev/null
+++ b/client/src/Pages/Logs/Queue/components/FailedJobTable/index.jsx
@@ -0,0 +1,82 @@
+import Stack from "@mui/material/Stack";
+import Typography from "@mui/material/Typography";
+import DataTable from "../../../../../Components/Table";
+
+import { useNavigate } from "react-router-dom";
+import { useTheme } from "@emotion/react";
+import { TypeToPathMap } from "../../../../../Utils/monitorUtils";
+import PropTypes from "prop-types";
+import { useTranslation } from "react-i18next";
+
+const FailedJobTable = ({ metrics = {} }) => {
+ const { t } = useTranslation();
+ const theme = useTheme();
+ const jobsWithFailures = metrics?.jobsWithFailures;
+ const navigate = useNavigate();
+
+ const headers = [
+ {
+ id: "monitorId",
+ content: t("queuePage.failedJobTable.monitorIdHeader"),
+ render: (row) => {
+ return row.monitorId;
+ },
+ },
+ {
+ id: "monitorUrl",
+ content: t("queuePage.failedJobTable.monitorUrlHeader"),
+ render: (row) => {
+ return row.monitorUrl;
+ },
+ },
+ {
+ id: "failCount",
+ content: t("queuePage.failedJobTable.failCountHeader"),
+ render: (row) => {
+ return row.failCount;
+ },
+ },
+ {
+ id: "failedAt",
+ content: t("queuePage.failedJobTable.failedAtHeader"),
+ render: (row) => {
+ return row.failedAt;
+ },
+ },
+ {
+ id: "failReason",
+ content: t("queuePage.failedJobTable.failReasonHeader"),
+ render: (row) => {
+ return row.failReason;
+ },
+ },
+ ];
+ return (
+
+ {t("queuePage.failedJobTable.title")}
+ {
+ const path = TypeToPathMap[row.monitorType];
+ navigate(`/${path}/${row.monitorId}`);
+ },
+ rowSX: {
+ cursor: "pointer",
+ "&:hover td": {
+ backgroundColor: theme.palette.tertiary.main,
+ transition: "background-color .3s ease",
+ },
+ },
+ }}
+ />
+
+ );
+};
+
+FailedJobTable.propTypes = {
+ metrics: PropTypes.object,
+};
+
+export default FailedJobTable;
diff --git a/client/src/Pages/Logs/Queue/components/JobTable/index.jsx b/client/src/Pages/Logs/Queue/components/JobTable/index.jsx
new file mode 100644
index 000000000..e27dd103f
--- /dev/null
+++ b/client/src/Pages/Logs/Queue/components/JobTable/index.jsx
@@ -0,0 +1,125 @@
+import Stack from "@mui/material/Stack";
+import DataTable from "../../../../../Components/Table";
+import Typography from "@mui/material/Typography";
+// Utils
+import PropTypes from "prop-types";
+import { useTheme } from "@emotion/react";
+import { useNavigate } from "react-router-dom";
+import { TypeToPathMap } from "../../../../../Utils/monitorUtils";
+import { useTranslation } from "react-i18next";
+import { createHeaderFactory } from "../../../../../Components/Table/TableUtils";
+
+const JobTable = ({ jobs = [] }) => {
+ const theme = useTheme();
+ const { t } = useTranslation();
+ const navigate = useNavigate();
+ const buildSx = (row) => {
+ if (row.lockedAt) {
+ return {
+ color: `${theme.palette.success.main} !important`,
+ };
+ }
+ if (!row.active) {
+ return {
+ color: `${theme.palette.warning.main} !important`,
+ };
+ }
+
+ if (row.failCount > 0 && row.lastFailedAt >= row.lastFinishedAt) {
+ return {
+ color: `${theme.palette.error.main} !important`,
+ };
+ }
+
+ return {};
+ };
+
+ const createHeader = createHeaderFactory(buildSx);
+ const headersData = [
+ {
+ id: "id",
+ content: t("queuePage.jobTable.idHeader"),
+ render: (row) => row.monitorId,
+ },
+ {
+ id: "url",
+ content: t("queuePage.jobTable.urlHeader"),
+ render: (row) => row.monitorUrl,
+ },
+ {
+ id: "type",
+ content: t("queuePage.jobTable.typeHeader"),
+ render: (row) => row.monitorType,
+ },
+ {
+ id: "active",
+ content: t("queuePage.jobTable.activeHeader"),
+ render: (row) => row.active.toString(),
+ },
+ {
+ id: "runCount",
+ content: t("queuePage.jobTable.runCountHeader"),
+ render: (row) => row.runCount,
+ },
+ {
+ id: "failCount",
+ content: t("queuePage.jobTable.failCountHeader"),
+ render: (row) => row.failCount,
+ },
+ {
+ id: "lastRun",
+ content: t("queuePage.jobTable.lastRunHeader"),
+ render: (row) => row.lastRunAt || "-",
+ },
+ {
+ id: "lockedAt",
+ content: t("queuePage.jobTable.lockedAtHeader"),
+ render: (row) => row.lockedAt || "-",
+ },
+
+ {
+ id: "lastFinish",
+ content: t("queuePage.jobTable.lastFinishedAtHeader"),
+ render: (row) => row.lastFinishedAt || "-",
+ },
+ {
+ id: "lastRunTook",
+ content: t("queuePage.jobTable.lastRunTookHeader"),
+ render: (row) => {
+ const value = row.lastRunTook ? row.lastRunTook + " ms" : "-";
+ return value;
+ },
+ },
+ ];
+
+ const headers = headersData.map((header) => createHeader(header));
+
+ return (
+
+ {t("queuePage.jobTable.title")}
+ {
+ const path = TypeToPathMap[row.monitorType];
+ navigate(`/${path}/${row.monitorId}`);
+ },
+ rowSX: {
+ cursor: "pointer",
+ "&:hover td": {
+ backgroundColor: theme.palette.tertiary.main,
+ transition: "background-color .3s ease",
+ },
+ },
+ }}
+ />
+
+ );
+};
+
+JobTable.propTypes = {
+ jobs: PropTypes.array,
+};
+
+export default JobTable;
diff --git a/client/src/Pages/Logs/Queue/components/Metrics/index.jsx b/client/src/Pages/Logs/Queue/components/Metrics/index.jsx
new file mode 100644
index 000000000..af1cf8010
--- /dev/null
+++ b/client/src/Pages/Logs/Queue/components/Metrics/index.jsx
@@ -0,0 +1,43 @@
+import Stack from "@mui/material/Stack";
+import Typography from "@mui/material/Typography";
+import StatBox from "../../../../../Components/StatBox";
+import StatusBoxes from "../../../../../Components/StatusBoxes";
+
+import { useTranslation } from "react-i18next";
+import { useTheme } from "@emotion/react";
+
+const camelToTitle = (str) => {
+ return str
+ .replace(/([A-Z])/g, " $1")
+ .toLowerCase()
+ .replace(/^./, (m) => m.toUpperCase());
+};
+
+const Metrics = ({ metrics = {} }) => {
+ const { t } = useTranslation();
+ const theme = useTheme();
+
+ const data = Object.keys(metrics)
+ .filter((key) => key !== "jobsWithFailures")
+ .map((key) => {
+ return { key, title: camelToTitle(key), value: metrics[key] };
+ });
+
+ return (
+
+ {t("queuePage.metricsTable.title")}
+
+ {data.map((metric) => {
+ return (
+
+ );
+ })}
+
+
+ );
+};
+export default Metrics;
diff --git a/client/src/Pages/Logs/Queue/components/MetricsTable/index.jsx b/client/src/Pages/Logs/Queue/components/MetricsTable/index.jsx
new file mode 100644
index 000000000..204b90035
--- /dev/null
+++ b/client/src/Pages/Logs/Queue/components/MetricsTable/index.jsx
@@ -0,0 +1,60 @@
+import Stack from "@mui/material/Stack";
+import Typography from "@mui/material/Typography";
+import DataTable from "../../../../../Components/Table";
+
+// Utils
+import { useTranslation } from "react-i18next";
+import { useTheme } from "@emotion/react";
+import PropTypes from "prop-types";
+
+const camelToTitle = (str) => {
+ return str
+ .replace(/([A-Z])/g, " $1")
+ .toLowerCase()
+ .replace(/^./, (m) => m.toUpperCase());
+};
+
+const Metrics = ({ metrics = {} }) => {
+ const { t } = useTranslation();
+ const theme = useTheme();
+ const keys = Object.keys(metrics);
+
+ const headers = [
+ {
+ id: "metric",
+ content: t("queuePage.metricsTable.metricHeader"),
+ render: (row) => {
+ return {row.key};
+ },
+ },
+ {
+ id: "value",
+ content: t("queuePage.metricsTable.valueHeader"),
+ render: (row) => {
+ return {row.value};
+ },
+ },
+ ];
+
+ const data = keys
+ .filter((key) => key !== "jobsWithFailures")
+ .map((key) => {
+ return { key: camelToTitle(key), value: metrics[key] };
+ });
+
+ return (
+
+ {t("queuePage.metricsTable.title")}
+
+
+ );
+};
+
+Metrics.propTypes = {
+ metrics: PropTypes.object,
+};
+
+export default Metrics;
diff --git a/client/src/Pages/Logs/Queue/index.jsx b/client/src/Pages/Logs/Queue/index.jsx
new file mode 100644
index 000000000..729f87fe3
--- /dev/null
+++ b/client/src/Pages/Logs/Queue/index.jsx
@@ -0,0 +1,66 @@
+// Components
+import Stack from "@mui/material/Stack";
+import JobTable from "./components/JobTable";
+import Metrics from "./components/Metrics";
+import FailedJobTable from "./components/FailedJobTable";
+import ButtonGroup from "@mui/material/ButtonGroup";
+import Button from "@mui/material/Button";
+
+// Utils
+import { useState } from "react";
+import { useFetchQueueData, useFlushQueue } from "../../../Hooks/queueHooks";
+import { useTranslation } from "react-i18next";
+import { useTheme } from "@emotion/react";
+
+const QueueDetails = () => {
+ // Local state
+ const [trigger, setTrigger] = useState(false);
+
+ // Hooks
+ const { t } = useTranslation();
+ const theme = useTheme();
+ const [jobs, metrics, isLoading, error] = useFetchQueueData(trigger);
+ const [flushQueue, isFlushing, flushError] = useFlushQueue();
+
+ if (isLoading) return Loading...
;
+ if (error || flushError) return Error: {error.message}
;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default QueueDetails;
diff --git a/client/src/Pages/Logs/index.jsx b/client/src/Pages/Logs/index.jsx
new file mode 100644
index 000000000..482491ad0
--- /dev/null
+++ b/client/src/Pages/Logs/index.jsx
@@ -0,0 +1,41 @@
+import Stack from "@mui/material/Stack";
+import Breadcrumbs from "../../Components/Breadcrumbs";
+import Tabs from "@mui/material/Tabs";
+import Tab from "@mui/material/Tab";
+import Queue from "./Queue";
+import LogsComponent from "./Logs";
+
+import { useTheme } from "@emotion/react";
+import { useTranslation } from "react-i18next";
+import { useState } from "react";
+
+const Logs = () => {
+ const { t } = useTranslation();
+ const theme = useTheme();
+
+ // Local state
+ const [value, setValue] = useState(0);
+
+ // Handlers
+ const handleChange = (event, newValue) => {
+ setValue(newValue);
+ };
+
+ const BREADCRUMBS = [{ name: t("logsPage.title"), path: "/logs" }];
+ return (
+
+
+
+
+
+
+ {value === 0 && }
+ {value === 1 && }
+
+ );
+};
+
+export default Logs;
diff --git a/client/src/Pages/Maintenance/MaintenanceTable/index.jsx b/client/src/Pages/Maintenance/MaintenanceTable/index.jsx
index a798838cb..bb5842e9f 100644
--- a/client/src/Pages/Maintenance/MaintenanceTable/index.jsx
+++ b/client/src/Pages/Maintenance/MaintenanceTable/index.jsx
@@ -11,6 +11,8 @@ import { formatDurationRounded } from "../../../Utils/timeUtils";
import { StatusLabel } from "../../../Components/Label";
import { setRowsPerPage } from "../../../Features/UI/uiSlice";
import { useTranslation } from "react-i18next";
+import { useTheme } from "@emotion/react";
+import { useNavigate } from "react-router-dom";
import dayjs from "dayjs";
/**
* Component for pagination actions (first, previous, next, last).
@@ -36,7 +38,8 @@ const MaintenanceTable = ({
}) => {
const rowsPerPage = useSelector((state) => state?.ui?.maintenance?.rowsPerPage ?? 5);
const dispatch = useDispatch();
-
+ const theme = useTheme();
+ const navigate = useNavigate();
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
@@ -180,6 +183,18 @@ const MaintenanceTable = ({
return (
<>
{
+ navigate(`/maintenance/create/${row._id}`);
+ },
+ }}
headers={headers}
data={maintenanceWindows}
/>
diff --git a/client/src/Pages/Notifications/create/index.jsx b/client/src/Pages/Notifications/create/index.jsx
index fba30185f..0fb6cce08 100644
--- a/client/src/Pages/Notifications/create/index.jsx
+++ b/client/src/Pages/Notifications/create/index.jsx
@@ -204,7 +204,7 @@ const CreateNotifications = () => {
spacing={theme.spacing(2)}
>
- {t("settingsAddDemoMonitors")}
- {t("settingsRemoveAllMonitors")}
+
{systemEmailHost &&
systemEmailPort &&
diff --git a/client/src/Pages/Settings/index.jsx b/client/src/Pages/Settings/index.jsx
index 76eaccd6c..0cd954e52 100644
--- a/client/src/Pages/Settings/index.jsx
+++ b/client/src/Pages/Settings/index.jsx
@@ -208,6 +208,7 @@ const Settings = () => {
emailPasswordHasBeenReset={emailPasswordHasBeenReset}
setEmailPasswordHasBeenReset={setEmailPasswordHasBeenReset}
/>
+
+
diff --git a/client/src/Pages/StatusPage/Create/index.jsx b/client/src/Pages/StatusPage/Create/index.jsx
index 9d876df45..3d3da1cea 100644
--- a/client/src/Pages/StatusPage/Create/index.jsx
+++ b/client/src/Pages/StatusPage/Create/index.jsx
@@ -21,7 +21,7 @@ import { useStatusPageDelete } from "../Status/Hooks/useStatusPageDelete";
//Constants
const ERROR_TAB_MAPPING = [
["companyName", "url", "timezone", "color", "isPublished", "logo"],
- ["monitors", "showUptimePercentage", "showCharts"],
+ ["monitors", "showUptimePercentage", "showCharts", "showAdminLoginLink"],
];
const CreateStatusPage = () => {
@@ -41,6 +41,7 @@ const CreateStatusPage = () => {
monitors: [],
showCharts: true,
showUptimePercentage: true,
+ showAdminLoginLink: false,
});
const [errors, setErrors] = useState({});
const [selectedMonitors, setSelectedMonitors] = useState([]);
@@ -210,6 +211,7 @@ const handleDelete = async () => {
logo: newLogo,
showCharts: statusPage?.showCharts ?? true,
showUptimePercentage: statusPage?.showUptimePercentage ?? true,
+ showAdminLoginLink: statusPage?.showAdminLoginLink ?? false,
};
});
setSelectedMonitors(statusPageMonitors);
diff --git a/client/src/Pages/StatusPage/Status/index.jsx b/client/src/Pages/StatusPage/Status/index.jsx
index ff747fa0f..af5eedeab 100644
--- a/client/src/Pages/StatusPage/Status/index.jsx
+++ b/client/src/Pages/StatusPage/Status/index.jsx
@@ -39,7 +39,7 @@ const PublicStatus = () => {
let link = undefined;
const isPublic = location.pathname.startsWith("/status/uptime/public");
// Public status page
- if (isPublic) {
+ if (isPublic && statusPage && statusPage.showAdminLoginLink === true) {
sx = {
paddingTop: theme.spacing(20),
paddingLeft: "20vw",
diff --git a/client/src/Pages/Uptime/Details/index.jsx b/client/src/Pages/Uptime/Details/index.jsx
index 5f36ee7e5..c72a5756f 100644
--- a/client/src/Pages/Uptime/Details/index.jsx
+++ b/client/src/Pages/Uptime/Details/index.jsx
@@ -111,6 +111,7 @@ const UptimeDetails = () => {
return (
+
{
monitor={monitor}
triggerUpdate={triggerUpdate}
/>
+
+
+
{t("distributedUptimeDetailsNoMonitorHistory")}
diff --git a/client/src/Routes/index.jsx b/client/src/Routes/index.jsx
index 29daa2942..60df27211 100644
--- a/client/src/Routes/index.jsx
+++ b/client/src/Routes/index.jsx
@@ -51,6 +51,7 @@ import ProtectedRoute from "../Components/ProtectedRoute";
import CreateNewMaintenanceWindow from "../Pages/Maintenance/CreateMaintenance";
import withAdminCheck from "../Components/HOC/withAdminCheck";
import BulkImport from "../Pages/Uptime/BulkImport";
+import Logs from "../Pages/Logs";
const Routes = () => {
const AdminCheckedRegister = withAdminCheck(AuthRegister);
@@ -186,6 +187,11 @@ const Routes = () => {
path="account/team"
element={}
/>
+
+ }
+ />
{
fd.append("monitors[]", monitorId);
@@ -1022,6 +1025,33 @@ class NetworkService {
const { id, notification } = config;
return this.axiosInstance.put(`/notifications/${id}`, notification);
}
+
+ async getQueueData() {
+ return this.axiosInstance.get(`/queue/all-metrics`, {
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ }
+
+ async flushQueue() {
+ return this.axiosInstance.post(`/queue/flush`);
+ }
+
+ async exportMonitors() {
+ const response = await this.axiosInstance.get("/monitors/export", {
+ responseType: "blob",
+ });
+ return response;
+ }
+
+ async getLogs() {
+ return this.axiosInstance.get(`/logs`, {
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ }
}
export default NetworkService;
diff --git a/client/src/Utils/Theme/constants.js b/client/src/Utils/Theme/constants.js
index 48dcdfd85..1b905bb79 100644
--- a/client/src/Utils/Theme/constants.js
+++ b/client/src/Utils/Theme/constants.js
@@ -85,6 +85,7 @@ const semanticColors = {
const newColors = {
offWhite: "#FEFEFE",
offBlack: "#131315",
+ gray0: "#FDFDFD",
gray10: "#F4F4FF",
gray100: "#F3F3F3",
gray200: "#EFEFEF",
@@ -178,7 +179,7 @@ const newSemanticColors = {
},
primaryBackground: {
main: {
- light: newColors.gray10,
+ light: newColors.gray0,
dark: "#000000",
},
},
diff --git a/client/src/Utils/Theme/globalTheme.js b/client/src/Utils/Theme/globalTheme.js
index 58d9943be..ae014432d 100644
--- a/client/src/Utils/Theme/globalTheme.js
+++ b/client/src/Utils/Theme/globalTheme.js
@@ -407,6 +407,7 @@ const baseTheme = (palette) => ({
}),
},
},
+
MuiTab: {
styleOverrides: {
root: ({ theme }) => ({
@@ -475,6 +476,8 @@ const baseTheme = (palette) => ({
MuiTabs: {
styleOverrides: {
root: ({ theme }) => ({
+ height: "34px",
+ minHeight: "34px",
display: "inline-flex",
borderRadius: 0,
"& .MuiTabs-indicator": {
diff --git a/client/src/Utils/i18n.js b/client/src/Utils/i18n.js
index 4468917a2..030091d48 100644
--- a/client/src/Utils/i18n.js
+++ b/client/src/Utils/i18n.js
@@ -24,6 +24,7 @@ i18n.use(initReactI18next).init({
interpolation: {
escapeValue: false,
},
+ returnEmptyString: false,
});
export default i18n;
diff --git a/client/src/Utils/monitorUtils.js b/client/src/Utils/monitorUtils.js
index cc92a86ae..e3645b0d0 100644
--- a/client/src/Utils/monitorUtils.js
+++ b/client/src/Utils/monitorUtils.js
@@ -36,3 +36,12 @@ export const parseDomainName = (url) => {
return url;
};
+
+export const TypeToPathMap = {
+ http: "uptime",
+ port: "uptime",
+ docker: "uptime",
+ ping: "uptime",
+ hardware: "infrastructure",
+ pagespeed: "pagespeed",
+};
diff --git a/client/src/Validation/validation.js b/client/src/Validation/validation.js
index 4e6a806c8..d86bbd556 100644
--- a/client/src/Validation/validation.js
+++ b/client/src/Validation/validation.js
@@ -277,6 +277,7 @@ const statusPageValidation = joi.object({
logo: logoImageValidation,
showUptimePercentage: joi.boolean(),
showCharts: joi.boolean(),
+ showAdminLoginLink: joi.boolean(),
});
const settingsValidation = joi.object({
diff --git a/client/src/assets/icons/logs.svg b/client/src/assets/icons/logs.svg
new file mode 100644
index 000000000..556c3ba33
--- /dev/null
+++ b/client/src/assets/icons/logs.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/client/src/locales/ar.json b/client/src/locales/ar.json
index fd4d925a9..81a2bb05f 100644
--- a/client/src/locales/ar.json
+++ b/client/src/locales/ar.json
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
- "settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
- "settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
@@ -583,6 +581,13 @@
},
"errors": {
"validation": ""
+ },
+ "fields": {
+ "password": {
+ "errors": {
+ "incorrect": ""
+ }
+ }
}
},
"login": {
@@ -709,13 +714,13 @@
"integrationKeyLabel": "",
"integrationKeyPlaceholder": ""
},
- "genericWebhookSettings": {
+ "discordSettings": {
"title": "",
"description": "",
"webhookLabel": "",
"webhookPlaceholder": ""
},
- "discordSettings": {
+ "webhookSettings": {
"title": "",
"description": "",
"webhookLabel": "",
@@ -740,5 +745,6 @@
"settingsEmailRejectUnauthorized": "",
"settingsEmailSecure": "",
"settingsEmailPool": "",
- "sendTestNotifications": ""
+ "sendTestNotifications": "",
+ "selectAll": ""
}
diff --git a/client/src/locales/cs.json b/client/src/locales/cs.json
index 66b2993d2..4e1170ee7 100644
--- a/client/src/locales/cs.json
+++ b/client/src/locales/cs.json
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
- "settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
- "settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
@@ -583,6 +581,13 @@
},
"errors": {
"validation": "Nastala chyba při ověřování dat."
+ },
+ "fields": {
+ "password": {
+ "errors": {
+ "incorrect": ""
+ }
+ }
}
},
"login": {
@@ -709,13 +714,13 @@
"integrationKeyLabel": "",
"integrationKeyPlaceholder": ""
},
- "genericWebhookSettings": {
+ "discordSettings": {
"title": "",
"description": "",
"webhookLabel": "",
"webhookPlaceholder": ""
},
- "discordSettings": {
+ "webhookSettings": {
"title": "",
"description": "",
"webhookLabel": "",
@@ -740,5 +745,6 @@
"settingsEmailRejectUnauthorized": "",
"settingsEmailSecure": "",
"settingsEmailPool": "",
- "sendTestNotifications": ""
+ "sendTestNotifications": "",
+ "selectAll": ""
}
diff --git a/client/src/locales/de.json b/client/src/locales/de.json
index 6d5c5a4d8..e33adab68 100644
--- a/client/src/locales/de.json
+++ b/client/src/locales/de.json
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
- "settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
- "settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
@@ -583,6 +581,13 @@
},
"errors": {
"validation": ""
+ },
+ "fields": {
+ "password": {
+ "errors": {
+ "incorrect": ""
+ }
+ }
}
},
"login": {
@@ -709,13 +714,13 @@
"integrationKeyLabel": "",
"integrationKeyPlaceholder": ""
},
- "genericWebhookSettings": {
+ "discordSettings": {
"title": "",
"description": "",
"webhookLabel": "",
"webhookPlaceholder": ""
},
- "discordSettings": {
+ "webhookSettings": {
"title": "",
"description": "",
"webhookLabel": "",
@@ -740,5 +745,6 @@
"settingsEmailRejectUnauthorized": "",
"settingsEmailSecure": "",
"settingsEmailPool": "",
- "sendTestNotifications": ""
+ "sendTestNotifications": "",
+ "selectAll": ""
}
diff --git a/client/src/locales/en.json b/client/src/locales/en.json
index dd09a2527..201c54782 100644
--- a/client/src/locales/en.json
+++ b/client/src/locales/en.json
@@ -1,552 +1,82 @@
{
- "submit": "Submit",
- "title": "Title",
- "distributedStatusHeaderText": "Real-time, real-device coverage",
- "distributedStatusSubHeaderText": "Powered by millions devices worldwide, view a system performance by global region, country or city",
- "settingsGeneralSettings": "General settings",
- "settingsDisplayTimezone": "Display timezone",
- "settingsDisplayTimezoneDescription": "Select the timezone used to display dates and times throughout the application.",
- "settingsAppearance": "Appearance",
- "settingsAppearanceDescription": "Switch between light and dark mode, or change user interface language",
- "settingsThemeMode": "Theme Mode",
- "settingsLanguage": "Language",
- "settingsEnabled": "Enabled",
- "settingsDisabled": "Disabled",
- "settingsHistoryAndMonitoring": "History of monitoring",
- "settingsHistoryAndMonitoringDescription": "Define how long you want to retain historical data. You can also clear all existing data.",
- "settingsTTLLabel": "The days you want to keep monitoring history.",
- "settingsTTLOptionalLabel": "0 for infinite",
- "settingsClearAllStats": "Clear all stats. This is irreversible.",
- "settingsClearAllStatsButton": "Clear all stats",
- "settingsClearAllStatsDialogTitle": "Do you want to clear all stats?",
- "settingsClearAllStatsDialogDescription": "Once removed, the monitoring history and stats cannot be retrieved.",
- "settingsClearAllStatsDialogConfirm": "Yes, clear all stats",
- "settingsDemoMonitors": "Demo monitors",
- "settingsDemoMonitorsDescription": "Add sample monitors for demonstration purposes.",
- "settingsAddDemoMonitors": "Adding demo monitors",
- "settingsAddDemoMonitorsButton": "Add demo monitors",
- "settingsRemoveAllMonitors": "Removing all monitors",
- "settingsRemoveAllMonitorsButton": "Remove all monitors",
- "settingsRemoveAllMonitorsDialogTitle": "Do you want to remove all monitors?",
- "settingsRemoveAllMonitorsDialogConfirm": "Yes, remove all monitors",
- "settingsAbout": "About",
- "settingsDevelopedBy": "Developed by Bluewave Labs.",
- "settingsSave": "Save",
- "settingsSuccessSaved": "Settings saved successfully",
- "settingsFailedToSave": "Failed to save settings",
- "settingsStatsCleared": "Stats cleared successfully",
- "settingsFailedToClearStats": "Failed to clear stats",
- "settingsDemoMonitorsAdded": "Successfully added demo monitors",
- "settingsFailedToAddDemoMonitors": "Failed to add demo monitors",
- "settingsMonitorsDeleted": "Successfully deleted all monitors",
- "settingsFailedToDeleteMonitors": "Failed to delete all monitors",
- "starPromptTitle": "Star Checkmate",
- "starPromptDescription": "See the latest releases and help grow the community on GitHub",
- "https": "HTTPS",
- "http": "HTTP",
- "monitor": "monitor",
- "aboutus": "About Us",
- "now": "Now",
- "delete": "Delete",
- "configure": "Configure",
- "responseTime": "Response time",
- "ms": "ms",
- "bar": "Bar",
- "area": "Area",
- "country": "COUNTRY",
- "city": "CITY",
- "response": "RESPONSE",
- "monitorStatusUp": "Monitor {name} ({url}) is now UP and responding",
- "monitorStatusDown": "Monitor {name} ({url}) is DOWN and not responding",
- "webhookSendSuccess": "Webhook notification sent successfully",
- "webhookSendError": "Error sending webhook notification to {platform}",
- "webhookUnsupportedPlatform": "Unsupported platform: {platform}",
- "distributedRightCategoryTitle": "Monitor",
- "distributedStatusServerMonitors": "Server Monitors",
- "distributedStatusServerMonitorsDescription": "Monitor status of related servers",
- "distributedUptimeCreateSelectURL": "Here you can select the URL of the host, together with the type of monitor.",
- "distributedUptimeCreateChecks": "Checks to perform",
- "distributedUptimeCreateChecksDescription": "You can always add or remove checks after adding your site.",
- "distributedUptimeCreateIncidentNotification": "Incident notifications",
- "distributedUptimeCreateIncidentDescription": "When there is an incident, notify users.",
- "distributedUptimeCreateAdvancedSettings": "Advanced settings",
- "distributedUptimeDetailsNoMonitorHistory": "There is no check history for this monitor yet.",
- "distributedUptimeDetailsStatusHeaderUptime": "Uptime:",
- "distributedUptimeDetailsStatusHeaderLastUpdate": "Last updated",
- "notifications": {
- "enableNotifications": "Enable {{platform}} notifications",
- "testNotification": "Test notification",
- "addOrEditNotifications": "Add or edit notifications",
- "slack": {
- "label": "Slack",
- "description": "To enable Slack notifications, create a Slack app and enable incoming webhooks. After that, simply provide the webhook URL here.",
- "webhookLabel": "Webhook URL",
- "webhookPlaceholder": "https://hooks.slack.com/services/...",
- "webhookRequired": "Slack webhook URL is required"
- },
- "discord": {
- "label": "Discord",
- "description": "To send data to a Discord channel from Checkmate via Discord notifications using webhooks, you can use Discord's incoming Webhooks feature.",
- "webhookLabel": "Discord Webhook URL",
- "webhookPlaceholder": "https://discord.com/api/webhooks/...",
- "webhookRequired": "Discord webhook URL is required"
- },
- "telegram": {
- "label": "Telegram",
- "description": "To enable Telegram notifications, create a Telegram bot using BotFather, an official bot for creating and managing Telegram bots. Then, get the API token and chat ID and write them down here.",
- "tokenLabel": "Your bot token",
- "tokenPlaceholder": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
- "chatIdLabel": "Your Chat ID",
- "chatIdPlaceholder": "-1001234567890",
- "fieldsRequired": "Telegram token and chat ID are required"
- },
- "webhook": {
- "label": "Webhooks",
- "description": "You can set up a custom webhook to receive notifications when incidents occur.",
- "urlLabel": "Webhook URL",
- "urlPlaceholder": "https://your-server.com/webhook",
- "urlRequired": "Webhook URL is required"
- },
- "testNotificationDevelop": "Test notification 2",
- "integrationButton": "Notification Integration",
- "testSuccess": "Test notification sent successfully!",
- "testFailed": "Failed to send test notification",
- "unsupportedType": "Unsupported notification type",
- "networkError": "Network error occurred",
- "fallback": {
- "title": "notification channel",
- "checks": [
- "Alert teams about downtime or performance issues",
- "Let engineers know when incidents happen",
- "Keep administrators informed of system changes"
- ]
- },
- "createButton": "Create notification channel",
- "createTitle": "Notification channel",
- "create": {
- "success": "Notification created successfully",
- "failed": "Failed to create notification"
- },
- "fetch": {
- "success": "Notifications fetched successfully",
- "failed": "Failed to fetch notifications"
- },
- "delete": {
- "success": "Notification deleted successfully",
- "failed": "Failed to delete notification"
- },
- "edit": {
- "success": "Notification updated successfully",
- "failed": "Failed to update notification"
- },
- "test": {
- "success": "Test notification sent successfully",
- "failed": "Failed to send test notification"
- }
- },
- "testLocale": "testLocale",
- "add": "Add",
- "monitors": "monitors",
- "distributedUptimeStatusCreateStatusPage": "status page",
- "distributedUptimeStatusCreateStatusPageAccess": "Access",
- "distributedUptimeStatusCreateStatusPageReady": "If your status page is ready, you can mark it as published.",
- "distributedUptimeStatusBasicInfoHeader": "Basic Information",
- "distributedUptimeStatusBasicInfoDescription": "Define company name and the subdomain that your status page points to.",
- "distributedUptimeStatusLogoHeader": "Logo",
- "distributedUptimeStatusLogoDescription": "Upload a logo for your status page",
- "distributedUptimeStatusLogoUploadButton": "Upload logo",
- "distributedUptimeStatusStandardMonitorsHeader": "Standard Monitors",
- "distributedUptimeStatusStandardMonitorsDescription": "Attach standard monitors to your status page.",
- "distributedUptimeStatusCreateYour": "Create your",
- "distributedUptimeStatusEditYour": "Edit your",
- "distributedUptimeStatusPublishedLabel": "Published and visible to the public",
- "distributedUptimeStatusCompanyNameLabel": "Company name",
- "distributedUptimeStatusPageAddressLabel": "Your status page address",
- "distributedUptimeStatus30Days": "30 days",
- "distributedUptimeStatus60Days": "60 days",
- "distributedUptimeStatus90Days": "90 days",
- "distributedUptimeStatusPageNotSetUp": "A status page is not set up.",
- "distributedUptimeStatusContactAdmin": "Please contact your administrator",
- "distributedUptimeStatusPageNotPublic": "This status page is not public.",
- "distributedUptimeStatusPageDeleteDialog": "Do you want to delete this status page?",
- "distributedUptimeStatusPageDeleteConfirm": "Yes, delete status page",
- "distributedUptimeStatusPageDeleteDescription": "Once deleted, your status page cannot be retrieved.",
- "distributedUptimeStatusDevices": "Devices",
- "distributedUptimeStatusUpt": "UPT",
- "distributedUptimeStatusUptBurned": "UPT Burned",
- "distributedUptimeStatusUptLogo": "Upt Logo",
- "incidentsTableNoIncidents": "No incidents recorded",
- "incidentsTablePaginationLabel": "incidents",
- "incidentsTableMonitorName": "Monitor Name",
- "incidentsTableStatus": "Status",
- "incidentsTableDateTime": "Date & Time",
- "incidentsTableStatusCode": "Status Code",
- "incidentsTableMessage": "Message",
- "incidentsOptionsHeader": "Incidents for:",
- "incidentsOptionsHeaderFilterBy": "Filter by:",
- "incidentsOptionsHeaderFilterAll": "All",
- "incidentsOptionsHeaderFilterDown": "Down",
- "incidentsOptionsHeaderFilterCannotResolve": "Cannot resolve",
- "incidentsOptionsHeaderShow": "Show:",
- "incidentsOptionsHeaderLastHour": "Last hour",
- "incidentsOptionsHeaderLastDay": "Last day",
- "incidentsOptionsHeaderLastWeek": "Last week",
- "incidentsOptionsPlaceholderAllServers": "All servers",
- "infrastructureCreateYour": "Create your",
- "infrastructureCreateGeneralSettingsDescription": "Here you can select the URL of the host, together with the friendly name and authorization secret to connect to the server agent.",
- "infrastructureServerRequirement": "The server you are monitoring must be running the",
- "infrastructureCustomizeAlerts": "Customize alerts",
- "infrastructureAlertNotificationDescription": "Send a notification to user(s) when thresholds exceed a specified percentage.",
- "infrastructureCreateMonitor": "Create Infrastructure Monitor",
- "infrastructureProtocol": "Protocol",
- "infrastructureServerUrlLabel": "Server URL",
- "infrastructureDisplayNameLabel": "Display name",
- "infrastructureAuthorizationSecretLabel": "Authorization secret",
- "gb": "GB",
- "mb": "MB",
- "mem": "Mem",
- "memoryUsage": "Memory usage",
- "cpu": "CPU",
- "cpuUsage": "CPU usage",
- "cpuTemperature": "CPU Temperature",
- "diskUsage": "Disk Usage",
- "used": "Used",
- "total": "Total",
- "cores": "Cores",
- "frequency": "Frequency",
- "status": "Status",
- "cpuPhysical": "CPU (Physical)",
- "cpuLogical": "CPU (Logical)",
- "cpuFrequency": "CPU Frequency",
- "avgCpuTemperature": "Average CPU Temperature",
- "memory": "Memory",
- "disk": "Disk",
- "uptime": "Uptime",
- "os": "OS",
- "host": "Host",
- "actions": "Actions",
- "integrations": "Integrations",
- "integrationsPrism": "Connect Prism to your favorite service.",
- "integrationsSlack": "Slack",
- "integrationsSlackInfo": "Connect with Slack and see incidents in a channel",
- "integrationsDiscord": "Discord",
- "integrationsDiscordInfo": "Connect with Discord and view incidents directly in a channel",
- "integrationsZapier": "Zapier",
- "integrationsZapierInfo": "Send all incidents to Zapier, and then see them everywhere",
- "commonSave": "Save",
- "createYour": "Create your",
- "createMonitor": "Create monitor",
- "pause": "Pause",
- "resume": "Resume",
- "editing": "Editing...",
- "url": "URL",
- "access": "Access",
- "timezone": "Timezone",
- "features": "Features",
- "administrator": "Administrator?",
- "loginHere": "Login here",
- "displayName": "Display name",
- "urlMonitor": "URL to monitor",
- "portToMonitor": "Port to monitor",
- "websiteMonitoring": "Website monitoring",
- "websiteMonitoringDescription": "Use HTTP(s) to monitor your website or API endpoint.",
- "pingMonitoring": "Ping monitoring",
- "pingMonitoringDescription": "Check whether your server is available or not.",
- "dockerContainerMonitoring": "Docker container monitoring",
- "dockerContainerMonitoringDescription": "Check whether your Docker container is running or not.",
- "portMonitoring": "Port monitoring",
- "portMonitoringDescription": "Check whether your port is open or not.",
- "createMaintenanceWindow": "Create maintenance window",
- "createMaintenance": "Create maintenance",
- "editMaintenance": "Edit maintenance",
- "maintenanceWindowName": "Maintenance Window Name",
- "friendlyNameInput": "Friendly name",
- "friendlyNamePlaceholder": "Maintenance at __ : __ for ___ minutes",
- "maintenanceRepeat": "Maintenance Repeat",
- "maintenance": "maintenance",
- "duration": "Duration",
- "addMonitors": "Add monitors",
- "window": "window",
- "cancel": "Cancel",
- "message": "Message",
- "low": "low",
- "high": "high",
- "statusCode": "Status code",
- "date&Time": "Date & Time",
- "type": "Type",
- "statusPageName": "Status page name",
- "statusPage": {
- "deleteSuccess": "Status page deleted successfully",
- "deleteFailed": "Failed to delete status page",
- "createSuccess": "Status page created successfully",
- "updateSuccess": "Status page updated successfully",
- "generalSettings": "General settings",
- "contents": "Contents"
- },
- "publicURL": "Public URL",
- "repeat": "Repeat",
- "edit": "Edit",
- "createA": "Create a",
- "remove": "Remove",
- "maintenanceWindowDescription": "Your pings won't be sent during this time frame",
- "startTime": "Start time",
- "timeZoneInfo": "All dates and times are in GMT+0 time zone.",
- "monitorsToApply": "Monitors to apply maintenance window to",
- "nextWindow": "Next window",
- "notFoundButton": "Go to the main dashboard",
- "pageSpeedConfigureSettingsDescription": "Here you can select the URL of the host, together with the type of monitor.",
- "monitorDisplayName": "Monitor display name",
- "whenNewIncident": "When there is a new incident,",
- "notifySMS": "Notify via SMS (coming soon)",
- "notifyEmails": "Also notify via email to multiple addresses (coming soon)",
- "seperateEmails": "You can separate multiple emails with a comma",
- "checkFrequency": "Check frequency",
- "matchMethod": "Match Method",
- "expectedValue": "Expected value",
- "deleteDialogTitle": "Do you really want to delete this monitor?",
- "deleteDialogDescription": "Once deleted, this monitor cannot be retrieved.",
- "pageSpeedMonitor": "PageSpeed monitor",
- "shown": "Shown",
- "ago": "ago",
- "companyName": "Company name",
- "pageSpeedDetailsPerformanceReport": "Values are estimated and may vary.",
- "pageSpeedDetailsPerformanceReportCalculator": "See calculator",
- "checkingEvery": "Checking every",
- "statusPageCreateSettings": "If your status page is ready, you can mark it as published.",
- "basicInformation": "Basic Information",
- "statusPageCreateBasicInfoDescription": "Define company name and the subdomain that your status page points to.",
- "statusPageCreateSelectTimeZoneDescription": "Select the timezone that your status page will be displayed in.",
- "statusPageCreateAppearanceDescription": "Define the default look and feel of your public status page.",
- "statusPageCreateSettingsCheckboxLabel": "Published and visible to the public",
- "statusPageCreateBasicInfoStatusPageAddress": "Your status page address",
- "statusPageCreateTabsContent": "Status page servers",
- "statusPageCreateTabsContentDescription": "You can add any number of servers that you monitor to your status page. You can also reorder them for the best viewing experience.",
- "statusPageCreateTabsContentFeaturesDescription": "Show more details on the status page",
- "showCharts": "Show charts",
- "showUptimePercentage": "Show uptime percentage",
- "removeLogo": "Remove Logo",
- "statusPageStatus": "A public status page is not set up.",
- "statusPageStatusContactAdmin": "Please contact to your administrator",
- "statusPageStatusNotPublic": "This status page is not public.",
- "statusPageStatusNoPage": "There's no status page here.",
- "statusPageStatusServiceStatus": "Service status",
- "deleteStatusPage": "Do you want to delete this status page?",
- "deleteStatusPageConfirm": "Yes, delete status page",
- "deleteStatusPageDescription": "Once deleted, your status page cannot be retrieved.",
- "uptimeCreate": "The expected value is used to match against response result, and the match determines the status.",
- "uptimeCreateJsonPath": "This expression will be evaluated against the reponse JSON data and the result will be used to match against the expected value. See",
- "uptimeCreateJsonPathQuery": "for query language documentation.",
- "maintenanceTableActionMenuDialogTitle": "Do you really want to remove this maintenance window?",
- "infrastructureEditYour": "Edit your",
- "infrastructureEditMonitor": "Save Infrastructure Monitor",
- "infrastructureMonitorCreated": "Infrastructure monitor created successfully!",
- "infrastructureMonitorUpdated": "Infrastructure monitor updated successfully!",
- "errorInvalidTypeId": "Invalid notification type provided",
- "errorInvalidFieldId": "Invalid field ID provided",
- "inviteNoTokenFound": "No invite token found",
- "pageSpeedWarning": "Warning: You haven't added a Google PageSpeed API key yet. Without it, the PageSpeed monitor won't function.",
- "pageSpeedLearnMoreLink": "Click here",
- "pageSpeedAddApiKey": "to add your API key.",
- "update": "Update",
- "invalidFileFormat": "Unsupported file format!",
- "invalidFileSize": "File size is too large!",
"ClickUpload": "Click to upload",
+ "DeleteAccountButton": "Remove account",
+ "DeleteAccountTitle": "Remove account",
+ "DeleteAccountWarning": "Removing your account means you won't be able to sign in again and all your data will be removed. This isn't reversible.",
+ "DeleteDescriptionText": "This will remove the account and all associated data from the server. This isn't reversible.",
+ "DeleteWarningTitle": "Really remove this account?",
"DragandDrop": "drag and drop",
- "MaxSize": "Maximum Size",
- "SupportedFormats": "Supported formats",
+ "EmailDescriptionText": "This is your current email address — it cannot be changed.",
"FirstName": "First name",
"LastName": "Last name",
- "EmailDescriptionText": "This is your current email address — it cannot be changed.",
- "YourPhoto": "Profile photo",
+ "MaxSize": "Maximum Size",
"PhotoDescriptionText": "This photo will be displayed in your profile page.",
- "save": "Save",
- "DeleteDescriptionText": "This will remove the account and all associated data from the server. This isn't reversible.",
- "DeleteAccountWarning": "Removing your account means you won't be able to sign in again and all your data will be removed. This isn't reversible.",
- "DeleteWarningTitle": "Really remove this account?",
- "bulkImport": {
- "title": "Bulk Import",
- "selectFileTips": "Select CSV file to upload",
- "selectFileDescription": "You can download our template or sample",
- "selectFile": "Select File",
- "parsingFailed": "Parsing failed",
- "uploadSuccess": "Monitors created successfully!",
- "validationFailed": "Validation failed",
- "noFileSelected": "No file selected",
- "fallbackPage": "Import a file to upload a list of servers in bulk"
- },
- "settingsSystemReset": "System reset",
- "settingsSystemResetDescription": "Remove all monitors from your system.",
- "DeleteAccountTitle": "Remove account",
- "DeleteAccountButton": "Remove account",
- "publicLink": "Public link",
- "maskedPageSpeedKeyPlaceholder": "*************************************",
- "pageSpeedApiKeyFieldTitle": "Google PageSpeed API key",
- "pageSpeedApiKeyFieldLabel": "PageSpeed API key",
- "pageSpeedApiKeyFieldDescription": "Enter your Google PageSpeed API key to enable pagespeed monitoring. Click Reset to update the key.",
- "pageSpeedApiKeyFieldResetLabel": "API key is set. Click Reset to change it.",
- "reset": "Reset",
- "ignoreTLSError": "Ignore TLS/SSL error",
- "tlsErrorIgnored": "TLS/SSL errors ignored",
- "ignoreTLSErrorDescription": "Ignore TLS/SSL errors and continue checking the website's availability",
- "createNew": "Create new",
- "greeting": {
- "prepend": "Hey there",
- "append": "The afternoon is your playground—let's make it epic!",
- "overview": "Here's an overview of your {{type}} monitors."
- },
- "roles": {
- "superAdmin": "Super admin",
- "admin": "Admin",
- "teamMember": "Team member",
- "demoUser": "Demo user"
- },
- "teamPanel": {
- "teamMembers": "Team members",
- "filter": {
- "all": "All",
- "member": "Member"
- },
- "inviteTeamMember": "Invite a team member",
- "inviteNewTeamMember": "Invite new team member",
- "inviteDescription": "When you add a new team member, they will get access to all monitors.",
- "email": "Email",
- "selectRole": "Select role",
- "inviteLink": "Invite link",
- "cancel": "Cancel",
- "noMembers": "There are no team members with this role",
- "getToken": "Get token",
- "emailToken": "E-mail token",
- "table": {
- "name": "Name",
- "email": "Email",
- "role": "Role",
- "created": "Created"
- }
- },
- "monitorState": {
- "paused": "Paused",
- "resumed": "Resumed",
- "active": "Active"
- },
- "menu": {
- "uptime": "Uptime",
- "pagespeed": "Pagespeed",
- "infrastructure": "Infrastructure",
- "incidents": "Incidents",
- "statusPages": "Status pages",
- "maintenance": "Maintenance",
- "integrations": "Integrations",
- "settings": "Settings",
- "support": "Support",
- "discussions": "Discussions",
- "docs": "Docs",
- "changelog": "Changelog",
- "profile": "Profile",
- "password": "Password",
- "team": "Team",
- "logOut": "Log out",
- "notifications": "Notifications"
- },
- "settingsEmail": "Email",
- "settingsEmailDescription": "Configure the email settings for your system. This is used to send notifications and alerts.",
- "settingsEmailHost": "Email host - Hostname or IP address to connect to",
- "settingsEmailPort": "Email port - Port to connect to",
- "settingsEmailAddress": "Email address - Used for authentication",
- "settingsEmailPassword": "Email password - Password for authentication",
- "settingsEmailUser": "Email user - Username for authentication, overrides email address if specified",
- "settingsEmailFieldResetLabel": "Password is set. Click Reset to change it.",
- "state": "State",
- "statusBreadCrumbsStatusPages": "Status Pages",
- "statusBreadCrumbsDetails": "Details",
- "settingsThemeModeLight": "Light",
- "settingsThemeModeDark": "Dark",
- "settingsClearAllStatsDialogCancel": "Cancel",
- "commonSaving": "Saving...",
- "navControls": "Controls",
- "incidentsPageTitle": "Incidents",
- "passwordPanel": {
- "passwordChangedSuccess": "Your password was changed successfully.",
- "passwordInputIncorrect": "Your password input was incorrect.",
- "currentPassword": "Current password",
- "enterCurrentPassword": "Enter your current password",
- "newPassword": "New password",
- "enterNewPassword": "Enter your new password",
- "confirmNewPassword": "Confirm new password",
- "passwordRequirements": "New password must contain at least 8 characters and must have at least one uppercase letter, one lowercase letter, one number and one special character.",
- "saving": "Saving..."
- },
- "settingsEmailConnectionHost": "Email connection host - Hostname to use in the HELO/EHLO greeting",
- "emailSent": "Email sent successfully",
- "failedToSendEmail": "Failed to send email",
- "settingsTestEmail": "Send test e-mail",
- "settingsTestEmailSuccess": "Test email sent successfully",
- "settingsTestEmailFailed": "Failed to send test email",
- "settingsTestEmailFailedWithReason": "Failed to send test email: {{reason}}",
- "settingsTestEmailUnknownError": "Unknown error",
- "settingsEmailRequiredFields": "Email address, host, port and password are required",
- "statusMsg": {
- "paused": "Monitoring is paused.",
- "up": "Your site is up.",
- "down": "Your site is down.",
- "pending": "Pending..."
- },
- "settingsURLTitle": "Monitor IP/URL on Status Page",
- "settingsURLDescription": "Display the IP address or URL of monitor on the public Status page. If it's disabled, only the monitor name will be shown to protect sensitive information.",
- "settingsURLSelectTitle": "Display IP/URL on status page",
- "settingsURLEnabled": "Enabled",
- "settingURLDisabled": "Disabled",
- "uptimeGeneralInstructions": {
- "http": "Enter the URL or IP to monitor (e.g., https://example.com/ or 192.168.1.100) and add a clear display name that appears on the dashboard.",
- "ping": "Enter the IP address or hostname to ping (e.g., 192.168.1.100 or example.com) and add a clear display name that appears on the dashboard.",
- "docker": "Enter the Docker ID of your container. Docker IDs must be the full 64 char Docker ID. You can run docker inspect to get the full container ID.",
- "port": "Enter the URL or IP of the server, the port number and a clear display name that appears on the dashboard."
- },
- "common": {
- "appName": "Checkmate",
- "monitoringAgentName": "Capture",
- "buttons": {
- "toggleTheme": "Toggles light & dark"
- },
- "toasts": {
- "networkError": "Network error",
- "checkConnection": "Please check your connection",
- "unknownError": "Unknown error"
- }
- },
+ "SupportedFormats": "Supported formats",
+ "YourPhoto": "Profile photo",
+ "aboutus": "About Us",
+ "access": "Access",
+ "actions": "Actions",
+ "add": "Add",
+ "addMonitors": "Add monitors",
+ "administrator": "Administrator?",
+ "advancedMatching": "Advanced matching",
+ "ago": "ago",
+ "area": "Area",
"auth": {
"common": {
- "navigation": {
- "continue": "Continue",
- "back": "Back"
+ "errors": {
+ "validation": "Error validating data."
+ },
+ "fields": {
+ "password": {
+ "errors": {
+ "incorrect": "The password you provided does not match our records"
+ }
+ }
},
"inputs": {
"email": {
- "label": "Email",
- "placeholder": "jordan.ellis@domain.com",
"errors": {
"empty": "To continue, please enter your email address",
"invalid": "Please recheck validity of entered email address"
- }
+ },
+ "label": "Email",
+ "placeholder": "jordan.ellis@domain.com"
+ },
+ "firstName": {
+ "errors": {
+ "empty": "Please enter your name",
+ "length": "Name must be less than 50 characters",
+ "pattern": "Name must contain only letters, spaces, apostrophes, or hyphens"
+ },
+ "label": "Name",
+ "placeholder": "Jordan"
+ },
+ "lastName": {
+ "errors": {
+ "empty": "Please enter your surname",
+ "length": "Surname must be less than 50 characters",
+ "pattern": "Surname must contain only letters, spaces, apostrophes, or hyphens"
+ },
+ "label": "Surname",
+ "placeholder": "Ellis"
},
"password": {
+ "errors": {
+ "empty": "Please enter your password",
+ "incorrect": "The password you provided does not match our records",
+ "length": "Password must be at least 8 characters long",
+ "lowercase": "Password must contain at least 1 lowercase letter",
+ "number": "Password must contain at least 1 number",
+ "special": "Password must contain at least 1 special character",
+ "uppercase": "Password must contain at least 1 uppercase letter"
+ },
"label": "Password",
"rules": {
"length": {
"beginning": "Must be at least",
"highlighted": "8 characters long"
},
- "special": {
- "beginning": "Must contain at least",
- "highlighted": "one special character"
- },
- "number": {
- "beginning": "Must contain at least",
- "highlighted": "one number"
- },
- "uppercase": {
- "beginning": "Must contain at least",
- "highlighted": "one upper character"
- },
"lowercase": {
"beginning": "Must contain at least",
"highlighted": "one lower character"
@@ -554,81 +84,88 @@
"match": {
"beginning": "Confirm password and password",
"highlighted": "must match"
+ },
+ "number": {
+ "beginning": "Must contain at least",
+ "highlighted": "one number"
+ },
+ "special": {
+ "beginning": "Must contain at least",
+ "highlighted": "one special character"
+ },
+ "uppercase": {
+ "beginning": "Must contain at least",
+ "highlighted": "one upper character"
}
- },
- "errors": {
- "empty": "Please enter your password",
- "length": "Password must be at least 8 characters long",
- "uppercase": "Password must contain at least 1 uppercase letter",
- "lowercase": "Password must contain at least 1 lowercase letter",
- "number": "Password must contain at least 1 number",
- "special": "Password must contain at least 1 special character",
- "incorrect": "The password you provided does not match our records"
}
},
"passwordConfirm": {
+ "errors": {
+ "different": "Entered passwords don't match, so one of them is probably mistyped",
+ "empty": "Please enter your password again for confirmation (helps with typos)"
+ },
"label": "Confirm password",
- "placeholder": "Re-enter password to confirm",
- "errors": {
- "empty": "Please enter your password again for confirmation (helps with typos)",
- "different": "Entered passwords don't match, so one of them is probably mistyped"
- }
- },
- "firstName": {
- "label": "Name",
- "placeholder": "Jordan",
- "errors": {
- "empty": "Please enter your name",
- "length": "Name must be less than 50 characters",
- "pattern": "Name must contain only letters, spaces, apostrophes, or hyphens"
- }
- },
- "lastName": {
- "label": "Surname",
- "placeholder": "Ellis",
- "errors": {
- "empty": "Please enter your surname",
- "length": "Surname must be less than 50 characters",
- "pattern": "Surname must contain only letters, spaces, apostrophes, or hyphens"
- }
+ "placeholder": "Re-enter password to confirm"
}
},
- "errors": {
- "validation": "Error validating data."
+ "navigation": {
+ "back": "Back",
+ "continue": "Continue"
+ }
+ },
+ "forgotPassword": {
+ "buttons": {
+ "openEmail": "Open email app",
+ "resetPassword": "Reset password"
+ },
+ "heading": "Forgot password?",
+ "imageAlts": {
+ "email": "Email icon",
+ "lock": "Lock icon",
+ "passwordConfirm": "Password confirm icon",
+ "passwordKey": "Password key icon"
+ },
+ "links": {
+ "login": "Go back to Log In",
+ "resend": "Didn't receive the email? Click to resend"
+ },
+ "subheadings": {
+ "stepFour": "Your password has been successfully reset. Click below to log in magically.",
+ "stepOne": "No worries, we'll send you reset instructions.",
+ "stepThree": "Your new password must be different from previously used passwords.",
+ "stepTwo": "We sent a password reset link to "
+ },
+ "toasts": {
+ "emailNotFound": "Email not found.",
+ "error": "Unable to reset password. Please try again later or contact support.",
+ "redirect": "Redirecting in ...",
+ "sent": "Instructions sent to .",
+ "success": "Your password was reset successfully."
}
},
"login": {
- "heading": "Log In",
- "subheadings": {
- "stepOne": "Enter your email",
- "stepTwo": "Enter your password"
- },
- "links": {
- "forgotPassword": "Forgot password?",
- "register": "Do not have an account?",
- "forgotPasswordLink": "Reset password",
- "registerLink": "Register here"
- },
- "toasts": {
- "success": "Welcome back! You're successfully logged in.",
- "incorrectPassword": "Incorrect password"
- },
"errors": {
"password": {
"incorrect": "The password you provided does not match our records"
}
+ },
+ "heading": "Log In",
+ "links": {
+ "forgotPassword": "Forgot password?",
+ "forgotPasswordLink": "Reset password",
+ "register": "Do not have an account?",
+ "registerLink": "Register here"
+ },
+ "subheadings": {
+ "stepOne": "Enter your email",
+ "stepTwo": "Enter your password"
+ },
+ "toasts": {
+ "incorrectPassword": "Incorrect password",
+ "success": "Welcome back! You're successfully logged in."
}
},
"registration": {
- "heading": {
- "superAdmin": "Create super admin",
- "user": "Sign Up"
- },
- "subheadings": {
- "stepOne": "Enter your personal details",
- "stepTwo": "Enter your email",
- "stepThree": "Create your password"
- },
"description": {
"superAdmin": "Create your super admin account to get started",
"user": "Sign up as a user and ask super admin for access to your monitors"
@@ -637,121 +174,647 @@
"superAdmin": "Create super admin account",
"user": "Sign up regular user"
},
- "termsAndPolicies": "By creating an account, you agree to our Terms of Service and Privacy Policy.",
+ "heading": {
+ "superAdmin": "Create super admin",
+ "user": "Sign Up"
+ },
"links": {
"login": "Already have an account? Log In"
},
+ "subheadings": {
+ "stepOne": "Enter your personal details",
+ "stepThree": "Create your password",
+ "stepTwo": "Enter your email"
+ },
+ "termsAndPolicies": "By creating an account, you agree to our Terms of Service and Privacy Policy.",
"toasts": {
"success": "Welcome! Your account was created successfully."
}
- },
- "forgotPassword": {
- "heading": "Forgot password?",
- "subheadings": {
- "stepOne": "No worries, we'll send you reset instructions.",
- "stepTwo": "We sent a password reset link to ",
- "stepThree": "Your new password must be different from previously used passwords.",
- "stepFour": "Your password has been successfully reset. Click below to log in magically."
- },
- "buttons": {
- "openEmail": "Open email app",
- "resetPassword": "Reset password"
- },
- "imageAlts": {
- "passwordKey": "Password key icon",
- "email": "Email icon",
- "lock": "Lock icon",
- "passwordConfirm": "Password confirm icon"
- },
- "links": {
- "login": "Go back to Log In",
- "resend": "Didn't receive the email? Click to resend"
- },
- "toasts": {
- "sent": "Instructions sent to .",
- "emailNotFound": "Email not found.",
- "redirect": "Redirecting in ...",
- "success": "Your password was reset successfully.",
- "error": "Unable to reset password. Please try again later or contact support."
- }
}
},
+ "avgCpuTemperature": "Average CPU Temperature",
+ "bar": "Bar",
+ "basicInformation": "Basic Information",
+ "bulkImport": {
+ "fallbackPage": "Import a file to upload a list of servers in bulk",
+ "noFileSelected": "No file selected",
+ "parsingFailed": "Parsing failed",
+ "selectFile": "Select File",
+ "selectFileDescription": "You can download our template or sample",
+ "selectFileTips": "Select CSV file to upload",
+ "title": "Bulk Import",
+ "uploadSuccess": "Monitors created successfully!",
+ "validationFailed": "Validation failed"
+ },
+ "cancel": "Cancel",
+ "checkFrequency": "Check frequency",
+ "checkingEvery": "Checking every",
+ "city": "CITY",
+ "common": {
+ "appName": "Checkmate",
+ "buttons": {
+ "toggleTheme": "Toggles light & dark"
+ },
+ "monitoringAgentName": "Capture",
+ "toasts": {
+ "checkConnection": "Please check your connection",
+ "networkError": "Network error",
+ "unknownError": "Unknown error"
+ }
+ },
+ "commonSave": "Save",
+ "commonSaving": "Saving...",
+ "companyName": "Company name",
+ "configure": "Configure",
+ "cores": "Cores",
+ "country": "COUNTRY",
+ "cpu": "CPU",
+ "cpuFrequency": "CPU Frequency",
+ "cpuLogical": "CPU (Logical)",
+ "cpuPhysical": "CPU (Physical)",
+ "cpuTemperature": "CPU Temperature",
+ "cpuUsage": "CPU usage",
+ "createA": "Create a",
+ "createMaintenance": "Create maintenance",
+ "createMaintenanceWindow": "Create maintenance window",
+ "createMonitor": "Create monitor",
+ "createNew": "Create new",
+ "createNotifications": {
+ "discordSettings": {
+ "description": "Configure your Discord webhook here",
+ "title": "Discord",
+ "webhookLabel": "Discord Webhook URL",
+ "webhookPlaceholder": "https://your-server.com/webhook"
+ },
+ "emailSettings": {
+ "description": "Destination email addresses.",
+ "emailLabel": "Email address",
+ "emailPlaceholder": "e.g. john@example.com",
+ "title": "Email"
+ },
+ "nameSettings": {
+ "description": "A descriptive name for your integration.",
+ "nameLabel": "Name",
+ "namePlaceholder": "e.g. Slack notifications",
+ "title": "Name"
+ },
+ "pagerdutySettings": {
+ "description": "Configure your PagerDuty integration here",
+ "integrationKeyLabel": "Integration key",
+ "integrationKeyPlaceholder": "1234567890",
+ "title": "PagerDuty"
+ },
+ "slackSettings": {
+ "description": "Configure your Slack webhook here",
+ "title": "Slack",
+ "webhookLabel": "Slack webhook URL",
+ "webhookPlaceholder": "https://hooks.slack.com/services/..."
+ },
+ "title": "Create notification channel",
+ "typeSettings": {
+ "description": "Select the type of notification channel you want to create.",
+ "title": "Type",
+ "typeLabel": "Type"
+ },
+ "webhookSettings": {
+ "description": "Configure your webhook here",
+ "title": "Webhook",
+ "webhookLabel": "Webhook URL",
+ "webhookPlaceholder": "https://your-server.com/webhook"
+ }
+ },
+ "createYour": "Create your",
+ "date&Time": "Date & Time",
+ "delete": "Delete",
+ "deleteDialogDescription": "Once deleted, this monitor cannot be retrieved.",
+ "deleteDialogTitle": "Do you really want to delete this monitor?",
+ "deleteStatusPage": "Do you want to delete this status page?",
+ "deleteStatusPageConfirm": "Yes, delete status page",
+ "deleteStatusPageDescription": "Once deleted, your status page cannot be retrieved.",
+ "disk": "Disk",
+ "diskUsage": "Disk Usage",
+ "displayName": "Display name",
+ "distributedRightCategoryTitle": "Monitor",
+ "distributedStatusHeaderText": "Real-time, real-device coverage",
+ "distributedStatusServerMonitors": "Server Monitors",
+ "distributedStatusServerMonitorsDescription": "Monitor status of related servers",
+ "distributedStatusSubHeaderText": "Powered by millions devices worldwide, view a system performance by global region, country or city",
+ "distributedUptimeCreateAdvancedSettings": "Advanced settings",
+ "distributedUptimeCreateChecks": "Checks to perform",
+ "distributedUptimeCreateChecksDescription": "You can always add or remove checks after adding your site.",
+ "distributedUptimeCreateIncidentDescription": "When there is an incident, notify users.",
+ "distributedUptimeCreateIncidentNotification": "Incident notifications",
+ "distributedUptimeCreateSelectURL": "Here you can select the URL of the host, together with the type of monitor.",
+ "distributedUptimeDetailsNoMonitorHistory": "There is no check history for this monitor yet.",
+ "distributedUptimeDetailsStatusHeaderLastUpdate": "Last updated",
+ "distributedUptimeDetailsStatusHeaderUptime": "Uptime:",
+ "distributedUptimeStatus30Days": "30 days",
+ "distributedUptimeStatus60Days": "60 days",
+ "distributedUptimeStatus90Days": "90 days",
+ "distributedUptimeStatusBasicInfoDescription": "Define company name and the subdomain that your status page points to.",
+ "distributedUptimeStatusBasicInfoHeader": "Basic Information",
+ "distributedUptimeStatusCompanyNameLabel": "Company name",
+ "distributedUptimeStatusContactAdmin": "Please contact your administrator",
+ "distributedUptimeStatusCreateStatusPage": "status page",
+ "distributedUptimeStatusCreateStatusPageAccess": "Access",
+ "distributedUptimeStatusCreateStatusPageReady": "If your status page is ready, you can mark it as published.",
+ "distributedUptimeStatusCreateYour": "Create your",
+ "distributedUptimeStatusDevices": "Devices",
+ "distributedUptimeStatusEditYour": "Edit your",
+ "distributedUptimeStatusLogoDescription": "Upload a logo for your status page",
+ "distributedUptimeStatusLogoHeader": "Logo",
+ "distributedUptimeStatusLogoUploadButton": "Upload logo",
+ "distributedUptimeStatusPageAddressLabel": "Your status page address",
+ "distributedUptimeStatusPageDeleteConfirm": "Yes, delete status page",
+ "distributedUptimeStatusPageDeleteDescription": "Once deleted, your status page cannot be retrieved.",
+ "distributedUptimeStatusPageDeleteDialog": "Do you want to delete this status page?",
+ "distributedUptimeStatusPageNotPublic": "This status page is not public.",
+ "distributedUptimeStatusPageNotSetUp": "A status page is not set up.",
+ "distributedUptimeStatusPublishedLabel": "Published and visible to the public",
+ "distributedUptimeStatusStandardMonitorsDescription": "Attach standard monitors to your status page.",
+ "distributedUptimeStatusStandardMonitorsHeader": "Standard Monitors",
+ "distributedUptimeStatusUpt": "UPT",
+ "distributedUptimeStatusUptBurned": "UPT Burned",
+ "distributedUptimeStatusUptLogo": "Upt Logo",
+ "dockerContainerMonitoring": "Docker container monitoring",
+ "dockerContainerMonitoringDescription": "Check whether your Docker container is running or not.",
+ "duration": "Duration",
+ "edit": "Edit",
+ "editMaintenance": "Edit maintenance",
+ "editing": "Editing...",
+ "emailSent": "Email sent successfully",
+ "errorInvalidFieldId": "Invalid field ID provided",
+ "errorInvalidTypeId": "Invalid notification type provided",
"errorPages": {
"serverUnreachable": {
- "toasts": {
- "reconnected": "Successfully reconnected to the server.",
- "stillUnreachable": "Server is still unreachable. Please try again later."
- },
"alertBox": "Server Connection Error",
"description": "We're unable to connect to the server. Please check your internet connection or verify your deployment configuration if the problem persists.",
"retryButton": {
"default": "Retry connection",
"processing": "Connecting..."
+ },
+ "toasts": {
+ "reconnected": "Successfully reconnected to the server.",
+ "stillUnreachable": "Server is still unreachable. Please try again later."
}
}
},
- "createNotifications": {
- "title": "Create notification channel",
- "nameSettings": {
- "title": "Name",
- "description": "A descriptive name for your integration.",
- "nameLabel": "Name",
- "namePlaceholder": "e.g. Slack notifications"
+ "expectedValue": "Expected value",
+ "export": {
+ "failed": "Failed to export monitors",
+ "success": "Monitors exported successfully!",
+ "title": "Export Monitors"
+ },
+ "failedToSendEmail": "Failed to send email",
+ "features": "Features",
+ "frequency": "Frequency",
+ "friendlyNameInput": "Friendly name",
+ "friendlyNamePlaceholder": "Maintenance at __ : __ for ___ minutes",
+ "gb": "GB",
+ "greeting": {
+ "append": "The afternoon is your playground—let's make it epic!",
+ "overview": "Here's an overview of your {{type}} monitors.",
+ "prepend": "Hey there"
+ },
+ "high": "high",
+ "host": "Host",
+ "http": "HTTP",
+ "https": "HTTPS",
+ "ignoreTLSError": "Ignore TLS/SSL error",
+ "ignoreTLSErrorDescription": "Ignore TLS/SSL errors and continue checking the website's availability",
+ "incidentsOptionsHeader": "Incidents for:",
+ "incidentsOptionsHeaderFilterAll": "All",
+ "incidentsOptionsHeaderFilterBy": "Filter by:",
+ "incidentsOptionsHeaderFilterCannotResolve": "Cannot resolve",
+ "incidentsOptionsHeaderFilterDown": "Down",
+ "incidentsOptionsHeaderLastDay": "Last day",
+ "incidentsOptionsHeaderLastHour": "Last hour",
+ "incidentsOptionsHeaderLastWeek": "Last week",
+ "incidentsOptionsHeaderShow": "Show:",
+ "incidentsOptionsPlaceholderAllServers": "All servers",
+ "incidentsPageTitle": "Incidents",
+ "incidentsTableDateTime": "Date & Time",
+ "incidentsTableMessage": "Message",
+ "incidentsTableMonitorName": "Monitor Name",
+ "incidentsTableNoIncidents": "No incidents recorded",
+ "incidentsTablePaginationLabel": "incidents",
+ "incidentsTableStatus": "Status",
+ "incidentsTableStatusCode": "Status Code",
+ "infrastructureAlertNotificationDescription": "Send a notification to user(s) when thresholds exceed a specified percentage.",
+ "infrastructureAuthorizationSecretLabel": "Authorization secret",
+ "infrastructureCreateGeneralSettingsDescription": "Here you can select the URL of the host, together with the friendly name and authorization secret to connect to the server agent.",
+ "infrastructureCreateMonitor": "Create Infrastructure Monitor",
+ "infrastructureCreateYour": "Create your",
+ "infrastructureCustomizeAlerts": "Customize alerts",
+ "infrastructureDisplayNameLabel": "Display name",
+ "infrastructureEditMonitor": "Save Infrastructure Monitor",
+ "infrastructureEditYour": "Edit your",
+ "infrastructureMonitorCreated": "Infrastructure monitor created successfully!",
+ "infrastructureMonitorUpdated": "Infrastructure monitor updated successfully!",
+ "infrastructureProtocol": "Protocol",
+ "infrastructureServerRequirement": "The server you are monitoring must be running the",
+ "infrastructureServerUrlLabel": "Server URL",
+ "integrations": "Integrations",
+ "integrationsDiscord": "Discord",
+ "integrationsDiscordInfo": "Connect with Discord and view incidents directly in a channel",
+ "integrationsPrism": "Connect Prism to your favorite service.",
+ "integrationsSlack": "Slack",
+ "integrationsSlackInfo": "Connect with Slack and see incidents in a channel",
+ "integrationsZapier": "Zapier",
+ "integrationsZapierInfo": "Send all incidents to Zapier, and then see them everywhere",
+ "invalidFileFormat": "Unsupported file format!",
+ "invalidFileSize": "File size is too large!",
+ "inviteNoTokenFound": "No invite token found",
+ "loginHere": "Login here",
+ "logsPage": {
+ "description": "This page shows the latest 1000 lines of logs from the Checkmate server",
+ "logLevelSelect": {
+ "title": "Log level",
+ "values": {
+ "all": "All",
+ "debug": "Debug",
+ "error": "Error",
+ "info": "Info",
+ "warn": "Warn"
+ }
},
- "typeSettings": {
- "title": "Type",
- "description": "Select the type of notification channel you want to create.",
- "typeLabel": "Type"
+ "tabs": {
+ "logs": "Server logs",
+ "queue": "Job queue"
},
- "emailSettings": {
- "title": "Email",
- "description": "Destination email addresses.",
- "emailLabel": "Email address",
- "emailPlaceholder": "e.g. john@example.com"
- },
- "slackSettings": {
- "title": "Slack",
- "description": "Configure your Slack webhook here",
- "webhookLabel": "Slack webhook URL",
- "webhookPlaceholder": "https://hooks.slack.com/services/..."
- },
- "pagerDutySettings": {
- "title": "PagerDuty",
- "description": "Configure your PagerDuty integration here",
- "integrationKeyLabel": "Integration key",
- "integrationKeyPlaceholder": "1234567890"
- },
- "webhookSettings": {
- "title": "Webhook",
- "description": "Configure your webhook here",
- "webhookLabel": "Webhook URL",
- "webhookPlaceholder": "https://your-server.com/webhook"
- },
- "discordSettings": {
- "title": "Discord",
- "description": "Configure your Discord webhook here",
- "webhookLabel": "Discord Webhook URL",
- "webhookPlaceholder": "https://your-server.com/webhook"
+ "title": "Logs",
+ "toast": {
+ "fetchLogsSuccess": "Logs fetched successfully"
}
},
- "notificationConfig": {
- "title": "Notifications",
- "description": "Select the notifications channels you want to use"
+ "low": "low",
+ "maintenance": "maintenance",
+ "maintenanceRepeat": "Maintenance Repeat",
+ "maintenanceTableActionMenuDialogTitle": "Do you really want to remove this maintenance window?",
+ "maintenanceWindowDescription": "Your pings won't be sent during this time frame",
+ "maintenanceWindowName": "Maintenance Window Name",
+ "maskedPageSpeedKeyPlaceholder": "*************************************",
+ "matchMethod": "Match Method",
+ "mb": "MB",
+ "mem": "Mem",
+ "memory": "Memory",
+ "memoryUsage": "Memory usage",
+ "menu": {
+ "changelog": "Changelog",
+ "discussions": "Discussions",
+ "docs": "Docs",
+ "incidents": "Incidents",
+ "infrastructure": "Infrastructure",
+ "integrations": "Integrations",
+ "logOut": "Log out",
+ "logs": "Logs",
+ "maintenance": "Maintenance",
+ "notifications": "Notifications",
+ "pagespeed": "Pagespeed",
+ "password": "Password",
+ "profile": "Profile",
+ "settings": "Settings",
+ "statusPages": "Status pages",
+ "support": "Support",
+ "team": "Team",
+ "uptime": "Uptime"
+ },
+ "message": "Message",
+ "monitor": "monitor",
+ "monitorActions": {
+ "export": "Export Monitors",
+ "import": "Import Monitors",
+ "title": "Export/Import"
+ },
+ "monitorDisplayName": "Monitor display name",
+ "monitorState": {
+ "active": "Active",
+ "paused": "Paused",
+ "resumed": "Resumed"
},
"monitorStatus": {
"checkingEvery": "Checking every {{interval}}",
- "withCaptureAgent": "with Capture agent {{version}}",
- "up": "up",
"down": "down",
- "paused": "paused"
+ "paused": "paused",
+ "up": "up",
+ "withCaptureAgent": "with Capture agent {{version}}"
},
- "advancedMatching": "Advanced matching",
- "settingsEmailTLSServername": "TLS Servername - Optional Hostname for TLS Validation when host is an IP",
- "settingsEmailIgnoreTLS": "Ignore TLS - Disable STARTTLS",
- "settingsEmailRequireTLS": "Require TLS - Force STARTTLS",
- "settingsEmailRejectUnauthorized": "Reject Unauthorized",
- "settingsEmailSecure": "Secure - Use SSL",
- "settingsEmailPool": "Pool - Enable connection pooling",
+ "monitorStatusDown": "Monitor {name} ({url}) is DOWN and not responding",
+ "monitorStatusUp": "Monitor {name} ({url}) is now UP and responding",
+ "monitors": "monitors",
+ "monitorsToApply": "Monitors to apply maintenance window to",
+ "ms": "ms",
+ "navControls": "Controls",
+ "nextWindow": "Next window",
+ "notFoundButton": "Go to the main dashboard",
+ "notificationConfig": {
+ "description": "Select the notifications channels you want to use",
+ "title": "Notifications"
+ },
+ "notifications": {
+ "addOrEditNotifications": "Add or edit notifications",
+ "create": {
+ "failed": "Failed to create notification",
+ "success": "Notification created successfully"
+ },
+ "createButton": "Create notification channel",
+ "createTitle": "Notification channel",
+ "delete": {
+ "failed": "Failed to delete notification",
+ "success": "Notification deleted successfully"
+ },
+ "discord": {
+ "description": "To send data to a Discord channel from Checkmate via Discord notifications using webhooks, you can use Discord's incoming Webhooks feature.",
+ "label": "Discord",
+ "webhookLabel": "Discord Webhook URL",
+ "webhookPlaceholder": "https://discord.com/api/webhooks/...",
+ "webhookRequired": "Discord webhook URL is required"
+ },
+ "edit": {
+ "failed": "Failed to update notification",
+ "success": "Notification updated successfully"
+ },
+ "enableNotifications": "Enable {{platform}} notifications",
+ "fallback": {
+ "checks": [
+ "Alert teams about downtime or performance issues",
+ "Let engineers know when incidents happen",
+ "Keep administrators informed of system changes"
+ ],
+ "title": "notification channel"
+ },
+ "fetch": {
+ "failed": "Failed to fetch notifications",
+ "success": "Notifications fetched successfully"
+ },
+ "integrationButton": "Notification Integration",
+ "networkError": "Network error occurred",
+ "slack": {
+ "description": "To enable Slack notifications, create a Slack app and enable incoming webhooks. After that, simply provide the webhook URL here.",
+ "label": "Slack",
+ "webhookLabel": "Webhook URL",
+ "webhookPlaceholder": "https://hooks.slack.com/services/...",
+ "webhookRequired": "Slack webhook URL is required"
+ },
+ "telegram": {
+ "chatIdLabel": "Your Chat ID",
+ "chatIdPlaceholder": "-1001234567890",
+ "description": "To enable Telegram notifications, create a Telegram bot using BotFather, an official bot for creating and managing Telegram bots. Then, get the API token and chat ID and write them down here.",
+ "fieldsRequired": "Telegram token and chat ID are required",
+ "label": "Telegram",
+ "tokenLabel": "Your bot token",
+ "tokenPlaceholder": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
+ },
+ "test": {
+ "failed": "Failed to send test notification",
+ "success": "Test notification sent successfully"
+ },
+ "testFailed": "Failed to send test notification",
+ "testNotification": "Test notification",
+ "testNotificationDevelop": "Test notification 2",
+ "testSuccess": "Test notification sent successfully!",
+ "unsupportedType": "Unsupported notification type",
+ "webhook": {
+ "description": "You can set up a custom webhook to receive notifications when incidents occur.",
+ "label": "Webhooks",
+ "urlLabel": "Webhook URL",
+ "urlPlaceholder": "https://your-server.com/webhook",
+ "urlRequired": "Webhook URL is required"
+ }
+ },
+ "notifyEmails": "Also notify via email to multiple addresses (coming soon)",
+ "notifySMS": "Notify via SMS (coming soon)",
+ "now": "Now",
+ "os": "OS",
+ "pageSpeedAddApiKey": "to add your API key.",
+ "pageSpeedApiKeyFieldDescription": "Enter your Google PageSpeed API key to enable pagespeed monitoring. Click Reset to update the key.",
+ "pageSpeedApiKeyFieldLabel": "PageSpeed API key",
+ "pageSpeedApiKeyFieldResetLabel": "API key is set. Click Reset to change it.",
+ "pageSpeedApiKeyFieldTitle": "Google PageSpeed API key",
+ "pageSpeedConfigureSettingsDescription": "Here you can select the URL of the host, together with the type of monitor.",
+ "pageSpeedDetailsPerformanceReport": "Values are estimated and may vary.",
+ "pageSpeedDetailsPerformanceReportCalculator": "See calculator",
+ "pageSpeedLearnMoreLink": "Click here",
+ "pageSpeedMonitor": "PageSpeed monitor",
+ "pageSpeedWarning": "Warning: You haven't added a Google PageSpeed API key yet. Without it, the PageSpeed monitor won't function.",
+ "passwordPanel": {
+ "confirmNewPassword": "Confirm new password",
+ "currentPassword": "Current password",
+ "enterCurrentPassword": "Enter your current password",
+ "enterNewPassword": "Enter your new password",
+ "newPassword": "New password",
+ "passwordChangedSuccess": "Your password was changed successfully.",
+ "passwordInputIncorrect": "Your password input was incorrect.",
+ "passwordRequirements": "New password must contain at least 8 characters and must have at least one uppercase letter, one lowercase letter, one number and one special character.",
+ "saving": "Saving..."
+ },
+ "pause": "Pause",
+ "pingMonitoring": "Ping monitoring",
+ "pingMonitoringDescription": "Check whether your server is available or not.",
+ "portMonitoring": "Port monitoring",
+ "portMonitoringDescription": "Check whether your port is open or not.",
+ "portToMonitor": "Port to monitor",
+ "publicLink": "Public link",
+ "publicURL": "Public URL",
+ "queuePage": {
+ "failedJobTable": {
+ "failCountHeader": "Fail count",
+ "failReasonHeader": "Fail reason",
+ "failedAtHeader": "Last failed at",
+ "monitorIdHeader": "Monitor ID",
+ "monitorUrlHeader": "Monitor URL",
+ "title": "Failed jobs"
+ },
+ "flushButton": "Flush queue",
+ "jobTable": {
+ "activeHeader": "Active",
+ "failCountHeader": "Fail count",
+ "idHeader": "Monitor ID",
+ "lastFinishedAtHeader": "Last finished at",
+ "lastRunHeader": "Last run at",
+ "lastRunTookHeader": "Last run took",
+ "lockedAtHeader": "Locked at",
+ "runCountHeader": "Run count",
+ "title": "Jobs currently in queue",
+ "typeHeader": "Type",
+ "urlHeader": "URL"
+ },
+ "metricsTable": {
+ "metricHeader": "Metric",
+ "title": "Queue metrics",
+ "valueHeader": "Value"
+ },
+ "refreshButton": "Refresh",
+ "title": "Queue"
+ },
+ "remove": "Remove",
+ "removeLogo": "Remove Logo",
+ "repeat": "Repeat",
+ "reset": "Reset",
+ "response": "RESPONSE",
+ "responseTime": "Response time",
+ "resume": "Resume",
+ "roles": {
+ "admin": "Admin",
+ "demoUser": "Demo user",
+ "superAdmin": "Super admin",
+ "teamMember": "Team member"
+ },
+ "save": "Save",
+ "selectAll": "Select all",
"sendTestNotifications": "Send test notifications",
- "selectAll": "Select all"
+ "seperateEmails": "You can separate multiple emails with a comma",
+ "settingURLDisabled": "Disabled",
+ "settingsAbout": "About",
+ "settingsAddDemoMonitorsButton": "Add demo monitors",
+ "settingsAppearance": "Appearance",
+ "settingsAppearanceDescription": "Switch between light and dark mode, or change user interface language",
+ "settingsClearAllStats": "Clear all stats. This is irreversible.",
+ "settingsClearAllStatsButton": "Clear all stats",
+ "settingsClearAllStatsDialogCancel": "Cancel",
+ "settingsClearAllStatsDialogConfirm": "Yes, clear all stats",
+ "settingsClearAllStatsDialogDescription": "Once removed, the monitoring history and stats cannot be retrieved.",
+ "settingsClearAllStatsDialogTitle": "Do you want to clear all stats?",
+ "settingsDemoMonitors": "Demo monitors",
+ "settingsDemoMonitorsAdded": "Successfully added demo monitors",
+ "settingsDemoMonitorsDescription": "Add sample monitors for demonstration purposes.",
+ "settingsDevelopedBy": "Developed by Bluewave Labs.",
+ "settingsDisabled": "Disabled",
+ "settingsDisplayTimezone": "Display timezone",
+ "settingsDisplayTimezoneDescription": "Select the timezone used to display dates and times throughout the application.",
+ "settingsEmail": "Email",
+ "settingsEmailAddress": "Email address - Used for authentication",
+ "settingsEmailConnectionHost": "Email connection host - Hostname to use in the HELO/EHLO greeting",
+ "settingsEmailDescription": "Configure the email settings for your system. This is used to send notifications and alerts.",
+ "settingsEmailFieldResetLabel": "Password is set. Click Reset to change it.",
+ "settingsEmailHost": "Email host - Hostname or IP address to connect to",
+ "settingsEmailIgnoreTLS": "Ignore TLS - Disable STARTTLS",
+ "settingsEmailPassword": "Email password - Password for authentication",
+ "settingsEmailPool": "Pool - Enable connection pooling",
+ "settingsEmailPort": "Email port - Port to connect to",
+ "settingsEmailRejectUnauthorized": "Reject Unauthorized",
+ "settingsEmailRequireTLS": "Require TLS - Force STARTTLS",
+ "settingsEmailRequiredFields": "Email address, host, port and password are required",
+ "settingsEmailSecure": "Secure - Use SSL",
+ "settingsEmailTLSServername": "TLS Servername - Optional Hostname for TLS Validation when host is an IP",
+ "settingsEmailTransportLinkDescription": "This builds an SMTP transport for NodeMailer",
+ "settingsEmailTransportLinkText": "See specifications here",
+ "settingsEmailUser": "Email user - Username for authentication, overrides email address if specified",
+ "settingsEnabled": "Enabled",
+ "settingsFailedToAddDemoMonitors": "Failed to add demo monitors",
+ "settingsFailedToClearStats": "Failed to clear stats",
+ "settingsFailedToDeleteMonitors": "Failed to delete all monitors",
+ "settingsFailedToSave": "Failed to save settings",
+ "settingsGeneralSettings": "General settings",
+ "settingsHistoryAndMonitoring": "History of monitoring",
+ "settingsHistoryAndMonitoringDescription": "Define how long you want to retain historical data. You can also clear all existing data.",
+ "settingsLanguage": "Language",
+ "settingsMonitorsDeleted": "Successfully deleted all monitors",
+ "settingsRemoveAllMonitorsButton": "Remove all monitors",
+ "settingsRemoveAllMonitorsDialogConfirm": "Yes, remove all monitors",
+ "settingsRemoveAllMonitorsDialogTitle": "Do you want to remove all monitors?",
+ "settingsSave": "Save",
+ "settingsStatsCleared": "Stats cleared successfully",
+ "settingsSuccessSaved": "Settings saved successfully",
+ "settingsSystemReset": "System reset",
+ "settingsSystemResetDescription": "Remove all monitors from your system.",
+ "settingsTTLLabel": "The days you want to keep monitoring history.",
+ "settingsTTLOptionalLabel": "0 for infinite",
+ "settingsTestEmail": "Send test e-mail",
+ "settingsTestEmailFailed": "Failed to send test email",
+ "settingsTestEmailFailedWithReason": "Failed to send test email: {{reason}}",
+ "settingsTestEmailSuccess": "Test email sent successfully",
+ "settingsTestEmailUnknownError": "Unknown error",
+ "settingsThemeMode": "Theme Mode",
+ "settingsThemeModeDark": "Dark",
+ "settingsThemeModeLight": "Light",
+ "settingsURLDescription": "Display the IP address or URL of monitor on the public Status page. If it's disabled, only the monitor name will be shown to protect sensitive information.",
+ "settingsURLEnabled": "Enabled",
+ "settingsURLSelectTitle": "Display IP/URL on status page",
+ "settingsURLTitle": "Monitor IP/URL on Status Page",
+ "showAdminLoginLink": "Show \"Administrator? Login Here\" link on the status page",
+ "showCharts": "Show charts",
+ "showUptimePercentage": "Show uptime percentage",
+ "shown": "Shown",
+ "starPromptDescription": "See the latest releases and help grow the community on GitHub",
+ "starPromptTitle": "Star Checkmate",
+ "startTime": "Start time",
+ "state": "State",
+ "status": "Status",
+ "statusBreadCrumbsDetails": "Details",
+ "statusBreadCrumbsStatusPages": "Status Pages",
+ "statusCode": "Status code",
+ "statusMsg": {
+ "down": "Your site is down.",
+ "paused": "Monitoring is paused.",
+ "pending": "Pending...",
+ "up": "Your site is up."
+ },
+ "statusPageCreateAppearanceDescription": "Define the default look and feel of your public status page.",
+ "statusPageCreateBasicInfoDescription": "Define company name and the subdomain that your status page points to.",
+ "statusPageCreateBasicInfoStatusPageAddress": "Your status page address",
+ "statusPageCreateSelectTimeZoneDescription": "Select the timezone that your status page will be displayed in.",
+ "statusPageCreateSettings": "If your status page is ready, you can mark it as published.",
+ "statusPageCreateSettingsCheckboxLabel": "Published and visible to the public",
+ "statusPageCreateTabsContent": "Status page servers",
+ "statusPageCreateTabsContentDescription": "You can add any number of servers that you monitor to your status page. You can also reorder them for the best viewing experience.",
+ "statusPageCreateTabsContentFeaturesDescription": "Show more details on the status page",
+ "statusPageName": "Status page name",
+ "statusPageStatus": "A public status page is not set up.",
+ "statusPageStatusContactAdmin": "Please contact to your administrator",
+ "statusPageStatusNoPage": "There's no status page here.",
+ "statusPageStatusNotPublic": "This status page is not public.",
+ "statusPageStatusServiceStatus": "Service status",
+ "submit": "Submit",
+ "teamPanel": {
+ "cancel": "Cancel",
+ "email": "Email",
+ "emailToken": "E-mail token",
+ "filter": {
+ "all": "All",
+ "member": "Member"
+ },
+ "getToken": "Get token",
+ "inviteDescription": "When you add a new team member, they will get access to all monitors.",
+ "inviteLink": "Invite link",
+ "inviteNewTeamMember": "Invite new team member",
+ "inviteTeamMember": "Invite a team member",
+ "noMembers": "There are no team members with this role",
+ "selectRole": "Select role",
+ "table": {
+ "created": "Created",
+ "email": "Email",
+ "name": "Name",
+ "role": "Role"
+ },
+ "teamMembers": "Team members"
+ },
+ "testLocale": "testLocale",
+ "timeZoneInfo": "All dates and times are in GMT+0 time zone.",
+ "timezone": "Timezone",
+ "title": "Title",
+ "tlsErrorIgnored": "TLS/SSL errors ignored",
+ "total": "Total",
+ "type": "Type",
+ "update": "Update",
+ "uptime": "Uptime",
+ "uptimeCreate": "The expected value is used to match against response result, and the match determines the status.",
+ "uptimeCreateJsonPath": "This expression will be evaluated against the reponse JSON data and the result will be used to match against the expected value. See",
+ "uptimeCreateJsonPathQuery": "for query language documentation.",
+ "uptimeGeneralInstructions": {
+ "docker": "Enter the Docker ID of your container. Docker IDs must be the full 64 char Docker ID. You can run docker inspect to get the full container ID.",
+ "http": "Enter the URL or IP to monitor (e.g., https://example.com/ or 192.168.1.100) and add a clear display name that appears on the dashboard.",
+ "ping": "Enter the IP address or hostname to ping (e.g., 192.168.1.100 or example.com) and add a clear display name that appears on the dashboard.",
+ "port": "Enter the URL or IP of the server, the port number and a clear display name that appears on the dashboard."
+ },
+ "url": "URL",
+ "urlMonitor": "URL to monitor",
+ "used": "Used",
+ "webhookSendError": "Error sending webhook notification to {platform}",
+ "webhookSendSuccess": "Webhook notification sent successfully",
+ "webhookUnsupportedPlatform": "Unsupported platform: {platform}",
+ "websiteMonitoring": "Website monitoring",
+ "websiteMonitoringDescription": "Use HTTP(s) to monitor your website or API endpoint.",
+ "whenNewIncident": "When there is a new incident,",
+ "window": "window"
}
diff --git a/client/src/locales/es.json b/client/src/locales/es.json
index fb8207e48..3579117ae 100644
--- a/client/src/locales/es.json
+++ b/client/src/locales/es.json
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
- "settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
- "settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
@@ -583,6 +581,13 @@
},
"errors": {
"validation": ""
+ },
+ "fields": {
+ "password": {
+ "errors": {
+ "incorrect": ""
+ }
+ }
}
},
"login": {
@@ -709,13 +714,13 @@
"integrationKeyLabel": "",
"integrationKeyPlaceholder": ""
},
- "genericWebhookSettings": {
+ "discordSettings": {
"title": "",
"description": "",
"webhookLabel": "",
"webhookPlaceholder": ""
},
- "discordSettings": {
+ "webhookSettings": {
"title": "",
"description": "",
"webhookLabel": "",
@@ -740,5 +745,6 @@
"settingsEmailRejectUnauthorized": "",
"settingsEmailSecure": "",
"settingsEmailPool": "",
- "sendTestNotifications": ""
+ "sendTestNotifications": "",
+ "selectAll": ""
}
diff --git a/client/src/locales/fi.json b/client/src/locales/fi.json
index f085b5857..d92fcf8db 100644
--- a/client/src/locales/fi.json
+++ b/client/src/locales/fi.json
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
- "settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
- "settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
@@ -583,6 +581,13 @@
},
"errors": {
"validation": ""
+ },
+ "fields": {
+ "password": {
+ "errors": {
+ "incorrect": ""
+ }
+ }
}
},
"login": {
@@ -709,13 +714,13 @@
"integrationKeyLabel": "",
"integrationKeyPlaceholder": ""
},
- "genericWebhookSettings": {
+ "discordSettings": {
"title": "",
"description": "",
"webhookLabel": "",
"webhookPlaceholder": ""
},
- "discordSettings": {
+ "webhookSettings": {
"title": "",
"description": "",
"webhookLabel": "",
@@ -740,5 +745,6 @@
"settingsEmailRejectUnauthorized": "",
"settingsEmailSecure": "",
"settingsEmailPool": "",
- "sendTestNotifications": ""
+ "sendTestNotifications": "",
+ "selectAll": ""
}
diff --git a/client/src/locales/fr.json b/client/src/locales/fr.json
index 48834602e..729296216 100644
--- a/client/src/locales/fr.json
+++ b/client/src/locales/fr.json
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "Oui, effacer les stats",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
- "settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
- "settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
@@ -583,6 +581,13 @@
},
"errors": {
"validation": ""
+ },
+ "fields": {
+ "password": {
+ "errors": {
+ "incorrect": ""
+ }
+ }
}
},
"login": {
@@ -709,13 +714,13 @@
"integrationKeyLabel": "",
"integrationKeyPlaceholder": ""
},
- "genericWebhookSettings": {
+ "discordSettings": {
"title": "",
"description": "",
"webhookLabel": "",
"webhookPlaceholder": ""
},
- "discordSettings": {
+ "webhookSettings": {
"title": "",
"description": "",
"webhookLabel": "",
@@ -740,5 +745,6 @@
"settingsEmailRejectUnauthorized": "",
"settingsEmailSecure": "",
"settingsEmailPool": "",
- "sendTestNotifications": ""
+ "sendTestNotifications": "",
+ "selectAll": ""
}
diff --git a/client/src/locales/pt-BR.json b/client/src/locales/pt-BR.json
index a783a820c..fa6197bb3 100644
--- a/client/src/locales/pt-BR.json
+++ b/client/src/locales/pt-BR.json
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "Sim, limpar todas as estatísticas",
"settingsDemoMonitors": "Monitores de demonstração",
"settingsDemoMonitorsDescription": "Adicione monitores de genéricos para fins de demonstração.",
- "settingsAddDemoMonitors": "Adicionando monitores de demonstração",
"settingsAddDemoMonitorsButton": "Adicionar monitores de demonstração",
- "settingsRemoveAllMonitors": "Removendo todos os monitores",
"settingsRemoveAllMonitorsButton": "Remover todos os monitores",
"settingsRemoveAllMonitorsDialogTitle": "Você deseja remover todos os monitores?",
"settingsRemoveAllMonitorsDialogConfirm": "Sim, remova todos os monitores",
@@ -312,6 +310,7 @@
"statusPageCreateTabsContentFeaturesDescription": "Mostrar mais detalhes na página de status",
"showCharts": "Mostrar gráficos",
"showUptimePercentage": "Mostrar porcentagem de Uptime",
+ "showAdminLoginLink": "Mostrar o link \"Administrador? Efetue login aqui\" na página de status",
"removeLogo": "Remover logo",
"statusPageStatus": "Uma página de status pública não está configurada.",
"statusPageStatusContactAdmin": "Entre em contato com seu administrador",
@@ -583,6 +582,13 @@
},
"errors": {
"validation": ""
+ },
+ "fields": {
+ "password": {
+ "errors": {
+ "incorrect": ""
+ }
+ }
}
},
"login": {
@@ -709,13 +715,13 @@
"integrationKeyLabel": "",
"integrationKeyPlaceholder": ""
},
- "genericWebhookSettings": {
+ "discordSettings": {
"title": "",
"description": "",
"webhookLabel": "",
"webhookPlaceholder": ""
},
- "discordSettings": {
+ "webhookSettings": {
"title": "",
"description": "",
"webhookLabel": "",
@@ -740,5 +746,6 @@
"settingsEmailRejectUnauthorized": "",
"settingsEmailSecure": "",
"settingsEmailPool": "",
- "sendTestNotifications": ""
+ "sendTestNotifications": "",
+ "selectAll": ""
}
diff --git a/client/src/locales/ru.json b/client/src/locales/ru.json
index 539bb2d51..908bc73e7 100644
--- a/client/src/locales/ru.json
+++ b/client/src/locales/ru.json
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "Да, очистить всю статистику",
"settingsDemoMonitors": "Демо мониторы",
"settingsDemoMonitorsDescription": "Здесь вы можете добавлять и удалять демонстрационные мониторы.",
- "settingsAddDemoMonitors": "Добавьте демонстрационные мониторы",
"settingsAddDemoMonitorsButton": "Добавьте демонстрационные мониторы",
- "settingsRemoveAllMonitors": "Удалить все демонстрационные мониторы",
"settingsRemoveAllMonitorsButton": "Удалить все демонстрационные мониторы",
"settingsRemoveAllMonitorsDialogTitle": "Хотите удалить все мониторы?",
"settingsRemoveAllMonitorsDialogConfirm": "Да, очистить все мониторы",
@@ -583,6 +581,13 @@
},
"errors": {
"validation": ""
+ },
+ "fields": {
+ "password": {
+ "errors": {
+ "incorrect": ""
+ }
+ }
}
},
"login": {
@@ -709,13 +714,13 @@
"integrationKeyLabel": "",
"integrationKeyPlaceholder": ""
},
- "genericWebhookSettings": {
+ "discordSettings": {
"title": "",
"description": "",
"webhookLabel": "",
"webhookPlaceholder": ""
},
- "discordSettings": {
+ "webhookSettings": {
"title": "",
"description": "",
"webhookLabel": "",
@@ -740,5 +745,6 @@
"settingsEmailRejectUnauthorized": "",
"settingsEmailSecure": "",
"settingsEmailPool": "",
- "sendTestNotifications": ""
+ "sendTestNotifications": "",
+ "selectAll": ""
}
diff --git a/client/src/locales/tr.json b/client/src/locales/tr.json
index 1d86de06c..c9e224d63 100644
--- a/client/src/locales/tr.json
+++ b/client/src/locales/tr.json
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "Evet, tüm istatistikleri temizle",
"settingsDemoMonitors": "Demo monitörler",
"settingsDemoMonitorsDescription": "Burada demo monitörler ekleyebilir ve kaldırabilirsiniz.",
- "settingsAddDemoMonitors": "Demo monitörler ekle",
"settingsAddDemoMonitorsButton": "Demo monitörler ekle",
- "settingsRemoveAllMonitors": "Tüm monitörleri kaldır",
"settingsRemoveAllMonitorsButton": "Tüm monitörleri kaldır",
"settingsRemoveAllMonitorsDialogTitle": "Tüm monitörleri kaldırmak istiyor musunuz?",
"settingsRemoveAllMonitorsDialogConfirm": "Evet, tüm monitörleri temizle",
@@ -117,11 +115,11 @@
"title": "",
"checks": [""]
},
- "createButton": "",
- "createTitle": "",
+ "createButton": "Bildirim kanalı oluştur",
+ "createTitle": "Bildirim kanalı",
"create": {
"success": "",
- "failed": ""
+ "failed": "Bildirim kanalı oluşturulamadı"
},
"fetch": {
"success": "",
@@ -136,7 +134,7 @@
"failed": ""
},
"test": {
- "success": "",
+ "success": "Test bildirimi başarıyla gönderildi",
"failed": ""
}
},
@@ -373,33 +371,33 @@
"pageSpeedApiKeyFieldDescription": "Sayfa hızı izlemeyi etkinleştirmek için Google PageSpeed API anahtarınızı girin. Anahtarı güncellemek için Sıfırla'ya tıklayın.",
"pageSpeedApiKeyFieldResetLabel": "API anahtarı ayarlandı. Değiştirmek için Sıfırla'ya tıklayın.",
"reset": "Sıfırla",
- "ignoreTLSError": "",
- "tlsErrorIgnored": "",
+ "ignoreTLSError": "TLS/SSL hatalarını gözardı et",
+ "tlsErrorIgnored": "TLS/SSL hataları gözardı ediliyor",
"ignoreTLSErrorDescription": "",
- "createNew": "",
+ "createNew": "Yeni oluştur",
"greeting": {
- "prepend": "",
+ "prepend": "Merhaba",
"append": "",
"overview": ""
},
"roles": {
- "superAdmin": "",
- "admin": "",
- "teamMember": "",
- "demoUser": ""
+ "superAdmin": "Süper yönetici",
+ "admin": "Yönetici",
+ "teamMember": "Ekip elemanı",
+ "demoUser": "Demo kullanıcısı"
},
"teamPanel": {
- "teamMembers": "",
+ "teamMembers": "Ekip elemanları",
"filter": {
- "all": "",
- "member": ""
+ "all": "Tümü",
+ "member": "Eleman"
},
- "inviteTeamMember": "",
- "inviteNewTeamMember": "",
+ "inviteTeamMember": "Ekip elemanı davet et",
+ "inviteNewTeamMember": "Yeni ekip elemanı davet et",
"inviteDescription": "",
- "email": "",
- "selectRole": "",
- "inviteLink": "",
+ "email": "Eposta",
+ "selectRole": "Rolü seçin",
+ "inviteLink": "Davet linki",
"cancel": "",
"noMembers": "",
"getToken": "",
@@ -422,21 +420,21 @@
"infrastructure": "",
"incidents": "",
"statusPages": "",
- "maintenance": "",
- "integrations": "",
- "settings": "",
- "support": "",
- "discussions": "",
- "docs": "",
- "changelog": "",
- "profile": "",
- "password": "",
- "team": "",
+ "maintenance": "Bakım",
+ "integrations": "Entegrasyonlar",
+ "settings": "Ayarlar",
+ "support": "Destek",
+ "discussions": "Soru-cevap",
+ "docs": "Dokümanlar",
+ "changelog": "Değişiklik günlüğü",
+ "profile": "Profil",
+ "password": "Parola",
+ "team": "Ekip",
"logOut": "Çıkış yap",
- "notifications": ""
+ "notifications": "Bildirimler"
},
- "settingsEmail": "",
- "settingsEmailDescription": "",
+ "settingsEmail": "Eposta",
+ "settingsEmailDescription": "Sistem için eposta ayarlarını düzenleyin. Bu işlem eposta üzerinden bildirim ve alarm almak için gereklidir.",
"settingsEmailHost": "",
"settingsEmailPort": "",
"settingsEmailAddress": "",
@@ -490,73 +488,73 @@
"port": ""
},
"common": {
- "appName": "",
+ "appName": "Checkmate",
"monitoringAgentName": "",
"buttons": {
"toggleTheme": ""
},
"toasts": {
"networkError": "",
- "checkConnection": "",
- "unknownError": ""
+ "checkConnection": "Lütfen bağlantınızı kontrol edin",
+ "unknownError": "Bilinmeyen hata"
}
},
"auth": {
"common": {
"navigation": {
- "continue": "",
- "back": ""
+ "continue": "Devam et",
+ "back": "Geri"
},
"inputs": {
"email": {
- "label": "",
+ "label": "Eposta",
"placeholder": "",
"errors": {
- "empty": "",
+ "empty": "Devam etmek için eposta adresinizi girin",
"invalid": ""
}
},
"password": {
- "label": "",
+ "label": "Parola",
"rules": {
"length": {
"beginning": "",
- "highlighted": ""
+ "highlighted": "8 karakter uzunluğunda"
},
"special": {
- "beginning": "",
- "highlighted": ""
+ "beginning": "En az içermelidir: ",
+ "highlighted": "bir özel karakter"
},
"number": {
- "beginning": "",
- "highlighted": ""
+ "beginning": "En az içermelidir: ",
+ "highlighted": "bir sayı"
},
"uppercase": {
- "beginning": "",
- "highlighted": ""
+ "beginning": "En az içermelidir: ",
+ "highlighted": "Bir büyük harf"
},
"lowercase": {
- "beginning": "",
- "highlighted": ""
+ "beginning": "En az içermelidir: ",
+ "highlighted": "Bir küçük harf"
},
"match": {
"beginning": "",
- "highlighted": ""
+ "highlighted": "eşleşmelidir"
}
},
"errors": {
- "empty": "",
- "length": "",
- "uppercase": "",
- "lowercase": "",
- "number": "",
- "special": "",
- "incorrect": ""
+ "empty": "Lütfen parolanızı girin",
+ "length": "Parolanız en az 8 karakter uzunluğunda olmalıdır",
+ "uppercase": "Parola en az 1 büyük harf içermelidir",
+ "lowercase": "Parola en az 1 küçük harf içermelidir",
+ "number": "Parola en az 1 sayı içermelidir",
+ "special": "Parola en az 1 özel karakter içermelidir",
+ "incorrect": "Parolanız sistemdeki ile uyuşmuyor"
}
},
"passwordConfirm": {
- "label": "",
- "placeholder": "",
+ "label": "Parolayı onayla",
+ "placeholder": "Onaylamak için parolayı yeniden gir",
"errors": {
"empty": "",
"different": ""
@@ -572,78 +570,85 @@
}
},
"lastName": {
- "label": "",
- "placeholder": "",
+ "label": "Soyadı",
+ "placeholder": "Ellis",
"errors": {
- "empty": "",
- "length": "",
+ "empty": "Lütfen soyadınızı girin",
+ "length": "Soyadı en çok 50 karakter olmalıdır",
"pattern": ""
}
}
},
"errors": {
"validation": ""
+ },
+ "fields": {
+ "password": {
+ "errors": {
+ "incorrect": ""
+ }
+ }
}
},
"login": {
"heading": "",
"subheadings": {
- "stepOne": "",
- "stepTwo": ""
+ "stepOne": "Eposta adresinizi girin",
+ "stepTwo": "Parolanızı girin"
},
"links": {
- "forgotPassword": "",
- "register": "",
- "forgotPasswordLink": "",
- "registerLink": ""
+ "forgotPassword": "Parolanızı mı unuttunuz?",
+ "register": "Hesabınız yok mu?",
+ "forgotPasswordLink": "Parolayı sıfırla",
+ "registerLink": "Buradan kayıt olun"
},
"toasts": {
- "success": "",
- "incorrectPassword": ""
+ "success": "Tekrar hoşgeldiniz! Başarıyla sisteme giriş yaptınız.",
+ "incorrectPassword": "Hatalı parola"
},
"errors": {
"password": {
- "incorrect": ""
+ "incorrect": "Giriş yaptığınız parola sistemdekiyle uyuşmuyor"
}
}
},
"registration": {
"heading": {
- "superAdmin": "",
- "user": ""
+ "superAdmin": "Süper yönetici oluştur",
+ "user": "Kayıt ol"
},
"subheadings": {
- "stepOne": "",
- "stepTwo": "",
- "stepThree": ""
+ "stepOne": "Kişisel bilgilerinizi girin",
+ "stepTwo": "Eposta adresinizi girin",
+ "stepThree": "Parolanızı oluturun"
},
"description": {
- "superAdmin": "",
+ "superAdmin": "Başlamak için süper yönetici hesabı oluşturun",
"user": ""
},
"gettingStartedButton": {
- "superAdmin": "",
+ "superAdmin": "Süper yönetici hesabı oluşturun",
"user": ""
},
"termsAndPolicies": "",
"links": {
- "login": ""
+ "login": "Bir hesabınız mı var? Giriş yapın"
},
"toasts": {
- "success": ""
+ "success": "Hoşgeldiniz! Hesabınız başarıyla oluşturuldu."
}
},
"forgotPassword": {
- "heading": "",
+ "heading": "Parolanızı mı kaybettiniz?",
"subheadings": {
- "stepOne": "",
- "stepTwo": "",
+ "stepOne": "Endişelenmeyin, sıfırlamak için gerekli bilgileri göndereceğiz.",
+ "stepTwo": "Parola sıfırlama için gerekli bilgileri hesabına ilettik.",
"stepThree": "",
"stepFour": ""
},
"buttons": {
- "openEmail": "",
- "resetPassword": ""
+ "openEmail": "Eposta uygulamasını aç",
+ "resetPassword": "Parolayı sıfırla"
},
"imageAlts": {
"passwordKey": "",
@@ -670,75 +675,76 @@
"reconnected": "",
"stillUnreachable": ""
},
- "alertBox": "",
+ "alertBox": "Sunucu bağlantı hatası",
"description": "",
"retryButton": {
- "default": "",
- "processing": ""
+ "default": "Bağlantıyı tekrarla",
+ "processing": "Bağlanıyor..."
}
}
},
"createNotifications": {
- "title": "",
+ "title": "Bildirim kanalları oluştur",
"nameSettings": {
- "title": "",
- "description": "",
- "nameLabel": "",
- "namePlaceholder": ""
+ "title": "İsim",
+ "description": "Entegrasyon için açıklama",
+ "nameLabel": "İsim",
+ "namePlaceholder": "örn. Slack bildirimleri"
},
"typeSettings": {
- "title": "",
+ "title": "Tür",
"description": "",
- "typeLabel": ""
+ "typeLabel": "Tür"
},
"emailSettings": {
- "title": "",
- "description": "",
- "emailLabel": "",
- "emailPlaceholder": ""
+ "title": "Eposta",
+ "description": "Hedef eposta adresi",
+ "emailLabel": "Eposta adresi",
+ "emailPlaceholder": "örn. john@example.com"
},
"slackSettings": {
- "title": "",
- "description": "",
- "webhookLabel": "",
- "webhookPlaceholder": ""
+ "title": "Slack",
+ "description": "Slack webhook'u burada tanımlayın",
+ "webhookLabel": "Slack webhook adresi",
+ "webhookPlaceholder": "https://hooks.slack.com/services/...\n"
},
"pagerdutySettings": {
- "title": "",
- "description": "",
- "integrationKeyLabel": "",
- "integrationKeyPlaceholder": ""
- },
- "genericWebhookSettings": {
- "title": "",
- "description": "",
- "webhookLabel": "",
- "webhookPlaceholder": ""
+ "title": "PagerDuty",
+ "description": "PagerDuty entegrasyonunu burada yapın",
+ "integrationKeyLabel": "Entegrasyon anahtarı",
+ "integrationKeyPlaceholder": "1234567890"
},
"discordSettings": {
- "title": "",
+ "title": "Discord",
+ "description": "Discord webhook'u buradan ayarlayın",
+ "webhookLabel": "Discord Webhook adresi",
+ "webhookPlaceholder": "https://sunucu-adı.com/webhook\n"
+ },
+ "webhookSettings": {
+ "title": "Webhook",
"description": "",
"webhookLabel": "",
- "webhookPlaceholder": ""
+ "webhookPlaceholder": "https://sunucu-adı.com/webhook\n"
}
},
"notificationConfig": {
- "title": "",
+ "title": "Bildirimler",
"description": ""
},
"monitorStatus": {
"checkingEvery": "",
"withCaptureAgent": "",
- "up": "",
- "down": "",
- "paused": ""
+ "up": "ayakta",
+ "down": "çalışmıyor",
+ "paused": "beklemede"
},
"advancedMatching": "",
"settingsEmailTLSServername": "",
"settingsEmailIgnoreTLS": "",
"settingsEmailRequireTLS": "",
"settingsEmailRejectUnauthorized": "",
- "settingsEmailSecure": "",
+ "settingsEmailSecure": "Güvenli - SSL kullan",
"settingsEmailPool": "",
- "sendTestNotifications": ""
+ "sendTestNotifications": "Bildirimleri dene",
+ "selectAll": "Tümünü seç"
}
diff --git a/client/src/locales/zh-TW.json b/client/src/locales/zh-TW.json
index 6e864b7ee..9dfd6effb 100644
--- a/client/src/locales/zh-TW.json
+++ b/client/src/locales/zh-TW.json
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
- "settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
- "settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
@@ -583,6 +581,13 @@
},
"errors": {
"validation": ""
+ },
+ "fields": {
+ "password": {
+ "errors": {
+ "incorrect": ""
+ }
+ }
}
},
"login": {
@@ -709,13 +714,13 @@
"integrationKeyLabel": "",
"integrationKeyPlaceholder": ""
},
- "genericWebhookSettings": {
+ "discordSettings": {
"title": "",
"description": "",
"webhookLabel": "",
"webhookPlaceholder": ""
},
- "discordSettings": {
+ "webhookSettings": {
"title": "",
"description": "",
"webhookLabel": "",
@@ -740,5 +745,6 @@
"settingsEmailRejectUnauthorized": "",
"settingsEmailSecure": "",
"settingsEmailPool": "",
- "sendTestNotifications": ""
+ "sendTestNotifications": "",
+ "selectAll": ""
}
diff --git a/docker/.gitignore b/docker/.gitignore
index 81ba730c9..9a6df883a 100755
--- a/docker/.gitignore
+++ b/docker/.gitignore
@@ -10,4 +10,5 @@ prod/redis/data/*
dist/docker-compose-test.yaml
/dist-mono/mongo/data/*
/dist-mono/redis/data/*
+/dist-arm/mongo/data/*
*.env
diff --git a/docker/dist-arm/docker-compose.yaml b/docker/dist-arm/docker-compose.yaml
index f66640f71..921d3b8c1 100644
--- a/docker/dist-arm/docker-compose.yaml
+++ b/docker/dist-arm/docker-compose.yaml
@@ -1,6 +1,6 @@
services:
server:
- image: ghcr.io/bluewave-labs/checkmate:backend-dist-mono-multiarch
+ image: ghcr.io/bluewave-labs/checkmate-backend-mono-multiarch:latest
restart: always
ports:
- "52345:52345"
@@ -12,16 +12,7 @@ services:
- CLIENT_HOST=http://localhost
- JWT_SECRET=my_secret
depends_on:
- - redis
- mongodb
- redis:
- image: redis:7
- container_name: checkmate-redis
- ports:
- - "6379:6379"
- volumes:
- - ./redis/data:/data
- restart: unless-stopped
mongodb:
image: mongo:4.4.18
container_name: checkmate-mongodb
diff --git a/docker/dist-mono/docker-compose.yaml b/docker/dist-mono/docker-compose.yaml
index fd5d54ddc..0dd97aafb 100755
--- a/docker/dist-mono/docker-compose.yaml
+++ b/docker/dist-mono/docker-compose.yaml
@@ -12,19 +12,7 @@ services:
- CLIENT_HOST=http://localhost
- JWT_SECRET=my_secret
depends_on:
- - redis
- mongodb
- redis:
- image: ghcr.io/bluewave-labs/checkmate-redis:latest
- restart: always
- volumes:
- - ./redis/data:/data
- healthcheck:
- test: ["CMD", "redis-cli", "ping"]
- interval: 30s
- timeout: 10s
- retries: 5
- start_period: 5s
mongodb:
image: ghcr.io/bluewave-labs/checkmate-mongo:latest
restart: always
diff --git a/docker/dist/docker-compose.yaml b/docker/dist/docker-compose.yaml
index 580f21e97..ff2f1b7ea 100755
--- a/docker/dist/docker-compose.yaml
+++ b/docker/dist/docker-compose.yaml
@@ -16,7 +16,6 @@ services:
ports:
- "52345:52345"
depends_on:
- - redis
- mongodb
environment:
- DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db?replicaSet=rs0
@@ -25,17 +24,6 @@ services:
- JWT_SECRET=my_secret
# volumes:
# - /var/run/docker.sock:/var/run/docker.sock:ro
- redis:
- image: ghcr.io/bluewave-labs/checkmate-redis:latest
- restart: always
- volumes:
- - ./redis/data:/data
- healthcheck:
- test: ["CMD", "redis-cli", "ping"]
- interval: 30s
- timeout: 10s
- retries: 5
- start_period: 5s
mongodb:
image: ghcr.io/bluewave-labs/checkmate-mongo:latest
restart: always
diff --git a/docker/prod/docker-compose.yaml b/docker/prod/docker-compose.yaml
index 9cc4a82cb..980a0e2f5 100755
--- a/docker/prod/docker-compose.yaml
+++ b/docker/prod/docker-compose.yaml
@@ -21,6 +21,8 @@ services:
volumes:
- ./certbot/www/:/var/www/certbot/:rw
- ./certbot/conf/:/etc/letsencrypt/:rw
+ entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot; sleep 12h & wait $${!}; done;'"
+
server:
image: ghcr.io/bluewave-labs/checkmate:backend-demo
restart: always
@@ -29,21 +31,9 @@ services:
env_file:
- server.env
depends_on:
- - redis
- mongodb
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- redis:
- image: ghcr.io/bluewave-labs/checkmate:redis-demo
- restart: always
- volumes:
- - ./redis/data:/data
- healthcheck:
- test: ["CMD", "redis-cli", "ping"]
- interval: 30s
- timeout: 10s
- retries: 5
- start_period: 5s
mongodb:
image: ghcr.io/bluewave-labs/checkmate:mongo-demo
restart: always
diff --git a/server/controllers/authController.js b/server/controllers/authController.js
index df4034aef..d517c50d9 100755
--- a/server/controllers/authController.js
+++ b/server/controllers/authController.js
@@ -82,7 +82,7 @@ class AuthController {
}
const newUser = await this.db.insertUser({ ...req.body }, req.file);
- logger.info({
+ this.logger.info({
message: this.stringService.authCreateUser,
service: SERVICE_NAME,
details: newUser._id,
@@ -111,7 +111,7 @@ class AuthController {
});
});
} catch (error) {
- logger.warn({
+ this.logger.warn({
message: error.message,
service: SERVICE_NAME,
method: "registerUser",
@@ -412,8 +412,7 @@ class AuthController {
// 1. Find all the monitors associated with the team ID if superadmin
const result = await this.db.getMonitorsByTeamId({
- query: {},
- params: { teamId: user.teamId.toString() },
+ teamId: user.teamId.toString(),
});
if (user.role.includes("superadmin")) {
diff --git a/server/controllers/logController.js b/server/controllers/logController.js
new file mode 100644
index 000000000..5381fd3fa
--- /dev/null
+++ b/server/controllers/logController.js
@@ -0,0 +1,23 @@
+import { handleError } from "./controllerUtils.js";
+
+const SERVICE_NAME = "JobQueueController";
+
+class LogController {
+ constructor(logger) {
+ this.logger = logger;
+ }
+
+ getLogs = async (req, res, next) => {
+ try {
+ const logs = await this.logger.getLogs();
+ res.success({
+ msg: "Logs fetched successfully",
+ data: logs,
+ });
+ } catch (error) {
+ next(handleError(error, SERVICE_NAME, "getLogs"));
+ return;
+ }
+ };
+}
+export default LogController;
diff --git a/server/controllers/monitorController.js b/server/controllers/monitorController.js
index e1d087c74..d32ef463e 100755
--- a/server/controllers/monitorController.js
+++ b/server/controllers/monitorController.js
@@ -681,7 +681,7 @@ class MonitorController {
explain,
});
return res.success({
- msg: "OK", // TODO
+ msg: "OK",
data: result,
});
} catch (error) {
@@ -698,6 +698,42 @@ class MonitorController {
next(handleError(error, SERVICE_NAME, "seedDb"));
}
};
+
+ exportMonitorsToCSV = async (req, res, next) => {
+ try {
+ const { teamId } = req.user;
+
+ const monitors = await this.db.getMonitorsByTeamId({ teamId });
+ if (!monitors || monitors.length === 0) {
+ return res.success({
+ msg: this.stringService.noMonitorsFound,
+ data: null,
+ });
+ }
+ const csvData = monitors?.filteredMonitors?.map((monitor) => ({
+ name: monitor.name,
+ description: monitor.description,
+ type: monitor.type,
+ url: monitor.url,
+ interval: monitor.interval,
+ port: monitor.port,
+ ignoreTlsErrors: monitor.ignoreTlsErrors,
+ isActive: monitor.isActive,
+ }));
+
+ const csv = pkg.unparse(csvData);
+
+ return res.file({
+ data: csv,
+ headers: {
+ "Content-Type": "text/csv",
+ "Content-Disposition": "attachment; filename=monitors.csv",
+ },
+ });
+ } catch (error) {
+ next(handleError(error, SERVICE_NAME, "exportMonitorsToCSV"));
+ }
+ };
}
export default MonitorController;
diff --git a/server/controllers/queueController.js b/server/controllers/queueController.js
index b1a7badee..968106a5a 100755
--- a/server/controllers/queueController.js
+++ b/server/controllers/queueController.js
@@ -25,7 +25,7 @@ class JobQueueController {
try {
const jobs = await this.jobQueue.getJobs();
return res.success({
- msg: this.stringService.queueGetMetrics,
+ msg: this.stringService.queueGetJobs,
data: jobs,
});
} catch (error) {
@@ -34,6 +34,20 @@ class JobQueueController {
}
};
+ getAllMetrics = async (req, res, next) => {
+ try {
+ const jobs = await this.jobQueue.getJobs();
+ const metrics = await this.jobQueue.getMetrics();
+ return res.success({
+ msg: this.stringService.queueGetAllMetrics,
+ data: { jobs, metrics },
+ });
+ } catch (error) {
+ next(handleError(error, SERVICE_NAME, "getAllMetrics"));
+ return;
+ }
+ };
+
addJob = async (req, res, next) => {
try {
await this.jobQueue.addJob(Math.random().toString(36).substring(7));
diff --git a/server/db/models/NetworkCheck.js b/server/db/models/NetworkCheck.js
new file mode 100644
index 000000000..eca69eef9
--- /dev/null
+++ b/server/db/models/NetworkCheck.js
@@ -0,0 +1,46 @@
+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);
diff --git a/server/db/models/StatusPage.js b/server/db/models/StatusPage.js
index e4c81707f..92cbab1cd 100755
--- a/server/db/models/StatusPage.js
+++ b/server/db/models/StatusPage.js
@@ -70,6 +70,10 @@ const StatusPageSchema = mongoose.Schema(
type: Boolean,
default: true,
},
+ showAdminLoginLink: {
+ type: Boolean,
+ default: false,
+ },
},
{ timestamps: true }
);
diff --git a/server/db/mongo/modules/networkCheckModule.js b/server/db/mongo/modules/networkCheckModule.js
new file mode 100644
index 000000000..53c55c7e3
--- /dev/null
+++ b/server/db/mongo/modules/networkCheckModule.js
@@ -0,0 +1,46 @@
+import NetworkCheck from "../../models/NetworkCheck.js";
+
+const SERVICE_NAME = "networkCheckModule";
+
+/**
+ * Creates and saves a new network check document to the database.
+ * @async
+ * @param {object} networkCheckData - The data for the new network check. This should conform to the NetworkCheckSchema.
+ * @param {string} networkCheckData.monitorId - The ID of the monitor associated with this check.
+ * @returns {Promise