diff --git a/.github/workflows/distribution-deploy.yml b/.github/workflows/distribution-deploy.yml
index 69f3afd73..d4bcfc8b7 100644
--- a/.github/workflows/distribution-deploy.yml
+++ b/.github/workflows/distribution-deploy.yml
@@ -11,9 +11,6 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
-
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
diff --git a/.github/workflows/distribution-mono-deploy-arm.yml b/.github/workflows/distribution-mono-deploy-arm.yml
new file mode 100644
index 000000000..345cab560
--- /dev/null
+++ b/.github/workflows/distribution-mono-deploy-arm.yml
@@ -0,0 +1,44 @@
+name: Distribution deploy - Monolithic Multiarch
+
+on:
+ push:
+ branches: ["master"]
+ workflow_dispatch:
+jobs:
+ docker-build-and-push-server:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Log in to GitHub Container Registry
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Build and push multi-arch Docker image
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ file: ./docker/dist-arm/server.Dockerfile
+ push: true
+ tags: ghcr.io/bluewave-labs/checkmate:backend-dist-mono-multiarch
+ platforms: linux/amd64,linux/arm64
+ labels: |
+ org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate
+
+ # - name: Build Server Docker image
+ # run: |
+ # docker build \
+ # -t ghcr.io/bluewave-labs/checkmate:backend-dist-mono-multiarch \
+ # -f ./docker/dist-arm/server.Dockerfile \
+ # --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \
+ # .
+
+ # - name: Push Server Docker image
+ # run: docker push ghcr.io/bluewave-labs/checkmate:backend-dist-mono-multiarch
diff --git a/.github/workflows/distribution-mono-deploy.yml b/.github/workflows/distribution-mono-deploy.yml
new file mode 100644
index 000000000..7dafbfc21
--- /dev/null
+++ b/.github/workflows/distribution-mono-deploy.yml
@@ -0,0 +1,30 @@
+name: Distribution deploy - Monolithic
+
+on:
+ push:
+ branches: ["master"]
+ workflow_dispatch:
+jobs:
+ docker-build-and-push-server:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Log in to GitHub Container Registry
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Build Server Docker image
+ run: |
+ docker build \
+ -t ghcr.io/bluewave-labs/checkmate:backend-dist-mono \
+ -f ./docker/dist-mono/server.Dockerfile \
+ --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \
+ .
+
+ - name: Push Server Docker image
+ run: docker push ghcr.io/bluewave-labs/checkmate:backend-dist-mono
diff --git a/client/package-lock.json b/client/package-lock.json
index b7859e0fe..e12e76f97 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -52,7 +52,10 @@
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.6",
"prettier": "^3.3.3",
- "vite": "^5.2.0"
+ "vite": "^5.4.19"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-linux-arm64-musl": "4.41.0"
}
},
"node_modules/@ampproject/remapping": {
@@ -69,44 +72,44 @@
}
},
"node_modules/@babel/code-frame": {
- "version": "7.26.2",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
- "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.27.1",
"js-tokens": "^4.0.0",
- "picocolors": "^1.0.0"
+ "picocolors": "^1.1.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/compat-data": {
- "version": "7.26.8",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz",
- "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==",
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz",
+ "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
- "version": "7.26.10",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
- "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz",
+ "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==",
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.26.2",
- "@babel/generator": "^7.26.10",
- "@babel/helper-compilation-targets": "^7.26.5",
- "@babel/helper-module-transforms": "^7.26.0",
- "@babel/helpers": "^7.26.10",
- "@babel/parser": "^7.26.10",
- "@babel/template": "^7.26.9",
- "@babel/traverse": "^7.26.10",
- "@babel/types": "^7.26.10",
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.27.1",
+ "@babel/helper-compilation-targets": "^7.27.1",
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helpers": "^7.27.1",
+ "@babel/parser": "^7.27.1",
+ "@babel/template": "^7.27.1",
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -128,13 +131,13 @@
"license": "MIT"
},
"node_modules/@babel/generator": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
- "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz",
+ "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.27.0",
- "@babel/types": "^7.27.0",
+ "@babel/parser": "^7.27.1",
+ "@babel/types": "^7.27.1",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^3.0.2"
@@ -144,13 +147,13 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.26.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz",
- "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==",
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
"license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.26.5",
- "@babel/helper-validator-option": "^7.25.9",
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
@@ -160,27 +163,27 @@
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
- "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.25.9",
- "@babel/types": "^7.25.9"
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz",
- "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz",
+ "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==",
"license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.25.9",
- "@babel/helper-validator-identifier": "^7.25.9",
- "@babel/traverse": "^7.25.9"
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
@@ -190,9 +193,9 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.26.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
- "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -200,52 +203,52 @@
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
- "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
- "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
- "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helpers": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
- "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz",
+ "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==",
"license": "MIT",
"dependencies": {
- "@babel/template": "^7.27.0",
- "@babel/types": "^7.27.0"
+ "@babel/template": "^7.27.1",
+ "@babel/types": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
- "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz",
+ "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==",
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.27.0"
+ "@babel/types": "^7.27.1"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -255,13 +258,13 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx-self": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz",
- "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
+ "@babel/helper-plugin-utils": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
@@ -271,13 +274,13 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx-source": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz",
- "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
+ "@babel/helper-plugin-utils": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
@@ -287,42 +290,39 @@
}
},
"node_modules/@babel/runtime": {
- "version": "7.26.10",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz",
- "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
+ "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
"license": "MIT",
- "dependencies": {
- "regenerator-runtime": "^0.14.0"
- },
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
- "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.26.2",
- "@babel/parser": "^7.27.0",
- "@babel/types": "^7.27.0"
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
- "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz",
+ "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==",
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.26.2",
- "@babel/generator": "^7.27.0",
- "@babel/parser": "^7.27.0",
- "@babel/template": "^7.27.0",
- "@babel/types": "^7.27.0",
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.27.1",
+ "@babel/parser": "^7.27.1",
+ "@babel/template": "^7.27.1",
+ "@babel/types": "^7.27.1",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@@ -331,13 +331,13 @@
}
},
"node_modules/@babel/types": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
- "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
+ "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
"license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.25.9",
- "@babel/helper-validator-identifier": "^7.25.9"
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
@@ -347,6 +347,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.1.0.tgz",
"integrity": "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==",
+ "license": "MIT",
"engines": {
"node": ">=14"
}
@@ -497,6 +498,262 @@
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
"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==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@esbuild/linux-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
@@ -513,10 +770,106 @@
"node": ">=12"
}
},
+ "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==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@eslint-community/eslint-utils": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
- "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
+ "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -593,22 +946,22 @@
}
},
"node_modules/@floating-ui/core": {
- "version": "1.6.8",
- "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz",
- "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz",
+ "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==",
"license": "MIT",
"dependencies": {
- "@floating-ui/utils": "^0.2.8"
+ "@floating-ui/utils": "^0.2.9"
}
},
"node_modules/@floating-ui/dom": {
- "version": "1.6.12",
- "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz",
- "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz",
+ "integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==",
"license": "MIT",
"dependencies": {
- "@floating-ui/core": "^1.6.0",
- "@floating-ui/utils": "^0.2.8"
+ "@floating-ui/core": "^1.7.0",
+ "@floating-ui/utils": "^0.2.9"
}
},
"node_modules/@floating-ui/react-dom": {
@@ -625,9 +978,9 @@
}
},
"node_modules/@floating-ui/utils": {
- "version": "0.2.8",
- "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz",
- "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==",
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
+ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
"license": "MIT"
},
"node_modules/@fontsource/roboto": {
@@ -710,9 +1063,9 @@
"license": "BSD-3-Clause"
},
"node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
- "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+ "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
@@ -741,18 +1094,6 @@
"node": ">=6.0.0"
}
},
- "node_modules/@jridgewell/source-map": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
- "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25"
- }
- },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
@@ -827,9 +1168,9 @@
}
},
"node_modules/@maplibre/maplibre-gl-style-spec": {
- "version": "23.1.0",
- "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-23.1.0.tgz",
- "integrity": "sha512-R6/ihEuC5KRexmKIYkWqUv84Gm+/QwsOUgHyt1yy2XqCdGdLvlBWVWIIeTZWN4NGdwmY6xDzdSGU2R9oBLNg2w==",
+ "version": "23.3.0",
+ "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-23.3.0.tgz",
+ "integrity": "sha512-IGJtuBbaGzOUgODdBRg66p8stnwj9iDXkgbYKoYcNiiQmaez5WVRfXm4b03MCDwmZyX93csbfHFWEJJYHnn5oA==",
"license": "ISC",
"dependencies": {
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
@@ -846,6 +1187,39 @@
"gl-style-validate": "dist/gl-style-validate.mjs"
}
},
+ "node_modules/@mui/base": {
+ "version": "5.0.0-beta.42",
+ "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.42.tgz",
+ "integrity": "sha512-fWRiUJVCHCPF+mxd5drn08bY2qRw3jj5f1SSQdUXmaJ/yKpk23ys8MgLO2KGVTRtbks/+ctRfgffGPbXifj0Ug==",
+ "deprecated": "This package has been replaced by @base-ui-components/react",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.4",
+ "@floating-ui/react-dom": "^2.0.8",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^6.0.0-alpha.1",
+ "@popperjs/core": "^2.11.8",
+ "clsx": "^2.1.0",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@mui/core-downloads-tracker": {
"version": "6.4.11",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.11.tgz",
@@ -923,32 +1297,114 @@
}
}
},
- "node_modules/@mui/lab/node_modules/@mui/base": {
- "version": "5.0.0-beta.42",
- "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.42.tgz",
- "integrity": "sha512-fWRiUJVCHCPF+mxd5drn08bY2qRw3jj5f1SSQdUXmaJ/yKpk23ys8MgLO2KGVTRtbks/+ctRfgffGPbXifj0Ug==",
- "deprecated": "This package has been replaced by @base-ui-components/react",
+ "node_modules/@mui/lab/node_modules/@mui/private-theming": {
+ "version": "6.4.9",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.9.tgz",
+ "integrity": "sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.24.4",
- "@floating-ui/react-dom": "^2.0.8",
- "@mui/types": "^7.2.14",
- "@mui/utils": "^6.0.0-alpha.1",
- "@popperjs/core": "^2.11.8",
- "clsx": "^2.1.0",
+ "@babel/runtime": "^7.26.0",
+ "@mui/utils": "^6.4.9",
"prop-types": "^15.8.1"
},
"engines": {
- "node": ">=12.0.0"
+ "node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
- "@types/react": "^17.0.0 || ^18.0.0",
- "react": "^17.0.0 || ^18.0.0",
- "react-dom": "^17.0.0 || ^18.0.0"
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/lab/node_modules/@mui/styled-engine": {
+ "version": "6.4.11",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.11.tgz",
+ "integrity": "sha512-74AUmlHXaGNbyUqdK/+NwDJOZqgRQw6BcNvhoWYLq3LGbLTkE+khaJ7soz6cIabE4CPYqO2/QAIU1Z/HEjjpcw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@emotion/cache": "^11.13.5",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/sheet": "^1.4.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.4.1",
+ "@emotion/styled": "^11.3.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/lab/node_modules/@mui/system": {
+ "version": "6.4.11",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.11.tgz",
+ "integrity": "sha512-gibtsrZEwnDaT5+I/KloOj/yHluX5G8heknuxBpQOdEQ3Gc0avjSImn5hSeKp8D4thiwZiApuggIjZw1dQguUA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@mui/private-theming": "^6.4.9",
+ "@mui/styled-engine": "^6.4.11",
+ "@mui/types": "~7.2.24",
+ "@mui/utils": "^6.4.9",
+ "clsx": "^2.1.1",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/lab/node_modules/@mui/types": {
+ "version": "7.2.24",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz",
+ "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
@@ -1005,13 +1461,7 @@
}
}
},
- "node_modules/@mui/material/node_modules/react-is": {
- "version": "19.1.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
- "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==",
- "license": "MIT"
- },
- "node_modules/@mui/private-theming": {
+ "node_modules/@mui/material/node_modules/@mui/private-theming": {
"version": "6.4.9",
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.9.tgz",
"integrity": "sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==",
@@ -1038,7 +1488,7 @@
}
}
},
- "node_modules/@mui/styled-engine": {
+ "node_modules/@mui/material/node_modules/@mui/styled-engine": {
"version": "6.4.11",
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.11.tgz",
"integrity": "sha512-74AUmlHXaGNbyUqdK/+NwDJOZqgRQw6BcNvhoWYLq3LGbLTkE+khaJ7soz6cIabE4CPYqO2/QAIU1Z/HEjjpcw==",
@@ -1072,7 +1522,7 @@
}
}
},
- "node_modules/@mui/system": {
+ "node_modules/@mui/material/node_modules/@mui/system": {
"version": "6.4.11",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.11.tgz",
"integrity": "sha512-gibtsrZEwnDaT5+I/KloOj/yHluX5G8heknuxBpQOdEQ3Gc0avjSImn5hSeKp8D4thiwZiApuggIjZw1dQguUA==",
@@ -1112,7 +1562,7 @@
}
}
},
- "node_modules/@mui/types": {
+ "node_modules/@mui/material/node_modules/@mui/types": {
"version": "7.2.24",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz",
"integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==",
@@ -1126,6 +1576,189 @@
}
}
},
+ "node_modules/@mui/private-theming": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.1.0.tgz",
+ "integrity": "sha512-4Kck4jxhqF6YxNwJdSae1WgDfXVg0lIH6JVJ7gtuFfuKcQCgomJxPvUEOySTFRPz1IZzwz5OAcToskRdffElDA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.27.1",
+ "@mui/utils": "^7.1.0",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/private-theming/node_modules/@mui/utils": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.0.tgz",
+ "integrity": "sha512-/OM3S8kSHHmWNOP+NH9xEtpYSG10upXeQ0wLZnfDgmgadTAk5F4MQfFLyZ5FCRJENB3eRzltMmaNl6UtDnPovw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.27.1",
+ "@mui/types": "^7.4.2",
+ "@types/prop-types": "^15.7.14",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-is": "^19.1.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/styled-engine": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.1.0.tgz",
+ "integrity": "sha512-m0mJ0c6iRC+f9hMeRe0W7zZX1wme3oUX0+XTVHjPG7DJz6OdQ6K/ggEOq7ZdwilcpdsDUwwMfOmvO71qDkYd2w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.27.1",
+ "@emotion/cache": "^11.13.5",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/sheet": "^1.4.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.4.1",
+ "@emotion/styled": "^11.3.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/system": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.1.0.tgz",
+ "integrity": "sha512-iedAWgRJMCxeMHvkEhsDlbvkK+qKf9me6ofsf7twk/jfT4P1ImVf7Rwb5VubEA0sikrVL+1SkoZM41M4+LNAVA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.27.1",
+ "@mui/private-theming": "^7.1.0",
+ "@mui/styled-engine": "^7.1.0",
+ "@mui/types": "^7.4.2",
+ "@mui/utils": "^7.1.0",
+ "clsx": "^2.1.1",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/system/node_modules/@mui/utils": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.0.tgz",
+ "integrity": "sha512-/OM3S8kSHHmWNOP+NH9xEtpYSG10upXeQ0wLZnfDgmgadTAk5F4MQfFLyZ5FCRJENB3eRzltMmaNl6UtDnPovw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.27.1",
+ "@mui/types": "^7.4.2",
+ "@types/prop-types": "^15.7.14",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-is": "^19.1.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/types": {
+ "version": "7.4.2",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.2.tgz",
+ "integrity": "sha512-edRc5JcLPsrlNFYyTPxds+d5oUovuUxnnDtpJUbP6WMeV4+6eaX/mqai1ZIWT62lCOe0nlrON0s9HDiv5en5bA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.27.1"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@mui/utils": {
"version": "6.4.9",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.9.tgz",
@@ -1156,16 +1789,24 @@
}
}
},
- "node_modules/@mui/utils/node_modules/react-is": {
- "version": "19.0.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz",
- "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==",
- "license": "MIT"
+ "node_modules/@mui/utils/node_modules/@mui/types": {
+ "version": "7.2.24",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz",
+ "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
},
"node_modules/@mui/x-charts": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.29.0.tgz",
- "integrity": "sha512-sFkMaw4qSQwy9yYlT9/T21c2pOVI3OAqfvy9aQp+GaEbDjTuyWwcWRX4dkN2fb674qofSbRgk5VW9xrnuGuYNQ==",
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.29.1.tgz",
+ "integrity": "sha512-5s9PX51HWhpMa+DCDa4RgjtODSaMe+PlTZUqoGIil2vaW/+4ouDLREXvyuVvIF93KfZwrPKAL2SJKSQS4YYB2w==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.25.7",
@@ -1395,17 +2036,20 @@
"node_modules/@react-dnd/asap": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz",
- "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A=="
+ "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==",
+ "license": "MIT"
},
"node_modules/@react-dnd/invariant": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz",
- "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw=="
+ "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==",
+ "license": "MIT"
},
"node_modules/@react-dnd/shallowequal": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz",
- "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA=="
+ "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==",
+ "license": "MIT"
},
"node_modules/@react-spring/animated": {
"version": "9.7.5",
@@ -1515,9 +2159,9 @@
}
},
"node_modules/@rollup/pluginutils": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz",
- "integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==",
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
+ "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
@@ -1536,10 +2180,205 @@
}
}
},
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz",
+ "integrity": "sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz",
+ "integrity": "sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz",
+ "integrity": "sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz",
+ "integrity": "sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz",
+ "integrity": "sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz",
+ "integrity": "sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz",
+ "integrity": "sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz",
+ "integrity": "sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz",
+ "integrity": "sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz",
+ "integrity": "sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz",
+ "integrity": "sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz",
+ "integrity": "sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz",
+ "integrity": "sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz",
+ "integrity": "sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz",
+ "integrity": "sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.27.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz",
- "integrity": "sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==",
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz",
+ "integrity": "sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==",
"cpu": [
"x64"
],
@@ -1550,9 +2389,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.27.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz",
- "integrity": "sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==",
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.0.tgz",
+ "integrity": "sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==",
"cpu": [
"x64"
],
@@ -1562,6 +2401,45 @@
"linux"
]
},
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz",
+ "integrity": "sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz",
+ "integrity": "sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz",
+ "integrity": "sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@sideway/address": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
@@ -1849,9 +2727,9 @@
}
},
"node_modules/@types/babel__generator": {
- "version": "7.6.8",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
- "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1870,9 +2748,9 @@
}
},
"node_modules/@types/babel__traverse": {
- "version": "7.20.6",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz",
- "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==",
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz",
+ "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1913,24 +2791,24 @@
}
},
"node_modules/@types/d3-path": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz",
- "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
"license": "MIT"
},
"node_modules/@types/d3-scale": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
- "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
"license": "MIT",
"dependencies": {
"@types/d3-time": "*"
}
},
"node_modules/@types/d3-shape": {
- "version": "3.1.6",
- "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz",
- "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
+ "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
"license": "MIT",
"dependencies": {
"@types/d3-path": "*"
@@ -1949,9 +2827,9 @@
"license": "MIT"
},
"node_modules/@types/estree": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
- "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"license": "MIT"
},
"node_modules/@types/geojson": {
@@ -1986,17 +2864,6 @@
"@types/pbf": "*"
}
},
- "node_modules/@types/node": {
- "version": "22.13.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.2.tgz",
- "integrity": "sha512-Z+r8y3XL9ZpI2EY52YYygAFmo2/oWfNSj4BCpAXE2McAexDk8VcnBMGC9Djn9gTKt4d2T/hhXqmPzo4hfIXtTg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "undici-types": "~6.20.0"
- }
- },
"node_modules/@types/parse-json": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
@@ -2016,9 +2883,9 @@
"license": "MIT"
},
"node_modules/@types/react": {
- "version": "18.3.20",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.20.tgz",
- "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==",
+ "version": "18.3.22",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.22.tgz",
+ "integrity": "sha512-vUhG0YmQZ7kL/tmKLrD3g5zXbXXreZXB3pmROW8bg3CnLnpjkRVwUlLne7Ufa2r9yJ8+/6B73RzhAek5TBKh2Q==",
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
@@ -2026,9 +2893,9 @@
}
},
"node_modules/@types/react-dom": {
- "version": "18.3.6",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.6.tgz",
- "integrity": "sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==",
+ "version": "18.3.7",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
+ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -2060,16 +2927,16 @@
"license": "MIT"
},
"node_modules/@ungap/structured-clone": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
- "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"dev": true,
"license": "ISC"
},
"node_modules/@vitejs/plugin-react": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.0.tgz",
- "integrity": "sha512-x/EztcTKVj+TDeANY1WjNeYsvZjZdfWRMP/KXi5Yn8BoTzpa13ZltaQqKfvWYbX8CE10GOHHdC5v86jY9x8i/g==",
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz",
+ "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2086,21 +2953,11 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0"
}
},
- "node_modules/@vitejs/plugin-react/node_modules/react-refresh": {
- "version": "0.17.0",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
- "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/acorn": {
- "version": "8.14.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
- "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
- "devOptional": true,
+ "version": "8.14.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
+ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+ "dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -2228,16 +3085,16 @@
}
},
"node_modules/array.prototype.flat": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
- "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.2.0",
- "es-abstract": "^1.22.1",
- "es-shim-unscopables": "^1.0.0"
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
@@ -2304,6 +3161,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -2327,9 +3194,9 @@
}
},
"node_modules/axios": {
- "version": "1.8.4",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
- "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
+ "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
@@ -2363,6 +3230,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
@@ -2379,9 +3247,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.24.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
- "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
+ "version": "4.24.5",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
+ "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
"funding": [
{
"type": "opencollective",
@@ -2398,10 +3266,10 @@
],
"license": "MIT",
"dependencies": {
- "caniuse-lite": "^1.0.30001688",
- "electron-to-chromium": "^1.5.73",
+ "caniuse-lite": "^1.0.30001716",
+ "electron-to-chromium": "^1.5.149",
"node-releases": "^2.0.19",
- "update-browserslist-db": "^1.1.1"
+ "update-browserslist-db": "^1.1.3"
},
"bin": {
"browserslist": "cli.js"
@@ -2410,14 +3278,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -2441,7 +3301,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -2490,9 +3349,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001699",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz",
- "integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==",
+ "version": "1.0.30001718",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
+ "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
"funding": [
{
"type": "opencollective",
@@ -2615,6 +3474,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
"integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
+ "license": "MIT",
"dependencies": {
"tiny-invariant": "^1.0.6"
}
@@ -2623,6 +3483,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+ "license": "MIT",
"dependencies": {
"utrie": "^1.0.2"
}
@@ -2827,9 +3688,9 @@
"license": "MIT"
},
"node_modules/debug": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
- "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -2914,6 +3775,7 @@
"version": "16.0.1",
"resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz",
"integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==",
+ "license": "MIT",
"dependencies": {
"@react-dnd/asap": "^5.0.1",
"@react-dnd/invariant": "^4.0.1",
@@ -2924,6 +3786,7 @@
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.9.2"
}
@@ -2965,7 +3828,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
- "dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
@@ -2983,9 +3845,9 @@
"license": "ISC"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.99",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.99.tgz",
- "integrity": "sha512-77c/+fCyL2U+aOyqfIFi89wYLBeSTCs55xCZL0oFH0KjqsvSvyh6AdQ+UIl1vgpnQQE6g+/KK8hOIupH6VwPtg==",
+ "version": "1.5.155",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
+ "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==",
"license": "ISC"
},
"node_modules/entities": {
@@ -3010,9 +3872,9 @@
}
},
"node_modules/es-abstract": {
- "version": "1.23.7",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.7.tgz",
- "integrity": "sha512-OygGC8kIcDhXX+6yAZRGLqwi2CmEXCbLQixeGUgYeR+Qwlppqmo7DIDr8XibtEBZp+fJcoYpoatp5qwLMEdcqQ==",
+ "version": "1.23.10",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.10.tgz",
+ "integrity": "sha512-MtUbM072wlJNyeYAe0mhzrD+M6DIJa96CZAOBBrhDbgKnB4MApIKefcyAB1eOdYn8cUNZgvwBvEzdoAYsxgEIw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3020,17 +3882,18 @@
"arraybuffer.prototype.slice": "^1.0.4",
"available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
+ "call-bound": "^1.0.4",
"data-view-buffer": "^1.0.2",
"data-view-byte-length": "^1.0.2",
"data-view-byte-offset": "^1.0.1",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "es-set-tostringtag": "^2.0.3",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
"es-to-primitive": "^1.3.0",
"function.prototype.name": "^1.1.8",
- "get-intrinsic": "^1.2.6",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
"get-symbol-description": "^1.1.0",
"globalthis": "^1.0.4",
"gopd": "^1.2.0",
@@ -3046,14 +3909,17 @@
"is-shared-array-buffer": "^1.0.4",
"is-string": "^1.1.1",
"is-typed-array": "^1.1.15",
- "is-weakref": "^1.1.0",
+ "is-weakref": "^1.1.1",
"math-intrinsics": "^1.1.0",
- "object-inspect": "^1.13.3",
+ "object-inspect": "^1.13.4",
"object-keys": "^1.1.1",
"object.assign": "^4.1.7",
- "regexp.prototype.flags": "^1.5.3",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
"safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
"safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
"string.prototype.trim": "^1.2.10",
"string.prototype.trimend": "^1.0.9",
"string.prototype.trimstart": "^1.0.8",
@@ -3062,7 +3928,7 @@
"typed-array-byte-offset": "^1.0.4",
"typed-array-length": "^1.0.7",
"unbox-primitive": "^1.1.0",
- "which-typed-array": "^1.1.18"
+ "which-typed-array": "^1.1.19"
},
"engines": {
"node": ">= 0.4"
@@ -3075,7 +3941,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -3085,7 +3950,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -3123,7 +3987,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
@@ -3133,28 +3996,31 @@
}
},
"node_modules/es-set-tostringtag": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
- "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
- "dev": true,
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
- "get-intrinsic": "^1.2.4",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
- "hasown": "^2.0.1"
+ "hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-shim-unscopables": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
- "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "hasown": "^2.0.0"
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
"node_modules/es-to-primitive": {
@@ -3338,9 +4204,9 @@
}
},
"node_modules/eslint-plugin-react-refresh": {
- "version": "0.4.19",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz",
- "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==",
+ "version": "0.4.20",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz",
+ "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -3507,9 +4373,9 @@
"license": "MIT"
},
"node_modules/fast-equals": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz",
- "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==",
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
+ "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==",
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -3530,9 +4396,9 @@
"license": "MIT"
},
"node_modules/fastq": {
- "version": "1.17.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
- "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3597,9 +4463,9 @@
}
},
"node_modules/flatted": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
- "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"dev": true,
"license": "ISC"
},
@@ -3624,23 +4490,30 @@
}
},
"node_modules/for-each": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
- "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "is-callable": "^1.1.3"
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/form-data": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
- "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
+ "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
"mime-types": "^2.1.12"
},
"engines": {
@@ -3727,7 +4600,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
@@ -3752,7 +4624,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
@@ -3901,7 +4772,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -3918,11 +4788,14 @@
"license": "MIT"
},
"node_modules/has-bigints": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
- "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
"dev": true,
"license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -3970,7 +4843,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -3983,7 +4855,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
@@ -4026,6 +4897,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
+ "license": "MIT",
"dependencies": {
"void-elements": "3.1.0"
}
@@ -4034,6 +4906,7 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+ "license": "MIT",
"dependencies": {
"css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3"
@@ -4116,12 +4989,13 @@
"node_modules/immutability-helper": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-3.1.1.tgz",
- "integrity": "sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ=="
+ "integrity": "sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ==",
+ "license": "MIT"
},
"node_modules/import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
@@ -4221,13 +5095,17 @@
"license": "MIT"
},
"node_modules/is-async-function": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
- "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "has-tostringtag": "^1.0.0"
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
@@ -4253,13 +5131,13 @@
}
},
"node_modules/is-boolean-object": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz",
- "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bound": "^1.0.2",
+ "call-bound": "^1.0.3",
"has-tostringtag": "^1.0.2"
},
"engines": {
@@ -4283,9 +5161,9 @@
}
},
"node_modules/is-core-module": {
- "version": "2.15.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
- "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
@@ -4359,13 +5237,16 @@
}
},
"node_modules/is-generator-function": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
- "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
+ "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "has-tostringtag": "^1.0.0"
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.0",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
@@ -4540,13 +5421,13 @@
}
},
"node_modules/is-weakref": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz",
- "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bound": "^1.0.2"
+ "call-bound": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
@@ -4587,17 +5468,17 @@
"license": "ISC"
},
"node_modules/iterator.prototype": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.4.tgz",
- "integrity": "sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==",
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
"dev": true,
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",
"es-object-atoms": "^1.0.0",
"get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
"has-symbols": "^1.1.0",
- "reflect.getprototypeof": "^1.0.8",
"set-function-name": "^2.0.2"
},
"engines": {
@@ -4636,9 +5517,9 @@
}
},
"node_modules/jsesc": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
- "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
@@ -4866,7 +5747,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -4950,15 +5830,16 @@
"license": "MIT"
},
"node_modules/nanoid": {
- "version": "3.3.8",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
- "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -4999,9 +5880,9 @@
}
},
"node_modules/object-inspect": {
- "version": "1.13.3",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
- "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5124,6 +6005,24 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -5263,9 +6162,9 @@
}
},
"node_modules/possible-typed-array-names": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
- "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5273,9 +6172,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.49",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
- "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+ "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"funding": [
{
"type": "opencollective",
@@ -5292,7 +6191,7 @@
],
"license": "MIT",
"dependencies": {
- "nanoid": "^3.3.7",
+ "nanoid": "^3.3.8",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@@ -5401,7 +6300,8 @@
"node_modules/raf-schd": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
- "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="
+ "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==",
+ "license": "MIT"
},
"node_modules/react": {
"version": "18.3.1",
@@ -5419,6 +6319,7 @@
"version": "16.0.1",
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz",
"integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==",
+ "license": "MIT",
"dependencies": {
"@react-dnd/invariant": "^4.0.1",
"@react-dnd/shallowequal": "^4.0.1",
@@ -5448,6 +6349,7 @@
"version": "16.0.1",
"resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz",
"integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==",
+ "license": "MIT",
"dependencies": {
"dnd-core": "^16.0.1"
}
@@ -5466,9 +6368,9 @@
}
},
"node_modules/react-i18next": {
- "version": "15.4.1",
- "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.4.1.tgz",
- "integrity": "sha512-ahGab+IaSgZmNPYXdV1n+OYky95TGpFwnKRflX/16dY04DsYYKHtVLjeny7sBSCREEcoMbAgSkFiGLF5g5Oofw==",
+ "version": "15.5.1",
+ "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.5.1.tgz",
+ "integrity": "sha512-C8RZ7N7H0L+flitiX6ASjq9p5puVJU1Z8VyL3OgM/QOMRf40BMZX+5TkpxzZVcTmOLPX5zlti4InEX5pFyiVeA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.25.0",
@@ -5476,7 +6378,8 @@
},
"peerDependencies": {
"i18next": ">= 23.2.3",
- "react": ">= 16.8.0"
+ "react": ">= 16.8.0",
+ "typescript": "^5"
},
"peerDependenciesMeta": {
"react-dom": {
@@ -5484,6 +6387,9 @@
},
"react-native": {
"optional": true
+ },
+ "typescript": {
+ "optional": true
}
}
},
@@ -5497,9 +6403,9 @@
}
},
"node_modules/react-is": {
- "version": "18.3.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
- "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
+ "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==",
"license": "MIT"
},
"node_modules/react-redux": {
@@ -5525,10 +6431,20 @@
}
}
},
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/react-router": {
- "version": "6.30.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz",
- "integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==",
+ "version": "6.30.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz",
+ "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.23.0"
@@ -5541,13 +6457,13 @@
}
},
"node_modules/react-router-dom": {
- "version": "6.30.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz",
- "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==",
+ "version": "6.30.1",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz",
+ "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.23.0",
- "react-router": "6.30.0"
+ "react-router": "6.30.1"
},
"engines": {
"node": ">=14.0.0"
@@ -5633,6 +6549,12 @@
"decimal.js-light": "^2.4.1"
}
},
+ "node_modules/recharts/node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "license": "MIT"
+ },
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
@@ -5658,19 +6580,19 @@
}
},
"node_modules/reflect.getprototypeof": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz",
- "integrity": "sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==",
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.8",
"define-properties": "^1.2.1",
- "dunder-proto": "^1.0.1",
- "es-abstract": "^1.23.6",
+ "es-abstract": "^1.23.9",
"es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.6",
- "gopd": "^1.2.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
"which-builtin-type": "^1.2.1"
},
"engines": {
@@ -5680,22 +6602,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/regenerator-runtime": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
- "license": "MIT"
- },
"node_modules/regexp.prototype.flags": {
- "version": "1.5.3",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz",
- "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==",
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bind": "^1.0.7",
+ "call-bind": "^1.0.8",
"define-properties": "^1.2.1",
"es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
"set-function-name": "^2.0.2"
},
"engines": {
@@ -5712,18 +6630,21 @@
"license": "MIT"
},
"node_modules/resolve": {
- "version": "1.22.8",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
- "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "version": "1.22.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
"license": "MIT",
"dependencies": {
- "is-core-module": "^2.13.0",
+ "is-core-module": "^2.16.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -5747,9 +6668,9 @@
}
},
"node_modules/reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5781,12 +6702,12 @@
"license": "Unlicense"
},
"node_modules/rollup": {
- "version": "4.27.4",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.4.tgz",
- "integrity": "sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==",
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz",
+ "integrity": "sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==",
"license": "MIT",
"dependencies": {
- "@types/estree": "1.0.6"
+ "@types/estree": "1.0.7"
},
"bin": {
"rollup": "dist/bin/rollup"
@@ -5796,24 +6717,26 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.27.4",
- "@rollup/rollup-android-arm64": "4.27.4",
- "@rollup/rollup-darwin-arm64": "4.27.4",
- "@rollup/rollup-darwin-x64": "4.27.4",
- "@rollup/rollup-freebsd-arm64": "4.27.4",
- "@rollup/rollup-freebsd-x64": "4.27.4",
- "@rollup/rollup-linux-arm-gnueabihf": "4.27.4",
- "@rollup/rollup-linux-arm-musleabihf": "4.27.4",
- "@rollup/rollup-linux-arm64-gnu": "4.27.4",
- "@rollup/rollup-linux-arm64-musl": "4.27.4",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.27.4",
- "@rollup/rollup-linux-riscv64-gnu": "4.27.4",
- "@rollup/rollup-linux-s390x-gnu": "4.27.4",
- "@rollup/rollup-linux-x64-gnu": "4.27.4",
- "@rollup/rollup-linux-x64-musl": "4.27.4",
- "@rollup/rollup-win32-arm64-msvc": "4.27.4",
- "@rollup/rollup-win32-ia32-msvc": "4.27.4",
- "@rollup/rollup-win32-x64-msvc": "4.27.4",
+ "@rollup/rollup-android-arm-eabi": "4.41.0",
+ "@rollup/rollup-android-arm64": "4.41.0",
+ "@rollup/rollup-darwin-arm64": "4.41.0",
+ "@rollup/rollup-darwin-x64": "4.41.0",
+ "@rollup/rollup-freebsd-arm64": "4.41.0",
+ "@rollup/rollup-freebsd-x64": "4.41.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.41.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.41.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.41.0",
+ "@rollup/rollup-linux-arm64-musl": "4.41.0",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.41.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.41.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.41.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.41.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.41.0",
+ "@rollup/rollup-linux-x64-gnu": "4.41.0",
+ "@rollup/rollup-linux-x64-musl": "4.41.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.41.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.41.0",
+ "@rollup/rollup-win32-x64-msvc": "4.41.0",
"fsevents": "~2.3.2"
}
},
@@ -5867,6 +6790,23 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/safe-regex-test": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
@@ -5937,6 +6877,21 @@
"node": ">= 0.4"
}
},
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -6064,29 +7019,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/source-map-support": {
- "version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
- "node_modules/source-map-support/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "license": "BSD-3-Clause",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/string.prototype.matchall": {
"version": "4.0.12",
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
@@ -6257,38 +7189,11 @@
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
"license": "MIT"
},
- "node_modules/terser": {
- "version": "5.39.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz",
- "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
- "license": "BSD-2-Clause",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "bin": {
- "terser": "bin/terser"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/terser/node_modules/commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/text-segmentation": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+ "license": "MIT",
"dependencies": {
"utrie": "^1.0.2"
}
@@ -6422,21 +7327,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/typescript": {
- "version": "5.7.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
- "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
- "license": "Apache-2.0",
- "optional": true,
- "peer": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
"node_modules/unbox-primitive": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
@@ -6456,18 +7346,10 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/undici-types": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
- "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/update-browserslist-db": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
- "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
"funding": [
{
"type": "opencollective",
@@ -6485,7 +7367,7 @@
"license": "MIT",
"dependencies": {
"escalade": "^3.2.0",
- "picocolors": "^1.1.0"
+ "picocolors": "^1.1.1"
},
"bin": {
"update-browserslist-db": "cli.js"
@@ -6505,9 +7387,9 @@
}
},
"node_modules/use-sync-external-store": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
- "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
+ "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -6517,6 +7399,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+ "license": "MIT",
"dependencies": {
"base64-arraybuffer": "^1.0.2"
}
@@ -6544,9 +7427,9 @@
}
},
"node_modules/vite": {
- "version": "5.4.18",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.18.tgz",
- "integrity": "sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==",
+ "version": "5.4.19",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
+ "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
"license": "MIT",
"dependencies": {
"esbuild": "^0.21.3",
@@ -6620,6 +7503,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -6719,16 +7603,17 @@
}
},
"node_modules/which-typed-array": {
- "version": "1.1.18",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz",
- "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==",
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
"dev": true,
"license": "MIT",
"dependencies": {
"available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "for-each": "^0.3.3",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-tostringtag": "^1.0.2"
},
diff --git a/client/package.json b/client/package.json
index ba745503a..483f660d1 100644
--- a/client/package.json
+++ b/client/package.json
@@ -15,21 +15,21 @@
"@emotion/styled": "^11.13.0",
"@fontsource/roboto": "^5.0.13",
"@hello-pangea/dnd": "^18.0.0",
- "@mui/x-charts": "^7.5.1",
- "@mui/x-data-grid": "7.29.0",
- "@mui/x-date-pickers": "7.29.0",
"@mui/icons-material": "6.4.11",
"@mui/lab": "6.0.0-dev.240424162023-9968b4889d",
"@mui/material": "6.4.11",
+ "@mui/x-charts": "^7.5.1",
+ "@mui/x-data-grid": "7.29.0",
+ "@mui/x-date-pickers": "7.29.0",
"@reduxjs/toolkit": "2.7.0",
"axios": "^1.7.4",
"dayjs": "1.11.13",
"flag-icons": "7.3.2",
- "jwt-decode": "^4.0.0",
"html2canvas": "^1.4.1",
"i18next": "^24.2.2",
"immutability-helper": "^3.1.1",
"joi": "17.13.3",
+ "jwt-decode": "^4.0.0",
"maplibre-gl": "5.3.1",
"mui-color-input": "^6.0.0",
"react": "18.3.1",
@@ -63,6 +63,9 @@
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.6",
"prettier": "^3.3.3",
- "vite": "^5.2.0"
+ "vite": "^5.4.19"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-linux-arm64-musl": "4.41.0"
}
}
diff --git a/client/src/Components/Host/index.jsx b/client/src/Components/Host/index.jsx
index 051e60331..d1cf382b2 100644
--- a/client/src/Components/Host/index.jsx
+++ b/client/src/Components/Host/index.jsx
@@ -14,7 +14,7 @@ import Dot from "../Dot";
* @param {number} params.percentage - The percentage to display.
* @returns {React.ElementType} Returns a div element with the host details.
*/
-const Host = ({ url, title, percentageColor, percentage }) => {
+const Host = ({ url, title, percentageColor, percentage, showURL }) => {
const theme = useTheme();
return (
@@ -40,7 +40,7 @@ const Host = ({ url, title, percentageColor, percentage }) => {
>
)}
- {url}
+ {showURL && {url}}
);
};
@@ -50,6 +50,7 @@ Host.propTypes = {
percentageColor: PropTypes.string,
percentage: PropTypes.string,
url: PropTypes.string,
+ showURL: PropTypes.bool,
};
export default Host;
diff --git a/client/src/Components/Inputs/Search/index.jsx b/client/src/Components/Inputs/Search/index.jsx
index a3b75161c..86ce57779 100644
--- a/client/src/Components/Inputs/Search/index.jsx
+++ b/client/src/Components/Inputs/Search/index.jsx
@@ -155,6 +155,7 @@ const Search = ({
renderOption={(props, option) => {
const { key, ...optionProps } = props;
const hasSecondaryLabel = secondaryLabel && option[secondaryLabel] !== undefined;
+ const port = option["port"];
return (
{option[filteredBy] +
- (hasSecondaryLabel ? ` (${option[secondaryLabel]})` : "")}
+ (hasSecondaryLabel
+ ? ` (${option[secondaryLabel]}${port ? `: ${port}` : ""})`
+ : "")}
);
}}
diff --git a/client/src/Components/MonitorDetailsControlHeader/index.jsx b/client/src/Components/MonitorDetailsControlHeader/index.jsx
new file mode 100644
index 000000000..aa9ef91b9
--- /dev/null
+++ b/client/src/Components/MonitorDetailsControlHeader/index.jsx
@@ -0,0 +1,120 @@
+import Stack from "@mui/material/Stack";
+import Status from "./status";
+import Skeleton from "./skeleton";
+import Button from "@mui/material/Button";
+import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined";
+import PauseOutlinedIcon from "@mui/icons-material/PauseOutlined";
+import PlayArrowOutlinedIcon from "@mui/icons-material/PlayArrowOutlined";
+import EmailIcon from "@mui/icons-material/Email";
+
+// Utils
+import PropTypes from "prop-types";
+import { useNavigate } from "react-router-dom";
+import { useTheme } from "@mui/material/styles";
+import { usePauseMonitor } from "../../Hooks/useMonitorControls";
+import { useSendTestEmail } from "../../Hooks/useSendTestEmail";
+import { useTranslation } from "react-i18next";
+
+/**
+ * MonitorDetailsControlHeader component displays the control header for monitor details.
+ * It includes status display, pause/resume button, and a configure button for admins.
+ *
+ * @component
+ * @param {Object} props - Component props
+ * @param {string} props.path - The base path for navigation
+ * @param {boolean} [props.isLoading=false] - Flag indicating if the data is loading
+ * @param {boolean} [props.isAdmin=false] - Flag indicating if the user is an admin
+ * @param {Object} props.monitor - The monitor object containing details
+ * @param {Function} props.triggerUpdate - Function to trigger an update
+ * @returns {JSX.Element} The rendered component
+ */
+const MonitorDetailsControlHeader = ({
+ path,
+ isLoading = false,
+ isAdmin = false,
+ monitor,
+ triggerUpdate,
+}) => {
+ const navigate = useNavigate();
+ const theme = useTheme();
+ const { t } = useTranslation();
+ const [pauseMonitor, isPausing, error] = usePauseMonitor({
+ monitorId: monitor?._id,
+ triggerUpdate,
+ });
+
+ const [isSending, emailError, sendTestEmail] = useSendTestEmail();
+
+ if (isLoading) {
+ return ;
+ }
+
+ return (
+
+
+
+
+ }
+ onClick={() => {
+ sendTestEmail();
+ }}
+ >
+ {t("sendTestEmail")}
+
+
+ :
+ }
+ onClick={() => {
+ pauseMonitor();
+ }}
+ >
+ {monitor?.isActive ? "Pause" : "Resume"}
+
+
+ {isAdmin && (
+ }
+ onClick={() => navigate(`/${path}/configure/${monitor._id}`)}
+ >
+ Configure
+
+ )}
+
+
+ );
+};
+
+MonitorDetailsControlHeader.propTypes = {
+ path: PropTypes.string,
+ isLoading: PropTypes.bool,
+ isAdmin: PropTypes.bool,
+ monitor: PropTypes.object,
+ triggerUpdate: PropTypes.func,
+};
+
+export default MonitorDetailsControlHeader;
diff --git a/client/src/Components/MonitorDetailsControlHeader/skeleton.jsx b/client/src/Components/MonitorDetailsControlHeader/skeleton.jsx
new file mode 100644
index 000000000..64dc0547f
--- /dev/null
+++ b/client/src/Components/MonitorDetailsControlHeader/skeleton.jsx
@@ -0,0 +1,23 @@
+import { Stack, Skeleton } from "@mui/material";
+
+const SkeletonLayout = () => {
+ return (
+
+
+
+
+ );
+};
+
+export default SkeletonLayout;
diff --git a/client/src/Components/MonitorDetailsControlHeader/status.jsx b/client/src/Components/MonitorDetailsControlHeader/status.jsx
new file mode 100644
index 000000000..9e01dd502
--- /dev/null
+++ b/client/src/Components/MonitorDetailsControlHeader/status.jsx
@@ -0,0 +1,56 @@
+// Components
+import Stack from "@mui/material/Stack";
+import PulseDot from "../../Components/Animated/PulseDot";
+import Typography from "@mui/material/Typography";
+import Dot from "../../Components/Dot";
+// Utils
+import { formatDurationRounded } from "../../Utils/timeUtils";
+import PropTypes from "prop-types";
+import { useTheme } from "@emotion/react";
+import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils";
+
+/**
+ * Status component displays the status information of a monitor.
+ * It includes the monitor's name, URL, and check interval.
+ *
+ * @component
+ * @param {Object} props - Component props
+ * @param {Object} props.monitor - The monitor object containing details
+ * @param {string} props.monitor.name - The name of the monitor
+ * @param {string} props.monitor.url - The URL of the monitor
+ * @param {number} props.monitor.interval - The interval at which the monitor checks
+ * @returns {JSX.Element} The rendered component
+ */
+const Status = ({ monitor }) => {
+ const theme = useTheme();
+ const { statusColor, determineState } = useUtils();
+
+ return (
+
+ {monitor?.name}
+
+
+
+ {monitor?.url?.replace(/^https?:\/\//, "") || "..."}
+
+
+
+ Checking every {formatDurationRounded(monitor?.interval)}.
+
+
+
+ );
+};
+
+Status.propTypes = {
+ monitor: PropTypes.object,
+};
+
+export default Status;
diff --git a/client/src/Features/UI/uiSlice.js b/client/src/Features/UI/uiSlice.js
index f20115b92..bd6e92804 100644
--- a/client/src/Features/UI/uiSlice.js
+++ b/client/src/Features/UI/uiSlice.js
@@ -20,6 +20,7 @@ const initialState = {
collapsed: false,
},
mode: initialMode,
+ showURL: false,
greeting: { index: 0, lastUpdate: null },
timezone: "America/Toronto",
distributedUptimeEnabled: false,
@@ -46,6 +47,9 @@ const uiSlice = createSlice({
setMode: (state, action) => {
state.mode = action.payload;
},
+ setShowURL: (state, action) => {
+ state.showURL = action.payload;
+ },
setGreeting(state, action) {
state.greeting.index = action.payload.index;
state.greeting.lastUpdate = action.payload.lastUpdate;
@@ -67,6 +71,7 @@ export const {
setRowsPerPage,
toggleSidebar,
setMode,
+ setShowURL,
setGreeting,
setTimezone,
setDistributedUptimeEnabled,
diff --git a/client/src/Hooks/useFetchUptimeMonitorDetails.js b/client/src/Hooks/useFetchUptimeMonitorDetails.js
index c8cb81e65..3c0ac7ee6 100644
--- a/client/src/Hooks/useFetchUptimeMonitorDetails.js
+++ b/client/src/Hooks/useFetchUptimeMonitorDetails.js
@@ -3,13 +3,12 @@ import { networkService } from "../main";
import { useNavigate } from "react-router-dom";
import { createToast } from "../Utils/toastUtils";
-export const useFetchUptimeMonitorDetails = ({ monitorId, dateRange }) => {
+export const useFetchUptimeMonitorDetails = ({ monitorId, dateRange, trigger }) => {
const [networkError, setNetworkError] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [monitor, setMonitor] = useState(undefined);
const [monitorStats, setMonitorStats] = useState(undefined);
const navigate = useNavigate();
-
useEffect(() => {
const fetchMonitors = async () => {
try {
@@ -29,7 +28,7 @@ export const useFetchUptimeMonitorDetails = ({ monitorId, dateRange }) => {
}
};
fetchMonitors();
- }, [dateRange, monitorId, navigate]);
+ }, [dateRange, monitorId, navigate, trigger]);
return [monitor, monitorStats, isLoading, networkError];
};
diff --git a/client/src/Hooks/useMonitorControls.js b/client/src/Hooks/useMonitorControls.js
new file mode 100644
index 000000000..b47603b58
--- /dev/null
+++ b/client/src/Hooks/useMonitorControls.js
@@ -0,0 +1,28 @@
+import { useState } from "react";
+import { networkService } from "../main";
+import { createToast } from "../Utils/toastUtils";
+
+const usePauseMonitor = ({ monitorId, triggerUpdate }) => {
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(undefined);
+ const pauseMonitor = async () => {
+ try {
+ setIsLoading(true);
+ const res = await networkService.pauseMonitorById({ monitorId });
+ createToast({
+ body: res.data.data.isActive
+ ? "Monitor resumed successfully"
+ : "Monitor paused successfully",
+ });
+ triggerUpdate();
+ } catch (error) {
+ setError(error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return [pauseMonitor, isLoading, error];
+};
+
+export { usePauseMonitor };
diff --git a/client/src/Hooks/useMonitorUtils.js b/client/src/Hooks/useMonitorUtils.js
index fc1966b69..f187f7250 100644
--- a/client/src/Hooks/useMonitorUtils.js
+++ b/client/src/Hooks/useMonitorUtils.js
@@ -1,6 +1,8 @@
import { useCallback } from "react";
+import { useTheme } from "@mui/material";
const useMonitorUtils = () => {
+
const getMonitorWithPercentage = useCallback((monitor, theme) => {
let uptimePercentage = "";
let percentageColor = "";
@@ -36,7 +38,16 @@ const useMonitorUtils = () => {
return monitor?.status == true ? "up" : "down";
}, []);
- return { getMonitorWithPercentage, determineState };
+ const theme = useTheme();
+
+ const statusColor = {
+ up: theme.palette.success.lowContrast,
+ down: theme.palette.error.lowContrast,
+ paused: theme.palette.warning.lowContrast,
+ pending: theme.palette.warning.lowContrast,
+ };
+
+ return { getMonitorWithPercentage, determineState, statusColor };
};
export { useMonitorUtils };
diff --git a/client/src/Hooks/useSendTestEmail.js b/client/src/Hooks/useSendTestEmail.js
index fef45c3ea..d186fd748 100644
--- a/client/src/Hooks/useSendTestEmail.js
+++ b/client/src/Hooks/useSendTestEmail.js
@@ -2,27 +2,48 @@ import { useState } from "react";
import { networkService } from "../main";
import { useSelector } from "react-redux";
import { createToast } from "../Utils/toastUtils";
+import { useTranslation } from "react-i18next";
const useSendTestEmail = () => {
const [isSending, setIsSending] = useState(false);
const [error, setError] = useState(null);
const user = useSelector((state) => state.auth.user);
+ const { t } = useTranslation();
- const sendTestEmail = async () => {
+ /**
+ * Send a test email with optional email configuration
+ * @param {Object} emailConfig - Optional email configuration parameters
+ */
+ const sendTestEmail = async (emailConfig = null) => {
try {
setIsSending(true);
setError(null);
- const response = await networkService.sendTestEmail({ to: user.email });
+ // Send the test email with or without configuration
+ const response = await networkService.sendTestEmail({
+ to: user.email,
+ emailConfig
+ });
+
if (typeof response?.data?.data?.messageId !== "undefined") {
createToast({
- body: "Test email sent successfully",
+ body: t("settingsTestEmailSuccess", "Test email sent successfully"),
});
} else {
- throw new Error("Failed to send test email");
+ throw new Error(response?.data?.error || t("settingsTestEmailFailed", "Failed to send test email"));
+
}
} catch (error) {
+ createToast({
+ body: t("failedToSendEmail"),
+ });
setError(error);
+ createToast({
+ body: t("settingsTestEmailFailedWithReason", "Failed to send test email: {{reason}}", {
+ reason: error.message || t("settingsTestEmailUnknownError", "Unknown error")
+ }),
+ variant: "error"
+ });
} finally {
setIsSending(false);
}
diff --git a/client/src/Pages/Infrastructure/Create/index.jsx b/client/src/Pages/Infrastructure/Create/index.jsx
index 144f499fd..a928037b7 100644
--- a/client/src/Pages/Infrastructure/Create/index.jsx
+++ b/client/src/Pages/Infrastructure/Create/index.jsx
@@ -294,7 +294,7 @@ const CreateInfrastructureMonitor = () => {
- {t("settingsGeneralSettings")}
+ {t("settingsGeneralSettings")}
{t("infrastructureCreateGeneralSettingsDescription")}
@@ -368,7 +368,7 @@ const CreateInfrastructureMonitor = () => {
- {t("distributedUptimeCreateIncidentNotification")}
+ {t("distributedUptimeCreateIncidentNotification")}
{t("distributedUptimeCreateIncidentDescription")}
@@ -385,7 +385,7 @@ const CreateInfrastructureMonitor = () => {
- {t("infrastructureCustomizeAlerts")}
+ {t("infrastructureCustomizeAlerts")}
{t("infrastructureAlertNotificationDescription")}
@@ -432,7 +432,7 @@ const CreateInfrastructureMonitor = () => {
- {t("distributedUptimeCreateAdvancedSettings")}
+ {t("distributedUptimeCreateAdvancedSettings")}
- {t("settingsGeneralSettings")}
+ {t("settingsGeneralSettings")}
{t("pageSpeedConfigureSettingsDescription")}
@@ -349,7 +349,7 @@ const PageSpeedConfigure = () => {
- {t("distributedUptimeCreateIncidentNotification")}
+ {t("distributedUptimeCreateIncidentNotification")}
{t("distributedUptimeCreateIncidentDescription")}
@@ -405,7 +405,7 @@ const PageSpeedConfigure = () => {
- {t("distributedUptimeCreateAdvancedSettings")}
+ {t("distributedUptimeCreateAdvancedSettings")}
- {t("settingsGeneralSettings")}
+ {t("settingsGeneralSettings")}
{t("distributedUptimeCreateSelectURL")}
@@ -420,7 +389,7 @@ const Configure = () => {
-
+
{t("distributedUptimeCreateIncidentNotification")}
@@ -488,7 +457,7 @@ const Configure = () => {
- {t("ignoreTLSError")}
+ {t("ignoreTLSError")}
{t("ignoreTLSErrorDescription")}
@@ -508,7 +477,7 @@ const Configure = () => {
-
+
{t("distributedUptimeCreateAdvancedSettings")}
diff --git a/client/src/Pages/Uptime/Create/index.jsx b/client/src/Pages/Uptime/Create/index.jsx
index f73590c91..f35a09d9c 100644
--- a/client/src/Pages/Uptime/Create/index.jsx
+++ b/client/src/Pages/Uptime/Create/index.jsx
@@ -261,7 +261,7 @@ const CreateMonitor = () => {
- {t("distributedUptimeCreateChecks")}
+ {t("distributedUptimeCreateChecks")}
{t("distributedUptimeCreateChecksDescription")}
@@ -342,7 +342,7 @@ const CreateMonitor = () => {
- {t("settingsGeneralSettings")}
+ {t("settingsGeneralSettings")}
{t("uptimeCreateSelectURL")}
@@ -386,7 +386,7 @@ const CreateMonitor = () => {
-
+
{t("distributedUptimeCreateIncidentNotification")}
@@ -417,7 +417,7 @@ const CreateMonitor = () => {
- {t("ignoreTLSError")}
+ {t("ignoreTLSError")}
{t("ignoreTLSErrorDescription")}
@@ -437,7 +437,7 @@ const CreateMonitor = () => {
-
+
{t("distributedUptimeCreateAdvancedSettings")}
diff --git a/client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx b/client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx
index 068211f5b..b60a4dd04 100644
--- a/client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx
+++ b/client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx
@@ -75,7 +75,7 @@ const ChartBoxes = ({
{hoveredUptimeData !== null
? Math.floor(hoveredUptimeData?.originalAvgResponseTime ?? 0)
- : Math.floor(monitorData?.groupedUptimePercentage ?? 0 * 100)}
+ : Math.floor((monitorData?.groupedUptimePercentage ?? 0) * 100)}
{hoveredUptimeData !== null ? " ms" : " %"}
diff --git a/client/src/Pages/Uptime/Details/index.jsx b/client/src/Pages/Uptime/Details/index.jsx
index be1ccbcd2..5ad8bd61b 100644
--- a/client/src/Pages/Uptime/Details/index.jsx
+++ b/client/src/Pages/Uptime/Details/index.jsx
@@ -1,5 +1,6 @@
// Components
import Breadcrumbs from "../../../Components/Breadcrumbs";
+import MonitorDetailsControlHeader from "../../../Components/MonitorDetailsControlHeader";
import MonitorStatusHeader from "../../../Components/MonitorStatusHeader";
import MonitorTimeFrameHeader from "../../../Components/MonitorTimeFrameHeader";
import ChartBoxes from "./Components/ChartBoxes";
@@ -36,9 +37,9 @@ const UptimeDetails = () => {
// Local state
const [dateRange, setDateRange] = useState("recent");
-
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
+ const [trigger, setTrigger] = useState(false);
// Utils
const dateFormat =
@@ -52,6 +53,7 @@ const UptimeDetails = () => {
useFetchUptimeMonitorDetails({
monitorId,
dateRange,
+ trigger,
});
const monitor = monitorData?.monitor;
@@ -73,6 +75,10 @@ const UptimeDetails = () => {
});
// Handlers
+ const triggerUpdate = () => {
+ setTrigger(!trigger);
+ };
+
const handlePageChange = (_, newPage) => {
setPage(newPage);
};
@@ -101,11 +107,12 @@ const UptimeDetails = () => {
return (
-
{t("distributedUptimeDetailsNoMonitorHistory")}
@@ -117,11 +124,12 @@ const UptimeDetails = () => {
return (
-
{
fd.append("monitors[]", monitorId);
@@ -1154,11 +1157,28 @@ class NetworkService {
}
// ************************************
- // Send tes t email
+ // Send test email
// ************************************
async sendTestEmail(config) {
- const { to } = config;
- return this.axiosInstance.post(`/monitors/test-email`, { to });
+ // Extract recipient and email configuration
+ const { to, emailConfig } = config;
+
+ // If emailConfig is provided, use the new endpoint with direct parameters
+ if (emailConfig) {
+ return this.axiosInstance.post(`/settings/test-email`, {
+ to,
+ systemEmailHost: emailConfig.systemEmailHost,
+ systemEmailPort: emailConfig.systemEmailPort,
+ systemEmailAddress: emailConfig.systemEmailAddress,
+ systemEmailPassword: emailConfig.systemEmailPassword,
+ // Only include these if they are present
+ ...(emailConfig.systemEmailConnectionHost && { systemEmailConnectionHost: emailConfig.systemEmailConnectionHost }),
+ ...(emailConfig.systemEmailUser && { systemEmailUser: emailConfig.systemEmailUser })
+ });
+ }
+
+ // Fallback to original behavior for backward compatibility
+ return this.axiosInstance.post(`/settings/test-email`, { to });
}
}
diff --git a/client/src/Validation/validation.js b/client/src/Validation/validation.js
index be6cb19f7..068340ba8 100644
--- a/client/src/Validation/validation.js
+++ b/client/src/Validation/validation.js
@@ -23,7 +23,8 @@ const lastnameSchema = joi
.messages({
"string.empty": "Surname is required",
"string.max": "Surname must be less than 50 characters",
- "string.pattern.base": "Surname must contain only letters, spaces, apostrophes, or hyphens"
+ "string.pattern.base":
+ "Surname must contain only letters, spaces, apostrophes, or hyphens",
});
const passwordSchema = joi
@@ -103,67 +104,69 @@ const credentials = joi.object({
});
const monitorValidation = joi.object({
- url: joi
- .when("type", {
- is: "docker",
- then: joi
- .string()
- .trim()
- .regex(/^[a-z0-9]{64}$/),
- otherwise: joi
- .string()
- .trim()
- .custom((value, helpers) => {
- // Regex from https://gist.github.com/dperini/729294
- var urlRegex = new RegExp(
- "^" +
- // protocol identifier (optional)
- // short syntax // still required
- "(?:(?:https?|ftp):\\/\\/)?" +
- // user:pass BasicAuth (optional)
- "(?:" +
- // IP address dotted notation octets
- // excludes loopback network 0.0.0.0
- // excludes reserved space >= 224.0.0.0
- // excludes network & broadcast addresses
- // (first & last IP address of each class)
- "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
- "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
- "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
- "|" +
- // host & domain names, may end with dot
- // can be replaced by a shortest alternative
- // (?![-_])(?:[-\\w\\u00a1-\\uffff]{0,63}[^-_]\\.)+
- "(?:" +
- "(?:" +
- "[a-z0-9\\u00a1-\\uffff]" +
- "[a-z0-9\\u00a1-\\uffff_-]{0,62}" +
- ")?" +
- "[a-z0-9\\u00a1-\\uffff]\\." +
- ")+" +
- // TLD identifier name, may end with dot
- "(?:[a-z\\u00a1-\\uffff]{2,}\\.?)" +
- ")" +
- // port number (optional)
- "(?::\\d{2,5})?" +
- // resource path (optional)
- "(?:[/?#]\\S*)?" +
- "$",
- "i"
- );
- if (!urlRegex.test(value)) {
- return helpers.error("string.invalidUrl");
- }
+ url: joi.when("type", {
+ is: "docker",
+ then: joi
+ .string()
+ .trim()
+ .regex(/^[a-z0-9]{64}$/)
+ .messages({
+ "string.empty": "This field is required.",
+ "string.pattern.base": "Please enter a valid 64-character Docker container ID.",
+ }),
+ otherwise: joi
+ .string()
+ .trim()
+ .custom((value, helpers) => {
+ // Regex from https://gist.github.com/dperini/729294
+ var urlRegex = new RegExp(
+ "^" +
+ // protocol identifier (optional)
+ // short syntax // still required
+ "(?:(?:https?|ftp):\\/\\/)?" +
+ // user:pass BasicAuth (optional)
+ "(?:" +
+ // IP address dotted notation octets
+ // excludes loopback network 0.0.0.0
+ // excludes reserved space >= 224.0.0.0
+ // excludes network & broadcast addresses
+ // (first & last IP address of each class)
+ "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
+ "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
+ "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
+ "|" +
+ // host & domain names, may end with dot
+ // can be replaced by a shortest alternative
+ // (?![-_])(?:[-\\w\\u00a1-\\uffff]{0,63}[^-_]\\.)+
+ "(?:" +
+ "(?:" +
+ "[a-z0-9\\u00a1-\\uffff]" +
+ "[a-z0-9\\u00a1-\\uffff_-]{0,62}" +
+ ")?" +
+ "[a-z0-9\\u00a1-\\uffff]\\." +
+ ")+" +
+ // TLD identifier name, may end with dot
+ "(?:[a-z\\u00a1-\\uffff]{2,}\\.?)" +
+ ")" +
+ // port number (optional)
+ "(?::\\d{2,5})?" +
+ // resource path (optional)
+ "(?:[/?#]\\S*)?" +
+ "$",
+ "i"
+ );
+ if (!urlRegex.test(value)) {
+ return helpers.error("string.invalidUrl");
+ }
- return value;
- }),
- })
- .messages({
- "string.empty": "This field is required.",
- "string.uri": "The URL you provided is not valid.",
- "string.invalidUrl": "Please enter a valid URL with optional port",
- "string.pattern.base": "Please enter a valid container ID.",
- }),
+ return value;
+ })
+ .messages({
+ "string.empty": "This field is required.",
+ "string.uri": "The URL you provided is not valid.",
+ "string.invalidUrl": "Please enter a valid URL with optional port",
+ }),
+ }),
port: joi
.number()
.integer()
@@ -171,7 +174,7 @@ const monitorValidation = joi.object({
.max(65535)
.when("type", {
is: "port",
- then: joi.required().messages({
+ then: joi.number().messages({
"number.base": "Port must be a number.",
"number.min": "Port must be at least 1.",
"number.max": "Port must be at most 65535.",
@@ -265,6 +268,7 @@ const statusPageValidation = joi.object({
showUptimePercentage: joi.boolean(),
showCharts: joi.boolean(),
});
+
const settingsValidation = joi.object({
checkTTL: joi.number().required().messages({
"string.empty": "Please enter a value",
@@ -278,6 +282,7 @@ const settingsValidation = joi.object({
systemEmailAddress: joi.string().allow(""),
systemEmailPassword: joi.string().allow(""),
systemEmailUser: joi.string().allow(""),
+ systemEmailConnectionHost: joi.string().allow(""),
});
const dayjsValidator = (value, helpers) => {
@@ -307,6 +312,7 @@ const advancedSettingsValidation = joi.object({
systemEmailPort: joi.number().allow(null, ""),
systemEmailAddress: joi.string().allow(""),
systemEmailPassword: joi.string().allow(""),
+ systemEmailConnectionHost: joi.string().allow(""),
jwtTTLNum: joi.number().messages({
"number.base": "JWT TTL is required.",
}),
diff --git a/client/src/locales/gb.json b/client/src/locales/gb.json
index 3391baec7..4b73daa54 100644
--- a/client/src/locales/gb.json
+++ b/client/src/locales/gb.json
@@ -75,6 +75,11 @@
"settingsThemeModeLight": "Light",
"settingsThemeModeDark": "Dark",
"settingsLanguage": "Language",
+ "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",
"settingsDistributedUptime": "Distributed uptime",
"settingsDistributedUptimeDescription": "Enable/disable distributed uptime monitoring.",
"settingsEnabled": "Enabled",
@@ -112,13 +117,20 @@
"settingsFailedToAddDemoMonitors": "Failed to add demo monitors",
"settingsMonitorsDeleted": "Successfully deleted all monitors",
"settingsFailedToDeleteMonitors": "Failed to delete all monitors",
- "settingsEmail": "Email settings",
- "settingsEmailDescription": "Configure email settings",
- "settingsEmailHost": "Email host",
- "settingsEmailPort": "Email port",
- "settingsEmailAddress": "Email address",
- "settingsEmailPassword": "Email password",
- "settingsEmailUser": "Email user",
+ "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 of the SMTP server",
+ "settingsEmailPort": "Email port - Port to connect to",
+ "settingsEmailUser": "Email user - Username for authentication, overrides email address if specified",
+ "settingsEmailAddress": "Email address - Used for authentication",
+ "settingsEmailPassword": "Email password - Password for authentication",
+ "settingsEmailConnectionHost": "Email connection host - Hostname to use in the HELO/EHLO greeting",
+ "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 host and port are required",
"settingsEmailFieldResetLabel": "Password is set. Click Reset to change it.",
"backendUnreachable": "Server Connection Error",
"backendUnreachableMessage": "We're unable to connect to the server. Please check your internet connection or verify your deployment configuration if the problem persists.",
@@ -521,8 +533,6 @@
"logOut": "Log out"
},
"navControls": "Controls",
- "statusBreadCrumbsStatusPages": "Status Pages",
- "statusBreadCrumbsDetails": "Details",
"incidentsPageTitle": "Incidents",
"passwordPanel": {
"passwordChangedSuccess": "Your password was changed successfully.",
@@ -535,5 +545,14 @@
"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..."
},
- "uptimeCreateSelectURL": "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."
+ "uptimeCreateSelectURL": "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.",
+ "sendTestEmail": "Send test email",
+ "emailSent": "Email sent successfully",
+ "failedToSendEmail": "Failed to send email",
+ "statusMsg" : {
+ "paused": "Monitoring is paused.",
+ "up": "Your site is up.",
+ "down": "Your site is down.",
+ "pending": "Pending..."
+ }
}
diff --git a/client/src/locales/ru.json b/client/src/locales/ru.json
index df1e5a5a7..d18d5f007 100644
--- a/client/src/locales/ru.json
+++ b/client/src/locales/ru.json
@@ -89,6 +89,11 @@
"settingsThemeModeLight": "Светлая",
"settingsThemeModeDark": "Тёмная",
"settingsLanguage": "Язык",
+ "settingsURLTitle": "Отображать IP/URL на странице статуса",
+ "settingsURLDescription": "Отображать IP-адрес или URL-адрес монитора на общедоступной странице статуса. Если отключено, будет отображаться только имя монитора для защиты конфиденциальной информации.",
+ "settingsURLSelectTitle": "Показать IP/URL на странице статуса",
+ "settingsURLEnabled": "Включено",
+ "settingURLDisabled": "Отключено",
"settingsDistributedUptime": "Distributed uptime",
"settingsDistributedUptimeDescription": "Включить/выключить distributed uptime monitoring.",
"settingsEnabled": "Включено",
@@ -115,6 +120,21 @@
"settingsRemoveAllMonitorsDialogConfirm": "Да, удалить все мониторы",
"settingsWallet": "Кошелёк",
"settingsWalletDescription": "Подключите свой кошелек здесь. Это необходимо для того, чтобы монитор Distributed Uptime мог подключиться к нескольким узлам по всему миру.",
+ "settingsEmail": "Настройки электронной почты",
+ "settingsEmailDescription": "Настройте параметры электронной почты",
+ "settingsEmailHost": "Хост электронной почты - имя хоста или IP-адрес SMTP-сервера",
+ "settingsEmailPort": "Порт электронной почты - порт для подключения",
+ "settingsEmailAddress": "Адрес электронной почты - используется для аутентификации",
+ "settingsEmailPassword": "Пароль электронной почты - пароль для аутентификации",
+ "settingsEmailUser": "Пользователь электронной почты - имя пользователя для аутентификации, переопределяет адрес электронной почты, если указан",
+ "settingsEmailFieldResetLabel": "Пароль установлен. Нажмите «Сбросить», чтобы изменить его.",
+ "settingsEmailConnectionHost": "Хост подключения электронной почты - имя хоста, используемое в приветствии HELO/EHLO",
+ "settingsTestEmail": "Отправить тестовое письмо",
+ "settingsTestEmailSuccess": "Тестовое письмо успешно отправлено",
+ "settingsTestEmailFailed": "Не удалось отправить тестовое письмо",
+ "settingsTestEmailFailedWithReason": "Не удалось отправить тестовое письмо: {{reason}}",
+ "settingsTestEmailUnknownError": "Неизвестная ошибка",
+ "settingsEmailRequiredFields": "Требуются хост и порт электронной почты",
"settingsAbout": "О",
"settingsDevelopedBy": "Developed by Bluewave Labs.",
"settingsSave": "Сохранить",
@@ -152,7 +172,6 @@
"http": "HTTP",
"monitor": "монитор",
"aboutus": "О Нас",
-
"now": "Сейчас",
"delete": "Удалить",
"configure": "Настроить",
@@ -409,6 +428,10 @@
"pageSpeedWarning": "Предупреждение: Вы не добавили ключ API Google PageSpeed. Без него монитор PageSpeed не будет работать.",
"pageSpeedLearnMoreLink": "Нажмите здесь, чтобы узнать",
"pageSpeedAddApiKey": "как добавить ваш ключ API.",
+ "pageSpeedApiKeyFieldTitle": "Ключ Google PageSpeed API",
+ "pageSpeedApiKeyFieldLabel": "Ключ PageSpeed API",
+ "pageSpeedApiKeyFieldDescription": "Введите свой ключ Google PageSpeed API, чтобы включить мониторинг скорости страниц. Нажмите «Сбросить», чтобы обновить ключ.",
+ "pageSpeedApiKeyFieldResetLabel": "Ключ API установлен. Нажмите «Сбросить», чтобы изменить его.",
"createNew": "Создать новый",
"greeting": {
"prepend": "Привет",
diff --git a/client/src/locales/tr.json b/client/src/locales/tr.json
index 5aee7cf51..6fc650ad3 100644
--- a/client/src/locales/tr.json
+++ b/client/src/locales/tr.json
@@ -73,6 +73,11 @@
"settingsThemeModeLight": "Açık",
"settingsThemeModeDark": "Koyu",
"settingsLanguage": "Dil",
+ "settingsURLTitle": "Durum Sayfasında IP/URL'yi Göster",
+ "settingsURLDescription": "Monitörün IP adresini veya URL'sini genel Durum sayfasında göster. Devre dışı bırakıldığında, hassas bilgileri korumak için yalnızca monitör adı gösterilecektir.",
+ "settingsURLSelectTitle": "Durum sayfasında IP/URL'yi göster",
+ "settingsURLEnabled": "Etkin",
+ "settingURLDisabled": "Devre Dışı",
"settingsDistributedUptime": "Dağıtılmış çalışma süresi",
"settingsDistributedUptimeDescription": "Dağıtılmış çalışma süresi izlemeyi etkinleştirin/devre dışı bırakın.",
"settingsEnabled": "Etkin",
@@ -99,6 +104,20 @@
"settingsRemoveAllMonitorsDialogConfirm": "Evet, tüm monitörleri kaldır",
"settingsWallet": "Cüzdan",
"settingsWalletDescription": "Cüzdanınızı buradan bağlayın. Bu, Dağıtılmış Çalışma Süresi monitörünün küresel olarak birden çok düğüme bağlanması için gereklidir.",
+ "settingsTestEmail": "Test e-postası gönder",
+ "settingsTestEmailSuccess": "Test e-postası başarıyla gönderildi",
+ "settingsTestEmailFailed": "Test e-postası gönderilemedi",
+ "settingsTestEmailFailedWithReason": "Test e-postası gönderilemedi: {{reason}}",
+ "settingsTestEmailUnknownError": "Bilinmeyen hata",
+ "settingsEmail": "E-posta",
+ "settingsEmailDescription": "Sisteminiz için e-posta ayarlarını yapılandırın. Bu, bildirim ve uyarıları göndermek için kullanılır.",
+ "settingsEmailHost": "E-posta sunucusu - SMTP sunucusunun ana bilgisayar adı veya IP adresi",
+ "settingsEmailPort": "E-posta portu - Bağlanılacak port",
+ "settingsEmailUser": "E-posta kullanıcısı - Kimlik doğrulama için kullanıcı adı, belirtilirse e-posta adresini geçersiz kılar",
+ "settingsEmailAddress": "E-posta adresi - Kimlik doğrulama için kullanılır",
+ "settingsEmailPassword": "E-posta şifresi - Kimlik doğrulama için şifre",
+ "settingsEmailConnectionHost": "E-posta bağlantı sunucusu - HELO/EHLO selamlamasında kullanılacak ana bilgisayar adı",
+ "settingsEmailRequiredFields": "E-posta sunucusu ve port gereklidir",
"settingsAbout": "Hakkında",
"settingsDevelopedBy": "Bluewave Labs tarafından geliştirilmiştir.",
"settingsSave": "Kaydet",
@@ -124,7 +143,6 @@
"http": "HTTP",
"monitor": "monitör",
"aboutus": "Hakkımızda",
-
"now": "Şimdi",
"delete": "Sil",
"configure": "Yapılandır",
@@ -397,7 +415,9 @@
"pageSpeedWarning": "Uyarı: Google PageSpeed API anahtarı eklemediniz. Bu olmadan, PageSpeed monitörü çalışmayacaktır.",
"pageSpeedLearnMoreLink": "Daha fazla bilgi için tıklayın",
"pageSpeedAddApiKey": "API anahtarınızı nasıl ekleyeceğinizi öğrenin.",
- "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.",
+ "pageSpeedApiKeyFieldTitle": "Google PageSpeed API anahtarı",
+ "pageSpeedApiKeyFieldLabel": "PageSpeed API anahtarı",
+ "pageSpeedApiKeyFieldDescription": "PageSpeed 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",
"invalidFileFormat": "",
@@ -513,4 +533,4 @@
"saving": "Kaydediliyor..."
},
"uptimeCreateSelectURL": "İzlenecek URL veya IP adresini girin (örn. https://example.com/ veya 192.168.1.100) ve kontrol panelinde görünecek net bir görüntü adı ekleyin."
-}
+}
\ No newline at end of file
diff --git a/client/vite.config.js b/client/vite.config.js
index 22fcd51fa..dc84ce055 100644
--- a/client/vite.config.js
+++ b/client/vite.config.js
@@ -4,6 +4,7 @@ import svgr from "vite-plugin-svgr";
// https://vitejs.dev/config/
export default defineConfig({
+ base: "/",
plugins: [svgr(), react()],
optimizeDeps: {
include: ["@mui/material/Tooltip", "@emotion/styled"],
diff --git a/docker/.gitignore b/docker/.gitignore
index 1e35b3731..81ba730c9 100755
--- a/docker/.gitignore
+++ b/docker/.gitignore
@@ -8,5 +8,6 @@ dist/redis/data/*
prod/mongo/data/*
prod/redis/data/*
dist/docker-compose-test.yaml
+/dist-mono/mongo/data/*
+/dist-mono/redis/data/*
*.env
-prod/certbot/*
\ No newline at end of file
diff --git a/docker/dist-arm/build_images.sh b/docker/dist-arm/build_images.sh
new file mode 100755
index 000000000..2780e5359
--- /dev/null
+++ b/docker/dist-arm/build_images.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+set -e # Exit on error
+
+# Change directory to root Server directory for correct Docker Context
+cd "$(dirname "$0")"
+cd ../..
+
+# Ensure buildx builder exists and is used
+if ! docker buildx inspect mybuilder &>/dev/null; then
+ docker buildx create --name mybuilder --use
+else
+ docker buildx use mybuilder
+fi
+
+# Define service names and Dockerfiles
+services=("mono_server_arm")
+dockerfiles=(
+ "./docker/dist-arm/server.Dockerfile"
+)
+
+# Static image name for GitHub Container Registry (GHCR)
+image="ghcr.io/bluewave-labs/checkmate:backend-dist-multi-arch"
+platforms="linux/amd64,linux/arm64"
+repo_url="https://github.com/bluewave-labs/checkmate"
+
+# Loop through each service and build
+for i in "${!services[@]}"; do
+ service="${services[$i]}"
+ dockerfile="${dockerfiles[$i]}"
+
+ echo "🚀 Building multi-arch image for $service..."
+ docker buildx build \
+ --platform "$platforms" \
+ -f "$dockerfile" \
+ -t "$image" \
+ --label "org.opencontainers.image.source=$repo_url" \
+ --push \
+ .
+
+ echo "✅ $image pushed for platforms: $platforms"
+done
+
+echo "🎉 All multi-arch images built and pushed successfully"
diff --git a/docker/dist-arm/docker-compose.yaml b/docker/dist-arm/docker-compose.yaml
new file mode 100644
index 000000000..274853278
--- /dev/null
+++ b/docker/dist-arm/docker-compose.yaml
@@ -0,0 +1,54 @@
+version: "3.8"
+
+services:
+ server:
+ image: ghcr.io/bluewave-labs/checkmate:backend-dist-mono-multiarch
+ restart: always
+ ports:
+ - "52345:52345"
+ environment:
+ - UPTIME_APP_API_BASE_URL=http://localhost:52345/api/v1
+ - UPTIME_APP_CLIENT_HOST=http://localhost
+ - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db?replicaSet=rs0
+ - REDIS_URL=redis://redis:6379
+ - 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
+ restart: always
+ command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
+ ports:
+ - "27017:27017"
+ volumes:
+ - ./mongo/data:/data/db
+
+ mongo-init:
+ image: mongo:4.4.18
+ depends_on:
+ - mongodb
+ entrypoint: >
+ bash -c "
+ echo 'Waiting for MongoDB to be ready...' &&
+ until mongo --host mongodb --eval 'db.adminCommand(\"ping\")' > /dev/null 2>&1; do
+ sleep 2
+ done &&
+ echo 'MongoDB is up. Initiating replica set...' &&
+ mongo --host mongodb --eval '
+ rs.initiate({
+ _id: \"rs0\",
+ members: [{ _id: 0, host: \"mongodb:27017\" }]
+ })
+ ' || echo 'Replica set may already be initialized.'
+ "
diff --git a/docker/dist-arm/server.Dockerfile b/docker/dist-arm/server.Dockerfile
new file mode 100644
index 000000000..981d1cf8f
--- /dev/null
+++ b/docker/dist-arm/server.Dockerfile
@@ -0,0 +1,25 @@
+FROM node:20-alpine AS frontend-build
+
+WORKDIR /app/client
+
+COPY client/package*.json ./
+RUN npm ci
+
+COPY client ./
+RUN npm run build
+
+FROM node:20-alpine AS backend
+
+WORKDIR /app/server
+
+COPY server ./
+
+COPY --from=frontend-build /app/client/dist ./public
+
+RUN npm ci
+
+RUN chmod +x ./scripts/inject-vars.sh
+
+EXPOSE 52345
+
+CMD ./scripts/inject-vars.sh && node ./index.js
diff --git a/docker/dist-mono/build_images.sh b/docker/dist-mono/build_images.sh
new file mode 100755
index 000000000..b56b9052c
--- /dev/null
+++ b/docker/dist-mono/build_images.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Change directory to root Server directory for correct Docker Context
+cd "$(dirname "$0")"
+cd ../..
+
+# Define service names and their corresponding Dockerfiles in parallel arrays
+services=("mono_mongo" "mono_redis" "mono_server")
+dockerfiles=(
+ "./docker/dist-mono/mongoDB.Dockerfile"
+ "./docker/dist-mono/redis.Dockerfile"
+ "./docker/dist-mono/server.Dockerfile"
+)
+
+# Loop through each service and build the corresponding image
+for i in "${!services[@]}"; do
+ service="${services[$i]}"
+ dockerfile="${dockerfiles[$i]}"
+
+ docker build -f "$dockerfile" -t "$service" .
+
+ # Check if the build succeeded
+ if [ $? -ne 0 ]; then
+ echo "Error building $service image. Exiting..."
+ exit 1
+ fi
+done
+
+echo "All images built successfully"
\ No newline at end of file
diff --git a/docker/dist-mono/docker-compose.yaml b/docker/dist-mono/docker-compose.yaml
new file mode 100755
index 000000000..9b3364975
--- /dev/null
+++ b/docker/dist-mono/docker-compose.yaml
@@ -0,0 +1,40 @@
+services:
+ server:
+ image: ghcr.io/bluewave-labs/checkmate:backend-dist-mono
+ restart: always
+ ports:
+ - "52345:52345"
+ environment:
+ - UPTIME_APP_API_BASE_URL=http://localhost:52345/api/v1
+ - UPTIME_APP_CLIENT_HOST=http://localhost
+ - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db?replicaSet=rs0
+ - REDIS_URL=redis://redis:6379
+ - CLIENT_HOST=http://localhost
+ - JWT_SECRET=my_secret
+ depends_on:
+ - redis
+ - mongodb
+ redis:
+ image: ghcr.io/bluewave-labs/checkmate:redis-dist
+ 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-dist
+ restart: always
+ command: ["mongod", "--quiet", "--replSet", "rs0", "--bind_ip_all"]
+ volumes:
+ - ./mongo/data:/data/db
+ healthcheck:
+ test: echo "try { rs.status() } catch (err) { rs.initiate({_id:'rs0',members:[{_id:0,host:'mongodb:27017'}]}) }" | mongosh --port 27017 --quiet
+ interval: 5s
+ timeout: 30s
+ start_period: 0s
+ start_interval: 1s
+ retries: 30
diff --git a/docker/dist-mono/mongoDB.Dockerfile b/docker/dist-mono/mongoDB.Dockerfile
new file mode 100755
index 000000000..969a320c2
--- /dev/null
+++ b/docker/dist-mono/mongoDB.Dockerfile
@@ -0,0 +1,3 @@
+FROM mongo
+EXPOSE 27017
+CMD ["mongod"]
diff --git a/docker/dist-mono/redis.Dockerfile b/docker/dist-mono/redis.Dockerfile
new file mode 100755
index 000000000..af68ec61e
--- /dev/null
+++ b/docker/dist-mono/redis.Dockerfile
@@ -0,0 +1,2 @@
+FROM redis
+EXPOSE 6379
\ No newline at end of file
diff --git a/docker/dist-mono/server.Dockerfile b/docker/dist-mono/server.Dockerfile
new file mode 100644
index 000000000..f28c834ea
--- /dev/null
+++ b/docker/dist-mono/server.Dockerfile
@@ -0,0 +1,25 @@
+FROM node:20-alpine AS frontend-build
+
+WORKDIR /app/client
+
+COPY client/package*.json ./
+RUN npm install
+
+COPY client ./
+RUN npm run build
+
+FROM node:20-alpine AS app
+
+WORKDIR /app/server
+
+COPY server ./
+
+COPY --from=frontend-build /app/client/dist ./public
+
+RUN npm install
+
+RUN chmod +x ./scripts/inject-vars.sh
+
+EXPOSE 52345
+
+CMD ./scripts/inject-vars.sh && node ./index.js
diff --git a/server/.gitignore b/server/.gitignore
index bab6fb27c..4bc621f83 100755
--- a/server/.gitignore
+++ b/server/.gitignore
@@ -2,8 +2,10 @@ node_modules
.env
*.log
*.sh
+!scripts/inject-vars.sh
.nyc_output
coverage
.clinic
node_modules
.vscode/*
+public
\ No newline at end of file
diff --git a/server/controllers/monitorController.js b/server/controllers/monitorController.js
index d537e8cee..e8eaeb106 100755
--- a/server/controllers/monitorController.js
+++ b/server/controllers/monitorController.js
@@ -595,8 +595,8 @@ class MonitorController {
{ new: true }
);
monitor.isActive === true
- ? await this.jobQueue.deleteJob(monitor)
- : await this.jobQueue.addJob(monitor._id, monitor);
+ ? await this.jobQueue.addJob(monitor._id, monitor)
+ : await this.jobQueue.deleteJob(monitor);
return res.success({
msg: monitor.isActive
diff --git a/server/controllers/queueController.js b/server/controllers/queueController.js
index 9d87a70f6..be3d764e7 100755
--- a/server/controllers/queueController.js
+++ b/server/controllers/queueController.js
@@ -23,7 +23,7 @@ class JobQueueController {
getJobs = async (req, res, next) => {
try {
- const jobs = await this.jobQueue.getJobStats();
+ const jobs = await this.jobQueue.getJobs();
return res.success({
msg: this.stringService.queueGetMetrics,
data: jobs,
@@ -60,7 +60,7 @@ class JobQueueController {
flushQueue = async (req, res, next) => {
try {
- const result = await this.jobQueue.flushQueue();
+ const result = await this.jobQueue.flushQueues();
return res.success({
msg: this.stringService.jobQueueFlush,
data: result,
diff --git a/server/controllers/settingsController.js b/server/controllers/settingsController.js
index 21f0050ba..58e8ce9f4 100755
--- a/server/controllers/settingsController.js
+++ b/server/controllers/settingsController.js
@@ -1,13 +1,14 @@
import { updateAppSettingsBodyValidation } from "../validation/joi.js";
import { handleValidationError, handleError } from "./controllerUtils.js";
-
+import { sendTestEmailBodyValidation } from "../validation/joi.js";
const SERVICE_NAME = "SettingsController";
class SettingsController {
- constructor(db, settingsService, stringService) {
+ constructor({ db, settingsService, stringService, emailService }) {
this.db = db;
this.settingsService = settingsService;
this.stringService = stringService;
+ this.emailService = emailService;
}
getAppSettings = async (req, res, next) => {
@@ -55,6 +56,59 @@ class SettingsController {
next(handleError(error, SERVICE_NAME, "updateAppSettings"));
}
};
+
+ sendTestEmail = async (req, res, next) => {
+ try {
+ await sendTestEmailBodyValidation.validateAsync(req.body);
+ } catch (error) {
+ next(handleValidationError(error, SERVICE_NAME));
+ return;
+ }
+
+ try {
+ const {
+ to,
+ systemEmailHost,
+ systemEmailPort,
+ systemEmailAddress,
+ systemEmailPassword,
+ systemEmailUser,
+ systemEmailConnectionHost,
+ } = req.body;
+
+ const subject = this.stringService.testEmailSubject;
+ const context = { testName: "Monitoring System" };
+
+ const messageId = await this.emailService.buildAndSendEmail(
+ "testEmailTemplate",
+ context,
+ to,
+ subject,
+ {
+ systemEmailHost,
+ systemEmailPort,
+ systemEmailUser,
+ systemEmailAddress,
+ systemEmailPassword,
+ systemEmailConnectionHost,
+ }
+ );
+
+ if (!messageId) {
+ return res.error({
+ msg: "Failed to send test email.",
+ });
+ }
+
+ return res.success({
+ msg: this.stringService.sendTestEmail,
+ data: { messageId },
+ });
+ } catch (error) {
+ next(handleValidationError(error, SERVICE_NAME));
+ return;
+ }
+ };
}
export default SettingsController;
diff --git a/server/db/models/AppSettings.js b/server/db/models/AppSettings.js
index 22e586131..c47de06fa 100755
--- a/server/db/models/AppSettings.js
+++ b/server/db/models/AppSettings.js
@@ -28,6 +28,10 @@ const AppSettingsSchema = mongoose.Schema(
systemEmailUser: {
type: String,
},
+ systemEmailConnectionHost: {
+ type: String,
+ default: "localhost",
+ },
singleton: {
type: Boolean,
required: true,
diff --git a/server/db/mongo/modules/checkModule.js b/server/db/mongo/modules/checkModule.js
index 2b0eb33ba..2339edbed 100755
--- a/server/db/mongo/modules/checkModule.js
+++ b/server/db/mongo/modules/checkModule.js
@@ -43,7 +43,7 @@ const createCheck = async (checkData) => {
const createChecks = async (checks) => {
try {
- await Check.insertMany(checks);
+ await Check.insertMany(checks, { ordered: false });
} catch (error) {
error.service = SERVICE_NAME;
error.method = "createCheck";
diff --git a/server/db/mongo/modules/distributedCheckModule.js b/server/db/mongo/modules/distributedCheckModule.js
index 10fc577b2..7200cba33 100755
--- a/server/db/mongo/modules/distributedCheckModule.js
+++ b/server/db/mongo/modules/distributedCheckModule.js
@@ -128,9 +128,8 @@ const createDistributedChecks = async (checksData) => {
};
});
- // Execute bulk operation
await DistributedUptimeCheck.bulkWrite(bulkOps, {
- ordered: false, // Allow parallel processing
+ ordered: false,
});
} catch (error) {
error.service = SERVICE_NAME;
diff --git a/server/db/mongo/modules/hardwareCheckModule.js b/server/db/mongo/modules/hardwareCheckModule.js
index 738412c60..868c83958 100755
--- a/server/db/mongo/modules/hardwareCheckModule.js
+++ b/server/db/mongo/modules/hardwareCheckModule.js
@@ -51,7 +51,7 @@ const createHardwareCheck = async (hardwareCheckData) => {
const createHardwareChecks = async (hardwareChecks) => {
try {
- await HardwareCheck.insertMany(hardwareChecks);
+ await HardwareCheck.insertMany(hardwareChecks, { ordered: false });
return true;
} catch (error) {
error.service = SERVICE_NAME;
diff --git a/server/db/mongo/modules/monitorModuleQueries.js b/server/db/mongo/modules/monitorModuleQueries.js
index 0ffaaf6f1..4af3f8985 100755
--- a/server/db/mongo/modules/monitorModuleQueries.js
+++ b/server/db/mongo/modules/monitorModuleQueries.js
@@ -490,6 +490,7 @@ const buildMonitorsByTeamIdPipeline = ({ matchStage, field, order }) => {
_id: 1,
name: 1,
type: 1,
+ port: 1,
},
},
];
diff --git a/server/db/mongo/modules/pageSpeedCheckModule.js b/server/db/mongo/modules/pageSpeedCheckModule.js
index f634fae27..9169a0836 100755
--- a/server/db/mongo/modules/pageSpeedCheckModule.js
+++ b/server/db/mongo/modules/pageSpeedCheckModule.js
@@ -26,7 +26,7 @@ const createPageSpeedCheck = async (pageSpeedCheckData) => {
};
const createPageSpeedChecks = async (pageSpeedChecks) => {
try {
- await PageSpeedCheck.insertMany(pageSpeedChecks);
+ await PageSpeedCheck.insertMany(pageSpeedChecks, { ordered: false });
return true;
} catch (error) {
error.service = SERVICE_NAME;
diff --git a/server/index.js b/server/index.js
index f299b32e9..eea5b015a 100755
--- a/server/index.js
+++ b/server/index.js
@@ -46,7 +46,8 @@ import DiagnosticRoutes from "./routes/diagnosticRoute.js";
import DiagnosticController from "./controllers/diagnosticController.js";
//JobQueue service and dependencies
-import JobQueue from "./service/jobQueue.js";
+import JobQueue from "./service/JobQueue/JobQueue.js";
+import JobQueueHelper from "./service/JobQueue/JobQueueHelper.js";
import { Queue, Worker } from "bullmq";
//Network service and dependencies
@@ -98,6 +99,8 @@ const openApiSpec = JSON.parse(
fs.readFileSync(path.join(__dirname, "openapi.json"), "utf8")
);
+const frontendPath = path.join(__dirname, "public");
+
let server;
const shutdown = async () => {
@@ -112,17 +115,13 @@ const shutdown = async () => {
service: SERVICE_NAME,
method: "shutdown",
});
- // flush Redis
- const redisService = ServiceRegistry.get(RedisService.SERVICE_NAME);
- await redisService.flushall();
-
+ await ServiceRegistry.get(RedisService.SERVICE_NAME).flushRedis();
process.exit(1);
}, SHUTDOWN_TIMEOUT);
try {
server.close();
- await ServiceRegistry.get(JobQueue.SERVICE_NAME).obliterate();
+ await ServiceRegistry.get(JobQueue.SERVICE_NAME).shutdown();
await ServiceRegistry.get(MongoDB.SERVICE_NAME).disconnect();
- await ServiceRegistry.get(RedisService.SERVICE_NAME).flushall();
logger.info({ message: "Graceful shutdown complete" });
process.exit(0);
} catch (error) {
@@ -182,23 +181,23 @@ const startApp = async () => {
stringService
);
- const redisService = await RedisService.createInstance({
- logger,
- IORedis,
- SettingsService: settingsService,
- });
+ const redisService = new RedisService({ Redis: IORedis, logger });
- const jobQueue = new JobQueue({
- db,
- statusService,
- networkService,
- notificationService,
- settingsService,
- stringService,
- logger,
+ const jobQueueHelper = new JobQueueHelper({
+ redisService,
Queue,
Worker,
- redisService,
+ logger,
+ db,
+ networkService,
+ statusService,
+ notificationService,
+ });
+ const jobQueue = await JobQueue.create({
+ db,
+ jobQueueHelper,
+ logger,
+ stringService,
});
// Register services
@@ -241,11 +240,12 @@ const startApp = async () => {
ServiceRegistry.get(EmailService.SERVICE_NAME)
);
- const settingsController = new SettingsController(
- ServiceRegistry.get(MongoDB.SERVICE_NAME),
- ServiceRegistry.get(SettingsService.SERVICE_NAME),
- ServiceRegistry.get(StringService.SERVICE_NAME)
- );
+ const settingsController = new SettingsController({
+ db: ServiceRegistry.get(MongoDB.SERVICE_NAME),
+ settingsService: ServiceRegistry.get(SettingsService.SERVICE_NAME),
+ stringService: ServiceRegistry.get(StringService.SERVICE_NAME),
+ emailService: ServiceRegistry.get(EmailService.SERVICE_NAME),
+ });
const checkController = new CheckController(
ServiceRegistry.get(MongoDB.SERVICE_NAME),
@@ -309,9 +309,8 @@ const startApp = async () => {
);
const notificationRoutes = new NotificationRoutes(notificationController);
const diagnosticRoutes = new DiagnosticRoutes(diagnosticController);
- // Init job queue
- await jobQueue.initJobQueue();
// Middleware
+ app.use(express.static(frontendPath));
app.use(responseHandler);
app.use(
cors({
@@ -322,7 +321,17 @@ const startApp = async () => {
})
);
app.use(express.json());
- app.use(helmet());
+ app.use(
+ helmet({
+ hsts: false,
+ contentSecurityPolicy: {
+ useDefaults: true,
+ directives: {
+ upgradeInsecureRequests: null,
+ },
+ },
+ })
+ );
app.use(
compression({
level: 6,
@@ -357,6 +366,10 @@ const startApp = async () => {
status: "OK",
});
});
+ // FE routes
+ app.get("*", (req, res) => {
+ res.sendFile(path.join(frontendPath, "index.html"));
+ });
app.use(handleErrors);
};
diff --git a/server/package-lock.json b/server/package-lock.json
index f236139d8..7bf4bfc78 100755
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -26,8 +26,9 @@
"mailersend": "^2.2.0",
"mjml": "^5.0.0-alpha.4",
"mongoose": "^8.3.3",
- "multer": "1.4.5-lts.1",
+ "multer": "^1.4.5-lts.1",
"nodemailer": "^6.9.14",
+ "papaparse": "^5.5.2",
"ping": "0.4.4",
"sharp": "0.33.5",
"ssl-checker": "2.0.10",
@@ -5499,6 +5500,12 @@
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"license": "BlueOak-1.0.0"
},
+ "node_modules/papaparse": {
+ "version": "5.5.2",
+ "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.2.tgz",
+ "integrity": "sha512-PZXg8UuAc4PcVwLosEEDYjPyfWnTEhOrUfdv+3Bx+NuAb+5NhDmXzg5fHWmdCh1mP5p7JAZfFr3IMQfcntNAdA==",
+ "license": "MIT"
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -7290,9 +7297,9 @@
"license": "MIT"
},
"node_modules/undici": {
- "version": "6.21.1",
- "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz",
- "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==",
+ "version": "6.21.3",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
+ "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
"license": "MIT",
"engines": {
"node": ">=18.17"
diff --git a/server/routes/monitorRoute.js b/server/routes/monitorRoute.js
index f9ef460c6..6d1db90ad 100755
--- a/server/routes/monitorRoute.js
+++ b/server/routes/monitorRoute.js
@@ -4,8 +4,8 @@ import multer from "multer";
import { fetchMonitorCertificate } from "../controllers/controllerUtils.js";
const upload = multer({
- storage: multer.memoryStorage() // Store file in memory as Buffer
- });
+ storage: multer.memoryStorage(), // Store file in memory as Buffer
+});
class MonitorRoutes {
constructor(monitorController) {
diff --git a/server/routes/settingsRoute.js b/server/routes/settingsRoute.js
index a5501aaf3..f293fd660 100755
--- a/server/routes/settingsRoute.js
+++ b/server/routes/settingsRoute.js
@@ -15,6 +15,11 @@ class SettingsRoutes {
isAllowed(["admin", "superadmin"]),
this.settingsController.updateAppSettings
);
+ this.router.post(
+ "/test-email",
+ isAllowed(["admin", "superadmin"]),
+ this.settingsController.sendTestEmail
+ );
}
getRouter() {
diff --git a/server/scripts/inject-vars.sh b/server/scripts/inject-vars.sh
new file mode 100755
index 000000000..34365b0d3
--- /dev/null
+++ b/server/scripts/inject-vars.sh
@@ -0,0 +1,7 @@
+for i in $(env | grep UPTIME_APP_)
+do
+ key=$(echo $i | cut -d '=' -f 1)
+ value=$(echo $i | cut -d '=' -f 2-)
+ echo $key=$value
+ find ./public/assets -type f \( -name '*.js' -o -name '*.css' \) -exec sed -i "s|${key}|${value}|g" '{}' +
+done
\ No newline at end of file
diff --git a/server/service/JobQueue/JobQueue.js b/server/service/JobQueue/JobQueue.js
new file mode 100644
index 000000000..32d531809
--- /dev/null
+++ b/server/service/JobQueue/JobQueue.js
@@ -0,0 +1,310 @@
+const QUEUE_NAMES = ["uptime", "pagespeed", "hardware", "distributed"];
+const SERVICE_NAME = "JobQueue";
+const HEALTH_CHECK_INTERVAL = 10 * 60 * 1000; // 10 minutes
+const QUEUE_LOOKUP = {
+ hardware: "hardware",
+ http: "uptime",
+ ping: "uptime",
+ port: "uptime",
+ docker: "uptime",
+ pagespeed: "pagespeed",
+ distributed_http: "distributed",
+};
+const getSchedulerId = (monitor) => `scheduler:${monitor.type}:${monitor._id}`;
+
+class JobQueue {
+ static SERVICE_NAME = SERVICE_NAME;
+
+ constructor({ db, jobQueueHelper, logger, stringService }) {
+ this.db = db;
+ this.jobQueueHelper = jobQueueHelper;
+ this.stringService = stringService;
+ this.logger = logger;
+ this.queues = {};
+ this.workers = [];
+ }
+
+ static async create({ db, jobQueueHelper, logger, stringService }) {
+ const instance = new JobQueue({ db, jobQueueHelper, logger, stringService });
+ await instance.init();
+ return instance;
+ }
+
+ async init() {
+ try {
+ await this.initQueues();
+ await this.initWorkers();
+ const monitors = await this.db.getAllMonitors();
+ await Promise.all(
+ monitors
+ .filter((monitor) => monitor.isActive)
+ .map(async (monitor) => {
+ try {
+ await this.addJob(monitor._id, monitor);
+ } catch (error) {
+ this.logger.error({
+ message: error.message,
+ service: SERVICE_NAME,
+ method: "initJobQueue",
+ stack: error.stack,
+ });
+ }
+ })
+ );
+
+ this.healthCheckInterval = setInterval(async () => {
+ try {
+ const queueHealthChecks = await this.checkQueueHealth();
+ const queueIsStuck = queueHealthChecks.some((healthCheck) => healthCheck.stuck);
+ if (queueIsStuck) {
+ this.logger.warn({
+ message: "Queue is stuck",
+ service: SERVICE_NAME,
+ method: "periodicHealthCheck",
+ details: queueHealthChecks,
+ });
+ await this.flushQueues();
+ }
+ } catch (error) {
+ this.logger.error({
+ message: error.message,
+ service: SERVICE_NAME,
+ method: "periodicHealthCheck",
+ stack: error.stack,
+ });
+ }
+ }, HEALTH_CHECK_INTERVAL);
+ } catch (error) {
+ this.logger.error({
+ message: error.message,
+ service: SERVICE_NAME,
+ method: "initJobQueue",
+ stack: error.stack,
+ });
+ }
+ }
+
+ async initQueues() {
+ const readyPromises = [];
+
+ for (const queueName of QUEUE_NAMES) {
+ const q = this.jobQueueHelper.createQueue(queueName);
+ this.queues[queueName] = q;
+ readyPromises.push(q.waitUntilReady());
+ }
+ await Promise.all(readyPromises);
+ this.logger.info({
+ message: "Queues ready",
+ service: SERVICE_NAME,
+ method: "initQueues",
+ });
+ }
+
+ async initWorkers() {
+ const workerReadyPromises = [];
+
+ for (const queueName of QUEUE_NAMES) {
+ const worker = this.jobQueueHelper.createWorker(queueName, this.queues[queueName]);
+ this.workers.push(worker);
+ workerReadyPromises.push(worker.waitUntilReady());
+ }
+ await Promise.all(workerReadyPromises);
+ this.logger.info({
+ message: "Workers ready",
+ service: SERVICE_NAME,
+ method: "initWorkers",
+ });
+ }
+
+ async addJob(jobName, monitor) {
+ this.logger.info({
+ message: `Adding job ${monitor?.url ?? "No URL"}`,
+ service: SERVICE_NAME,
+ method: "addJob",
+ });
+
+ const queueName = QUEUE_LOOKUP[monitor.type];
+ const queue = this.queues[queueName];
+ if (typeof queue === "undefined") {
+ throw new Error(`Queue for ${monitor.type} not found`);
+ }
+ const jobTemplate = {
+ name: jobName,
+ data: monitor,
+ opts: {
+ attempts: 1,
+ backoff: {
+ type: "exponential",
+ delay: 1000,
+ },
+ removeOnComplete: true,
+ removeOnFail: false,
+ timeout: 1 * 60 * 1000,
+ },
+ };
+
+ const schedulerId = getSchedulerId(monitor);
+ await queue.upsertJobScheduler(
+ schedulerId,
+ { every: monitor?.interval ?? 60000 },
+ jobTemplate
+ );
+ }
+
+ async deleteJob(monitor) {
+ try {
+ const queue = this.queues[QUEUE_LOOKUP[monitor.type]];
+ const schedulerId = getSchedulerId(monitor);
+ const wasDeleted = await queue.removeJobScheduler(schedulerId);
+
+ if (wasDeleted === true) {
+ this.logger.info({
+ message: this.stringService.jobQueueDeleteJob,
+ service: SERVICE_NAME,
+ method: "deleteJob",
+ details: `Deleted job ${monitor._id}`,
+ });
+ return true;
+ } else {
+ this.logger.error({
+ message: this.stringService.jobQueueDeleteJob,
+ service: SERVICE_NAME,
+ method: "deleteJob",
+ details: `Failed to delete job ${monitor._id}`,
+ });
+ return false;
+ }
+ } catch (error) {
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "deleteJob") : null;
+ throw error;
+ }
+ }
+
+ async getJobs() {
+ try {
+ let stats = {};
+ await Promise.all(
+ QUEUE_NAMES.map(async (name) => {
+ const queue = this.queues[name];
+ const jobs = await queue.getJobs();
+ const ret = await Promise.all(
+ jobs.map(async (job) => {
+ const state = await job.getState();
+ return { url: job.data.url, state, progress: job.progress };
+ })
+ );
+ stats[name] = { jobs: ret };
+ })
+ );
+ return stats;
+ } catch (error) {
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "getJobStats") : null;
+ throw error;
+ }
+ }
+
+ async getMetrics() {
+ try {
+ let metrics = {};
+
+ await Promise.all(
+ QUEUE_NAMES.map(async (name) => {
+ const queue = this.queues[name];
+ const [waiting, active, failed, delayed, repeatableJobs] = await Promise.all([
+ queue.getWaitingCount(),
+ queue.getActiveCount(),
+ queue.getFailedCount(),
+ queue.getDelayedCount(),
+ queue.getRepeatableJobs(),
+ ]);
+
+ metrics[name] = {
+ waiting,
+ active,
+ failed,
+ delayed,
+ repeatableJobs: repeatableJobs.length,
+ };
+ })
+ );
+
+ return metrics;
+ } catch (error) {
+ this.logger.error({
+ message: error.message,
+ service: SERVICE_NAME,
+ method: "getMetrics",
+ stack: error.stack,
+ });
+ }
+ }
+
+ async checkQueueHealth() {
+ const res = [];
+ for (const queueName of QUEUE_NAMES) {
+ const q = this.queues[queueName];
+ await q.waitUntilReady();
+
+ const lastJobProcessedTime = q.lastJobProcessedTime;
+ const currentTime = Date.now();
+ const timeDiff = currentTime - lastJobProcessedTime;
+
+ // Check for jobs
+ const jobCounts = await q.getJobCounts();
+ const hasJobs = Object.values(jobCounts).some((count) => count > 0);
+
+ res.push({
+ queueName,
+ timeSinceLastJob: timeDiff,
+ stuck: hasJobs && timeDiff > 10000,
+ jobCounts,
+ });
+ }
+ return res;
+ }
+
+ async flushQueues() {
+ try {
+ this.logger.warn({
+ message: "Flushing queues",
+ method: "flushQueues",
+ service: SERVICE_NAME,
+ });
+ for (const worker of this.workers) {
+ await worker.close();
+ }
+ this.workers = [];
+
+ for (const queue of Object.values(this.queues)) {
+ await queue.obliterate();
+ }
+ this.queue = {};
+ await this.init();
+ return true;
+ } catch (error) {
+ this.logger.warn({
+ message: `${error.message} - Flushing redis manually`,
+ service: SERVICE_NAME,
+ method: "flushQueues",
+ });
+ return await this.jobQueueHelper.flushRedis();
+ }
+ }
+
+ async shutdown() {
+ if (this.healthCheckInterval) {
+ clearInterval(this.healthCheckInterval);
+ this.healthCheckInterval = null;
+ }
+ for (const worker of this.workers) {
+ await worker.close();
+ }
+ for (const queue of Object.values(this.queues)) {
+ await queue.obliterate();
+ }
+ }
+}
+
+export default JobQueue;
diff --git a/server/service/JobQueue/JobQueueHelper.js b/server/service/JobQueue/JobQueueHelper.js
new file mode 100644
index 000000000..de1e00fb8
--- /dev/null
+++ b/server/service/JobQueue/JobQueueHelper.js
@@ -0,0 +1,318 @@
+const SERVICE_NAME = "JobQueueHelper";
+
+class JobQueueHelper {
+ constructor({
+ redisService,
+ Queue,
+ Worker,
+ logger,
+ db,
+ networkService,
+ statusService,
+ notificationService,
+ }) {
+ this.db = db;
+ this.redisService = redisService;
+ this.Queue = Queue;
+ this.Worker = Worker;
+ this.logger = logger;
+ this.networkService = networkService;
+ this.statusService = statusService;
+ this.notificationService = notificationService;
+ }
+
+ createQueue(queueName) {
+ const connection = this.redisService.getNewConnection();
+ const q = new this.Queue(queueName, {
+ connection,
+ });
+ q.lastJobProcessedTime = Date.now();
+ q.on("cleaned", (jobs, type) => {
+ this.logger.debug({
+ message: `Queue ${queueName} is cleaned with jobs: ${jobs} and type: ${type}`,
+ service: SERVICE_NAME,
+ method: "createQueue:cleaned",
+ });
+ });
+ q.on("error", (err) => {
+ this.logger.error({
+ message: `Queue ${queueName} is error with msg: ${err}`,
+ service: SERVICE_NAME,
+ method: "createQueue:error",
+ });
+ });
+ q.on("ioredis:close", () => {
+ this.logger.debug({
+ message: `Queue ${queueName} is ioredis:close`,
+ service: SERVICE_NAME,
+ method: "createQueue:ioredis:close",
+ });
+ });
+ q.on("paused", () => {
+ this.logger.debug({
+ message: `Queue ${queueName} is paused`,
+ service: SERVICE_NAME,
+ method: "createQueue:paused",
+ });
+ });
+ q.on("progress", (job, progress) => {
+ this.logger.debug({
+ message: `Queue ${queueName} is progress with msg: ${progress}`,
+ service: SERVICE_NAME,
+ method: "createQueue:progress",
+ });
+ });
+ q.on("removed", (job) => {
+ this.logger.debug({
+ message: `Queue ${queueName} is removed with msg: ${job}`,
+ service: SERVICE_NAME,
+ method: "createQueue:removed",
+ });
+ });
+ q.on("resumed", () => {
+ this.logger.debug({
+ message: `Queue ${queueName} is resumed`,
+ service: SERVICE_NAME,
+ method: "createQueue:resumed",
+ });
+ });
+ q.on("waiting", () => {
+ this.logger.debug({
+ message: `Queue ${queueName} is waiting`,
+ service: SERVICE_NAME,
+ method: "createQueue:waiting",
+ });
+ });
+ return q;
+ }
+
+ createWorker(queueName, queue) {
+ const connection = this.redisService.getNewConnection({
+ maxRetriesPerRequest: null,
+ });
+ const worker = new this.Worker(queueName, this.createJobHandler(queue), {
+ connection,
+ concurrency: 50,
+ });
+ worker.on("active", (job) => {
+ this.logger.debug({
+ message: `Worker ${queueName} is active`,
+ service: SERVICE_NAME,
+ method: "createWorker:active",
+ });
+ });
+
+ worker.on("closed", () => {
+ this.logger.debug({
+ message: `Worker ${queueName} is closed`,
+ service: SERVICE_NAME,
+ method: "createWorker:closed",
+ });
+ });
+
+ worker.on("closing", (msg) => {
+ this.logger.debug({
+ message: `Worker ${queueName} is closing with msg: ${msg}`,
+ service: SERVICE_NAME,
+ method: "createWorker:closing",
+ });
+ });
+
+ worker.on("completed", (job) => {
+ this.logger.debug({
+ message: `Worker ${queueName} is completed`,
+ service: SERVICE_NAME,
+ method: "createWorker:completed",
+ });
+ });
+
+ worker.on("drained", () => {
+ this.logger.debug({
+ message: `Worker ${queueName} is drained`,
+ service: SERVICE_NAME,
+ method: "createWorker:drained",
+ });
+ });
+
+ worker.on("error", (failedReason) => {
+ this.logger.error({
+ message: `Worker ${queueName} is error with msg: ${failedReason}`,
+ service: SERVICE_NAME,
+ method: "createWorker:error",
+ });
+ });
+
+ worker.on("failed", (job, error, prev) => {
+ this.logger.error({
+ message: `Worker ${queueName} is failed with msg: ${error.message}`,
+ service: error?.service ?? SERVICE_NAME,
+ method: error?.method ?? "createWorker:failed",
+ stack: error?.stack,
+ });
+ });
+
+ worker.on("ioredis:close", () => {
+ this.logger.debug({
+ message: `Worker ${queueName} is ioredis:close`,
+ service: SERVICE_NAME,
+ method: "createWorker:ioredis:close",
+ });
+ });
+
+ worker.on("paused", () => {
+ this.logger.debug({
+ message: `Worker ${queueName} is paused`,
+ service: SERVICE_NAME,
+ method: "createWorker:paused",
+ });
+ });
+
+ worker.on("progress", (job, progress) => {
+ this.logger.debug({
+ message: `Worker ${queueName} is progress with msg: ${progress}`,
+ service: SERVICE_NAME,
+ method: "createWorker:progress",
+ });
+ });
+
+ worker.on("ready", () => {
+ this.logger.debug({
+ message: `Worker ${queueName} is ready`,
+ service: SERVICE_NAME,
+ method: "createWorker:ready",
+ });
+ });
+
+ worker.on("resumed", () => {
+ this.logger.debug({
+ message: `Worker ${queueName} is resumed`,
+ service: SERVICE_NAME,
+ method: "createWorker:resumed",
+ });
+ });
+
+ worker.on("stalled", () => {
+ this.logger.warn({
+ message: `Worker ${queueName} is stalled`,
+ service: SERVICE_NAME,
+ method: "createWorker:stalled",
+ });
+ });
+ return worker;
+ }
+
+ async isInMaintenanceWindow(monitorId) {
+ const maintenanceWindows = await this.db.getMaintenanceWindowsByMonitorId(monitorId);
+ // Check for active maintenance window:
+ const maintenanceWindowIsActive = maintenanceWindows.reduce((acc, window) => {
+ if (window.active) {
+ const start = new Date(window.start);
+ const end = new Date(window.end);
+ const now = new Date();
+ const repeatInterval = window.repeat || 0;
+
+ // If start is < now and end > now, we're in maintenance
+ if (start <= now && end >= now) return true;
+
+ // If maintenance window was set in the past with a repeat,
+ // we need to advance start and end to see if we are in range
+
+ while (start < now && repeatInterval !== 0) {
+ start.setTime(start.getTime() + repeatInterval);
+ end.setTime(end.getTime() + repeatInterval);
+ if (start <= now && end >= now) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return acc;
+ }, false);
+ return maintenanceWindowIsActive;
+ }
+
+ createJobHandler(q) {
+ return async (job) => {
+ try {
+ // Update the last job processed time for this queue
+ q.lastJobProcessedTime = Date.now();
+ // Get all maintenance windows for this monitor
+ await job.updateProgress(0);
+ const monitorId = job.data._id;
+ const maintenanceWindowActive = await this.isInMaintenanceWindow(monitorId);
+ // If a maintenance window is active, we're done
+
+ if (maintenanceWindowActive) {
+ await job.updateProgress(100);
+ this.logger.info({
+ message: `Monitor ${monitorId} is in maintenance window`,
+ service: SERVICE_NAME,
+ method: "createWorker",
+ });
+ return false;
+ }
+
+ // Get the current status
+ await job.updateProgress(30);
+ const networkResponse = await this.networkService.getStatus(job);
+ if (
+ job.data.type === "distributed_http" ||
+ job.data.type === "distributed_test"
+ ) {
+ await job.updateProgress(100);
+ return true;
+ }
+
+ // If the network response is not found, we're done
+ if (!networkResponse) {
+ await job.updateProgress(100);
+ return false;
+ }
+
+ // Handle status change
+ await job.updateProgress(60);
+ const { monitor, statusChanged, prevStatus } =
+ await this.statusService.updateStatus(networkResponse);
+ // Handle notifications
+ await job.updateProgress(80);
+ this.notificationService
+ .handleNotifications({
+ ...networkResponse,
+ monitor,
+ prevStatus,
+ statusChanged,
+ })
+ .catch((error) => {
+ this.logger.error({
+ message: error.message,
+ service: SERVICE_NAME,
+ method: "createJobHandler",
+ details: `Error sending notifications for job ${job.id}: ${error.message}`,
+ stack: error.stack,
+ });
+ });
+ await job.updateProgress(100);
+ return true;
+ } catch (error) {
+ await job.updateProgress(100);
+ throw error;
+ }
+ };
+ }
+ async flushRedis() {
+ try {
+ const connection = this.redisService.getNewConnection();
+ const flushResult = await connection.flushall();
+ return flushResult;
+ } catch (error) {
+ this.logger.warn({
+ message: error.message,
+ service: SERVICE_NAME,
+ method: "flushRedis",
+ });
+ return false;
+ }
+ }
+}
+
+export default JobQueueHelper;
diff --git a/server/service/bufferService.js b/server/service/bufferService.js
index c98dde759..255c3db9f 100755
--- a/server/service/bufferService.js
+++ b/server/service/bufferService.js
@@ -80,7 +80,16 @@ class BufferService {
});
continue;
}
- await operation(buffer);
+ try {
+ await operation(buffer);
+ } catch (error) {
+ this.logger.error({
+ message: error.message,
+ service: this.SERVICE_NAME,
+ method: "flushBuffers",
+ stack: error.stack,
+ });
+ }
this.buffers[bufferName] = [];
}
this.logger.info({
diff --git a/server/service/emailService.js b/server/service/emailService.js
index 94d19f0f4..035cc6505 100755
--- a/server/service/emailService.js
+++ b/server/service/emailService.js
@@ -11,6 +11,7 @@ const SERVICE_NAME = "EmailService";
*/
class EmailService {
static SERVICE_NAME = SERVICE_NAME;
+
/**
* Constructs an instance of the EmailService, initializing template loaders and the email transporter.
* @param {Object} settingsService - The settings service to get email configuration.
@@ -88,17 +89,25 @@ class EmailService {
* @param {string} subject - The subject of the email.
* @returns {Promise} A promise that resolves to the messageId of the sent email.
*/
- buildAndSendEmail = async (template, context, to, subject) => {
+ buildAndSendEmail = async (template, context, to, subject, transportConfig) => {
// TODO - Consider an update transporter method so this only needs to be recreated when smtp settings change
+ let config;
+ if (typeof transportConfig !== "undefined") {
+ config = transportConfig;
+ } else {
+ config = await this.settingsService.getDBSettings();
+ }
+
const {
systemEmailHost,
systemEmailPort,
systemEmailUser,
systemEmailAddress,
systemEmailPassword,
- } = await this.settingsService.getDBSettings();
+ systemEmailConnectionHost,
+ } = config;
- const emailConfig = {
+ const baseEmailConfig = {
host: systemEmailHost,
port: systemEmailPort,
secure: true,
@@ -109,6 +118,22 @@ class EmailService {
connectionTimeout: 5000,
};
+ const isSmtps = Number(systemEmailPort) === 465;
+
+ const emailConfig = !isSmtps
+ ? {
+ ...baseEmailConfig,
+ name: systemEmailConnectionHost || "localhost",
+ secure: false,
+ pool: true,
+ tls: { rejectUnauthorized: false },
+ }
+ : baseEmailConfig;
+
+ if (!isSmtps) {
+ delete emailConfig.auth;
+ }
+
this.transporter = this.nodemailer.createTransport(emailConfig);
const buildHtml = async (template, context) => {
@@ -149,4 +174,5 @@ class EmailService {
return info?.messageId;
};
}
+
export default EmailService;
diff --git a/server/service/jobQueue.js b/server/service/jobQueue.js
deleted file mode 100755
index 8f16f910d..000000000
--- a/server/service/jobQueue.js
+++ /dev/null
@@ -1,814 +0,0 @@
-const QUEUE_NAMES = ["uptime", "pagespeed", "hardware", "distributed"];
-const SERVICE_NAME = "JobQueue";
-const JOBS_PER_WORKER = 5;
-const HEALTH_CHECK_INTERVAL = 10 * 60 * 1000; // 10 minutes
-const QUEUE_LOOKUP = {
- hardware: "hardware",
- http: "uptime",
- ping: "uptime",
- port: "uptime",
- docker: "uptime",
- pagespeed: "pagespeed",
- distributed_http: "distributed",
-};
-const getSchedulerId = (monitor) => `scheduler:${monitor.type}:${monitor._id}`;
-
-class NewJobQueue {
- static SERVICE_NAME = SERVICE_NAME;
-
- constructor({
- db,
- statusService,
- networkService,
- notificationService,
- settingsService,
- stringService,
- logger,
- Queue,
- Worker,
- redisService,
- }) {
- this.connection = redisService.getConnection();
- this.queues = {};
- this.workers = {};
- this.lastJobProcessedTime = {};
-
- this.db = db;
- this.networkService = networkService;
- this.statusService = statusService;
- this.notificationService = notificationService;
- this.settingsService = settingsService;
- this.logger = logger;
- this.Worker = Worker;
- this.stringService = stringService;
-
- QUEUE_NAMES.forEach((name) => {
- const q = new Queue(name, { connection: this.connection });
- this.lastJobProcessedTime[q.name] = Date.now();
- q.on("error", (error) => {
- this.logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "queue:error",
- stack: error.stack,
- });
- });
- this.queues[name] = q;
-
- this.workers[name] = [];
- });
-
- this.healthCheckInterval = setInterval(async () => {
- try {
- const health = await this.checkQueueHealth();
- if (health.stuck === true) {
- this.logger.error({
- message: `Queue is stuck: ${health.stuckQueues.join(", ")}`,
- service: SERVICE_NAME,
- method: "healthCheckInterval",
- });
- await this.flushQueue();
- }
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "periodicHealthCheck",
- stack: error.stack,
- });
- }
- }, HEALTH_CHECK_INTERVAL);
- }
-
- /**
- * Initializes job queues by adding jobs for all active monitors
- * @async
- * @function initJobQueue
- * @description Retrieves all monitors from the database and adds jobs for active ones to their respective queues
- * @throws {Error} If there's an error retrieving monitors or adding jobs
- * @returns {Promise}
- */
- async initJobQueue() {
- await this.connection.flushall();
- const monitors = await this.db.getAllMonitors();
- await Promise.all(
- monitors
- .filter((monitor) => monitor.isActive)
- .map(async (monitor) => {
- try {
- await this.addJob(monitor._id, monitor);
- } catch (error) {
- this.logger.error({
- message: `Failed to add job for monitor ${monitor._id}:`,
- service: SERVICE_NAME,
- method: "initJobQueue",
- stack: error.stack,
- });
- }
- })
- );
- }
-
- /**
- * Checks if a monitor is currently in a maintenance window
- * @async
- * @param {string} monitorId - The ID of the monitor to check
- * @returns {Promise} Returns true if the monitor is in an active maintenance window, false otherwise
- * @throws {Error} If there's an error retrieving maintenance windows from the database
- * @description
- * Retrieves all maintenance windows for a monitor and checks if any are currently active.
- * A maintenance window is considered active if:
- * 1. The window is marked as active AND
- * 2. Either:
- * - Current time falls between start and end times
- * - For repeating windows: Current time falls between any repeated interval
- */
- async isInMaintenanceWindow(monitorId) {
- const maintenanceWindows = await this.db.getMaintenanceWindowsByMonitorId(monitorId);
- // Check for active maintenance window:
- const maintenanceWindowIsActive = maintenanceWindows.reduce((acc, window) => {
- if (window.active) {
- const start = new Date(window.start);
- const end = new Date(window.end);
- const now = new Date();
- const repeatInterval = window.repeat || 0;
-
- // If start is < now and end > now, we're in maintenance
- if (start <= now && end >= now) return true;
-
- // If maintenance window was set in the past with a repeat,
- // we need to advance start and end to see if we are in range
-
- while (start < now && repeatInterval !== 0) {
- start.setTime(start.getTime() + repeatInterval);
- end.setTime(end.getTime() + repeatInterval);
- if (start <= now && end >= now) {
- return true;
- }
- }
- return false;
- }
- return acc;
- }, false);
- return maintenanceWindowIsActive;
- }
-
- /**
- * Creates a job processing handler for monitor checks
- * @function createJobHandler
- * @returns {Function} An async function that processes monitor check jobs
- * @description
- * Creates and returns a job handler that:
- * 1. Checks if monitor is in maintenance window
- * 2. If not in maintenance, performs network status check
- * 3. Updates monitor status in database
- * 4. Triggers notifications if status changed
- *
- * @param {Object} job - The job to process
- * @param {Object} job.data - The monitor data
- * @param {string} job.data._id - Monitor ID
- * @param {string} job.id - Job ID
- *
- * @throws {Error} Logs errors but doesn't throw them to prevent job failure
- * @returns {Promise} Resolves when job processing is complete
- */
- createJobHandler() {
- return async (job) => {
- try {
- // Update the last job processed time for this queue
- this.lastJobProcessedTime[job.queue.name] = Date.now();
- // Get all maintenance windows for this monitor
- await job.updateProgress(0);
- const monitorId = job.data._id;
- const maintenanceWindowActive = await this.isInMaintenanceWindow(monitorId);
- // If a maintenance window is active, we're done
-
- if (maintenanceWindowActive) {
- await job.updateProgress(100);
- this.logger.info({
- message: `Monitor ${monitorId} is in maintenance window`,
- service: SERVICE_NAME,
- method: "createWorker",
- });
- return false;
- }
-
- // Get the current status
- await job.updateProgress(30);
- const networkResponse = await this.networkService.getStatus(job);
- if (
- job.data.type === "distributed_http" ||
- job.data.type === "distributed_test"
- ) {
- return;
- }
- // Handle status change
- await job.updateProgress(60);
- const { monitor, statusChanged, prevStatus } =
- await this.statusService.updateStatus(networkResponse);
- // Handle notifications
- await job.updateProgress(80);
- this.notificationService
- .handleNotifications({
- ...networkResponse,
- monitor,
- prevStatus,
- statusChanged,
- })
- .catch((error) => {
- this.logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "createJobHandler",
- details: `Error sending notifications for job ${job.id}: ${error.message}`,
- stack: error.stack,
- });
- });
- await job.updateProgress(100);
- return true;
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: error.service ?? SERVICE_NAME,
- method: error.method ?? "createJobHandler",
- details: `Error processing job ${job.id}: ${error.message}`,
- stack: error.stack,
- });
- throw error;
- }
- };
- }
-
- /**
- * Creates a new worker for processing jobs in a queue
- * @param {Queue} queue - The BullMQ queue to create a worker for
- * @returns {Worker} A new BullMQ worker instance
- * @description
- * Creates and configures a new worker with:
- * - Queue-specific job handler
- * - Redis connection settings
- * - Default worker options
- * The worker processes jobs from the specified queue using the job handler
- * created by createJobHandler()
- *
- * @throws {Error} If worker creation fails or connection is invalid
- */
- createWorker(queue) {
- try {
- const worker = new this.Worker(queue.name, this.createJobHandler(), {
- connection: this.connection,
- concurrency: 5,
- stalledInterval: 10000,
- maxStalledCount: 1,
- lockDuration: 60000,
- });
-
- worker.on("failed", (job, err) => {
- this.logger.error({
- message: `Job ${job.id} failed: ${err.message}`,
- service: SERVICE_NAME,
- method: "worker:failed",
- stack: err.stack,
- jobData: job.data,
- });
- });
- worker.on("error", (job, err) => {
- this.logger.error({
- message: `Job ${job.id} error: ${err.message}`,
- service: SERVICE_NAME,
- method: "worker:error",
- stack: err.stack,
- jobData: job.data,
- });
- });
-
- worker.on("stalled", (jobId) => {
- this.logger.warn({
- message: `Job ${jobId} stalled`,
- service: SERVICE_NAME,
- method: "worker:stalled",
- });
- });
-
- return worker;
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "createWorker",
- stack: error.stack,
- });
- error.service = SERVICE_NAME;
- error.method = "createWorker";
- throw error;
- }
- }
-
- /**
- * Gets stats related to the workers
- * This is used for scaling workers right now
- * In the future we will likely want to scale based on server performance metrics
- * CPU Usage & memory usage, if too high, scale down workers.
- * When to scale up? If jobs are taking too long to complete?
- * @async
- * @returns {Promise} - Returns the worker stats
- */
- async getWorkerStats(queue) {
- try {
- const jobs = await queue.getRepeatableJobs();
- const load = jobs.length / this.workers[queue.name].length;
- return { jobs, load };
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "getWorkerStats") : null;
- throw error;
- }
- }
-
- /**
- * Scales workers up or down based on queue load
- * @async
- * @param {Object} workerStats - Statistics about current worker load
- * @param {number} workerStats.load - Current load per worker
- * @param {Array} workerStats.jobs - Array of current jobs
- * @param {Queue} queue - The BullMQ queue to scale workers for
- * @returns {Promise} True if scaling occurred, false if no scaling was needed
- * @throws {Error} If no workers array exists for the queue
- * @description
- * Scales workers based on these rules:
- * - Maintains minimum of 5 workers
- * - Adds workers if load exceeds JOBS_PER_WORKER
- * - Removes workers if load is below JOBS_PER_WORKER
- * - Creates initial workers if none exist
- * Worker scaling is calculated based on excess jobs or excess capacity
- */
- async scaleWorkers(workerStats, queue) {
- const workers = this.workers[queue.name];
- if (workers === undefined) {
- throw new Error(`No workers found for ${queue.name}`);
- }
-
- if (workers.length === 0) {
- // There are no workers, need to add one
- for (let i = 0; i < 5; i++) {
- const worker = this.createWorker(queue);
- workers.push(worker);
- }
- return true;
- }
- if (workerStats.load > JOBS_PER_WORKER) {
- // Find out how many more jobs we have than current workers can handle
- const excessJobs = workerStats.jobs.length - workers.length * JOBS_PER_WORKER;
- // Divide by jobs/worker to find out how many workers to add
- const workersToAdd = Math.ceil(excessJobs / JOBS_PER_WORKER);
- for (let i = 0; i < workersToAdd; i++) {
- const worker = this.createWorker(queue);
- workers.push(worker);
- }
- return true;
- }
-
- if (workerStats.load < JOBS_PER_WORKER) {
- // Find out how much excess capacity we have
- const workerCapacity = workers.length * JOBS_PER_WORKER;
- const excessCapacity = workerCapacity - workerStats.jobs.length;
- // Calculate how many workers to remove
- let workersToRemove = Math.floor(excessCapacity / JOBS_PER_WORKER); // Make sure there are always at least 5
- while (workersToRemove > 0 && workers.length > 5) {
- const worker = workers.pop();
- workersToRemove--;
- await worker.close().catch((error) => {
- // Catch the error instead of throwing it
- this.logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "scaleWorkers",
- stack: error.stack,
- });
- });
- }
- return true;
- }
- return false;
- }
-
- /**
- * Gets all jobs in the queue.
- *
- * @async
- * @returns {Promise>}
- * @throws {Error} - Throws error if getting jobs fails
- */
- async getJobs(queue) {
- try {
- const jobs = await queue.getRepeatableJobs();
- return jobs;
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "getJobs") : null;
- throw error;
- }
- }
-
- /**
- * Retrieves detailed statistics about jobs and workers for all queues
- * @async
- * @returns {Promise