diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 69f98c0e4..2e10be48c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Project Info -First of all, I want to thank everyone who have wrote issues or shared pull requests for Uptime Kuma. +First of all, I want to thank everyone who has submitted issues or shared pull requests for Uptime Kuma. I never thought the GitHub community would be so nice! Because of this, I also never thought that other people would actually read and edit my code. Parts of the code are not very well-structured or commented, sorry about that. @@ -9,7 +9,7 @@ The project was created with `vite.js` and is written in `vue3`. Our backend lives in the `server`-directory and mostly communicates via websockets. Both frontend and backend share the same `package.json`. -For production, the frontend is build into `dist`-directory and the server (`express.js`) exposes the `dist` directory as the root of the endpoint. +For production, the frontend is built into the `dist`-directory and the server (`express.js`) exposes the `dist` directory as the root of the endpoint. For development, we run vite in development mode on another port. ## Directories @@ -28,7 +28,7 @@ For development, we run vite in development mode on another port. ## Can I create a pull request for Uptime Kuma? Yes or no, it depends on what you will try to do. -Both your and our maintainers time is precious, and we don't want to waste both time. +Both yours and our maintainers' time is precious, and we don't want to waste either. If you have any questions about any process/.. is not clear, you are likely not alone => please ask them ^^ @@ -49,11 +49,11 @@ Different guidelines exist for different types of pull requests (PRs):

If you come across a bug and think you can solve, we appreciate your work. - Please make sure that you follow by these rules: + Please make sure that you follow these rules: - keep the PR as small as possible, fix only one thing at a time => keeping it reviewable - - test that your code does what you came it does. + - test that your code does what you claim it does. - Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area. + Because maintainer time is precious, junior maintainers may merge uncontroversial PRs in this area.

-
translations / internationalisation (i18n) @@ -68,7 +68,7 @@ Different guidelines exist for different types of pull requests (PRs): - language keys need to be **added to `en.json`** to be visible in weblate. If this has not happened, a PR is appreciated. - **Adding a new language** requires a new file see [these instructions](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md) - Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area. + Because maintainer time is precious, junior maintainers may merge uncontroversial PRs in this area.

-
new notification providers @@ -102,7 +102,7 @@ Different guidelines exist for different types of pull requests (PRs): Therefore, making sure that they work is also really important. Because testing notification providers is quite time intensive, we mostly offload this onto the person contributing a notification provider. - To make shure you have tested the notification provider, please include screenshots of the following events in the pull-request description: + To make sure you have tested the notification provider, please include screenshots of the following events in the pull-request description: - `UP`/`DOWN` - Certificate Expiry via https://expired.badssl.com/ - Testing (the test button on the notification provider setup page) @@ -117,7 +117,7 @@ Different guidelines exist for different types of pull requests (PRs): | Testing | paste-image-here | paste-image-here | ``` - Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area. + Because maintainer time is precious, junior maintainers may merge uncontroversial PRs in this area.

-
new monitoring types @@ -138,14 +138,14 @@ Different guidelines exist for different types of pull requests (PRs): - - Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area. + Because maintainer time is precious, junior maintainers may merge uncontroversial PRs in this area.

-
new features/ major changes / breaking bugfixes

be sure to **create an empty draft pull request or open an issue, so we can have a discussion first**. - This is especially important for a large pull request or you don't know if it will be merged or not. + This is especially important for a large pull request or when you don't know if it will be merged or not. Because of the large impact of this work, only senior maintainers may merge PRs in this area.

