mirror of
https://github.com/pelican-dev/panel.git
synced 2025-05-20 00:34:44 +02:00
Health page (#469)
* add spatie health * change slug for health page * add check for panel version * only check for debug mode if env isn't local * add check for node versions * improve short summary * fix outdated check * run pint * fix health checks during tests * add count to ok message * fix typo * temp fix for phpstan job * fix pint... * improve "outdated" count Co-authored-by: MartinOscar <40749467+RMartinOscar@users.noreply.github.com> * run pint * skip node versions check if no nodes are created * auto run health checks if they didn't run before * small refactor * update navigation Co-authored-by: Charles <sir3lit@gmail.com> * fix errors if tests didn't run yet * fix disk usage check * remove plugin and use own page * use health status indicator from spatie * fix after merge * update icon * update color classes * fix after merge * add back imports oops... * wrong import oops²... * update spatie/laravel-health to latest * move Health page to correct namespace * update NodeVersionsCheck * use style instead of tailwind classes workaround until we have vite * cleanup custom checks --------- Co-authored-by: MartinOscar <40749467+RMartinOscar@users.noreply.github.com> Co-authored-by: Charles <sir3lit@gmail.com>
This commit is contained in:
parent
02a0c5c3eb
commit
00644c2c60
43
app/Checks/NodeVersionsCheck.php
Normal file
43
app/Checks/NodeVersionsCheck.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Checks;
|
||||||
|
|
||||||
|
use App\Models\Node;
|
||||||
|
use App\Services\Helpers\SoftwareVersionService;
|
||||||
|
use Spatie\Health\Checks\Check;
|
||||||
|
use Spatie\Health\Checks\Result;
|
||||||
|
use Spatie\Health\Enums\Status;
|
||||||
|
|
||||||
|
class NodeVersionsCheck extends Check
|
||||||
|
{
|
||||||
|
public function __construct(private SoftwareVersionService $versionService) {}
|
||||||
|
|
||||||
|
public function run(): Result
|
||||||
|
{
|
||||||
|
$all = Node::query()->count();
|
||||||
|
|
||||||
|
if ($all === 0) {
|
||||||
|
$result = Result::make()->notificationMessage('No Nodes created')->shortSummary('No Nodes');
|
||||||
|
$result->status = Status::skipped();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$latestVersion = $this->versionService->latestWingsVersion();
|
||||||
|
|
||||||
|
$outdated = Node::query()->get()
|
||||||
|
->filter(fn (Node $node) => !isset($node->systemInformation()['exception']) && $node->systemInformation()['version'] !== $latestVersion)
|
||||||
|
->count();
|
||||||
|
|
||||||
|
$result = Result::make()
|
||||||
|
->meta([
|
||||||
|
'all' => $all,
|
||||||
|
'outdated' => $outdated,
|
||||||
|
])
|
||||||
|
->shortSummary($outdated === 0 ? 'All up-to-date' : "{$outdated}/{$all} outdated");
|
||||||
|
|
||||||
|
return $outdated === 0
|
||||||
|
? $result->ok('All Nodes are up-to-date.')
|
||||||
|
: $result->failed(':outdated/:all Nodes are outdated.');
|
||||||
|
}
|
||||||
|
}
|
31
app/Checks/PanelVersionCheck.php
Normal file
31
app/Checks/PanelVersionCheck.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Checks;
|
||||||
|
|
||||||
|
use App\Services\Helpers\SoftwareVersionService;
|
||||||
|
use Spatie\Health\Checks\Check;
|
||||||
|
use Spatie\Health\Checks\Result;
|
||||||
|
|
||||||
|
class PanelVersionCheck extends Check
|
||||||
|
{
|
||||||
|
public function __construct(private SoftwareVersionService $versionService) {}
|
||||||
|
|
||||||
|
public function run(): Result
|
||||||
|
{
|
||||||
|
$isLatest = $this->versionService->isLatestPanel();
|
||||||
|
$currentVersion = $this->versionService->currentPanelVersion();
|
||||||
|
$latestVersion = $this->versionService->latestPanelVersion();
|
||||||
|
|
||||||
|
$result = Result::make()
|
||||||
|
->meta([
|
||||||
|
'isLatest' => $isLatest,
|
||||||
|
'currentVersion' => $currentVersion,
|
||||||
|
'latestVersion' => $latestVersion,
|
||||||
|
])
|
||||||
|
->shortSummary($isLatest ? 'up-to-date' : 'outdated');
|
||||||
|
|
||||||
|
return $isLatest
|
||||||
|
? $result->ok('Panel is up-to-date.')
|
||||||
|
: $result->failed('Installed version is `:currentVersion` but latest is `:latestVersion`.');
|
||||||
|
}
|
||||||
|
}
|
16
app/Checks/UsedDiskSpaceCheck.php
Normal file
16
app/Checks/UsedDiskSpaceCheck.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Checks;
|
||||||
|
|
||||||
|
use Spatie\Health\Checks\Checks\UsedDiskSpaceCheck as BaseCheck;
|
||||||
|
|
||||||
|
class UsedDiskSpaceCheck extends BaseCheck
|
||||||
|
{
|
||||||
|
protected function getDiskUsagePercentage(): int
|
||||||
|
{
|
||||||
|
$freeSpace = disk_free_space($this->filesystemName ?? '/');
|
||||||
|
$totalSpace = disk_total_space($this->filesystemName ?? '/');
|
||||||
|
|
||||||
|
return 100 - ($freeSpace * 100 / $totalSpace);
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,8 @@ use App\Models\Webhook;
|
|||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
use Illuminate\Database\Console\PruneCommand;
|
use Illuminate\Database\Console\PruneCommand;
|
||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
use Spatie\Health\Commands\RunHealthChecksCommand;
|
||||||
|
use Spatie\Health\Commands\ScheduleCheckHeartbeatCommand;
|
||||||
|
|
||||||
class Kernel extends ConsoleKernel
|
class Kernel extends ConsoleKernel
|
||||||
{
|
{
|
||||||
@ -53,5 +55,8 @@ class Kernel extends ConsoleKernel
|
|||||||
if (config('panel.webhook.prune_days')) {
|
if (config('panel.webhook.prune_days')) {
|
||||||
$schedule->command(PruneCommand::class, ['--model' => [Webhook::class]])->daily();
|
$schedule->command(PruneCommand::class, ['--model' => [Webhook::class]])->daily();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$schedule->command(ScheduleCheckHeartbeatCommand::class)->everyMinute();
|
||||||
|
$schedule->command(RunHealthChecksCommand::class)->everyFiveMinutes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
120
app/Filament/Admin/Pages/Health.php
Normal file
120
app/Filament/Admin/Pages/Health.php
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Admin\Pages;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Notifications\Notification;
|
||||||
|
use Filament\Pages\Page;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Spatie\Health\Commands\RunHealthChecksCommand;
|
||||||
|
use Spatie\Health\ResultStores\ResultStore;
|
||||||
|
|
||||||
|
class Health extends Page
|
||||||
|
{
|
||||||
|
protected static ?string $navigationIcon = 'tabler-heart';
|
||||||
|
|
||||||
|
protected static ?string $navigationGroup = 'Advanced';
|
||||||
|
|
||||||
|
protected static string $view = 'filament.pages.health';
|
||||||
|
|
||||||
|
// @phpstan-ignore-next-line
|
||||||
|
protected $listeners = [
|
||||||
|
'refresh-component' => '$refresh',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected function getActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Action::make('refresh')
|
||||||
|
->button()
|
||||||
|
->action('refresh'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getViewData(): array
|
||||||
|
{
|
||||||
|
// @phpstan-ignore-next-line
|
||||||
|
$checkResults = app(ResultStore::class)->latestResults();
|
||||||
|
|
||||||
|
if ($checkResults === null) {
|
||||||
|
Artisan::call(RunHealthChecksCommand::class);
|
||||||
|
|
||||||
|
$this->dispatch('refresh-component');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'lastRanAt' => new Carbon($checkResults?->finishedAt),
|
||||||
|
'checkResults' => $checkResults,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refresh(): void
|
||||||
|
{
|
||||||
|
Artisan::call(RunHealthChecksCommand::class);
|
||||||
|
|
||||||
|
$this->dispatch('refresh-component');
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title('Health check results refreshed')
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getNavigationBadge(): ?string
|
||||||
|
{
|
||||||
|
// @phpstan-ignore-next-line
|
||||||
|
$results = app(ResultStore::class)->latestResults();
|
||||||
|
|
||||||
|
if ($results === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = json_decode($results->toJson(), true);
|
||||||
|
|
||||||
|
$failed = array_reduce($results['checkResults'], function ($numFailed, $result) {
|
||||||
|
return $numFailed + ($result['status'] === 'failed' ? 1 : 0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return $failed === 0 ? null : (string) $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getNavigationBadgeColor(): string
|
||||||
|
{
|
||||||
|
return self::getNavigationBadge() > null ? 'danger' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getNavigationBadgeTooltip(): ?string
|
||||||
|
{
|
||||||
|
// @phpstan-ignore-next-line
|
||||||
|
$results = app(ResultStore::class)->latestResults();
|
||||||
|
|
||||||
|
if ($results === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = json_decode($results->toJson(), true);
|
||||||
|
|
||||||
|
$failedNames = array_reduce($results['checkResults'], function ($carry, $result) {
|
||||||
|
if ($result['status'] === 'failed') {
|
||||||
|
$carry[] = $result['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $carry;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return 'Failed: ' . implode(', ', $failedNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getNavigationIcon(): string
|
||||||
|
{
|
||||||
|
// @phpstan-ignore-next-line
|
||||||
|
$results = app(ResultStore::class)->latestResults();
|
||||||
|
|
||||||
|
if ($results === null) {
|
||||||
|
return 'tabler-heart-question';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results->containsFailingCheck() ? 'tabler-heart-exclamation' : 'tabler-heart-check';
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Checks\NodeVersionsCheck;
|
||||||
|
use App\Checks\PanelVersionCheck;
|
||||||
|
use App\Checks\UsedDiskSpaceCheck;
|
||||||
use App\Filament\Server\Pages\Console;
|
use App\Filament\Server\Pages\Console;
|
||||||
use App\Models;
|
use App\Models;
|
||||||
use App\Models\ApiKey;
|
use App\Models\ApiKey;
|
||||||
@ -26,6 +29,12 @@ use Illuminate\Support\ServiceProvider;
|
|||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Laravel\Sanctum\Sanctum;
|
use Laravel\Sanctum\Sanctum;
|
||||||
use SocialiteProviders\Manager\SocialiteWasCalled;
|
use SocialiteProviders\Manager\SocialiteWasCalled;
|
||||||
|
use Spatie\Health\Checks\Checks\CacheCheck;
|
||||||
|
use Spatie\Health\Checks\Checks\DatabaseCheck;
|
||||||
|
use Spatie\Health\Checks\Checks\DebugModeCheck;
|
||||||
|
use Spatie\Health\Checks\Checks\EnvironmentCheck;
|
||||||
|
use Spatie\Health\Checks\Checks\ScheduleCheck;
|
||||||
|
use Spatie\Health\Facades\Health;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@ -103,6 +112,20 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
scopes: Console::class,
|
scopes: Console::class,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Don't run any health checks during tests
|
||||||
|
if (!$app->runningUnitTests()) {
|
||||||
|
Health::checks([
|
||||||
|
DebugModeCheck::new()->if($app->isProduction()),
|
||||||
|
EnvironmentCheck::new(),
|
||||||
|
CacheCheck::new(),
|
||||||
|
DatabaseCheck::new(),
|
||||||
|
ScheduleCheck::new(),
|
||||||
|
UsedDiskSpaceCheck::new(),
|
||||||
|
PanelVersionCheck::new(),
|
||||||
|
NodeVersionsCheck::new(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
Gate::before(function (User $user, $ability) {
|
Gate::before(function (User $user, $ability) {
|
||||||
return $user->isRootAdmin() ? true : null;
|
return $user->isRootAdmin() ? true : null;
|
||||||
});
|
});
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
"socialiteproviders/discord": "^4.2",
|
"socialiteproviders/discord": "^4.2",
|
||||||
"socialiteproviders/steam": "^4.2",
|
"socialiteproviders/steam": "^4.2",
|
||||||
"spatie/laravel-fractal": "^6.2",
|
"spatie/laravel-fractal": "^6.2",
|
||||||
|
"spatie/laravel-health": "^1.30",
|
||||||
"spatie/laravel-permission": "^6.9",
|
"spatie/laravel-permission": "^6.9",
|
||||||
"spatie/laravel-query-builder": "^5.8.1",
|
"spatie/laravel-query-builder": "^5.8.1",
|
||||||
"spatie/temporary-directory": "^2.2",
|
"spatie/temporary-directory": "^2.2",
|
||||||
|
237
composer.lock
generated
237
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "d3b70b68927bf912d19adea9705d70ff",
|
"content-hash": "2fddfe7f5db269cdf9e906b83c815756",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "abdelhamiderrahmouni/filament-monaco-editor",
|
"name": "abdelhamiderrahmouni/filament-monaco-editor",
|
||||||
@ -7162,6 +7162,82 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-09-20T14:00:15+00:00"
|
"time": "2024-09-20T14:00:15+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "spatie/enum",
|
||||||
|
"version": "3.13.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/spatie/enum.git",
|
||||||
|
"reference": "f1a0f464ba909491a53e60a955ce84ad7cd93a2c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/spatie/enum/zipball/f1a0f464ba909491a53e60a955ce84ad7cd93a2c",
|
||||||
|
"reference": "f1a0f464ba909491a53e60a955ce84ad7cd93a2c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"php": "^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"fakerphp/faker": "^1.9.1",
|
||||||
|
"larapack/dd": "^1.1",
|
||||||
|
"phpunit/phpunit": "^9.0",
|
||||||
|
"vimeo/psalm": "^4.3"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"fakerphp/faker": "To use the enum faker provider",
|
||||||
|
"phpunit/phpunit": "To use the enum assertions"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Spatie\\Enum\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Brent Roose",
|
||||||
|
"email": "brent@spatie.be",
|
||||||
|
"homepage": "https://spatie.be",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tom Witkowski",
|
||||||
|
"email": "dev@gummibeer.de",
|
||||||
|
"homepage": "https://gummibeer.de",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHP Enums",
|
||||||
|
"homepage": "https://github.com/spatie/enum",
|
||||||
|
"keywords": [
|
||||||
|
"enum",
|
||||||
|
"enumerable",
|
||||||
|
"spatie"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"docs": "https://docs.spatie.be/enum",
|
||||||
|
"issues": "https://github.com/spatie/enum/issues",
|
||||||
|
"source": "https://github.com/spatie/enum"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://spatie.be/open-source/support-us",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/spatie",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-04-22T08:51:55+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "spatie/fractalistic",
|
"name": "spatie/fractalistic",
|
||||||
"version": "2.9.5",
|
"version": "2.9.5",
|
||||||
@ -7363,6 +7439,100 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-06-04T09:33:08+00:00"
|
"time": "2024-06-04T09:33:08+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "spatie/laravel-health",
|
||||||
|
"version": "1.30.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/spatie/laravel-health.git",
|
||||||
|
"reference": "98e91b8a4b5ffc9086cf5d9cd2e7fa9cf1be0661"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/spatie/laravel-health/zipball/98e91b8a4b5ffc9086cf5d9cd2e7fa9cf1be0661",
|
||||||
|
"reference": "98e91b8a4b5ffc9086cf5d9cd2e7fa9cf1be0661",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"dragonmantank/cron-expression": "^3.3.1",
|
||||||
|
"guzzlehttp/guzzle": "^6.5|^7.4.5|^7.2",
|
||||||
|
"illuminate/console": "^8.75|^9.0|^10.0|^11.0",
|
||||||
|
"illuminate/contracts": "^8.75|^9.0|^10.0|^11.0",
|
||||||
|
"illuminate/database": "^8.75|^9.0|^10.0|^11.0",
|
||||||
|
"illuminate/notifications": "^8.75|^9.0|^10.0|^11.0",
|
||||||
|
"illuminate/support": "^8.75|^9.0|^10.0|^11.0",
|
||||||
|
"laravel/serializable-closure": "^1.3",
|
||||||
|
"nunomaduro/termwind": "^1.0|^2.0",
|
||||||
|
"php": "^8.0",
|
||||||
|
"spatie/enum": "^3.13",
|
||||||
|
"spatie/laravel-package-tools": "^1.12.1",
|
||||||
|
"spatie/regex": "^3.1.1|^3.1",
|
||||||
|
"spatie/temporary-directory": "^2.2",
|
||||||
|
"symfony/process": "^5.4|^6.0|^7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"larastan/larastan": "^1.0.3|^2.4",
|
||||||
|
"laravel/horizon": "^5.9.10",
|
||||||
|
"laravel/slack-notification-channel": "^2.4|^3.2",
|
||||||
|
"nunomaduro/collision": "^5.10|^6.2.1|^6.1|^8.0",
|
||||||
|
"orchestra/testbench": "^6.23|^7.6|^8.0|^9.0",
|
||||||
|
"pestphp/pest": "^1.21.3|^2.34",
|
||||||
|
"pestphp/pest-plugin-laravel": "^1.2|^2.3",
|
||||||
|
"phpstan/extension-installer": "^1.1",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^1.0",
|
||||||
|
"phpstan/phpstan-phpunit": "^1.1.1",
|
||||||
|
"phpunit/phpunit": "^9.5.21|^9.5.10|^10.5",
|
||||||
|
"spatie/laravel-ray": "^1.30",
|
||||||
|
"spatie/pest-plugin-snapshots": "^1.1|^2.1",
|
||||||
|
"spatie/pest-plugin-test-time": "^1.1.1|^1.1|^2.0",
|
||||||
|
"spatie/test-time": "^1.3"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Spatie\\Health\\HealthServiceProvider"
|
||||||
|
],
|
||||||
|
"aliases": {
|
||||||
|
"Health": "Spatie\\Health\\Facades\\Health"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Spatie\\Health\\": "src",
|
||||||
|
"Spatie\\Health\\Database\\Factories\\": "database/factories"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Freek Van der Herten",
|
||||||
|
"email": "freek@spatie.be",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Monitor the health of a Laravel application",
|
||||||
|
"homepage": "https://github.com/spatie/laravel-health",
|
||||||
|
"keywords": [
|
||||||
|
"laravel",
|
||||||
|
"laravel-health",
|
||||||
|
"spatie"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/spatie/laravel-health/tree/1.30.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/spatie",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-08-02T14:01:48+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "spatie/laravel-package-tools",
|
"name": "spatie/laravel-package-tools",
|
||||||
"version": "1.16.5",
|
"version": "1.16.5",
|
||||||
@ -7579,6 +7749,69 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-05-10T08:19:35+00:00"
|
"time": "2024-05-10T08:19:35+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "spatie/regex",
|
||||||
|
"version": "3.1.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/spatie/regex.git",
|
||||||
|
"reference": "d543de2019a0068e7b80da0ba24f1c51c7469303"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/spatie/regex/zipball/d543de2019a0068e7b80da0ba24f1c51c7469303",
|
||||||
|
"reference": "d543de2019a0068e7b80da0ba24f1c51c7469303",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^8.0|^8.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.3"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Spatie\\Regex\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sebastian De Deyne",
|
||||||
|
"email": "sebastian@spatie.be",
|
||||||
|
"homepage": "https://spatie.be",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A sane interface for php's built in preg_* functions",
|
||||||
|
"homepage": "https://github.com/spatie/regex",
|
||||||
|
"keywords": [
|
||||||
|
"expression",
|
||||||
|
"expressions",
|
||||||
|
"regex",
|
||||||
|
"regular",
|
||||||
|
"spatie"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/spatie/regex/issues",
|
||||||
|
"source": "https://github.com/spatie/regex/tree/3.1.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://spatie.be/open-source/support-us",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/spatie",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2021-11-30T21:13:59+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "spatie/temporary-directory",
|
"name": "spatie/temporary-directory",
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
@ -13756,5 +13989,5 @@
|
|||||||
"ext-zip": "*"
|
"ext-zip": "*"
|
||||||
},
|
},
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
"plugin-api-version": "2.3.0"
|
"plugin-api-version": "2.6.0"
|
||||||
}
|
}
|
||||||
|
123
config/health.php
Normal file
123
config/health.php
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
/*
|
||||||
|
* A result store is responsible for saving the results of the checks. The
|
||||||
|
* `EloquentHealthResultStore` will save results in the database. You
|
||||||
|
* can use multiple stores at the same time.
|
||||||
|
*/
|
||||||
|
'result_stores' => [
|
||||||
|
/*
|
||||||
|
Spatie\Health\ResultStores\EloquentHealthResultStore::class => [
|
||||||
|
'connection' => env('HEALTH_DB_CONNECTION', env('DB_CONNECTION')),
|
||||||
|
'model' => Spatie\Health\Models\HealthCheckResultHistoryItem::class,
|
||||||
|
'keep_history_for_days' => 5,
|
||||||
|
],
|
||||||
|
*/
|
||||||
|
|
||||||
|
Spatie\Health\ResultStores\CacheHealthResultStore::class => [
|
||||||
|
'store' => 'file',
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
Spatie\Health\ResultStores\JsonFileHealthResultStore::class => [
|
||||||
|
'disk' => 's3',
|
||||||
|
'path' => 'health.json',
|
||||||
|
],
|
||||||
|
|
||||||
|
Spatie\Health\ResultStores\InMemoryHealthResultStore::class,
|
||||||
|
*/
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* You can get notified when specific events occur. Out of the box you can use 'mail' and 'slack'.
|
||||||
|
* For Slack you need to install laravel/slack-notification-channel.
|
||||||
|
*/
|
||||||
|
'notifications' => [
|
||||||
|
/*
|
||||||
|
* Notifications will only get sent if this option is set to `true`.
|
||||||
|
*/
|
||||||
|
'enabled' => false,
|
||||||
|
|
||||||
|
'notifications' => [
|
||||||
|
Spatie\Health\Notifications\CheckFailedNotification::class => ['mail'],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here you can specify the notifiable to which the notifications should be sent. The default
|
||||||
|
* notifiable will use the variables specified in this config file.
|
||||||
|
*/
|
||||||
|
'notifiable' => Spatie\Health\Notifications\Notifiable::class,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When checks start failing, you could potentially end up getting
|
||||||
|
* a notification every minute.
|
||||||
|
*
|
||||||
|
* With this setting, notifications are throttled. By default, you'll
|
||||||
|
* only get one notification per hour.
|
||||||
|
*/
|
||||||
|
'throttle_notifications_for_minutes' => 60,
|
||||||
|
'throttle_notifications_key' => 'health:latestNotificationSentAt:',
|
||||||
|
|
||||||
|
'mail' => [
|
||||||
|
'to' => 'your@example.com',
|
||||||
|
|
||||||
|
'from' => [
|
||||||
|
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
||||||
|
'name' => env('MAIL_FROM_NAME', 'Example'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'slack' => [
|
||||||
|
'webhook_url' => env('HEALTH_SLACK_WEBHOOK_URL', ''),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this is set to null the default channel of the webhook will be used.
|
||||||
|
*/
|
||||||
|
'channel' => null,
|
||||||
|
|
||||||
|
'username' => null,
|
||||||
|
|
||||||
|
'icon' => null,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* You can let Oh Dear monitor the results of all health checks. This way, you'll
|
||||||
|
* get notified of any problems even if your application goes totally down. Via
|
||||||
|
* Oh Dear, you can also have access to more advanced notification options.
|
||||||
|
*/
|
||||||
|
'oh_dear_endpoint' => [
|
||||||
|
'enabled' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When this option is enabled, the checks will run before sending a response.
|
||||||
|
* Otherwise, we'll send the results from the last time the checks have run.
|
||||||
|
*/
|
||||||
|
'always_send_fresh_results' => true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The secret that is displayed at the Application Health settings at Oh Dear.
|
||||||
|
*/
|
||||||
|
'secret' => env('OH_DEAR_HEALTH_CHECK_SECRET'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The URL that should be configured in the Application health settings at Oh Dear.
|
||||||
|
*/
|
||||||
|
'url' => '/oh-dear-health-check-results',
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* You can set a theme for the local results page
|
||||||
|
*
|
||||||
|
* - light: light mode
|
||||||
|
* - dark: dark mode
|
||||||
|
*/
|
||||||
|
'theme' => 'light',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When enabled, completed `HealthQueueJob`s will be displayed
|
||||||
|
* in Horizon's silenced jobs screen.
|
||||||
|
*/
|
||||||
|
'silence_health_queue_job' => true,
|
||||||
|
];
|
71
resources/views/filament/pages/health.blade.php
Normal file
71
resources/views/filament/pages/health.blade.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
@php
|
||||||
|
if(! function_exists('backgroundColor')) {
|
||||||
|
function backgroundColor($status) {
|
||||||
|
return match ($status) {
|
||||||
|
Spatie\Health\Enums\Status::ok()->value => 'background-color: rgb(209 250 229);', // bg-emerald-100
|
||||||
|
Spatie\Health\Enums\Status::warning()->value => 'background-color: rgb(254 249 195);', // bg-yellow-100
|
||||||
|
Spatie\Health\Enums\Status::skipped()->value => 'background-color: rgb(219 234 254);', // bg-blue-100
|
||||||
|
Spatie\Health\Enums\Status::failed()->value, Spatie\Health\Enums\Status::crashed()->value => 'background-color: rgb(254 226 226);', // bg-red-100
|
||||||
|
default => 'background-color: rgb(243 244 246);' // bg-gray-100
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(! function_exists('iconColor')) {
|
||||||
|
function iconColor($status)
|
||||||
|
{
|
||||||
|
return match ($status) {
|
||||||
|
Spatie\Health\Enums\Status::ok()->value => 'color: rgb(16 185 129);', // text-emerald-500
|
||||||
|
Spatie\Health\Enums\Status::warning()->value => 'color: rgb(234 179 8);', // text-yellow-500
|
||||||
|
Spatie\Health\Enums\Status::skipped()->value => 'color: rgb(59 130 246);', // text-blue-500
|
||||||
|
Spatie\Health\Enums\Status::failed()->value, Spatie\Health\Enums\Status::crashed()->value => 'color: rgb(239 68 68);', // text-red-500
|
||||||
|
default => 'color: rgb(107 114 128);' // text-gray-500
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(! function_exists('icon')) {
|
||||||
|
function icon($status)
|
||||||
|
{
|
||||||
|
return match ($status) {
|
||||||
|
Spatie\Health\Enums\Status::ok()->value => 'tabler-circle-check',
|
||||||
|
Spatie\Health\Enums\Status::warning()->value => 'tabler-exclamation-circle',
|
||||||
|
Spatie\Health\Enums\Status::skipped()->value => 'tabler-circle-chevron-right',
|
||||||
|
Spatie\Health\Enums\Status::failed()->value, Spatie\Health\Enums\Status::crashed()->value => 'tabler-circle-x',
|
||||||
|
default => 'tabler-help-circle'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<x-filament-panels::page>
|
||||||
|
@if (count($checkResults?->storedCheckResults ?? []))
|
||||||
|
<x-filament::grid default="1" sm="2" class="gap-6 mb-5">
|
||||||
|
@foreach ($checkResults->storedCheckResults as $result)
|
||||||
|
<div class="flex items-start px-4 space-x-2 overflow-hidden py-5 text-opacity-0 transition transform bg-white shadow-md shadow-gray-200 dark:shadow-black/25 dark:shadow-md dark:bg-gray-800 rounded-xl sm:p-6 md:space-x-3 md:min-h-[130px] dark:border-t dark:border-gray-700">
|
||||||
|
<div class="flex justify-center items-center rounded-full p-2" style="margin-right: 0.5rem; {{ backgroundColor($result->status) }}">
|
||||||
|
<x-filament::icon icon="{{ icon($result->status) }}" class="h-6 w-6" style="{{ iconColor($result->status) }}" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dd class="-mt-1 font-bold md:mt-1 md:text-xl" style="color: light-dark(rgb(17 24 39), rgb(255 255 255));">
|
||||||
|
{{ $result->label }}
|
||||||
|
</dd>
|
||||||
|
<dt class="mt-0 text-sm font-medium md:mt-1" style="color: light-dark(rgb(75 85 99), rgb(209 213 219));">
|
||||||
|
@if (!empty($result->notificationMessage))
|
||||||
|
{{ $result->notificationMessage }}
|
||||||
|
@else
|
||||||
|
{{ $result->shortSummary }}
|
||||||
|
@endif
|
||||||
|
</dt>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</x-filament::grid>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($lastRanAt)
|
||||||
|
<div class="text-md text-center font-medium" style="{{ $lastRanAt->diffInMinutes() > 5 ? 'color: rgb(239 68 68);' : 'color: light-dark(rgb(156 163 175), rgb(229 231 235));' }}">
|
||||||
|
Check results from {{ $lastRanAt->diffForHumans() }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</x-filament-panels::page>
|
Loading…
x
Reference in New Issue
Block a user