feat(monitoring): add pulse

- Added a new gate called `viewPulse` in the `AuthServiceProvider` class.
- The gate allows only admin users to view the Pulse feature.
This commit is contained in:
Rico van Zelst
2023-12-01 21:49:26 +01:00
parent 6c2deaa953
commit 2374a39c77
12 changed files with 796 additions and 264 deletions

View File

@@ -6,6 +6,8 @@ APP_URL=http://127.0.0.1:8000
RGAPI_KEY="RGAPI-00000000-0000-0000-0000-000000000000"
LOGIN_ROUTE_NAME="/your/secret/login/route"
USER_AGENT="Heimerdinger/1.0 (Heimerdinger.lol) PHP"
CLOUDFLARE_ZONE_ID="YOUR_CLOUDFLARE_ZONE"

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash;
class UserCreateCommand extends Command
{
protected $signature = 'user:create';
protected $description = 'Create a new user from CLI';
public function handle(): void
{
if (config('app.env') === 'production') {
if (!$this->confirm('You are in production mode. Are you sure you want to continue?')) {
return;
}
}
$this->info('Creating a new user...');
$name = $this->ask('Name');
$email = $this->ask('Email');
$password = $this->secret('Password');
$password_confirmation = $this->secret('Confirm password');
$admin = $this->confirm('Is this user an admin?');
$this->info('Name: ' . $name);
$this->info('Email: ' . $email);
$this->info('Password: ' . $password);
$this->info('Password confirmation: ' . $password_confirmation);
$this->info('Admin: ' . $admin);
if ($password !== $password_confirmation) {
$this->error('Passwords do not match!');
return;
}
$user = User::create([
'name' => $name,
'email' => $email,
'password' => bcrypt($password),
'admin' => $admin,
]);
$this->info('User with name ' . $user->name . 'created successfully.');
}
}

View File