@@ -201,7 +201,7 @@ The rationale behind this is that we can align the direction and scope of the fe ## Project Styles -I personally do not like something that requires so many configurations before you can finally start the app. +I personally do not like something that requires a lot of configuration before you can finally start the app. The goal is to make the Uptime Kuma installation as easy as installing a mobile app. - Easy to install for non-Docker users @@ -260,7 +260,7 @@ Port `3000` and port `3001` will be used. npm run dev ``` -But sometimes, you would like to restart the server, but not the frontend, you can run these commands in two terminals: +But sometimes you may want to restart the server without restarting the frontend. In that case, you can run these commands in two terminals: ```bash npm run start-frontend-dev @@ -409,7 +409,7 @@ https://github.com/louislam/uptime-kuma/issues?q=sort%3Aupdated-desc ### What is a maintainer and what are their roles? -This project has multiple maintainers which specialise in different areas. +This project has multiple maintainers who specialise in different areas. Currently, there are 3 maintainers: | Person | Role | Main Area | diff --git a/db/knex_migrations/2024-10-1315-rabbitmq-monitor.js b/db/knex_migrations/2024-10-1315-rabbitmq-monitor.js new file mode 100644 index 000000000..6a17f3366 --- /dev/null +++ b/db/knex_migrations/2024-10-1315-rabbitmq-monitor.js @@ -0,0 +1,17 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.text("rabbitmq_nodes"); + table.string("rabbitmq_username"); + table.string("rabbitmq_password"); + }); + +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("rabbitmq_nodes"); + table.dropColumn("rabbitmq_username"); + table.dropColumn("rabbitmq_password"); + }); + +}; diff --git a/package-lock.json b/package-lock.json index f1442bab9..24f63d036 100644 --- a/package-lock.json +++ b/package-lock.json @@ -92,6 +92,8 @@ "@fortawesome/vue-fontawesome": "~3.0.0-5", "@playwright/test": "~1.39.0", "@popperjs/core": "~2.10.2", + "@testcontainers/hivemq": "^10.13.1", + "@testcontainers/rabbitmq": "^10.13.2", "@types/bootstrap": "~5.1.9", "@types/node": "^20.8.6", "@typescript-eslint/eslint-plugin": "^6.7.5", @@ -127,6 +129,7 @@ "stylelint-config-standard": "~25.0.0", "terser": "~5.15.0", "test": "~3.3.0", + "testcontainers": "^10.13.1", "typescript": "~4.4.4", "v-pagination-3": "~0.1.7", "vite": "~5.2.8", @@ -1241,13 +1244,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/highlight": "^7.25.7", "picocolors": "^1.0.0" }, "engines": { @@ -1306,34 +1309,21 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", - "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", + "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.6", + "@babel/types": "^7.25.7", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", @@ -1523,9 +1513,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", "dev": true, "license": "MIT", "engines": { @@ -1533,9 +1523,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", "dev": true, "license": "MIT", "engines": { @@ -1567,13 +1557,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.7", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -1583,13 +1573,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", + "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.6" + "@babel/types": "^7.25.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -1726,40 +1716,33 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/runtime/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==", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", + "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", - "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", + "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.6", - "@babel/parser": "^7.25.6", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1768,20 +1751,27 @@ } }, "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", + "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@balena/dockerignore": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", + "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@csstools/css-parser-algorithms": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz", @@ -4173,6 +4163,25 @@ "integrity": "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==", "license": "MIT" }, + "node_modules/@testcontainers/hivemq": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/@testcontainers/hivemq/-/hivemq-10.13.1.tgz", + "integrity": "sha512-LvXx+l6XEOckAX9GwD4+WQgo/Y5jITDAfyultLd9jMl/OlzBU6sLfHl/vYFjG17xZObiXznGDrKVQYIp70wxZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "testcontainers": "^10.13.1" + } + }, + "node_modules/@testcontainers/rabbitmq": { + "version": "10.13.2", + "resolved": "https://registry.npmjs.org/@testcontainers/rabbitmq/-/rabbitmq-10.13.2.tgz", + "integrity": "sha512-npBKBnq3c6hETmxGZ/gVMke9cc1J/pcftNW9S3WidL48hxFBIPjYNM9FdTfWuoNER/8kuf4xJ8yCuJEYGH3ZAg==", + "dev": true, + "dependencies": { + "testcontainers": "^10.13.2" + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -4260,11 +4269,33 @@ "version": "2.8.17", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", - "license": "MIT", "dependencies": { "@types/node": "*" } }, + "node_modules/@types/docker-modem": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", + "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/dockerode": { + "version": "3.3.31", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.31.tgz", + "integrity": "sha512-42R9eoVqJDSvVspV89g7RwRqfNExgievLNWoHkg7NoWIqAmavIbgQBb4oc0qRtHkxE+I3Xxvqv7qVXFABKPBTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -4459,6 +4490,16 @@ "@types/node": "^18.11.18" } }, + "node_modules/@types/ssh2-streams": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz", + "integrity": "sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/ssh2/node_modules/@types/node": { "version": "18.19.54", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz", @@ -5270,6 +5311,185 @@ "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", "license": "ISC" }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/archiver-utils/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/are-docs-informative": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", @@ -5399,6 +5619,20 @@ "node": ">=8" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -5438,6 +5672,13 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/babel-plugin-add-module-exports": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz", @@ -5495,6 +5736,58 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/bare-events": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.0.tgz", + "integrity": "sha512-pVRWciewGUeCyKEuRxwv06M079r+fRjAQjBEK2P6OYGrO43O+Z0LrPZZEjlc4mB6C2RpZ9AxJ1s7NLEtOHO6eA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "b4a": "^1.6.6", + "streamx": "^2.20.0" + } + }, "node_modules/barse": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/barse/-/barse-0.4.3.tgz", @@ -5832,6 +6125,16 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -5894,6 +6197,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -6401,6 +6714,65 @@ "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", "license": "MIT" }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -6745,6 +7117,75 @@ "node": ">=10.0.0" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/crc32-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/croner": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/croner/-/croner-8.1.2.tgz", @@ -7241,27 +7682,108 @@ "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", "dev": true, - "license": "MIT" + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/dns2/-/dns2-2.0.5.tgz", + "integrity": "sha512-dznYrQU+Txcz++klGLBY9YR3WLOGHTy2vAKAqF+yYw1KaKFm5f5Y4jbbFEvohJf8YtZ0J2SzZlZx2k6LV4zJqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/docker-compose": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/docker-modem": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.8.tgz", + "integrity": "sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^1.11.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/dockerode": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.5.tgz", + "integrity": "sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "docker-modem": "^3.0.0", + "tar-fs": "~2.0.1" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/dockerode/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/dockerode/node_modules/tar-fs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", + "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", "dev": true, "license": "MIT", "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" } }, - "node_modules/dns2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/dns2/-/dns2-2.0.5.tgz", - "integrity": "sha512-dznYrQU+Txcz++klGLBY9YR3WLOGHTy2vAKAqF+yYw1KaKFm5f5Y4jbbFEvohJf8YtZ0J2SzZlZx2k6LV4zJqQ==", + "node_modules/dockerode/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } }, "node_modules/doctrine": { "version": "3.0.0", @@ -8318,6 +8840,12 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "dev": true, "license": "MIT" }, @@ -8700,6 +9228,13 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -9053,6 +9588,19 @@ "node": ">=8.0.0" } }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-port-please": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.1.2.tgz", @@ -10617,6 +11165,52 @@ "dev": true, "license": "MIT" }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -11275,6 +11869,13 @@ "node": ">=10" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, "node_modules/mongodb": { "version": "4.17.2", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.2.tgz", @@ -12140,6 +12741,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -12819,6 +13427,35 @@ "node": ">=8" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/properties-reader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", + "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/properties?sponsor=1" + } + }, "node_modules/protobufjs": { "version": "7.2.6", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", @@ -13074,6 +13711,13 @@ ], "license": "MIT" }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true, + "license": "MIT" + }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -13265,6 +13909,39 @@ "node": ">= 6" } }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -13412,6 +14089,13 @@ "@redis/time-series": "1.0.4" } }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", @@ -13547,8 +14231,7 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "license": "MIT", - "optional": true, + "devOptional": true, "engines": { "node": ">= 4" } @@ -14315,6 +14998,13 @@ "node": ">=0.10.0" } }, + "node_modules/split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", + "dev": true, + "license": "ISC" + }, "node_modules/split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -14339,6 +15029,28 @@ "node": ">= 0.6" } }, + "node_modules/ssh-remote-port-forward": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz", + "integrity": "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ssh2": "^0.5.48", + "ssh2": "^1.4.0" + } + }, + "node_modules/ssh-remote-port-forward/node_modules/@types/ssh2": { + "version": "0.5.52", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.52.tgz", + "integrity": "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ssh2-streams": "*" + } + }, "node_modules/ssh2": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz", @@ -14395,6 +15107,21 @@ "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", "license": "MIT" }, + "node_modules/streamx": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -14971,6 +15698,33 @@ "node": ">=10" } }, + "node_modules/tar-fs": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/tar/node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", @@ -15180,6 +15934,39 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/testcontainers": { + "version": "10.13.2", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.13.2.tgz", + "integrity": "sha512-LfEll+AG/1Ks3n4+IA5lpyBHLiYh/hSfI4+ERa6urwfQscbDU+M2iW1qPQrHQi+xJXQRYy4whyK1IEHdmxWa3Q==", + "dev": true, + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@types/dockerode": "^3.3.29", + "archiver": "^7.0.1", + "async-lock": "^1.4.1", + "byline": "^5.0.0", + "debug": "^4.3.5", + "docker-compose": "^0.24.8", + "dockerode": "^3.3.5", + "get-port": "^5.1.1", + "proper-lockfile": "^4.1.2", + "properties-reader": "^2.3.0", + "ssh-remote-port-forward": "^1.0.4", + "tar-fs": "^3.0.6", + "tmp": "^0.2.3", + "undici": "^5.28.4" + } + }, + "node_modules/text-decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.0.tgz", + "integrity": "sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -15204,6 +15991,16 @@ "node": ">=8" } }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -16778,6 +17575,19 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -16827,6 +17637,63 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zip-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } } } } diff --git a/package.json b/package.json index 0d51984c7..5186cafcf 100644 --- a/package.json +++ b/package.json @@ -154,6 +154,8 @@ "@fortawesome/vue-fontawesome": "~3.0.0-5", "@playwright/test": "~1.39.0", "@popperjs/core": "~2.10.2", + "@testcontainers/hivemq": "^10.13.1", + "@testcontainers/rabbitmq": "^10.13.2", "@types/bootstrap": "~5.1.9", "@types/node": "^20.8.6", "@typescript-eslint/eslint-plugin": "^6.7.5", @@ -189,6 +191,7 @@ "stylelint-config-standard": "~25.0.0", "terser": "~5.15.0", "test": "~3.3.0", + "testcontainers": "^10.13.1", "typescript": "~4.4.4", "v-pagination-3": "~0.1.7", "vite": "~5.2.8", diff --git a/server/model/monitor.js b/server/model/monitor.js index 5b7e5871a..9a30a6689 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -153,6 +153,7 @@ class Monitor extends BeanModel { snmpOid: this.snmpOid, jsonPathOperator: this.jsonPathOperator, snmpVersion: this.snmpVersion, + rabbitmqNodes: JSON.parse(this.rabbitmqNodes), conditions: JSON.parse(this.conditions), }; @@ -183,6 +184,8 @@ class Monitor extends BeanModel { tlsCert: this.tlsCert, tlsKey: this.tlsKey, kafkaProducerSaslOptions: JSON.parse(this.kafkaProducerSaslOptions), + rabbitmqUsername: this.rabbitmqUsername, + rabbitmqPassword: this.rabbitmqPassword, }; } @@ -1508,10 +1511,8 @@ class Monitor extends BeanModel { return await R.getAll(` SELECT monitor_notification.monitor_id, monitor_notification.notification_id FROM monitor_notification - WHERE monitor_notification.monitor_id IN (?) - `, [ - monitorIDs, - ]); + WHERE monitor_notification.monitor_id IN (${monitorIDs.map((_) => "?").join(",")}) + `, monitorIDs); } /** @@ -1521,13 +1522,11 @@ class Monitor extends BeanModel { */ static async getMonitorTag(monitorIDs) { return await R.getAll(` - SELECT monitor_tag.monitor_id, tag.name, tag.color + SELECT monitor_tag.monitor_id, monitor_tag.tag_id, tag.name, tag.color FROM monitor_tag JOIN tag ON monitor_tag.tag_id = tag.id - WHERE monitor_tag.monitor_id IN (?) - `, [ - monitorIDs, - ]); + WHERE monitor_tag.monitor_id IN (${monitorIDs.map((_) => "?").join(",")}) + `, monitorIDs); } /** @@ -1567,6 +1566,7 @@ class Monitor extends BeanModel { tagsMap.set(row.monitor_id, []); } tagsMap.get(row.monitor_id).push({ + tag_id: row.tag_id, name: row.name, color: row.color }); diff --git a/server/monitor-types/rabbitmq.js b/server/monitor-types/rabbitmq.js new file mode 100644 index 000000000..165a0ed91 --- /dev/null +++ b/server/monitor-types/rabbitmq.js @@ -0,0 +1,67 @@ +const { MonitorType } = require("./monitor-type"); +const { log, UP, DOWN } = require("../../src/util"); +const { axiosAbortSignal } = require("../util-server"); +const axios = require("axios"); + +class RabbitMqMonitorType extends MonitorType { + name = "rabbitmq"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, server) { + let baseUrls = []; + try { + baseUrls = JSON.parse(monitor.rabbitmqNodes); + } catch (error) { + throw new Error("Invalid RabbitMQ Nodes"); + } + + heartbeat.status = DOWN; + for (let baseUrl of baseUrls) { + try { + // Without a trailing slash, path in baseUrl will be removed. https://example.com/api -> https://example.com + if ( !baseUrl.endsWith("/") ) { + baseUrl += "/"; + } + const options = { + // Do not start with slash, it will strip the trailing slash from baseUrl + url: new URL("api/health/checks/alarms/", baseUrl).href, + method: "get", + timeout: monitor.timeout * 1000, + headers: { + "Accept": "application/json", + "Authorization": "Basic " + Buffer.from(`${monitor.rabbitmqUsername || ""}:${monitor.rabbitmqPassword || ""}`).toString("base64"), + }, + signal: axiosAbortSignal((monitor.timeout + 10) * 1000), + // Capture reason for 503 status + validateStatus: (status) => status === 200 || status === 503, + }; + log.debug("monitor", `[${monitor.name}] Axios Request: ${JSON.stringify(options)}`); + const res = await axios.request(options); + log.debug("monitor", `[${monitor.name}] Axios Response: status=${res.status} body=${JSON.stringify(res.data)}`); + if (res.status === 200) { + heartbeat.status = UP; + heartbeat.msg = "OK"; + break; + } else if (res.status === 503) { + heartbeat.msg = res.data.reason; + } else { + heartbeat.msg = `${res.status} - ${res.statusText}`; + } + } catch (error) { + if (axios.isCancel(error)) { + heartbeat.msg = "Request timed out"; + log.debug("monitor", `[${monitor.name}] Request timed out`); + } else { + log.debug("monitor", `[${monitor.name}] Axios Error: ${JSON.stringify(error.message)}`); + heartbeat.msg = error.message; + } + } + } + } +} + +module.exports = { + RabbitMqMonitorType, +}; diff --git a/server/notification-providers/46elks.js b/server/notification-providers/46elks.js new file mode 100644 index 000000000..4b15e9fad --- /dev/null +++ b/server/notification-providers/46elks.js @@ -0,0 +1,35 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Elks extends NotificationProvider { + name = "Elks"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://api.46elks.com/a1/sms"; + + try { + let data = new URLSearchParams(); + data.append("from", notification.elksFromNumber); + data.append("to", notification.elksToNumber ); + data.append("message", msg); + + const config = { + headers: { + "Authorization": "Basic " + Buffer.from(`${notification.elksUsername}:${notification.elksAuthToken}`).toString("base64") + } + }; + + await axios.post(url, data, config); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Elks; diff --git a/server/notification-providers/send-grid.js b/server/notification-providers/send-grid.js new file mode 100644 index 000000000..3489f6385 --- /dev/null +++ b/server/notification-providers/send-grid.js @@ -0,0 +1,65 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class SendGrid extends NotificationProvider { + name = "SendGrid"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${notification.sendgridApiKey}`, + }, + }; + + let personalizations = { + to: [{ email: notification.sendgridToEmail }], + }; + + // Add CC recipients if provided + if (notification.sendgridCcEmail) { + personalizations.cc = notification.sendgridCcEmail + .split(",") + .map((email) => ({ email: email.trim() })); + } + + // Add BCC recipients if provided + if (notification.sendgridBccEmail) { + personalizations.bcc = notification.sendgridBccEmail + .split(",") + .map((email) => ({ email: email.trim() })); + } + + let data = { + personalizations: [ personalizations ], + from: { email: notification.sendgridFromEmail.trim() }, + subject: + notification.sendgridSubject || + "Notification from Your Uptime Kuma", + content: [ + { + type: "text/plain", + value: msg, + }, + ], + }; + + await axios.post( + "https://api.sendgrid.com/v3/mail/send", + data, + config + ); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SendGrid; diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index f28a643e0..209c7c0c6 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -32,7 +32,7 @@ class Slack extends NotificationProvider { * @param {object} monitorJSON The monitor config * @returns {Array} The relevant action objects */ - static buildActions(baseURL, monitorJSON) { + buildActions(baseURL, monitorJSON) { const actions = []; if (baseURL) { @@ -73,7 +73,7 @@ class Slack extends NotificationProvider { * @param {string} msg The message body * @returns {Array} The rich content blocks for the Slack message */ - static buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg) { + buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg) { //create an array to dynamically add blocks const blocks = []; @@ -150,7 +150,7 @@ class Slack extends NotificationProvider { data.attachments.push( { "color": (heartbeatJSON["status"] === UP) ? "#2eb886" : "#e01e5a", - "blocks": Slack.buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg), + "blocks": this.buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg), } ); } else { diff --git a/server/notification-providers/techulus-push.js b/server/notification-providers/techulus-push.js index 230897f3c..bf688b194 100644 --- a/server/notification-providers/techulus-push.js +++ b/server/notification-providers/techulus-push.js @@ -10,11 +10,22 @@ class TechulusPush extends NotificationProvider { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { const okMsg = "Sent Successfully."; + let data = { + "title": notification?.pushTitle?.length ? notification.pushTitle : "Uptime-Kuma", + "body": msg, + "timeSensitive": notification.pushTimeSensitive ?? true, + }; + + if (notification.pushChannel) { + data.channel = notification.pushChannel; + } + + if (notification.pushSound) { + data.sound = notification.pushSound; + } + try { - await axios.post(`https://push.techulus.com/api/v1/notify/${notification.pushAPIKey}`, { - "title": "Uptime-Kuma", - "body": msg, - }); + await axios.post(`https://push.techulus.com/api/v1/notify/${notification.pushAPIKey}`, data); return okMsg; } catch (error) { this.throwGeneralAxiosError(error); diff --git a/server/notification.js b/server/notification.js index 60eae4d73..e7977eb4a 100644 --- a/server/notification.js +++ b/server/notification.js @@ -11,6 +11,7 @@ const CallMeBot = require("./notification-providers/call-me-bot"); const SMSC = require("./notification-providers/smsc"); const DingDing = require("./notification-providers/dingding"); const Discord = require("./notification-providers/discord"); +const Elks = require("./notification-providers/46elks"); const Feishu = require("./notification-providers/feishu"); const FreeMobile = require("./notification-providers/freemobile"); const GoogleChat = require("./notification-providers/google-chat"); @@ -67,6 +68,7 @@ const GtxMessaging = require("./notification-providers/gtx-messaging"); const Cellsynt = require("./notification-providers/cellsynt"); const Onesender = require("./notification-providers/onesender"); const Wpush = require("./notification-providers/wpush"); +const SendGrid = require("./notification-providers/send-grid"); class Notification { @@ -95,6 +97,7 @@ class Notification { new SMSC(), new DingDing(), new Discord(), + new Elks(), new Feishu(), new FreeMobile(), new GoogleChat(), @@ -151,6 +154,7 @@ class Notification { new GtxMessaging(), new Cellsynt(), new Wpush(), + new SendGrid() ]; for (let item of list) { if (! item.name) { diff --git a/server/server.js b/server/server.js index db58ae829..c88daca88 100644 --- a/server/server.js +++ b/server/server.js @@ -718,6 +718,8 @@ let needSetup = false; monitor.conditions = JSON.stringify(monitor.conditions); + monitor.rabbitmqNodes = JSON.stringify(monitor.rabbitmqNodes); + bean.import(monitor); bean.user_id = socket.userID; @@ -868,6 +870,9 @@ let needSetup = false; bean.snmpOid = monitor.snmpOid; bean.jsonPathOperator = monitor.jsonPathOperator; bean.timeout = monitor.timeout; + bean.rabbitmqNodes = JSON.stringify(monitor.rabbitmqNodes); + bean.rabbitmqUsername = monitor.rabbitmqUsername; + bean.rabbitmqPassword = monitor.rabbitmqPassword; bean.conditions = JSON.stringify(monitor.conditions); bean.validate(); diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 76bf42565..062f098d7 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -115,6 +115,7 @@ class UptimeKumaServer { UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType(); UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType(); UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType(); + UptimeKumaServer.monitorTypeList["rabbitmq"] = new RabbitMqMonitorType(); // Allow all CORS origins (polling) in development let cors = undefined; @@ -552,4 +553,5 @@ const { DnsMonitorType } = require("./monitor-types/dns"); const { MqttMonitorType } = require("./monitor-types/mqtt"); const { SNMPMonitorType } = require("./monitor-types/snmp"); const { MongodbMonitorType } = require("./monitor-types/mongodb"); +const { RabbitMqMonitorType } = require("./monitor-types/rabbitmq"); const Monitor = require("./model/monitor"); diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index 96a62cf61..429ca9f91 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -4,11 +4,17 @@
+ > +
+
this.maxBeat) { - let width = -(this.beatWidth + this.beatMargin * 2); + let width = -(this.beatWidth + this.beatHoverAreaPadding * 2); return { transition: "all ease-in-out 0.25s", @@ -137,12 +143,17 @@ export default { }, + beatHoverAreaStyle() { + return { + padding: this.beatHoverAreaPadding + "px", + "--hover-scale": this.hoverScale, + }; + }, + beatStyle() { return { width: this.beatWidth + "px", height: this.beatHeight + "px", - margin: this.beatMargin + "px", - "--hover-scale": this.hoverScale, }; }, @@ -152,7 +163,7 @@ export default { */ timeStyle() { return { - "margin-left": this.numPadding * (this.beatWidth + this.beatMargin * 2) + "px", + "margin-left": this.numPadding * (this.beatWidth + this.beatHoverAreaPadding * 2) + "px", }; }, @@ -219,20 +230,20 @@ export default { if (this.size !== "big") { this.beatWidth = 5; this.beatHeight = 16; - this.beatMargin = 2; + this.beatHoverAreaPadding = 2; } // Suddenly, have an idea how to handle it universally. // If the pixel * ratio != Integer, then it causes render issue, round it to solve it!! const actualWidth = this.beatWidth * window.devicePixelRatio; - const actualMargin = this.beatMargin * window.devicePixelRatio; + const actualHoverAreaPadding = this.beatHoverAreaPadding * window.devicePixelRatio; if (!Number.isInteger(actualWidth)) { this.beatWidth = Math.round(actualWidth) / window.devicePixelRatio; } - if (!Number.isInteger(actualMargin)) { - this.beatMargin = Math.round(actualMargin) / window.devicePixelRatio; + if (!Number.isInteger(actualHoverAreaPadding)) { + this.beatHoverAreaPadding = Math.round(actualHoverAreaPadding) / window.devicePixelRatio; } window.addEventListener("resize", this.resize); @@ -245,7 +256,7 @@ export default { */ resize() { if (this.$refs.wrap) { - this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2)); + this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatHoverAreaPadding * 2)); } }, @@ -273,31 +284,40 @@ export default { } .hp-bar-big { - .beat { + .beat-hover-area { display: inline-block; - background-color: $primary; - border-radius: $border-radius; - &.empty { - background-color: aliceblue; + &:not(.empty):hover { + transition: all ease-in-out 0.15s; + opacity: 0.8; + transform: scale(var(--hover-scale)); } - &.down { - background-color: $danger; - } + .beat { + background-color: $primary; + border-radius: $border-radius; - &.pending { - background-color: $warning; - } + /* + pointer-events needs to be changed because + tooltip momentarily disappears when crossing between .beat-hover-area and .beat + */ + pointer-events: none; - &.maintenance { - background-color: $maintenance; - } + &.empty { + background-color: aliceblue; + } - &:not(.empty):hover { - transition: all ease-in-out 0.15s; - opacity: 0.8; - transform: scale(var(--hover-scale)); + &.down { + background-color: $danger; + } + + &.pending { + background-color: $warning; + } + + &.maintenance { + background-color: $maintenance; + } } } } diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index 864cbf5f4..f6d728029 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -118,6 +118,7 @@ export default { "clicksendsms": "ClickSend SMS", "CallMeBot": "CallMeBot (WhatsApp, Telegram Call, Facebook Messanger)", "discord": "Discord", + "Elks": "46elks", "GoogleChat": "Google Chat (Google Workspace)", "gorush": "Gorush", "gotify": "Gotify", @@ -164,6 +165,7 @@ export default { "whapi": "WhatsApp (Whapi)", "gtxmessaging": "GtxMessaging", "Cellsynt": "Cellsynt", + "SendGrid": "SendGrid" }; // Put notifications here if it's not supported in most regions or its documentation is not in English diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue index c5d7d4500..bacddbf13 100644 --- a/src/components/PublicGroupList.vue +++ b/src/components/PublicGroupList.vue @@ -33,7 +33,7 @@ + +
@@ -549,7 +596,7 @@
-
+
@@ -1122,6 +1169,9 @@ const monitorDefaults = { kafkaProducerAllowAutoTopicCreation: false, gamedigGivenPortOnly: true, remote_browser: null, + rabbitmqNodes: [], + rabbitmqUsername: "", + rabbitmqPassword: "", conditions: [] }; @@ -1709,6 +1759,10 @@ message HealthCheckResponse { this.monitor.kafkaProducerBrokers.push(newBroker); }, + addRabbitmqNode(newNode) { + this.monitor.rabbitmqNodes.push(newNode); + }, + /** * Validate form input * @returns {boolean} Is the form input valid? @@ -1736,6 +1790,17 @@ message HealthCheckResponse { return false; } } + + if (this.monitor.type === "rabbitmq") { + if (this.monitor.rabbitmqNodes.length === 0) { + toast.error(this.$t("rabbitmqNodesRequired")); + return false; + } + if (!this.monitor.rabbitmqNodes.every(node => node.startsWith("http://") || node.startsWith("https://"))) { + toast.error(this.$t("rabbitmqNodesInvalid")); + return false; + } + } return true; }, diff --git a/test/backend-test/test-mqtt.js b/test/backend-test/test-mqtt.js new file mode 100644 index 000000000..450310298 --- /dev/null +++ b/test/backend-test/test-mqtt.js @@ -0,0 +1,102 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { HiveMQContainer } = require("@testcontainers/hivemq"); +const mqtt = require("mqtt"); +const { MqttMonitorType } = require("../../server/monitor-types/mqtt"); +const { UP, PENDING } = require("../../src/util"); + +/** + * Runs an MQTT test with the + * @param {string} mqttSuccessMessage the message that the monitor expects + * @param {null|"keyword"|"json-query"} mqttCheckType the type of check we perform + * @param {string} receivedMessage what message is recieved from the mqtt channel + * @returns {Promise} the heartbeat produced by the check + */ +async function testMqtt(mqttSuccessMessage, mqttCheckType, receivedMessage) { + const hiveMQContainer = await new HiveMQContainer().start(); + const connectionString = hiveMQContainer.getConnectionString(); + const mqttMonitorType = new MqttMonitorType(); + const monitor = { + jsonPath: "firstProp", // always return firstProp for the json-query monitor + hostname: connectionString.split(":", 2).join(":"), + mqttTopic: "test", + port: connectionString.split(":")[2], + mqttUsername: null, + mqttPassword: null, + interval: 20, // controls the timeout + mqttSuccessMessage: mqttSuccessMessage, // for keywords + expectedValue: mqttSuccessMessage, // for json-query + mqttCheckType: mqttCheckType, + }; + const heartbeat = { + msg: "", + status: PENDING, + }; + + const testMqttClient = mqtt.connect(hiveMQContainer.getConnectionString()); + testMqttClient.on("connect", () => { + testMqttClient.subscribe("test", (error) => { + if (!error) { + testMqttClient.publish("test", receivedMessage); + } + }); + }); + + try { + await mqttMonitorType.check(monitor, heartbeat, {}); + } finally { + testMqttClient.end(); + hiveMQContainer.stop(); + } + return heartbeat; +} + +describe("MqttMonitorType", { + concurrency: true, + skip: !!process.env.CI && (process.platform !== "linux" || process.arch !== "x64") +}, () => { + test("valid keywords (type=default)", async () => { + const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Topic: test; Message: -> KEYWORD <-"); + }); + + test("valid keywords (type=keyword)", async () => { + const heartbeat = await testMqtt("KEYWORD", "keyword", "-> KEYWORD <-"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Topic: test; Message: -> KEYWORD <-"); + }); + test("invalid keywords (type=default)", async () => { + await assert.rejects( + testMqtt("NOT_PRESENT", null, "-> KEYWORD <-"), + new Error("Message Mismatch - Topic: test; Message: -> KEYWORD <-"), + ); + }); + + test("invalid keyword (type=keyword)", async () => { + await assert.rejects( + testMqtt("NOT_PRESENT", "keyword", "-> KEYWORD <-"), + new Error("Message Mismatch - Topic: test; Message: -> KEYWORD <-"), + ); + }); + test("valid json-query", async () => { + // works because the monitors' jsonPath is hard-coded to "firstProp" + const heartbeat = await testMqtt("present", "json-query", "{\"firstProp\":\"present\"}"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Message received, expected value is found"); + }); + test("invalid (because query fails) json-query", async () => { + // works because the monitors' jsonPath is hard-coded to "firstProp" + await assert.rejects( + testMqtt("[not_relevant]", "json-query", "{}"), + new Error("Message received but value is not equal to expected value, value was: [undefined]"), + ); + }); + test("invalid (because successMessage fails) json-query", async () => { + // works because the monitors' jsonPath is hard-coded to "firstProp" + await assert.rejects( + testMqtt("[wrong_success_messsage]", "json-query", "{\"firstProp\":\"present\"}"), + new Error("Message received but value is not equal to expected value, value was: [present]") + ); + }); +}); diff --git a/test/backend-test/test-rabbitmq.js b/test/backend-test/test-rabbitmq.js new file mode 100644 index 000000000..5782ef250 --- /dev/null +++ b/test/backend-test/test-rabbitmq.js @@ -0,0 +1,53 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { RabbitMQContainer } = require("@testcontainers/rabbitmq"); +const { RabbitMqMonitorType } = require("../../server/monitor-types/rabbitmq"); +const { UP, DOWN, PENDING } = require("../../src/util"); + +describe("RabbitMQ Single Node", { + skip: !!process.env.CI && (process.platform !== "linux" || process.arch !== "x64"), +}, () => { + test("RabbitMQ is running", async () => { + // The default timeout of 30 seconds might not be enough for the container to start + const rabbitMQContainer = await new RabbitMQContainer().withStartupTimeout(60000).start(); + const rabbitMQMonitor = new RabbitMqMonitorType(); + const connectionString = `http://${rabbitMQContainer.getHost()}:${rabbitMQContainer.getMappedPort(15672)}`; + + const monitor = { + rabbitmqNodes: JSON.stringify([ connectionString ]), + rabbitmqUsername: "guest", + rabbitmqPassword: "guest", + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await rabbitMQMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "OK"); + } finally { + rabbitMQContainer.stop(); + } + }); + + test("RabbitMQ is not running", async () => { + const rabbitMQMonitor = new RabbitMqMonitorType(); + const monitor = { + rabbitmqNodes: JSON.stringify([ "http://localhost:15672" ]), + rabbitmqUsername: "rabbitmqUser", + rabbitmqPassword: "rabbitmqPass", + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await rabbitMQMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, DOWN); + }); + +});