From 811fbfcc12de80616755a94380f76eb9c88d07aa Mon Sep 17 00:00:00 2001 From: Alex Justesen Date: Thu, 8 Aug 2024 12:36:44 -0400 Subject: [PATCH 1/3] Laravel 11.20.0 Shift (#1644) * Bump Laravel version constraint * composer update * updated composer dependencies --------- Co-authored-by: Shift --- _ide_helper.php | 86 +++++++++++++++++++++++++++++++------ composer.json | 10 ++--- composer.lock | 111 ++++++++++++++++++++++++------------------------ 3 files changed, 134 insertions(+), 73 deletions(-) diff --git a/_ide_helper.php b/_ide_helper.php index 298ffcecb..9cf4865fa 100644 --- a/_ide_helper.php +++ b/_ide_helper.php @@ -5,7 +5,7 @@ /** * A helper file for Laravel, to provide autocomplete information to your IDE - * Generated for Laravel 11.19.0. + * Generated for Laravel 11.20.0. * * This file should not be included in your code, only analyzed by your IDE! * @@ -3573,7 +3573,7 @@ * * * @see \Illuminate\Cache\CacheManager - * @mixin \Illuminate\Cache\Repository + * @see \Illuminate\Cache\Repository */ class Cache { /** * Get a cache store instance by name, wrapped in a repository. @@ -4696,6 +4696,34 @@ { /** @var \Illuminate\Log\Context\Repository $instance */ return $instance->pushHidden($key, ...$values); + } + /** + * Determine if the given value is in the given stack. + * + * @param string $key + * @param mixed $value + * @param bool $strict + * @return bool + * @throws \RuntimeException + * @static + */ public static function stackContains($key, $value, $strict = false) + { + /** @var \Illuminate\Log\Context\Repository $instance */ + return $instance->stackContains($key, $value, $strict); + } + /** + * Determine if the given value is in the given hidden stack. + * + * @param string $key + * @param mixed $value + * @param bool $strict + * @return bool + * @throws \RuntimeException + * @static + */ public static function hiddenStackContains($key, $value, $strict = false) + { + /** @var \Illuminate\Log\Context\Repository $instance */ + return $instance->hiddenStackContains($key, $value, $strict); } /** * Determine if the repository is empty. @@ -5525,6 +5553,16 @@ { /** @var \Illuminate\Database\DatabaseManager $instance */ return $instance->macroCall($method, $parameters); + } + /** + * Get a human-readable name for the given connection driver. + * + * @return string + * @static + */ public static function getDriverTitle() + { + /** @var \Illuminate\Database\SQLiteConnection $instance */ + return $instance->getDriverTitle(); } /** * Get a schema builder instance for the connection. @@ -5748,6 +5786,16 @@ { //Method inherited from \Illuminate\Database\Connection /** @var \Illuminate\Database\SQLiteConnection $instance */ return $instance->unprepared($query); + } + /** + * Get the number of open connections for the database. + * + * @return int|null + * @static + */ public static function threadCount() + { //Method inherited from \Illuminate\Database\Connection + /** @var \Illuminate\Database\SQLiteConnection $instance */ + return $instance->threadCount(); } /** * Execute the given callback in "dry run" mode. @@ -7802,6 +7850,18 @@ { /** @var \Illuminate\Hashing\HashManager $instance */ return $instance->getDefaultDriver(); + } + /** + * Verifies that the configuration is less than or equal to what is configured. + * + * @param array $value + * @return bool + * @internal + * @static + */ public static function verifyConfiguration($value) + { + /** @var \Illuminate\Hashing\HashManager $instance */ + return $instance->verifyConfiguration($value); } /** * Get a driver instance. @@ -25136,7 +25196,7 @@ class Eloquent extends \Illuminate\Database\Eloquent\Model { /** * Add a "where" clause to the query for multiple columns with "and" conditions between them. * - * @param string[] $columns + * @param \Illuminate\Contracts\Database\Query\Expression[]|string[] $columns * @param mixed $operator * @param mixed $value * @param string $boolean @@ -25150,8 +25210,8 @@ class Eloquent extends \Illuminate\Database\Eloquent\Model { /** * Add an "or where" clause to the query for multiple columns with "and" conditions between them. * - * @param string[] $columns - * @param string $operator + * @param \Illuminate\Contracts\Database\Query\Expression[]|string[] $columns + * @param mixed $operator * @param mixed $value * @return \Illuminate\Database\Query\Builder * @static @@ -25163,8 +25223,8 @@ class Eloquent extends \Illuminate\Database\Eloquent\Model { /** * Add a "where" clause to the query for multiple columns with "or" conditions between them. * - * @param string[] $columns - * @param string $operator + * @param \Illuminate\Contracts\Database\Query\Expression[]|string[] $columns + * @param mixed $operator * @param mixed $value * @param string $boolean * @return \Illuminate\Database\Query\Builder @@ -25177,8 +25237,8 @@ class Eloquent extends \Illuminate\Database\Eloquent\Model { /** * Add an "or where" clause to the query for multiple columns with "or" conditions between them. * - * @param string[] $columns - * @param string $operator + * @param \Illuminate\Contracts\Database\Query\Expression[]|string[] $columns + * @param mixed $operator * @param mixed $value * @return \Illuminate\Database\Query\Builder * @static @@ -25190,8 +25250,8 @@ class Eloquent extends \Illuminate\Database\Eloquent\Model { /** * Add a "where not" clause to the query for multiple columns where none of the conditions should be true. * - * @param string[] $columns - * @param string $operator + * @param \Illuminate\Contracts\Database\Query\Expression[]|string[] $columns + * @param mixed $operator * @param mixed $value * @param string $boolean * @return \Illuminate\Database\Query\Builder @@ -25204,8 +25264,8 @@ class Eloquent extends \Illuminate\Database\Eloquent\Model { /** * Add an "or where not" clause to the query for multiple columns where none of the conditions should be true. * - * @param string[] $columns - * @param string $operator + * @param \Illuminate\Contracts\Database\Query\Expression[]|string[] $columns + * @param mixed $operator * @param mixed $value * @return \Illuminate\Database\Query\Builder * @static diff --git a/composer.json b/composer.json index 1bfcd3d07..851bb090c 100644 --- a/composer.json +++ b/composer.json @@ -18,12 +18,12 @@ "guzzlehttp/guzzle": "^7.9.2", "influxdata/influxdb-client-php": "^3.6", "laravel-notification-channels/telegram": "^5.0", - "laravel/framework": "^11.19", + "laravel/framework": "^11.20", "laravel/prompts": "^0.1.24", "laravel/sanctum": "^4.0.2", "laravel/tinker": "^2.9.0", "livewire/livewire": "^3.5.4", - "lorisleiva/laravel-actions": "^2.8.0", + "lorisleiva/laravel-actions": "^2.8.1", "maennchen/zipstream-php": "^2.4", "spatie/laravel-settings": "^3.3.2", "spatie/laravel-webhook-server": "^3.8.1", @@ -32,9 +32,9 @@ "require-dev": { "barryvdh/laravel-ide-helper": "^3.1", "fakerphp/faker": "^1.23.1", - "laravel/pint": "^1.17.1", - "laravel/sail": "^1.31.0", - "laravel/telescope": "^5.1.1", + "laravel/pint": "^1.17.2", + "laravel/sail": "^1.31.1", + "laravel/telescope": "^5.2.0", "mockery/mockery": "^1.6.12", "nunomaduro/collision": "^8.4.0", "phpunit/phpunit": "^11.3.0", diff --git a/composer.lock b/composer.lock index ed1f1e0f6..4ceb0fa92 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dbfd4ab715230ad9ac6e6b2162b2e8a4", + "content-hash": "0b1b064aa394c00a4c3aba46d92a76b3", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -805,16 +805,16 @@ }, { "name": "doctrine/dbal", - "version": "3.8.6", + "version": "3.8.7", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "b7411825cf7efb7e51f9791dea19d86e43b399a1" + "reference": "2093d670ca17f634f3c095ec10a20687eccebd99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/b7411825cf7efb7e51f9791dea19d86e43b399a1", - "reference": "b7411825cf7efb7e51f9791dea19d86e43b399a1", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/2093d670ca17f634f3c095ec10a20687eccebd99", + "reference": "2093d670ca17f634f3c095ec10a20687eccebd99", "shasum": "" }, "require": { @@ -830,12 +830,12 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.11.5", + "phpstan/phpstan": "1.11.7", "phpstan/phpstan-strict-rules": "^1.6", - "phpunit/phpunit": "9.6.19", + "phpunit/phpunit": "9.6.20", "psalm/plugin-phpunit": "0.18.4", "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.10.1", + "squizlabs/php_codesniffer": "3.10.2", "symfony/cache": "^5.4|^6.0|^7.0", "symfony/console": "^4.4|^5.4|^6.0|^7.0", "vimeo/psalm": "4.30.0" @@ -898,7 +898,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.8.6" + "source": "https://github.com/doctrine/dbal/tree/3.8.7" }, "funding": [ { @@ -914,7 +914,7 @@ "type": "tidelift" } ], - "time": "2024-06-19T10:38:17+00:00" + "time": "2024-08-07T11:57:25+00:00" }, { "name": "doctrine/deprecations", @@ -2597,16 +2597,16 @@ }, { "name": "laravel/framework", - "version": "v11.19.0", + "version": "v11.20.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "5e103d499e9ee5bcfc184412d034c4e516b87085" + "reference": "3cd7593dd9b67002fc416b46616f4d4d1da3e571" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/5e103d499e9ee5bcfc184412d034c4e516b87085", - "reference": "5e103d499e9ee5bcfc184412d034c4e516b87085", + "url": "https://api.github.com/repos/laravel/framework/zipball/3cd7593dd9b67002fc416b46616f4d4d1da3e571", + "reference": "3cd7593dd9b67002fc416b46616f4d4d1da3e571", "shasum": "" }, "require": { @@ -2799,7 +2799,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-07-30T15:22:41+00:00" + "time": "2024-08-06T14:39:21+00:00" }, { "name": "laravel/prompts", @@ -2925,26 +2925,27 @@ }, { "name": "laravel/serializable-closure", - "version": "v1.3.3", + "version": "v1.3.4", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "3dbf8a8e914634c48d389c1234552666b3d43754" + "reference": "61b87392d986dc49ad5ef64e75b1ff5fee24ef81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754", - "reference": "3dbf8a8e914634c48d389c1234552666b3d43754", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/61b87392d986dc49ad5ef64e75b1ff5fee24ef81", + "reference": "61b87392d986dc49ad5ef64e75b1ff5fee24ef81", "shasum": "" }, "require": { "php": "^7.3|^8.0" }, "require-dev": { - "nesbot/carbon": "^2.61", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0", + "nesbot/carbon": "^2.61|^3.0", "pestphp/pest": "^1.21.3", "phpstan/phpstan": "^1.8.2", - "symfony/var-dumper": "^5.4.11" + "symfony/var-dumper": "^5.4.11|^6.2.0|^7.0.0" }, "type": "library", "extra": { @@ -2981,7 +2982,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2023-11-08T14:08:06+00:00" + "time": "2024-08-02T07:48:17+00:00" }, { "name": "laravel/tinker", @@ -3765,16 +3766,16 @@ }, { "name": "lorisleiva/laravel-actions", - "version": "v2.8.0", + "version": "v2.8.1", "source": { "type": "git", "url": "https://github.com/lorisleiva/laravel-actions.git", - "reference": "d5c2ca544f40d85f877b38eb6d23e9c967ecb69f" + "reference": "18346c05fc4782e1a3da1bda917a25c0a4d1aa7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lorisleiva/laravel-actions/zipball/d5c2ca544f40d85f877b38eb6d23e9c967ecb69f", - "reference": "d5c2ca544f40d85f877b38eb6d23e9c967ecb69f", + "url": "https://api.github.com/repos/lorisleiva/laravel-actions/zipball/18346c05fc4782e1a3da1bda917a25c0a4d1aa7d", + "reference": "18346c05fc4782e1a3da1bda917a25c0a4d1aa7d", "shasum": "" }, "require": { @@ -3828,7 +3829,7 @@ ], "support": { "issues": "https://github.com/lorisleiva/laravel-actions/issues", - "source": "https://github.com/lorisleiva/laravel-actions/tree/v2.8.0" + "source": "https://github.com/lorisleiva/laravel-actions/tree/v2.8.1" }, "funding": [ { @@ -3836,7 +3837,7 @@ "type": "github" } ], - "time": "2024-03-13T12:47:32+00:00" + "time": "2024-08-08T08:37:26+00:00" }, { "name": "lorisleiva/lody", @@ -4389,20 +4390,20 @@ }, { "name": "nette/utils", - "version": "v4.0.4", + "version": "v4.0.5", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218" + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/d3ad0aa3b9f934602cb3e3902ebccf10be34d218", - "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218", + "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", "shasum": "" }, "require": { - "php": ">=8.0 <8.4" + "php": "8.0 - 8.4" }, "conflict": { "nette/finder": "<3", @@ -4469,9 +4470,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.4" + "source": "https://github.com/nette/utils/tree/v4.0.5" }, - "time": "2024-01-17T16:50:36+00:00" + "time": "2024-08-07T15:39:19+00:00" }, { "name": "nikic/php-parser", @@ -9770,16 +9771,16 @@ }, { "name": "laravel/pint", - "version": "v1.17.1", + "version": "v1.17.2", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "b5b6f716db298671c1dfea5b1082ec2c0ae7064f" + "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/b5b6f716db298671c1dfea5b1082ec2c0ae7064f", - "reference": "b5b6f716db298671c1dfea5b1082ec2c0ae7064f", + "url": "https://api.github.com/repos/laravel/pint/zipball/e8a88130a25e3f9d4d5785e6a1afca98268ab110", + "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110", "shasum": "" }, "require": { @@ -9790,13 +9791,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.59.3", - "illuminate/view": "^10.48.12", - "larastan/larastan": "^2.9.7", + "friendsofphp/php-cs-fixer": "^3.61.1", + "illuminate/view": "^10.48.18", + "larastan/larastan": "^2.9.8", "laravel-zero/framework": "^10.4.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.34.8" + "pestphp/pest": "^2.35.0" }, "bin": [ "builds/pint" @@ -9832,20 +9833,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-08-01T09:06:33+00:00" + "time": "2024-08-06T15:11:54+00:00" }, { "name": "laravel/sail", - "version": "v1.31.0", + "version": "v1.31.1", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "48d89608a3bb5be763c9bb87121d31e7da27c1cb" + "reference": "3d06dd18cee8059baa7b388af00ba47f6d96bd85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/48d89608a3bb5be763c9bb87121d31e7da27c1cb", - "reference": "48d89608a3bb5be763c9bb87121d31e7da27c1cb", + "url": "https://api.github.com/repos/laravel/sail/zipball/3d06dd18cee8059baa7b388af00ba47f6d96bd85", + "reference": "3d06dd18cee8059baa7b388af00ba47f6d96bd85", "shasum": "" }, "require": { @@ -9895,20 +9896,20 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2024-07-22T14:36:50+00:00" + "time": "2024-08-02T07:45:47+00:00" }, { "name": "laravel/telescope", - "version": "v5.1.1", + "version": "v5.2.0", "source": { "type": "git", "url": "https://github.com/laravel/telescope.git", - "reference": "7355643b998027f8fa9393e6c8c884f126204a80" + "reference": "dd3f5e205dea0cb58f89a79470a38e0dc9732876" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/telescope/zipball/7355643b998027f8fa9393e6c8c884f126204a80", - "reference": "7355643b998027f8fa9393e6c8c884f126204a80", + "url": "https://api.github.com/repos/laravel/telescope/zipball/dd3f5e205dea0cb58f89a79470a38e0dc9732876", + "reference": "dd3f5e205dea0cb58f89a79470a38e0dc9732876", "shasum": "" }, "require": { @@ -9962,9 +9963,9 @@ ], "support": { "issues": "https://github.com/laravel/telescope/issues", - "source": "https://github.com/laravel/telescope/tree/v5.1.1" + "source": "https://github.com/laravel/telescope/tree/v5.2.0" }, - "time": "2024-06-27T07:12:23+00:00" + "time": "2024-08-02T07:49:39+00:00" }, { "name": "mockery/mockery", From 362311c8961c4e97776145d1184eebb07e42e04e Mon Sep 17 00:00:00 2001 From: Alex Justesen Date: Thu, 8 Aug 2024 13:01:19 -0400 Subject: [PATCH 2/3] Release v0.21.0 (#1648) * Develop v0.21.0 (additional notification channels) (#1647) * Add Placeholder text for notifications (#1570) first_commit * Add Gotify Notifications via Webhook (#1561) * first_commit * Ready! * Add_placeholder * change url --------- Co-authored-by: Alex Justesen * Add Support for Slack Notifications via Webhook (#1522) * First Commit * Fix lint * add_url_placeholder * Fix the liniting * Add HealthCheck.io Notifications via Webhooks (#1567) * first_commit * linting * add_descrip_for_threshold * Change name * add_url_placeholder * fix_linting * Add Pushover Notifications via Webhooks (#1574) * add_pushover * add_placeholder * Linting --------- Co-authored-by: Alex Justesen * Add ntfy Notifications via Webhooks (#1579) * first_push * Fix_json_payload * Add_auth_option * fix lint * fix packet_loss_% * added eof line --------- Co-authored-by: Alex Justesen * Add Ookla URL to the notification (#1615) * first commit * added eof line --------- Co-authored-by: Alex Justesen * Fix notifications layouts (#1639) first commit --------- Co-authored-by: Sven van Ginkel * updated npm dependencies --------- Co-authored-by: Sven van Ginkel --- .../SendGotifyTestNotification.php | 37 +++ .../SendHealthCheckTestNotification.php | 37 +++ .../SendNtfyTestNotification.php | 49 ++++ .../SendPushoverTestNotification.php | 41 +++ .../SendSlackTestNotification.php | 37 +++ .../Pages/Settings/NotificationPage.php | 257 ++++++++++++++++++ .../SendSpeedtestCompletedNotification.php | 1 + .../SendSpeedtestThresholdNotification.php | 1 + .../SendSpeedtestCompletedNotification.php | 59 ++++ .../SendSpeedtestThresholdNotification.php | 133 +++++++++ .../SendSpeedtestCompletedNotification.php | 51 ++++ .../SendSpeedtestThresholdNotification.php | 126 +++++++++ .../SendSpeedtestCompletedNotification.php | 69 +++++ .../SendSpeedtestThresholdNotification.php | 144 ++++++++++ .../SendSpeedtestCompletedNotification.php | 63 +++++ .../SendSpeedtestThresholdNotification.php | 137 ++++++++++ .../SendSpeedtestCompletedNotification.php | 59 ++++ .../SendSpeedtestThresholdNotification.php | 133 +++++++++ .../SendSpeedtestCompletedNotification.php | 1 + .../SendSpeedtestThresholdNotification.php | 1 + .../SendSpeedtestCompletedNotification.php | 1 + .../SendSpeedtestThresholdNotification.php | 1 + app/Mail/SpeedtestCompletedMail.php | 1 + app/Mail/SpeedtestThresholdMail.php | 1 + app/Settings/NotificationSettings.php | 40 +++ ...eate_healthcheck_notification_settings.php | 14 + ...4650_create_ntfy_notification_settings.php | 14 + ...650_create_slack_notification_settings.php | 14 + ...54_create_gotify_notification_settings.php | 14 + ..._create_pushover_notification_settings.php | 14 + package-lock.json | 206 +++++++------- .../discord/speedtest-completed.blade.php | 1 + .../discord/speedtest-threshold.blade.php | 1 + .../emails/speedtest-completed.blade.php | 7 +- .../emails/speedtest-threshold.blade.php | 4 + .../gotify/speedtest-completed.blade.php | 13 + .../gotify/speedtest-threshold.blade.php | 9 + .../views/ntfy/speedtest-completed.blade.php | 13 + .../views/ntfy/speedtest-threshold.blade.php | 9 + .../pushover/speedtest-completed.blade.php | 13 + .../pushover/speedtest-threshold.blade.php | 9 + .../views/slack/speedtest-completed.blade.php | 13 + .../views/slack/speedtest-threshold.blade.php | 9 + .../telegram/speedtest-completed.blade.php | 1 + .../telegram/speedtest-threshold.blade.php | 1 + 45 files changed, 1757 insertions(+), 102 deletions(-) create mode 100644 app/Actions/Notifications/SendGotifyTestNotification.php create mode 100644 app/Actions/Notifications/SendHealthCheckTestNotification.php create mode 100644 app/Actions/Notifications/SendNtfyTestNotification.php create mode 100644 app/Actions/Notifications/SendPushoverTestNotification.php create mode 100644 app/Actions/Notifications/SendSlackTestNotification.php create mode 100644 app/Listeners/Gotify/SendSpeedtestCompletedNotification.php create mode 100644 app/Listeners/Gotify/SendSpeedtestThresholdNotification.php create mode 100644 app/Listeners/HealthCheck/SendSpeedtestCompletedNotification.php create mode 100644 app/Listeners/HealthCheck/SendSpeedtestThresholdNotification.php create mode 100644 app/Listeners/Ntfy/SendSpeedtestCompletedNotification.php create mode 100644 app/Listeners/Ntfy/SendSpeedtestThresholdNotification.php create mode 100644 app/Listeners/Pushover/SendSpeedtestCompletedNotification.php create mode 100644 app/Listeners/Pushover/SendSpeedtestThresholdNotification.php create mode 100644 app/Listeners/Slack/SendSpeedtestCompletedNotification.php create mode 100644 app/Listeners/Slack/SendSpeedtestThresholdNotification.php create mode 100644 database/settings/2024_02_22_144620_create_healthcheck_notification_settings.php create mode 100644 database/settings/2024_02_22_144650_create_ntfy_notification_settings.php create mode 100644 database/settings/2024_02_22_144650_create_slack_notification_settings.php create mode 100644 database/settings/2024_02_22_144654_create_gotify_notification_settings.php create mode 100644 database/settings/2024_02_22_144680_create_pushover_notification_settings.php create mode 100644 resources/views/gotify/speedtest-completed.blade.php create mode 100644 resources/views/gotify/speedtest-threshold.blade.php create mode 100644 resources/views/ntfy/speedtest-completed.blade.php create mode 100644 resources/views/ntfy/speedtest-threshold.blade.php create mode 100644 resources/views/pushover/speedtest-completed.blade.php create mode 100644 resources/views/pushover/speedtest-threshold.blade.php create mode 100644 resources/views/slack/speedtest-completed.blade.php create mode 100644 resources/views/slack/speedtest-threshold.blade.php diff --git a/app/Actions/Notifications/SendGotifyTestNotification.php b/app/Actions/Notifications/SendGotifyTestNotification.php new file mode 100644 index 000000000..fdd6405de --- /dev/null +++ b/app/Actions/Notifications/SendGotifyTestNotification.php @@ -0,0 +1,37 @@ +title('You need to add Gotify urls!') + ->warning() + ->send(); + + return; + } + + foreach ($webhooks as $webhook) { + WebhookCall::create() + ->url($webhook['url']) + ->payload(['message' => '👋 Testing the Gotify notification channel.']) + ->doNotSign() + ->dispatch(); + } + + Notification::make() + ->title('Test Gotify notification sent.') + ->success() + ->send(); + } +} diff --git a/app/Actions/Notifications/SendHealthCheckTestNotification.php b/app/Actions/Notifications/SendHealthCheckTestNotification.php new file mode 100644 index 000000000..f37e20fad --- /dev/null +++ b/app/Actions/Notifications/SendHealthCheckTestNotification.php @@ -0,0 +1,37 @@ +title('You need to add HealthCheck.io urls!') + ->warning() + ->send(); + + return; + } + + foreach ($webhooks as $webhook) { + WebhookCall::create() + ->url($webhook['url']) + ->payload(['message' => '👋 Testing the HealthCheck.io notification channel.']) + ->doNotSign() + ->dispatch(); + } + + Notification::make() + ->title('Test HealthCheck.io notification sent.') + ->success() + ->send(); + } +} diff --git a/app/Actions/Notifications/SendNtfyTestNotification.php b/app/Actions/Notifications/SendNtfyTestNotification.php new file mode 100644 index 000000000..8975febaa --- /dev/null +++ b/app/Actions/Notifications/SendNtfyTestNotification.php @@ -0,0 +1,49 @@ +title('You need to add ntfy urls!') + ->warning() + ->send(); + + return; + } + + foreach ($webhooks as $webhook) { + $webhookCall = WebhookCall::create() + ->url($webhook['url']) + ->payload([ + 'topic' => $webhook['topic'], + 'message' => '👋 Testing the ntfy notification channel.', + ]) + ->doNotSign(); + + // Only add authentication if username and password are provided + if (! empty($webhook['username']) && ! empty($webhook['password'])) { + $authHeader = 'Basic '.base64_encode($webhook['username'].':'.$webhook['password']); + $webhookCall->withHeaders([ + 'Authorization' => $authHeader, + ]); + } + + $webhookCall->dispatch(); + } + + Notification::make() + ->title('Test ntfy notification sent.') + ->success() + ->send(); + } +} diff --git a/app/Actions/Notifications/SendPushoverTestNotification.php b/app/Actions/Notifications/SendPushoverTestNotification.php new file mode 100644 index 000000000..d37b0594d --- /dev/null +++ b/app/Actions/Notifications/SendPushoverTestNotification.php @@ -0,0 +1,41 @@ +title('You need to add Pushover URLs!') + ->warning() + ->send(); + + return; + } + + foreach ($webhooks as $webhook) { + WebhookCall::create() + ->url($webhook['url']) + ->payload([ + 'token' => $webhook['api_token'], + 'user' => $webhook['user_key'], + 'message' => '👋 Testing the Pushover notification channel.', + ]) + ->doNotSign() + ->dispatch(); + } + + Notification::make() + ->title('Test Pushover notification sent.') + ->success() + ->send(); + } +} diff --git a/app/Actions/Notifications/SendSlackTestNotification.php b/app/Actions/Notifications/SendSlackTestNotification.php new file mode 100644 index 000000000..95db18439 --- /dev/null +++ b/app/Actions/Notifications/SendSlackTestNotification.php @@ -0,0 +1,37 @@ +title('You need to add Slack URLs!') + ->warning() + ->send(); + + return; + } + + foreach ($webhooks as $webhook) { + WebhookCall::create() + ->url($webhook['url']) + ->payload(['text' => '👋 Testing the Slack notification channel.']) + ->doNotSign() + ->dispatch(); + } + + Notification::make() + ->title('Test Slack notification sent.') + ->success() + ->send(); + } +} diff --git a/app/Filament/Pages/Settings/NotificationPage.php b/app/Filament/Pages/Settings/NotificationPage.php index a40a5e080..bd7df5902 100755 --- a/app/Filament/Pages/Settings/NotificationPage.php +++ b/app/Filament/Pages/Settings/NotificationPage.php @@ -4,7 +4,12 @@ use App\Actions\Notifications\SendDatabaseTestNotification; use App\Actions\Notifications\SendDiscordTestNotification; +use App\Actions\Notifications\SendGotifyTestNotification; +use App\Actions\Notifications\SendHealthCheckTestNotification; use App\Actions\Notifications\SendMailTestNotification; +use App\Actions\Notifications\SendNtfyTestNotification; +use App\Actions\Notifications\SendPushoverTestNotification; +use App\Actions\Notifications\SendSlackTestNotification; use App\Actions\Notifications\SendTelegramTestNotification; use App\Actions\Notifications\SendWebhookTestNotification; use App\Settings\NotificationSettings; @@ -84,6 +89,63 @@ public function form(Form $form): Form 'md' => 2, ]), + Forms\Components\Section::make('Pushover') + ->schema([ + Forms\Components\Toggle::make('pushover_enabled') + ->label('Enable Pushover webhook notifications') + ->reactive() + ->columnSpanFull(), + Forms\Components\Grid::make([ + 'default' => 1, + ]) + ->hidden(fn (Forms\Get $get) => $get('pushover_enabled') !== true) + ->schema([ + Forms\Components\Fieldset::make('Triggers') + ->schema([ + Forms\Components\Toggle::make('pushover_on_speedtest_run') + ->label('Notify on every speedtest run') + ->columnSpanFull(), + Forms\Components\Toggle::make('pushover_on_threshold_failure') + ->label('Notify on threshold failures') + ->columnSpanFull(), + ]), + Forms\Components\Repeater::make('pushover_webhooks') + ->label('Pushover Webhooks') + ->schema([ + Forms\Components\TextInput::make('url') + ->label('URL') + ->placeholder('http://api.pushover.net/1/messages.json') + ->maxLength(2000) + ->required() + ->url(), + Forms\Components\TextInput::make('user_key') + ->label('User Key') + ->placeholder('Your Pushover User Key') + ->maxLength(200) + ->required(), + Forms\Components\TextInput::make('api_token') + ->label('API Token') + ->placeholder('Your Pushover API Token') + ->maxLength(200) + ->required(), + ]) + ->columnSpanFull(), + Forms\Components\Actions::make([ + Forms\Components\Actions\Action::make('test pushover') + ->label('Test Pushover webhook') + ->action(fn (Forms\Get $get) => SendPushoverTestNotification::run( + webhooks: $get('pushover_webhooks') + )) + ->hidden(fn (Forms\Get $get) => ! count($get('pushover_webhooks'))), + ]), + ]), + ]) + ->compact() + ->columns([ + 'default' => 1, + 'md' => 2, + ]), + Forms\Components\Section::make('Discord') ->schema([ Forms\Components\Toggle::make('discord_enabled') @@ -108,6 +170,7 @@ public function form(Form $form): Form ->label('Webhooks') ->schema([ Forms\Components\TextInput::make('url') + ->placeholder('https://discord.com/api/webhooks/longstringofcharacters') ->maxLength(2000) ->required() ->url(), @@ -127,6 +190,152 @@ public function form(Form $form): Form 'md' => 2, ]), + Forms\Components\Section::make('Gotify') + ->schema([ + Forms\Components\Toggle::make('gotify_enabled') + ->label('Enable Gotify webhook notifications') + ->reactive() + ->columnSpanFull(), + Forms\Components\Grid::make([ + 'default' => 1, + ]) + ->hidden(fn (Forms\Get $get) => $get('gotify_enabled') !== true) + ->schema([ + Forms\Components\Fieldset::make('Triggers') + ->schema([ + Forms\Components\Toggle::make('gotify_on_speedtest_run') + ->label('Notify on every speedtest run') + ->columnSpanFull(), + Forms\Components\Toggle::make('gotify_on_threshold_failure') + ->label('Notify on threshold failures') + ->columnSpanFull(), + ]), + Forms\Components\Repeater::make('gotify_webhooks') + ->label('Webhooks') + ->schema([ + Forms\Components\TextInput::make('url') + ->placeholder('https://example.com/message?token=') + ->maxLength(2000) + ->required() + ->url(), + ]) + ->columnSpanFull(), + Forms\Components\Actions::make([ + Forms\Components\Actions\Action::make('test gotify') + ->label('Test Gotify webhook') + ->action(fn (Forms\Get $get) => SendgotifyTestNotification::run(webhooks: $get('gotify_webhooks'))) + ->hidden(fn (Forms\Get $get) => ! count($get('gotify_webhooks'))), + ]), + ]), + ]) + ->compact() + ->columns([ + 'default' => 1, + 'md' => 2, + ]), + + Forms\Components\Section::make('Slack') + ->schema([ + Forms\Components\Toggle::make('slack_enabled') + ->label('Enable Slack webhook notifications') + ->reactive() + ->columnSpanFull(), + Forms\Components\Grid::make([ + 'default' => 1, + ]) + ->hidden(fn (Forms\Get $get) => $get('slack_enabled') !== true) + ->schema([ + Forms\Components\Fieldset::make('Triggers') + ->schema([ + Forms\Components\Toggle::make('slack_on_speedtest_run') + ->label('Notify on every speedtest run') + ->columnSpanFull(), + Forms\Components\Toggle::make('slack_on_threshold_failure') + ->label('Notify on threshold failures') + ->columnSpanFull(), + ]), + Forms\Components\Repeater::make('slack_webhooks') + ->label('Webhooks') + ->schema([ + Forms\Components\TextInput::make('url') + ->placeholder('https://hooks.slack.com/services/abc/xyz') + ->maxLength(2000) + ->required() + ->url(), + ]) + ->columnSpanFull(), + Forms\Components\Actions::make([ + Forms\Components\Actions\Action::make('test Slack') + ->label('Test slack webhook') + ->action(fn (Forms\Get $get) => SendSlackTestNotification::run(webhooks: $get('slack_webhooks'))) + ->hidden(fn (Forms\Get $get) => ! count($get('slack_webhooks'))), + ]), + ]), + ]) + ->compact() + ->columns([ + 'default' => 1, + 'md' => 2, + ]), + + Forms\Components\Section::make('Ntfy') + ->schema([ + Forms\Components\Toggle::make('ntfy_enabled') + ->label('Enable Ntfy webhook notifications') + ->reactive() + ->columnSpanFull(), + Forms\Components\Grid::make([ + 'default' => 1, + ]) + ->hidden(fn (Forms\Get $get) => $get('ntfy_enabled') !== true) + ->schema([ + Forms\Components\Fieldset::make('Triggers') + ->schema([ + Forms\Components\Toggle::make('ntfy_on_speedtest_run') + ->label('Notify on every speedtest run') + ->columnSpanFull(), + Forms\Components\Toggle::make('ntfy_on_threshold_failure') + ->label('Notify on threshold failures') + ->columnSpanFull(), + ]), + Forms\Components\Repeater::make('ntfy_webhooks') + ->label('Webhooks') + ->schema([ + Forms\Components\TextInput::make('url') + ->maxLength(2000) + ->placeholder('Your ntfy server url') + ->required() + ->url(), + Forms\Components\TextInput::make('topic') + ->label('Topic') + ->placeholder('Your ntfy Topic') + ->maxLength(200) + ->required(), + Forms\Components\TextInput::make('username') + ->label('Username') + ->placeholder('Username for Basic Auth (optional)') + ->maxLength(200), + Forms\Components\TextInput::make('password') + ->label('Password') + ->placeholder('Password for Basic Auth (optional)') + ->password() + ->maxLength(200), + ]) + ->columnSpanFull(), + Forms\Components\Actions::make([ + Forms\Components\Actions\Action::make('test ntfy') + ->label('Test Ntfy webhook') + ->action(fn (Forms\Get $get) => SendNtfyTestNotification::run(webhooks: $get('ntfy_webhooks'))) + ->hidden(fn (Forms\Get $get) => ! count($get('ntfy_webhooks'))), + ]), + ]), + ]) + ->compact() + ->columns([ + 'default' => 1, + 'md' => 2, + ]), + Forms\Components\Section::make('Mail') ->schema([ Forms\Components\Toggle::make('mail_enabled') @@ -151,6 +360,7 @@ public function form(Form $form): Form ->label('Recipients') ->schema([ Forms\Components\TextInput::make('email_address') + ->placeholder('your@email.com') ->email() ->required(), ]) @@ -169,6 +379,51 @@ public function form(Form $form): Form 'md' => 2, ]), + Forms\Components\Section::make('Healthcheck.io') + ->schema([ + Forms\Components\Toggle::make('healthcheck_enabled') + ->label('Enable healthcheck.io webhook notifications') + ->reactive() + ->columnSpanFull(), + Forms\Components\Grid::make([ + 'default' => 1, + ]) + ->hidden(fn (Forms\Get $get) => $get('healthcheck_enabled') !== true) + ->schema([ + Forms\Components\Fieldset::make('Triggers') + ->schema([ + Forms\Components\Toggle::make('healthcheck_on_speedtest_run') + ->label('Notify on every speedtest run') + ->columnSpanFull(), + Forms\Components\Toggle::make('healthcheck_on_threshold_failure') + ->label('Notify on threshold failures') + ->helperText('Threshold notifications will be sent to the /fail path of the URL.') + ->columnSpanFull(), + ]), + Forms\Components\Repeater::make('healthcheck_webhooks') + ->label('webhooks') + ->schema([ + Forms\Components\TextInput::make('url') + ->placeholder('https://hc-ping.com/your-uuid-here') + ->maxLength(2000) + ->required() + ->url(), + ]) + ->columnSpanFull(), + Forms\Components\Actions::make([ + Forms\Components\Actions\Action::make('test healthcheck') + ->label('Test healthcheck.io webhook') + ->action(fn (Forms\Get $get) => SendHealthCheckTestNotification::run(webhooks: $get('healthcheck_webhooks'))) + ->hidden(fn (Forms\Get $get) => ! count($get('healthcheck_webhooks'))), + ]), + ]), + ]) + ->compact() + ->columns([ + 'default' => 1, + 'md' => 2, + ]), + Forms\Components\Section::make('Telegram') ->schema([ Forms\Components\Toggle::make('telegram_enabled') @@ -199,6 +454,7 @@ public function form(Form $form): Form ->label('Recipients') ->schema([ Forms\Components\TextInput::make('telegram_chat_id') + ->placeholder('12345678910') ->label('Telegram Chat ID') ->maxLength(50) ->required(), @@ -242,6 +498,7 @@ public function form(Form $form): Form ->label('Recipients') ->schema([ Forms\Components\TextInput::make('url') + ->placeholder('https://webhook.site/longstringofcharacters') ->maxLength(2000) ->required() ->url(), diff --git a/app/Listeners/Discord/SendSpeedtestCompletedNotification.php b/app/Listeners/Discord/SendSpeedtestCompletedNotification.php index bca6ba2ac..5200886e2 100644 --- a/app/Listeners/Discord/SendSpeedtestCompletedNotification.php +++ b/app/Listeners/Discord/SendSpeedtestCompletedNotification.php @@ -43,6 +43,7 @@ public function handle(SpeedtestCompleted $event): void 'download' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), 'upload' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), 'packetLoss' => $event->result->packet_loss, + 'speedtest_url' => $event->result->result_url, 'url' => url('/admin/results'), ])->render(), ]; diff --git a/app/Listeners/Discord/SendSpeedtestThresholdNotification.php b/app/Listeners/Discord/SendSpeedtestThresholdNotification.php index fc7ad9446..4fad0a5eb 100644 --- a/app/Listeners/Discord/SendSpeedtestThresholdNotification.php +++ b/app/Listeners/Discord/SendSpeedtestThresholdNotification.php @@ -69,6 +69,7 @@ public function handle(SpeedtestCompleted $event): void 'serverId' => $event->result->server_id, 'isp' => $event->result->isp, 'metrics' => $failed, + 'speedtest_url' => $event->result->result_url, 'url' => url('/admin/results'), ])->render(), ]; diff --git a/app/Listeners/Gotify/SendSpeedtestCompletedNotification.php b/app/Listeners/Gotify/SendSpeedtestCompletedNotification.php new file mode 100644 index 000000000..8265426c9 --- /dev/null +++ b/app/Listeners/Gotify/SendSpeedtestCompletedNotification.php @@ -0,0 +1,59 @@ +gotify_enabled) { + return; + } + + if (! $notificationSettings->gotify_on_speedtest_run) { + return; + } + + if (! count($notificationSettings->gotify_webhooks)) { + Log::warning('Gotify urls not found, check Gotify notification channel settings.'); + + return; + } + + $payload = [ + 'message' => view('gotify.speedtest-completed', [ + 'id' => $event->result->id, + 'service' => Str::title($event->result->service), + 'serverName' => $event->result->server_name, + 'serverId' => $event->result->server_id, + 'isp' => $event->result->isp, + 'ping' => round($event->result->ping).' ms', + 'download' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), + 'upload' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), + 'packetLoss' => $event->result->packet_loss, + 'speedtest_url' => $event->result->result_url, + 'url' => url('/admin/results'), + ])->render(), + ]; + + foreach ($notificationSettings->gotify_webhooks as $url) { + WebhookCall::create() + ->url($url['url']) + ->payload($payload) + ->doNotSign() + ->dispatch(); + } + } +} diff --git a/app/Listeners/Gotify/SendSpeedtestThresholdNotification.php b/app/Listeners/Gotify/SendSpeedtestThresholdNotification.php new file mode 100644 index 000000000..8da2daedf --- /dev/null +++ b/app/Listeners/Gotify/SendSpeedtestThresholdNotification.php @@ -0,0 +1,133 @@ +gotify_enabled) { + return; + } + + if (! $notificationSettings->gotify_on_threshold_failure) { + return; + } + + if (! count($notificationSettings->gotify_webhooks)) { + Log::warning('Gotify urls not found, check gotify notification channel settings.'); + + return; + } + + $thresholdSettings = new ThresholdSettings(); + + if (! $thresholdSettings->absolute_enabled) { + return; + } + + $failed = []; + + if ($thresholdSettings->absolute_download > 0) { + array_push($failed, $this->absoluteDownloadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_upload > 0) { + array_push($failed, $this->absoluteUploadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_ping > 0) { + array_push($failed, $this->absolutePingThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + $failed = array_filter($failed); + + if (! count($failed)) { + Log::warning('Failed Gotify thresholds not found, won\'t send notification.'); + + return; + } + + $payload = [ + 'message' => view('gotify.speedtest-threshold', [ + 'id' => $event->result->id, + 'service' => Str::title($event->result->service), + 'serverName' => $event->result->server_name, + 'serverId' => $event->result->server_id, + 'isp' => $event->result->isp, + 'metrics' => $failed, + 'speedtest_url' => $event->result->result_url, + 'url' => url('/admin/results'), + ])->render(), + ]; + + foreach ($notificationSettings->gotify_webhooks as $url) { + WebhookCall::create() + ->url($url['url']) + ->payload($payload) + ->doNotSign() + ->dispatch(); + } + } + + /** + * Build gotify notification if absolute download threshold is breached. + */ + protected function absoluteDownloadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteDownloadThresholdFailed($thresholdSettings->absolute_download, $event->result->download)) { + return false; + } + + return [ + 'name' => 'Download', + 'threshold' => $thresholdSettings->absolute_download.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), + ]; + } + + /** + * Build gotify notification if absolute upload threshold is breached. + */ + protected function absoluteUploadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteUploadThresholdFailed($thresholdSettings->absolute_upload, $event->result->upload)) { + return false; + } + + return [ + 'name' => 'Upload', + 'threshold' => $thresholdSettings->absolute_upload.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), + ]; + } + + /** + * Build gotify notification if absolute ping threshold is breached. + */ + protected function absolutePingThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absolutePingThresholdFailed($thresholdSettings->absolute_ping, $event->result->ping)) { + return false; + } + + return [ + 'name' => 'Ping', + 'threshold' => $thresholdSettings->absolute_ping.' ms', + 'value' => round($event->result->ping, 2).' ms', + ]; + } +} diff --git a/app/Listeners/HealthCheck/SendSpeedtestCompletedNotification.php b/app/Listeners/HealthCheck/SendSpeedtestCompletedNotification.php new file mode 100644 index 000000000..3d127f59b --- /dev/null +++ b/app/Listeners/HealthCheck/SendSpeedtestCompletedNotification.php @@ -0,0 +1,51 @@ +healthcheck_enabled) { + return; + } + + if (! $notificationSettings->healthcheck_on_speedtest_run) { + return; + } + + if (! count($notificationSettings->healthcheck_webhooks)) { + Log::warning('healthcheck urls not found, check healthcheck notification channel settings.'); + + return; + } + + foreach ($notificationSettings->healthcheck_webhooks as $url) { + WebhookCall::create() + ->url($url['url']) + ->payload([ + 'result_id' => $event->result->id, + 'site_name' => config('app.name'), + 'isp' => $event->result->isp, + 'ping' => $event->result->ping, + 'download' => $event->result->downloadBits, + 'upload' => $event->result->uploadBits, + 'packetLoss' => $event->result->packet_loss, + 'speedtest_url' => $event->result->result_url, + 'url' => url('/admin/results'), + ]) + ->doNotSign() + ->dispatch(); + } + } +} diff --git a/app/Listeners/HealthCheck/SendSpeedtestThresholdNotification.php b/app/Listeners/HealthCheck/SendSpeedtestThresholdNotification.php new file mode 100644 index 000000000..11db9fec1 --- /dev/null +++ b/app/Listeners/HealthCheck/SendSpeedtestThresholdNotification.php @@ -0,0 +1,126 @@ +healthcheck_enabled) { + return; + } + + if (! $notificationSettings->healthcheck_on_threshold_failure) { + return; + } + + if (! count($notificationSettings->healthcheck_webhooks)) { + Log::warning('HealthCheck urls not found, check healthcheck notification channel settings.'); + + return; + } + + $thresholdSettings = new ThresholdSettings(); + + if (! $thresholdSettings->absolute_enabled) { + return; + } + + $failed = []; + + if ($thresholdSettings->absolute_download > 0) { + array_push($failed, $this->absoluteDownloadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_upload > 0) { + array_push($failed, $this->absoluteUploadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_ping > 0) { + array_push($failed, $this->absolutePingThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + $failed = array_filter($failed); + + if (! count($failed)) { + Log::warning('Failed healthcheck thresholds not found, won\'t send notification.'); + + return; + } + + foreach ($notificationSettings->healthcheck_webhooks as $url) { + WebhookCall::create() + ->url($url['url'].'/fail') + ->payload([ + 'result_id' => $event->result->id, + 'site_name' => config('app.name'), + 'isp' => $event->result->isp, + 'metrics' => $failed, + 'speedtest_url' => $event->result->result_url, + 'url' => url('/admin/results'), + ]) + ->doNotSign() + ->dispatch(); + } + } + + /** + * Build HealthCheck notification if absolute download threshold is breached. + */ + protected function absoluteDownloadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteDownloadThresholdFailed($thresholdSettings->absolute_download, $event->result->download)) { + return false; + } + + return [ + 'name' => 'Download', + 'threshold' => $thresholdSettings->absolute_download.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), + ]; + } + + /** + * Build Healthcheck notification if absolute upload threshold is breached. + */ + protected function absoluteUploadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteUploadThresholdFailed($thresholdSettings->absolute_upload, $event->result->upload)) { + return false; + } + + return [ + 'name' => 'Upload', + 'threshold' => $thresholdSettings->absolute_upload.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), + ]; + } + + /** + * Build Healthcheck notification if absolute ping threshold is breached. + */ + protected function absolutePingThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absolutePingThresholdFailed($thresholdSettings->absolute_ping, $event->result->ping)) { + return false; + } + + return [ + 'name' => 'Ping', + 'threshold' => $thresholdSettings->absolute_ping.' ms', + 'value' => round($event->result->ping, 2).' ms', + ]; + } +} diff --git a/app/Listeners/Ntfy/SendSpeedtestCompletedNotification.php b/app/Listeners/Ntfy/SendSpeedtestCompletedNotification.php new file mode 100644 index 000000000..f63cdcd13 --- /dev/null +++ b/app/Listeners/Ntfy/SendSpeedtestCompletedNotification.php @@ -0,0 +1,69 @@ +ntfy_enabled) { + return; + } + + if (! $notificationSettings->ntfy_on_speedtest_run) { + return; + } + + if (! count($notificationSettings->ntfy_webhooks)) { + Log::warning('Ntfy urls not found, check Ntfy notification channel settings.'); + + return; + } + + $payload = + view('ntfy.speedtest-completed', [ + 'id' => $event->result->id, + 'service' => Str::title($event->result->service), + 'serverName' => $event->result->server_name, + 'serverId' => $event->result->server_id, + 'isp' => $event->result->isp, + 'ping' => round($event->result->ping).' ms', + 'download' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), + 'upload' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), + 'packetLoss' => $event->result->packet_loss, + 'speedtest_url' => $event->result->result_url, + 'url' => url('/admin/results'), + ])->render(); + + foreach ($notificationSettings->ntfy_webhooks as $url) { + $webhookCall = WebhookCall::create() + ->url($url['url']) + ->payload([ + 'topic' => $url['topic'], + 'message' => $payload, + ]) + ->doNotSign(); + + // Only add authentication if username and password are provided + if (! empty($url['username']) && ! empty($url['password'])) { + $authHeader = 'Basic '.base64_encode($url['username'].':'.$url['password']); + $webhookCall->withHeaders([ + 'Authorization' => $authHeader, + ]); + } + $webhookCall->dispatch(); + } + } +} diff --git a/app/Listeners/Ntfy/SendSpeedtestThresholdNotification.php b/app/Listeners/Ntfy/SendSpeedtestThresholdNotification.php new file mode 100644 index 000000000..2ca1a93e1 --- /dev/null +++ b/app/Listeners/Ntfy/SendSpeedtestThresholdNotification.php @@ -0,0 +1,144 @@ +ntfy_enabled) { + return; + } + + if (! $notificationSettings->ntfy_on_threshold_failure) { + return; + } + + if (! count($notificationSettings->ntfy_webhooks)) { + Log::warning('Ntfy urls not found, check Ntfy notification channel settings.'); + + return; + } + + $thresholdSettings = new ThresholdSettings(); + + if (! $thresholdSettings->absolute_enabled) { + return; + } + + $failed = []; + + if ($thresholdSettings->absolute_download > 0) { + array_push($failed, $this->absoluteDownloadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_upload > 0) { + array_push($failed, $this->absoluteUploadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_ping > 0) { + array_push($failed, $this->absolutePingThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + $failed = array_filter($failed); + + if (! count($failed)) { + Log::warning('Failed ntfy thresholds not found, won\'t send notification.'); + + return; + } + + $payload = + view('ntfy.speedtest-threshold', [ + 'id' => $event->result->id, + 'service' => Str::title($event->result->service), + 'serverName' => $event->result->server_name, + 'serverId' => $event->result->server_id, + 'isp' => $event->result->isp, + 'metrics' => $failed, + 'speedtest_url' => $event->result->result_url, + 'url' => url('/admin/results'), + ])->render(); + + foreach ($notificationSettings->ntfy_webhooks as $url) { + $webhookCall = WebhookCall::create() + ->url($url['url']) + ->payload([ + 'topic' => $url['topic'], + 'message' => $payload, + ]) + ->doNotSign(); + + // Only add authentication if username and password are provided + if (! empty($url['username']) && ! empty($url['password'])) { + $authHeader = 'Basic '.base64_encode($url['username'].':'.$url['password']); + $webhookCall->withHeaders([ + 'Authorization' => $authHeader, + ]); + } + + $webhookCall->dispatch(); + } + } + + /** + * Build Ntfy notification if absolute download threshold is breached. + */ + protected function absoluteDownloadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteDownloadThresholdFailed($thresholdSettings->absolute_download, $event->result->download)) { + return false; + } + + return [ + 'name' => 'Download', + 'threshold' => $thresholdSettings->absolute_download.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), + ]; + } + + /** + * Build Ntfy notification if absolute upload threshold is breached. + */ + protected function absoluteUploadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteUploadThresholdFailed($thresholdSettings->absolute_upload, $event->result->upload)) { + return false; + } + + return [ + 'name' => 'Upload', + 'threshold' => $thresholdSettings->absolute_upload.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), + ]; + } + + /** + * Build Ntfy notification if absolute ping threshold is breached. + */ + protected function absolutePingThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absolutePingThresholdFailed($thresholdSettings->absolute_ping, $event->result->ping)) { + return false; + } + + return [ + 'name' => 'Ping', + 'threshold' => $thresholdSettings->absolute_ping.' ms', + 'value' => round($event->result->ping, 2).' ms', + ]; + } +} diff --git a/app/Listeners/Pushover/SendSpeedtestCompletedNotification.php b/app/Listeners/Pushover/SendSpeedtestCompletedNotification.php new file mode 100644 index 000000000..465c98f9d --- /dev/null +++ b/app/Listeners/Pushover/SendSpeedtestCompletedNotification.php @@ -0,0 +1,63 @@ +pushover_enabled) { + return; + } + + if (! $notificationSettings->pushover_on_speedtest_run) { + return; + } + + if (! count($notificationSettings->pushover_webhooks)) { + Log::warning('Pushover urls not found, check Pushover notification channel settings.'); + + return; + } + + $payload = [ + view('pushover.speedtest-completed', [ + 'id' => $event->result->id, + 'service' => Str::title($event->result->service), + 'serverName' => $event->result->server_name, + 'serverId' => $event->result->server_id, + 'isp' => $event->result->isp, + 'ping' => round($event->result->ping).' ms', + 'download' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), + 'upload' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), + 'packetLoss' => $event->result->packet_loss, + 'speedtest_url' => $event->result->result_url, + 'url' => url('/admin/results'), + ])->render(), + ]; + + foreach ($notificationSettings->pushover_webhooks as $url) { + WebhookCall::create() + ->url($url['url']) + ->payload([ + 'token' => $url['api_token'], + 'user' => $url['user_key'], + 'message' => $payload, + ]) + ->doNotSign() + ->dispatch(); + } + } +} diff --git a/app/Listeners/Pushover/SendSpeedtestThresholdNotification.php b/app/Listeners/Pushover/SendSpeedtestThresholdNotification.php new file mode 100644 index 000000000..7643702f8 --- /dev/null +++ b/app/Listeners/Pushover/SendSpeedtestThresholdNotification.php @@ -0,0 +1,137 @@ +pushover_enabled) { + return; + } + + if (! $notificationSettings->pushover_on_threshold_failure) { + return; + } + + if (! count($notificationSettings->pushover_webhooks)) { + Log::warning('Pushover urls not found, check Pushover notification channel settings.'); + + return; + } + + $thresholdSettings = new ThresholdSettings(); + + if (! $thresholdSettings->absolute_enabled) { + return; + } + + $failed = []; + + if ($thresholdSettings->absolute_download > 0) { + array_push($failed, $this->absoluteDownloadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_upload > 0) { + array_push($failed, $this->absoluteUploadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_ping > 0) { + array_push($failed, $this->absolutePingThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + $failed = array_filter($failed); + + if (! count($failed)) { + Log::warning('Failed Pushover thresholds not found, won\'t send notification.'); + + return; + } + + $payload = [ + view('pushover.speedtest-threshold', [ + 'id' => $event->result->id, + 'service' => Str::title($event->result->service), + 'serverName' => $event->result->server_name, + 'serverId' => $event->result->server_id, + 'isp' => $event->result->isp, + 'metrics' => $failed, + 'speedtest_url' => $event->result->result_url, + 'url' => url('/admin/results'), + ])->render(), + ]; + + foreach ($notificationSettings->pushover_webhooks as $url) { + WebhookCall::create() + ->url($url['url']) + ->payload([ + 'token' => $url['api_token'], + 'user' => $url['user_key'], + 'message' => $payload, + ]) + ->doNotSign() + ->dispatch(); + } + } + + /** + * Build Pushover notification if absolute download threshold is breached. + */ + protected function absoluteDownloadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteDownloadThresholdFailed($thresholdSettings->absolute_download, $event->result->download)) { + return false; + } + + return [ + 'name' => 'Download', + 'threshold' => $thresholdSettings->absolute_download.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), + ]; + } + + /** + * Build Pushover notification if absolute upload threshold is breached. + */ + protected function absoluteUploadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteUploadThresholdFailed($thresholdSettings->absolute_upload, $event->result->upload)) { + return false; + } + + return [ + 'name' => 'Upload', + 'threshold' => $thresholdSettings->absolute_upload.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), + ]; + } + + /** + * Build Pushover notification if absolute ping threshold is breached. + */ + protected function absolutePingThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absolutePingThresholdFailed($thresholdSettings->absolute_ping, $event->result->ping)) { + return false; + } + + return [ + 'name' => 'Ping', + 'threshold' => $thresholdSettings->absolute_ping.' ms', + 'value' => round($event->result->ping, 2).' ms', + ]; + } +} diff --git a/app/Listeners/Slack/SendSpeedtestCompletedNotification.php b/app/Listeners/Slack/SendSpeedtestCompletedNotification.php new file mode 100644 index 000000000..791aaad33 --- /dev/null +++ b/app/Listeners/Slack/SendSpeedtestCompletedNotification.php @@ -0,0 +1,59 @@ +slack_enabled) { + return; + } + + if (! $notificationSettings->slack_on_speedtest_run) { + return; + } + + if (! count($notificationSettings->slack_webhooks)) { + Log::warning('Slack URLs not found, check Slack notification channel settings.'); + + return; + } + + $payload = [ + 'text' => view('slack.speedtest-completed', [ + 'id' => $event->result->id, + 'service' => Str::title($event->result->service), + 'serverName' => $event->result->server_name, + 'serverId' => $event->result->server_id, + 'isp' => $event->result->isp, + 'ping' => round($event->result->ping).' ms', + 'download' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), + 'upload' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), + 'packetLoss' => $event->result->packet_loss, + 'speedtest_url' => $event->result->result_url, + 'url' => url('/admin/results'), + ])->render(), + ]; + + foreach ($notificationSettings->slack_webhooks as $url) { + WebhookCall::create() + ->url($url['url']) + ->payload($payload) + ->doNotSign() + ->dispatch(); + } + } +} diff --git a/app/Listeners/Slack/SendSpeedtestThresholdNotification.php b/app/Listeners/Slack/SendSpeedtestThresholdNotification.php new file mode 100644 index 000000000..debeb76cf --- /dev/null +++ b/app/Listeners/Slack/SendSpeedtestThresholdNotification.php @@ -0,0 +1,133 @@ +slack_enabled) { + return; + } + + if (! $notificationSettings->slack_on_threshold_failure) { + return; + } + + if (! count($notificationSettings->slack_webhooks)) { + Log::warning('Slack urls not found, check Slack notification channel settings.'); + + return; + } + + $thresholdSettings = new ThresholdSettings(); + + if (! $thresholdSettings->absolute_enabled) { + return; + } + + $failed = []; + + if ($thresholdSettings->absolute_download > 0) { + array_push($failed, $this->absoluteDownloadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_upload > 0) { + array_push($failed, $this->absoluteUploadThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + if ($thresholdSettings->absolute_ping > 0) { + array_push($failed, $this->absolutePingThreshold(event: $event, thresholdSettings: $thresholdSettings)); + } + + $failed = array_filter($failed); + + if (! count($failed)) { + Log::warning('Failed Slack thresholds not found, won\'t send notification.'); + + return; + } + + $payload = [ + 'text' => view('slack.speedtest-threshold', [ + 'id' => $event->result->id, + 'service' => Str::title($event->result->service), + 'serverName' => $event->result->server_name, + 'serverId' => $event->result->server_id, + 'isp' => $event->result->isp, + 'metrics' => $failed, + 'speedtest_url' => $event->result->result_url, + 'url' => url('/admin/results'), + ])->render(), + ]; + + foreach ($notificationSettings->slack_webhooks as $url) { + WebhookCall::create() + ->url($url['url']) + ->payload($payload) + ->doNotSign() + ->dispatch(); + } + } + + /** + * Build Slack notification if absolute download threshold is breached. + */ + protected function absoluteDownloadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteDownloadThresholdFailed($thresholdSettings->absolute_download, $event->result->download)) { + return false; + } + + return [ + 'name' => 'Download', + 'threshold' => $thresholdSettings->absolute_download.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), + ]; + } + + /** + * Build Slack notification if absolute upload threshold is breached. + */ + protected function absoluteUploadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absoluteUploadThresholdFailed($thresholdSettings->absolute_upload, $event->result->upload)) { + return false; + } + + return [ + 'name' => 'Upload', + 'threshold' => $thresholdSettings->absolute_upload.' Mbps', + 'value' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), + ]; + } + + /** + * Build Slack notification if absolute ping threshold is breached. + */ + protected function absolutePingThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array + { + if (! absolutePingThresholdFailed($thresholdSettings->absolute_ping, $event->result->ping)) { + return false; + } + + return [ + 'name' => 'Ping', + 'threshold' => $thresholdSettings->absolute_ping.' ms', + 'value' => round($event->result->ping, 2).' ms', + ]; + } +} diff --git a/app/Listeners/Telegram/SendSpeedtestCompletedNotification.php b/app/Listeners/Telegram/SendSpeedtestCompletedNotification.php index be407f3c2..93e69fbb4 100644 --- a/app/Listeners/Telegram/SendSpeedtestCompletedNotification.php +++ b/app/Listeners/Telegram/SendSpeedtestCompletedNotification.php @@ -43,6 +43,7 @@ public function handle(SpeedtestCompleted $event): void 'download' => Number::toBitRate(bits: $event->result->download_bits, precision: 2), 'upload' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2), 'packetLoss' => is_numeric($event->result->packet_loss) ? round($event->result->packet_loss, 2) : 'n/a', + 'speedtest_url' => $event->result->result_url, 'url' => url('/admin/results'), ])->render(); diff --git a/app/Listeners/Telegram/SendSpeedtestThresholdNotification.php b/app/Listeners/Telegram/SendSpeedtestThresholdNotification.php index d906cc593..6ae2375b2 100644 --- a/app/Listeners/Telegram/SendSpeedtestThresholdNotification.php +++ b/app/Listeners/Telegram/SendSpeedtestThresholdNotification.php @@ -69,6 +69,7 @@ public function handle(SpeedtestCompleted $event): void 'serverId' => $event->result->server_id, 'isp' => $event->result->isp, 'metrics' => $failed, + 'speedtest_url' => $event->result->result_url, 'url' => url('/admin/results'), ])->render(); diff --git a/app/Listeners/Webhook/SendSpeedtestCompletedNotification.php b/app/Listeners/Webhook/SendSpeedtestCompletedNotification.php index 88560ce20..3956d2bed 100644 --- a/app/Listeners/Webhook/SendSpeedtestCompletedNotification.php +++ b/app/Listeners/Webhook/SendSpeedtestCompletedNotification.php @@ -41,6 +41,7 @@ public function handle(SpeedtestCompleted $event): void 'download' => $event->result->downloadBits, 'upload' => $event->result->uploadBits, 'packetLoss' => $event->result->packet_loss, + 'speedtest_url' => $event->result->result_url, 'url' => url('/admin/results'), ]) ->doNotSign() diff --git a/app/Listeners/Webhook/SendSpeedtestThresholdNotification.php b/app/Listeners/Webhook/SendSpeedtestThresholdNotification.php index aac1866a7..8bf45d768 100644 --- a/app/Listeners/Webhook/SendSpeedtestThresholdNotification.php +++ b/app/Listeners/Webhook/SendSpeedtestThresholdNotification.php @@ -68,6 +68,7 @@ public function handle(SpeedtestCompleted $event): void 'site_name' => config('app.name'), 'isp' => $event->result->isp, 'metrics' => $failed, + 'speedtest_url' => $event->result->result_url, 'url' => url('/admin/results'), ]) ->doNotSign() diff --git a/app/Mail/SpeedtestCompletedMail.php b/app/Mail/SpeedtestCompletedMail.php index f8480f1bb..c6d43bfc3 100644 --- a/app/Mail/SpeedtestCompletedMail.php +++ b/app/Mail/SpeedtestCompletedMail.php @@ -52,6 +52,7 @@ public function content(): Content 'download' => Number::toBitRate(bits: $this->result->download_bits, precision: 2), 'upload' => Number::toBitRate(bits: $this->result->upload_bits, precision: 2), 'packetLoss' => is_numeric($this->result->packet_loss) ? $this->result->packet_loss : 'n/a', + 'speedtest_url' => $this->result->result_url, 'url' => url('/admin/results'), ], ); diff --git a/app/Mail/SpeedtestThresholdMail.php b/app/Mail/SpeedtestThresholdMail.php index 46202f28f..038709e90 100644 --- a/app/Mail/SpeedtestThresholdMail.php +++ b/app/Mail/SpeedtestThresholdMail.php @@ -48,6 +48,7 @@ public function content(): Content 'serverName' => $this->result->server_name, 'serverId' => $this->result->server_id, 'isp' => $this->result->isp, + 'speedtest_url' => $this->result->result_url, 'url' => url('/admin/results'), 'metrics' => $this->metrics, ], diff --git a/app/Settings/NotificationSettings.php b/app/Settings/NotificationSettings.php index ff2d4d22a..0796be61a 100644 --- a/app/Settings/NotificationSettings.php +++ b/app/Settings/NotificationSettings.php @@ -46,6 +46,46 @@ class NotificationSettings extends Settings public ?array $discord_webhooks; + public bool $ntfy_enabled; + + public bool $ntfy_on_speedtest_run; + + public bool $ntfy_on_threshold_failure; + + public ?array $ntfy_webhooks; + + public bool $pushover_enabled; + + public bool $pushover_on_speedtest_run; + + public bool $pushover_on_threshold_failure; + + public ?array $pushover_webhooks; + + public bool $healthcheck_enabled; + + public bool $healthcheck_on_speedtest_run; + + public bool $healthcheck_on_threshold_failure; + + public ?array $healthcheck_webhooks; + + public bool $slack_enabled; + + public bool $slack_on_speedtest_run; + + public bool $slack_on_threshold_failure; + + public ?array $slack_webhooks; + + public bool $gotify_enabled; + + public bool $gotify_on_speedtest_run; + + public bool $gotify_on_threshold_failure; + + public ?array $gotify_webhooks; + public static function group(): string { return 'notification'; diff --git a/database/settings/2024_02_22_144620_create_healthcheck_notification_settings.php b/database/settings/2024_02_22_144620_create_healthcheck_notification_settings.php new file mode 100644 index 000000000..b5dd742bb --- /dev/null +++ b/database/settings/2024_02_22_144620_create_healthcheck_notification_settings.php @@ -0,0 +1,14 @@ +migrator->add('notification.healthcheck_enabled', false); + $this->migrator->add('notification.healthcheck_on_speedtest_run', false); + $this->migrator->add('notification.healthcheck_on_threshold_failure', false); + $this->migrator->add('notification.healthcheck_webhooks', null); + } +}; diff --git a/database/settings/2024_02_22_144650_create_ntfy_notification_settings.php b/database/settings/2024_02_22_144650_create_ntfy_notification_settings.php new file mode 100644 index 000000000..aca36e39f --- /dev/null +++ b/database/settings/2024_02_22_144650_create_ntfy_notification_settings.php @@ -0,0 +1,14 @@ +migrator->add('notification.ntfy_enabled', false); + $this->migrator->add('notification.ntfy_on_speedtest_run', false); + $this->migrator->add('notification.ntfy_on_threshold_failure', false); + $this->migrator->add('notification.ntfy_webhooks', null); + } +}; diff --git a/database/settings/2024_02_22_144650_create_slack_notification_settings.php b/database/settings/2024_02_22_144650_create_slack_notification_settings.php new file mode 100644 index 000000000..755a70b74 --- /dev/null +++ b/database/settings/2024_02_22_144650_create_slack_notification_settings.php @@ -0,0 +1,14 @@ +migrator->add('notification.slack_enabled', false); + $this->migrator->add('notification.slack_on_speedtest_run', false); + $this->migrator->add('notification.slack_on_threshold_failure', false); + $this->migrator->add('notification.slack_webhooks', null); + } +}; diff --git a/database/settings/2024_02_22_144654_create_gotify_notification_settings.php b/database/settings/2024_02_22_144654_create_gotify_notification_settings.php new file mode 100644 index 000000000..af0d818d7 --- /dev/null +++ b/database/settings/2024_02_22_144654_create_gotify_notification_settings.php @@ -0,0 +1,14 @@ +migrator->add('notification.gotify_enabled', false); + $this->migrator->add('notification.gotify_on_speedtest_run', false); + $this->migrator->add('notification.gotify_on_threshold_failure', false); + $this->migrator->add('notification.gotify_webhooks', null); + } +}; diff --git a/database/settings/2024_02_22_144680_create_pushover_notification_settings.php b/database/settings/2024_02_22_144680_create_pushover_notification_settings.php new file mode 100644 index 000000000..92f394001 --- /dev/null +++ b/database/settings/2024_02_22_144680_create_pushover_notification_settings.php @@ -0,0 +1,14 @@ +migrator->add('notification.pushover_enabled', false); + $this->migrator->add('notification.pushover_on_speedtest_run', false); + $this->migrator->add('notification.pushover_on_threshold_failure', false); + $this->migrator->add('notification.pushover_webhooks', null); + } +}; diff --git a/package-lock.json b/package-lock.json index 78efd58e2..a6756dad6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -539,9 +539,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz", - "integrity": "sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz", + "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==", "cpu": [ "arm" ], @@ -553,9 +553,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.1.tgz", - "integrity": "sha512-thFUbkHteM20BGShD6P08aungq4irbIZKUNbG70LN8RkO7YztcGPiKTTGZS7Kw+x5h8hOXs0i4OaHwFxlpQN6A==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz", + "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==", "cpu": [ "arm64" ], @@ -567,9 +567,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.1.tgz", - "integrity": "sha512-8o6eqeFZzVLia2hKPUZk4jdE3zW7LCcZr+MD18tXkgBBid3lssGVAYuox8x6YHoEPDdDa9ixTaStcmx88lio5Q==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz", + "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==", "cpu": [ "arm64" ], @@ -581,9 +581,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.1.tgz", - "integrity": "sha512-4T42heKsnbjkn7ovYiAdDVRRWZLU9Kmhdt6HafZxFcUdpjlBlxj4wDrt1yFWLk7G4+E+8p2C9tcmSu0KA6auGA==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz", + "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==", "cpu": [ "x64" ], @@ -595,9 +595,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.1.tgz", - "integrity": "sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz", + "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==", "cpu": [ "arm" ], @@ -609,9 +609,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.1.tgz", - "integrity": "sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz", + "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==", "cpu": [ "arm" ], @@ -623,9 +623,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.1.tgz", - "integrity": "sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz", + "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==", "cpu": [ "arm64" ], @@ -637,9 +637,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.1.tgz", - "integrity": "sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz", + "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==", "cpu": [ "arm64" ], @@ -651,9 +651,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.1.tgz", - "integrity": "sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz", + "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==", "cpu": [ "ppc64" ], @@ -665,9 +665,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.1.tgz", - "integrity": "sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz", + "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==", "cpu": [ "riscv64" ], @@ -679,9 +679,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.1.tgz", - "integrity": "sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz", + "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==", "cpu": [ "s390x" ], @@ -693,9 +693,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.1.tgz", - "integrity": "sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", + "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", "cpu": [ "x64" ], @@ -707,9 +707,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.1.tgz", - "integrity": "sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz", + "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==", "cpu": [ "x64" ], @@ -721,9 +721,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.1.tgz", - "integrity": "sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz", + "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==", "cpu": [ "arm64" ], @@ -735,9 +735,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.1.tgz", - "integrity": "sha512-LdxxcqRVSXi6k6JUrTah1rHuaupoeuiv38du8Mt4r4IPer3kwlTo+RuvfE8KzZ/tL6BhaPlzJ3835i6CxrFIRQ==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz", + "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==", "cpu": [ "ia32" ], @@ -749,9 +749,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz", - "integrity": "sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz", + "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==", "cpu": [ "x64" ], @@ -776,9 +776,9 @@ } }, "node_modules/@tailwindcss/typography": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.13.tgz", - "integrity": "sha512-ADGcJ8dX21dVVHIwTRgzrcunY6YY9uSlAHHGVKvkA+vLc5qLwEszvKts40lx7z0qc4clpjclwLeK5rVCV2P/uw==", + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.14.tgz", + "integrity": "sha512-ZvOCjUbsJBjL9CxQBn+VEnFpouzuKhxh2dH8xMIWHILL+HfOYtlAkWcyoon8LlzE53d2Yo6YO6pahKKNW3q1YQ==", "dev": true, "license": "MIT", "dependencies": { @@ -853,9 +853,9 @@ "license": "MIT" }, "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "funding": [ { @@ -873,11 +873,11 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -934,9 +934,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, "funding": [ { @@ -954,9 +954,9 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, "bin": { @@ -977,9 +977,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001643", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz", - "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==", + "version": "1.0.30001651", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", "dev": true, "funding": [ { @@ -1115,9 +1115,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.2.tgz", - "integrity": "sha512-kc4r3U3V3WLaaZqThjYz/Y6z8tJe+7K0bbjUVo3i+LWIypVdMx5nXCkwRe6SWbY6ILqLdc1rKcKmr3HoH7wjSQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.5.tgz", + "integrity": "sha512-QR7/A7ZkMS8tZuoftC/jfqNkZLQO779SSW3YuZHP4eXpj3EffGLFcB/Xu9AAZQzLccTiCV+EmUo3ha4mQ9wnlA==", "dev": true, "license": "ISC" }, @@ -1231,9 +1231,9 @@ } }, "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, "license": "ISC", "dependencies": { @@ -1723,9 +1723,9 @@ } }, "node_modules/postcss": { - "version": "8.4.40", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz", - "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==", + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "dev": true, "funding": [ { @@ -1973,9 +1973,9 @@ } }, "node_modules/rollup": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.1.tgz", - "integrity": "sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", + "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", "dev": true, "license": "MIT", "dependencies": { @@ -1989,22 +1989,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.19.1", - "@rollup/rollup-android-arm64": "4.19.1", - "@rollup/rollup-darwin-arm64": "4.19.1", - "@rollup/rollup-darwin-x64": "4.19.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.19.1", - "@rollup/rollup-linux-arm-musleabihf": "4.19.1", - "@rollup/rollup-linux-arm64-gnu": "4.19.1", - "@rollup/rollup-linux-arm64-musl": "4.19.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.19.1", - "@rollup/rollup-linux-riscv64-gnu": "4.19.1", - "@rollup/rollup-linux-s390x-gnu": "4.19.1", - "@rollup/rollup-linux-x64-gnu": "4.19.1", - "@rollup/rollup-linux-x64-musl": "4.19.1", - "@rollup/rollup-win32-arm64-msvc": "4.19.1", - "@rollup/rollup-win32-ia32-msvc": "4.19.1", - "@rollup/rollup-win32-x64-msvc": "4.19.1", + "@rollup/rollup-android-arm-eabi": "4.20.0", + "@rollup/rollup-android-arm64": "4.20.0", + "@rollup/rollup-darwin-arm64": "4.20.0", + "@rollup/rollup-darwin-x64": "4.20.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.20.0", + "@rollup/rollup-linux-arm-musleabihf": "4.20.0", + "@rollup/rollup-linux-arm64-gnu": "4.20.0", + "@rollup/rollup-linux-arm64-musl": "4.20.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.20.0", + "@rollup/rollup-linux-riscv64-gnu": "4.20.0", + "@rollup/rollup-linux-s390x-gnu": "4.20.0", + "@rollup/rollup-linux-x64-gnu": "4.20.0", + "@rollup/rollup-linux-x64-musl": "4.20.0", + "@rollup/rollup-win32-arm64-msvc": "4.20.0", + "@rollup/rollup-win32-ia32-msvc": "4.20.0", + "@rollup/rollup-win32-x64-msvc": "4.20.0", "fsevents": "~2.3.2" } }, @@ -2219,9 +2219,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.7.tgz", - "integrity": "sha512-rxWZbe87YJb4OcSopb7up2Ba4U82BoiSGUdoDr3Ydrg9ckxFS/YWsvhN323GMcddgU65QRy7JndC7ahhInhvlQ==", + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.9.tgz", + "integrity": "sha512-1SEOvRr6sSdV5IDf9iC+NU4dhwdqzF4zKKq3sAbasUWHEM6lsMhX+eNN5gkPx1BvLFEnZQEUFbXnGj8Qlp83Pg==", "dev": true, "license": "MIT", "dependencies": { @@ -2352,14 +2352,14 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz", - "integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", + "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.39", + "postcss": "^8.4.40", "rollup": "^4.13.0" }, "bin": { @@ -2379,6 +2379,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -2396,6 +2397,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, diff --git a/resources/views/discord/speedtest-completed.blade.php b/resources/views/discord/speedtest-completed.blade.php index dda7a8782..023fa9464 100644 --- a/resources/views/discord/speedtest-completed.blade.php +++ b/resources/views/discord/speedtest-completed.blade.php @@ -9,4 +9,5 @@ - **Download:** {{ $download }} - **Upload:** {{ $upload }} - **Packet Loss:** {{ $packetLoss }} **%** +- **Ookla Speedtest:** {{ $speedtest_url }} - **URL:** {{ $url }} diff --git a/resources/views/discord/speedtest-threshold.blade.php b/resources/views/discord/speedtest-threshold.blade.php index 2cb708643..dc38dd40c 100644 --- a/resources/views/discord/speedtest-threshold.blade.php +++ b/resources/views/discord/speedtest-threshold.blade.php @@ -5,4 +5,5 @@ @foreach ($metrics as $item) - **{{ $item['name'] }}** {{ $item['threshold'] }}: {{ $item['value'] }} @endforeach +- **Ookla Speedtest:** {{ $speedtest_url }} - **URL:** {{ $url }} diff --git a/resources/views/emails/speedtest-completed.blade.php b/resources/views/emails/speedtest-completed.blade.php index b8ce7d1d4..a9f3c1ac8 100644 --- a/resources/views/emails/speedtest-completed.blade.php +++ b/resources/views/emails/speedtest-completed.blade.php @@ -12,7 +12,8 @@ | Ping | {{ $ping }} | | Download | {{ $download }} | | Upload | {{ $upload }} | -| Packet Loss | {{ $packetLoss }}**%** | +| Packet Loss | {{ $packetLoss }} **%** | + @@ -20,6 +21,10 @@ View Results + +View Results on Ookla + + Thanks,
{{ config('app.name') }} diff --git a/resources/views/emails/speedtest-threshold.blade.php b/resources/views/emails/speedtest-threshold.blade.php index 18879fd8a..373e33f47 100644 --- a/resources/views/emails/speedtest-threshold.blade.php +++ b/resources/views/emails/speedtest-threshold.blade.php @@ -15,6 +15,10 @@ View Results + +View Results on Ookla + + Thanks,
{{ config('app.name') }} diff --git a/resources/views/gotify/speedtest-completed.blade.php b/resources/views/gotify/speedtest-completed.blade.php new file mode 100644 index 000000000..ee50f07e3 --- /dev/null +++ b/resources/views/gotify/speedtest-completed.blade.php @@ -0,0 +1,13 @@ +Speedtest Completed - #{{ $id }} + +A new speedtest was completed using {{ $service }}. + +- Server name: {{ $serverName }} +- Server ID: {{ $serverId }} +- ISP: {{ $isp }} +- Ping: {{ $ping }} +- Download: {{ $download }} +- Upload: {{ $upload }} +- Packet Loss: {{ $packetLoss }} % +- Ookla Speedtest:{{ $speedtest_url }} +- URL: {{ $url }} diff --git a/resources/views/gotify/speedtest-threshold.blade.php b/resources/views/gotify/speedtest-threshold.blade.php new file mode 100644 index 000000000..4a7fe325e --- /dev/null +++ b/resources/views/gotify/speedtest-threshold.blade.php @@ -0,0 +1,9 @@ +Speedtest Threshold Breached - #{{ $id }} + +A new speedtest was completed using {{ $service }} on {{ $isp }} but a threshold was breached. + +@foreach ($metrics as $item) +- {{ $item['name'] }} {{ $item['threshold'] }}: {{ $item['value'] }} +@endforeach +- Ookla Speedtest: {{ $speedtest_url }} +- URL: {{ $url }} diff --git a/resources/views/ntfy/speedtest-completed.blade.php b/resources/views/ntfy/speedtest-completed.blade.php new file mode 100644 index 000000000..5976b28d4 --- /dev/null +++ b/resources/views/ntfy/speedtest-completed.blade.php @@ -0,0 +1,13 @@ +Speedtest Completed - #{{ $id }} + +A new speedtest was completed using {{ $service }}. + +Server name: {{ $serverName }} +Server ID: {{ $serverId }} +ISP: {{ $isp }} +Ping: {{ $ping }} +Download: {{ $download }} +Upload: {{ $upload }} +Packet Loss: {{ $packetLoss }} % +Ookla Speedtest: {{ $speedtest_url }} +URL: {{ $url }} diff --git a/resources/views/ntfy/speedtest-threshold.blade.php b/resources/views/ntfy/speedtest-threshold.blade.php new file mode 100644 index 000000000..4a7fe325e --- /dev/null +++ b/resources/views/ntfy/speedtest-threshold.blade.php @@ -0,0 +1,9 @@ +Speedtest Threshold Breached - #{{ $id }} + +A new speedtest was completed using {{ $service }} on {{ $isp }} but a threshold was breached. + +@foreach ($metrics as $item) +- {{ $item['name'] }} {{ $item['threshold'] }}: {{ $item['value'] }} +@endforeach +- Ookla Speedtest: {{ $speedtest_url }} +- URL: {{ $url }} diff --git a/resources/views/pushover/speedtest-completed.blade.php b/resources/views/pushover/speedtest-completed.blade.php new file mode 100644 index 000000000..2a209023c --- /dev/null +++ b/resources/views/pushover/speedtest-completed.blade.php @@ -0,0 +1,13 @@ +Speedtest Completed - #{{ $id }} + +A new speedtest was completed using {{ $service }}. + +- Server name: {{ $serverName }} +- Server ID: {{ $serverId }} +- ISP: {{ $isp }} +- Ping: {{ $ping }} +- Download: {{ $download }} +- Upload: {{ $upload }} +- Packet Loss: {{ $packetLoss }} % +- Ookla Speedtest: {{ $speedtest_url }} +- URL: {{ $url }} diff --git a/resources/views/pushover/speedtest-threshold.blade.php b/resources/views/pushover/speedtest-threshold.blade.php new file mode 100644 index 000000000..4a7fe325e --- /dev/null +++ b/resources/views/pushover/speedtest-threshold.blade.php @@ -0,0 +1,9 @@ +Speedtest Threshold Breached - #{{ $id }} + +A new speedtest was completed using {{ $service }} on {{ $isp }} but a threshold was breached. + +@foreach ($metrics as $item) +- {{ $item['name'] }} {{ $item['threshold'] }}: {{ $item['value'] }} +@endforeach +- Ookla Speedtest: {{ $speedtest_url }} +- URL: {{ $url }} diff --git a/resources/views/slack/speedtest-completed.blade.php b/resources/views/slack/speedtest-completed.blade.php new file mode 100644 index 000000000..19875a61e --- /dev/null +++ b/resources/views/slack/speedtest-completed.blade.php @@ -0,0 +1,13 @@ +*Speedtest Completed - #{{ $id }}* + +A new speedtest was completed using *{{ $service }}*. + +- *Server name:* {{ $serverName }} +- *Server ID:* {{ $serverId }} +- *ISP:* {{ $isp }} +- *Ping:* {{ $ping }} +- *Download:* {{ $download }} +- *Upload:* {{ $upload }} +- *Packet Loss:* {{ $packetLoss }} *%* +- *Ookla Speedtest:* {{ $speedtest_url }} +- *URL:* {{ $url }} diff --git a/resources/views/slack/speedtest-threshold.blade.php b/resources/views/slack/speedtest-threshold.blade.php new file mode 100644 index 000000000..87a365730 --- /dev/null +++ b/resources/views/slack/speedtest-threshold.blade.php @@ -0,0 +1,9 @@ +**Speedtest Threshold Breached - #{{ $id }}** + +A new speedtest was completed using *{{ $service }}* on *{{ $isp }}* but a threshold was breached. + +@foreach ($metrics as $item) +- *{{ $item['name'] }}* {{ $item['threshold'] }}: {{ $item['value'] }} +@endforeach +- *Ookla Speedtest:* {{ $speedtest_url }} +- *URL:* {{ $url }} diff --git a/resources/views/telegram/speedtest-completed.blade.php b/resources/views/telegram/speedtest-completed.blade.php index 75cd08663..6b1ac81f6 100644 --- a/resources/views/telegram/speedtest-completed.blade.php +++ b/resources/views/telegram/speedtest-completed.blade.php @@ -9,4 +9,5 @@ - *Download:* {{ $download }} - *Upload:* {{ $upload }} - **Packet Loss:** {{ $packetLoss }}**%** +- **Ookla Speedtest:** {{ $speedtest_url }} - **URL:** {{ $url }} diff --git a/resources/views/telegram/speedtest-threshold.blade.php b/resources/views/telegram/speedtest-threshold.blade.php index 2cb708643..dc38dd40c 100644 --- a/resources/views/telegram/speedtest-threshold.blade.php +++ b/resources/views/telegram/speedtest-threshold.blade.php @@ -5,4 +5,5 @@ @foreach ($metrics as $item) - **{{ $item['name'] }}** {{ $item['threshold'] }}: {{ $item['value'] }} @endforeach +- **Ookla Speedtest:** {{ $speedtest_url }} - **URL:** {{ $url }} From f2d6825f190111e1d47d36d3a0157eec2ea0d31a Mon Sep 17 00:00:00 2001 From: Alex Justesen Date: Thu, 8 Aug 2024 13:07:26 -0400 Subject: [PATCH 3/3] Bumped build info (#1649) bumped build info --- config/speedtest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/speedtest.php b/config/speedtest.php index 3d532b5c8..01219ee8c 100644 --- a/config/speedtest.php +++ b/config/speedtest.php @@ -4,9 +4,9 @@ return [ - 'build_date' => Carbon::parse('2024-08-05'), + 'build_date' => Carbon::parse('2024-08-08'), - 'build_version' => 'v0.20.9', + 'build_version' => 'v0.21.0', /** * General settings.