@@ -23,6 +23,7 @@ class User extends Authenticatable
'name',
'email',
'password',
'admin',
];
/**

View File

@@ -2,28 +2,19 @@
namespace App\Providers;
// use Illuminate\Support\Facades\Gate;
use App\Models\SummonerEmote;
use App\Policies\SummonerEmotePolicy;
use App\Models\User;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
/**
* The model to policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
//
SummonerEmote::class => SummonerEmotePolicy::class,
];
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
//
Gate::define('viewPulse', function (User $user) {
return $user->admin;
});
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace App\View\Components\Home;
namespace App\View\Components\home;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;

View File

@@ -17,6 +17,7 @@
"guzzlehttp/guzzle": "^7.2",
"intervention/image": "^2.7",
"laravel/framework": "^10.10",
"laravel/pulse": "^1.0@beta",
"laravel/sanctum": "^3.2",
"laravel/tinker": "^2.8",
"saade/blade-iconsax": "^1.1",
@@ -81,6 +82,6 @@
"php-http/discovery": true
}
},
"minimum-stability": "stable",
"minimum-stability": "beta",
"prefer-stable": true
}

712
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,8 @@ return [
'name' => env('APP_NAME', 'Laravel'),
'login_route' => env('LOGIN_ROUTE_NAME', 'login'),
/*
|--------------------------------------------------------------------------
| Application Environment

227
config/pulse.php Normal file
View File

@@ -0,0 +1,227 @@
<?php
use Laravel\Pulse\Http\Middleware\Authorize;
use Laravel\Pulse\Recorders;
return [
/*
|--------------------------------------------------------------------------
| Pulse Domain
|--------------------------------------------------------------------------
|
| This is the subdomain which the Pulse dashboard will be accessible from.
| When set to null, the dashboard will reside under the same domain as
| the application. Remember to configure your DNS entries correctly.
|
*/
'domain' => env('PULSE_DOMAIN'),
/*
|--------------------------------------------------------------------------
| Pulse Path
|--------------------------------------------------------------------------
|
| This is the path which the Pulse dashboard will be accessible from. Feel
| free to change this path to anything you'd like. Note that this won't
| affect the path of the internal API that is never exposed to users.
|
*/
'path' => env('PULSE_PATH', 'pulse'),
/*
|--------------------------------------------------------------------------
| Pulse Master Switch
|--------------------------------------------------------------------------
|
| This configuration option may be used to completely disable all Pulse
| data recorders regardless of their individual configurations. This
| provides a single option to quickly disable all Pulse recording.
|
*/
'enabled' => env('PULSE_ENABLED', true),
/*
|--------------------------------------------------------------------------
| Pulse Storage Driver
|--------------------------------------------------------------------------
|
| This configuration option determines which storage driver will be used
| while storing entries from Pulse's recorders. In addition, you also
| may provide any options to configure the selected storage driver.
|
*/
'storage' => [
'driver' => env('PULSE_STORAGE_DRIVER', 'database'),
'database' => [
'connection' => env('PULSE_DB_CONNECTION', null),
'chunk' => 1000,
],
],
/*
|--------------------------------------------------------------------------
| Pulse Ingest Driver
|--------------------------------------------------------------------------
|
| This configuration options determines the ingest driver that will be used
| to capture entries from Pulse's recorders. Ingest drivers are great to
| free up your request workers quickly by offloading the data storage.
|
*/
'ingest' => [
'driver' => env('PULSE_INGEST_DRIVER', 'storage'),
'trim_lottery' => [1, 1_000],
'redis' => [
'connection' => env('PULSE_REDIS_CONNECTION'),
'chunk' => 1000,
],
],
/*
|--------------------------------------------------------------------------
| Pulse Cache Driver
|--------------------------------------------------------------------------
|
| This configuration option determines the cache driver that will be used
| for various tasks, including caching dashboard results, establishing
| locks for events that should only occur on one server and signals.
|
*/
'cache' => env('PULSE_CACHE_DRIVER'),
/*
|--------------------------------------------------------------------------
| Pulse Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will be assigned to every Pulse route, giving you the
| chance to add your own middleware to this list or change any of the
| existing middleware. Of course, reasonable defaults are provided.
|
*/
'middleware' => [
'web',
Authorize::class,
],
/*
|--------------------------------------------------------------------------
| Pulse Recorders
|--------------------------------------------------------------------------
|
| The following array lists the "recorders" that will be registered with
| Pulse, along with their configuration. Recorders gather application
| event data from requests and tasks to pass to your ingest driver.
|
*/
'recorders' => [
Recorders\CacheInteractions::class => [
'enabled' => env('PULSE_CACHE_INTERACTIONS_ENABLED', true),
'sample_rate' => env('PULSE_CACHE_INTERACTIONS_SAMPLE_RATE', 1),
'ignore' => [
'/^laravel:pulse:/', // Internal Pulse keys...
'/^illuminate:/', // Internal Laravel keys...
'/^telescope:/', // Internal Telescope keys...
'/^nova/', // Internal Nova keys...
'/^.+@.+\|(?:(?:\d+\.\d+\.\d+\.\d+)|[0-9a-fA-F:]+)(?::timer)?$/', // Breeze / Jetstream authentication rate limiting...
'/^[a-zA-Z0-9]{40}$/', // Session IDs...
],
'groups' => [
'/^job-exceptions:.*/' => 'job-exceptions:*',
// '/:\d+/' => ':*',
],
],
Recorders\Exceptions::class => [
'enabled' => env('PULSE_EXCEPTIONS_ENABLED', true),
'sample_rate' => env('PULSE_EXCEPTIONS_SAMPLE_RATE', 1),
'location' => env('PULSE_EXCEPTIONS_LOCATION', true),
'ignore' => [
// '/^Package\\\\Exceptions\\\\/',
],
],
Recorders\Queues::class => [
'enabled' => env('PULSE_QUEUES_ENABLED', true),
'sample_rate' => env('PULSE_QUEUES_SAMPLE_RATE', 1),
'ignore' => [
// '/^Package\\\\Jobs\\\\/',
],
],
Recorders\Servers::class => [
'server_name' => env('PULSE_SERVER_NAME', gethostname()),
'directories' => explode(':', env('PULSE_SERVER_DIRECTORIES', '/')),
],
Recorders\SlowJobs::class => [
'enabled' => env('PULSE_SLOW_JOBS_ENABLED', true),
'sample_rate' => env('PULSE_SLOW_JOBS_SAMPLE_RATE', 1),
'threshold' => env('PULSE_SLOW_JOBS_THRESHOLD', 1000),
'ignore' => [
// '/^Package\\\\Jobs\\\\/',
],
],
Recorders\SlowOutgoingRequests::class => [
'enabled' => env('PULSE_SLOW_OUTGOING_REQUESTS_ENABLED', true),
'sample_rate' => env('PULSE_SLOW_OUTGOING_REQUESTS_SAMPLE_RATE', 1),
'threshold' => env('PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD', 1000),
'ignore' => [
// '#^http://127\.0\.0\.1:13714#', // Inertia SSR...
],
'groups' => [
// '#^https://api\.github\.com/repos/.*$#' => 'api.github.com/repos/*',
// '#^https?://([^/]*).*$#' => '\1',
// '#/\d+#' => '/*',
],
],
Recorders\SlowQueries::class => [
'enabled' => env('PULSE_SLOW_QUERIES_ENABLED', true),
'sample_rate' => env('PULSE_SLOW_QUERIES_SAMPLE_RATE', 1),
'threshold' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
'location' => env('PULSE_SLOW_QUERIES_LOCATION', true),
'ignore' => [
'/(["`])pulse_[\w]+?\1/', // Pulse tables...
],
],
Recorders\SlowRequests::class => [
'enabled' => env('PULSE_SLOW_REQUESTS_ENABLED', true),
'sample_rate' => env('PULSE_SLOW_REQUESTS_SAMPLE_RATE', 1),
'threshold' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
'ignore' => [
'#^/pulse$#', // Pulse dashboard...
],
],
Recorders\UserJobs::class => [
'enabled' => env('PULSE_USER_JOBS_ENABLED', true),
'sample_rate' => env('PULSE_USER_JOBS_SAMPLE_RATE', 1),
'ignore' => [
// '/^Package\\\\Jobs\\\\/',
],
],
Recorders\UserRequests::class => [
'enabled' => env('PULSE_USER_REQUESTS_ENABLED', true),
'sample_rate' => env('PULSE_USER_REQUESTS_SAMPLE_RATE', 1),
'ignore' => [
'#^/pulse$#', // Pulse dashboard...
],
],
],
];

View File

@@ -0,0 +1,21 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('admin')->default(false);
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('admin');
});
}
};

View File

@@ -0,0 +1,15 @@
<x-pulse>
<livewire:pulse.servers cols="full" />
<livewire:pulse.cache cols="4" />
<livewire:pulse.slow-queries cols="8" />
<livewire:pulse.exceptions cols="6" />
<livewire:pulse.slow-requests cols="6" />
<livewire:pulse.slow-jobs cols="6" />
<livewire:pulse.slow-outgoing-requests cols="6" />
</x-pulse>

View File

@@ -52,3 +52,8 @@ Route::get('/assets', [
AssetsController::class,
'index'
])->name('assets.index');
Route::get(config('app.login_route'), function () {
return redirect('/pulse');
})->name('login')->middleware('auth.basic');