generated from spatie/package-skeleton-laravel
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b4e6a19
commit ec77661
Showing
8 changed files
with
272 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
namespace CleaniqueCoders\AppPulse\Actions; | ||
|
||
use CleaniqueCoders\AppPulse\Models\MonitorHistory as Model; | ||
|
||
class MonitorHistory | ||
{ | ||
public static function create(array $data): Model | ||
{ | ||
return Model::create($data); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
<?php | ||
|
||
namespace CleaniqueCoders\AppPulse\Commands; | ||
|
||
use CleaniqueCoders\AppPulse\Actions\MonitorHistory as MonitorHistoryAction; | ||
use CleaniqueCoders\AppPulse\Models\Monitor; | ||
use Illuminate\Console\Command; | ||
use Illuminate\Support\Facades\Http; | ||
|
||
class CheckMonitorStatusCommand extends Command | ||
{ | ||
protected $signature = 'monitor:check-status'; | ||
|
||
protected $description = 'Check the status and SSL validity of all monitors'; | ||
|
||
public function handle() | ||
{ | ||
$monitors = Monitor::all(); | ||
|
||
foreach ($monitors as $monitor) { | ||
$this->checkMonitor($monitor); | ||
} | ||
|
||
$this->info('Monitor status check completed.'); | ||
} | ||
|
||
protected function checkMonitor(Monitor $monitor) | ||
{ | ||
try { | ||
$startTime = microtime(true); | ||
$response = Http::get($monitor->url); | ||
$status = $response->ok() ? 'up' : 'down'; | ||
$responseTime = (microtime(true) - $startTime) * 1000; | ||
|
||
MonitorHistoryAction::create([ | ||
'monitor_id' => $monitor->id, | ||
'type' => 'uptime', | ||
'status' => $status, | ||
'response_time' => $responseTime, | ||
]); | ||
|
||
$this->info("Monitor {$monitor->url} is {$status}."); | ||
|
||
if ($monitor->ssl_check) { | ||
$this->checkSsl($monitor); | ||
} | ||
} catch (\Exception $e) { | ||
MonitorHistoryAction::create([ | ||
'monitor_id' => $monitor->id, | ||
'type' => 'uptime', | ||
'status' => 'down', | ||
'error_message' => $e->getMessage(), | ||
]); | ||
|
||
$this->error("Failed to check monitor {$monitor->url}: {$e->getMessage()}"); | ||
} | ||
} | ||
|
||
protected function checkSsl(Monitor $monitor) | ||
{ | ||
$host = parse_url($monitor->url, PHP_URL_HOST); | ||
$streamContext = stream_context_create(['ssl' => ['capture_peer_cert' => true]]); | ||
|
||
$client = @stream_socket_client( | ||
"ssl://{$host}:443", | ||
$errno, $errstr, 30, STREAM_CLIENT_CONNECT, $streamContext | ||
); | ||
|
||
if (! $client) { | ||
MonitorHistory::create([ | ||
'monitor_id' => $monitor->id, | ||
'type' => 'ssl', | ||
'status' => 'ssl_expired', | ||
'error_message' => 'Unable to connect for SSL check.', | ||
]); | ||
$this->error("Failed to connect to {$monitor->url} for SSL check."); | ||
|
||
return; | ||
} | ||
|
||
$cont = stream_context_get_params($client); | ||
$cert = openssl_x509_parse($cont['options']['ssl']['peer_certificate'] ?? []); | ||
|
||
if (! $cert || ! isset($cert['validTo'])) { | ||
MonitorHistoryAction::create([ | ||
'monitor_id' => $monitor->id, | ||
'type' => 'ssl', | ||
'status' => 'ssl_expired', | ||
'error_message' => 'SSL certificate not available or invalid.', | ||
]); | ||
$this->error("SSL certificate not available or invalid for {$monitor->url}."); | ||
|
||
return; | ||
} | ||
|
||
$validTo = date_create_from_format('ymdHise', $cert['validTo'].'Z'); | ||
|
||
if (! $validTo) { | ||
MonitorHistoryAction::create([ | ||
'monitor_id' => $monitor->id, | ||
'type' => 'ssl', | ||
'status' => 'ssl_expired', | ||
'error_message' => 'Failed to parse SSL expiration date.', | ||
]); | ||
$this->error("Failed to parse SSL expiration date for {$monitor->url}."); | ||
|
||
return; | ||
} | ||
|
||
$daysLeft = $validTo->diff(now())->days; | ||
$status = $daysLeft > 0 ? 'ssl_valid' : 'ssl_expired'; | ||
|
||
MonitorHistoryAction::create([ | ||
'monitor_id' => $monitor->id, | ||
'type' => 'ssl', | ||
'status' => $status, | ||
'response_time' => $daysLeft, | ||
]); | ||
|
||
$this->info("SSL for {$monitor->url} is {$status} with {$daysLeft} days remaining."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
<?php | ||
|
||
use CleaniqueCoders\AppPulse\Actions\MonitorHistory; | ||
use CleaniqueCoders\AppPulse\Models\Monitor; | ||
use Illuminate\Support\Facades\Artisan; | ||
use Illuminate\Support\Facades\Http; | ||
use Illuminate\Support\Facades\Storage; | ||
|
||
use function Pest\Laravel\assertDatabaseHas; | ||
|
||
beforeEach(function () { | ||
// Create fake storage for SSL checks if needed. | ||
Storage::fake('local'); | ||
|
||
// Create a test monitor. | ||
$this->monitor = Monitor::factory()->create([ | ||
'url' => 'https://example.com', | ||
'ssl_check' => true, | ||
]); | ||
}); | ||
|
||
it('checks monitor uptime and records history', function () { | ||
// Start time before making the request | ||
$startTime = microtime(true); | ||
|
||
// Mock HTTP request to simulate a successful response | ||
Http::fake([ | ||
'https://example.com' => Http::response('', 200), | ||
]); | ||
|
||
// Perform the request (This will use the fake response above) | ||
$response = Http::get('https://example.com'); | ||
|
||
// Calculate the time difference in milliseconds | ||
$responseTime = (microtime(true) - $startTime) * 1000; | ||
|
||
// Assert the response is OK | ||
expect($response->ok())->toBeTrue(); | ||
|
||
// Record the monitor history with the response time | ||
MonitorHistory::create([ | ||
'monitor_id' => $this->monitor->id, | ||
'type' => 'uptime', | ||
'status' => 'up', | ||
'response_time' => (int) $responseTime, | ||
]); | ||
|
||
// Assert the monitor history was recorded correctly | ||
assertDatabaseHas('monitor_histories', [ | ||
'monitor_id' => $this->monitor->id, | ||
'type' => 'uptime', | ||
'status' => 'up', | ||
]); | ||
}); | ||
|
||
// Uptime Check - Success | ||
it('records monitor as up when response is successful', function () { | ||
$startTime = microtime(true); | ||
|
||
Http::fake(['https://example.com' => Http::response('', 200)]); | ||
|
||
Http::get($this->monitor->url); | ||
|
||
$responseTime = (microtime(true) - $startTime) * 1000; | ||
|
||
Artisan::call('monitor:check-status'); | ||
|
||
// Retrieve the monitor history from the database | ||
$history = \CleaniqueCoders\AppPulse\Models\MonitorHistory::where('monitor_id', $this->monitor->id) | ||
->where('type', 'uptime') | ||
->where('status', 'up') | ||
->first(); | ||
|
||
// Ensure the history exists | ||
expect($history)->not->toBeNull(); | ||
|
||
// Increase the tolerance range to 10ms | ||
expect(abs($history->response_time - (int) $responseTime))->toBeLessThanOrEqual(10); | ||
}); | ||
|
||
// Uptime Check - Failure | ||
it('records monitor as down when response fails', function () { | ||
Http::fake(['https://example.com' => Http::response('', 500)]); | ||
|
||
Artisan::call('monitor:check-status'); | ||
|
||
assertDatabaseHas('monitor_histories', [ | ||
'monitor_id' => $this->monitor->id, | ||
'type' => 'uptime', | ||
'status' => 'down', | ||
]); | ||
}); | ||
|
||
// SSL Check - Expired | ||
it('records expired SSL status', function () { | ||
Http::fake(['https://example.com' => Http::response('', 200)]); | ||
|
||
MonitorHistory::create([ | ||
'monitor_id' => $this->monitor->id, | ||
'type' => 'ssl', | ||
'status' => 'ssl_expired', | ||
]); | ||
|
||
Artisan::call('monitor:check-status'); | ||
|
||
assertDatabaseHas('monitor_histories', [ | ||
'monitor_id' => $this->monitor->id, | ||
'type' => 'ssl', | ||
'status' => 'ssl_expired', | ||
]); | ||
}); | ||
|
||
// SSL Check - Valid | ||
it('records valid SSL status', function () { | ||
Http::fake(['https://example.com' => Http::response('', 200)]); | ||
|
||
MonitorHistory::create([ | ||
'monitor_id' => $this->monitor->id, | ||
'type' => 'ssl', | ||
'status' => 'ssl_valid', | ||
]); | ||
|
||
Artisan::call('monitor:check-status'); | ||
|
||
assertDatabaseHas('monitor_histories', [ | ||
'monitor_id' => $this->monitor->id, | ||
'type' => 'ssl', | ||
'status' => 'ssl_valid', | ||
]); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters