elvinaqalarov99/laravel-health-panel

A config-driven status panel for Laravel apps built on top of spatie/laravel-health.

Maintainers

Package info

github.com/elvinaqalarov99/laravel-health-panel

Language:Blade

pkg:composer/elvinaqalarov99/laravel-health-panel

Statistics

Installs: 9

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.1.1 2026-04-27 08:59 UTC

This package is auto-updated.

Last update: 2026-04-27 09:01:13 UTC


README

Latest Version on Packagist License

A beautiful, config-driven status panel for Laravel built on top of spatie/laravel-health.

spatie/laravel-health does the heavy lifting — running checks, storing results, alerting. laravel-health-panel turns those results into a polished public status page with uptime history, incident timelines, and zero boilerplate.

What it gives you

/status Live status of every service group — Operational / Degraded / Down
/status/history 90-day uptime bars + incident timeline with noise reduction
Config-driven Define services, checks, labels and feature flags in one file — no PHP class changes
Swappable Bind your own service or repository via contracts
Publishable views Override the default dark-mode Tailwind UI with your own Blade templates

Requirements

Installation

composer require elvinaqalarov99/laravel-health-panel

Publish the config and migration:

php artisan vendor:publish --tag=status-page:config
php artisan vendor:publish --tag=status-page:migrations
php artisan migrate

Visit /status — you're live.

Configuration

All behaviour is controlled from config/status-page.php.

Defining services

Map your Spatie health check names into logical service groups:

'services' => [

    'api' => [
        'label'         => 'API',
        'checks'        => ['Cache', 'Database', 'Redis', 'Queue'],
        'degraded_only' => false,   // failures here → system DOWN
        'enabled'       => true,
    ],

    'payment' => [
        'label'         => 'Payment Processing',
        'checks'        => ['stripe', 'spreedly'],
        'degraded_only' => true,    // failures here → system DEGRADED, not down
        'enabled'       => env('STATUS_PAGE_PAYMENT_ENABLED', true),
    ],

    'email' => [
        'label'         => 'Email Delivery',
        'checks'        => ['mailgun'],
        'degraded_only' => true,
        'enabled'       => env('STATUS_PAGE_EMAIL_ENABLED', true),
    ],

],

Set enabled to false (or wire it to an env var) to hide a service entirely — it disappears from the status page, uptime bars, and all history queries.

Check labels

Give individual check names a human-readable label shown on the panel:

'check_labels' => [
    'Cache'    => 'Redis Cache',
    'Database' => 'MySQL Database',
    'stripe'   => 'Stripe',
],

Any check not listed here falls back to a title-cased version of the name.

Routes

'routes' => [
    'enabled'    => true,
    'prefix'     => '',              // mount under /admin by setting 'admin'
    'middleware' => ['web'],
    'status'     => 'status',        // → /status
    'history'    => 'status/history', // → /status/history
],

Disable auto-registration entirely and define your own routes:

// config/status-page.php
'routes' => ['enabled' => false],
// routes/web.php
use Elvinaqalarov99\StatusPage\Http\Controllers\StatusPageController;

Route::middleware(['web', 'auth'])->group(function () {
    Route::get('/admin/status',         [StatusPageController::class, 'index'])->name('status-page.index');
    Route::get('/admin/status/history', [StatusPageController::class, 'history'])->name('status-page.history');
});

History settings

'incidents_per_page'         => 7,   // date-groups shown per history page
'incident_stability_minutes' => 15,  // gaps shorter than this merge into one incident
'history_retention_days'     => 90,  // days shown in the uptime bar

Swapping the model

By default the panel reads from Spatie's HealthCheckResultHistoryItem. Swap it with any model that has the same columns:

'model' => \App\Models\MyHealthResult::class,

Customising the UI

Publish the default views:

php artisan vendor:publish --tag=status-page:views

Views land in resources/views/vendor/status-page/. Edit freely — the panel respects Laravel's standard view cascade so your versions always take precedence.

The default views use Tailwind CSS via CDN and require no build step.

Extending the service layer

Every component is bound through a contract, so you can swap any layer from your AppServiceProvider:

use Elvinaqalarov99\StatusPage\Contracts\StatusPageServiceContract;
use Elvinaqalarov99\StatusPage\Contracts\StatusPageRepositoryContract;

// Add a caching layer on top of the default service
$this->app->bind(StatusPageServiceContract::class, CachedStatusPageService::class);

// Use a different data source entirely
$this->app->bind(StatusPageRepositoryContract::class, MyStatusPageRepository::class);

How it works

spatie/laravel-health
  └── runs checks every minute (via scheduler)
  └── stores results in health_check_result_history_items

laravel-health-panel
  └── StatusPageRepository   reads history with bounded queries (no OOM)
  └── StatusPageService      groups checks → services, detects incidents,
  │                          reduces noise, calculates uptime %
  └── StatusPageController   thin — calls service, returns view
  └── Views                  dark-mode Tailwind UI, fully publishable

Key performance decisions:

  • Latest status uses MAX(id) GROUP BY check_name — two small queries instead of unbounded full-table scans
  • Uptime bars are aggregated entirely in MySQL (GROUP BY day, check_name) — never loaded as Eloquent models
  • Incident history selects only needed columns and caps rows at 100k
  • A (check_name, created_at) composite index is included in the published migration

Changelog

See CHANGELOG.md.

License

MIT — see LICENSE.