144 lines
3.1 KiB
PHP
144 lines
3.1 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
namespace App\Models;
|
||
|
|
|
||
|
|
use Illuminate\Database\Eloquent\Model;
|
||
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||
|
|
|
||
|
|
class HealthCheck extends Model
|
||
|
|
{
|
||
|
|
protected $fillable = [
|
||
|
|
'site_id',
|
||
|
|
'endpoint',
|
||
|
|
'expected_json_keys',
|
||
|
|
'last_checked_at',
|
||
|
|
'is_healthy',
|
||
|
|
'response_data',
|
||
|
|
'response_time_ms',
|
||
|
|
'performance_threshold',
|
||
|
|
'status_code',
|
||
|
|
'is_active',
|
||
|
|
];
|
||
|
|
|
||
|
|
protected $casts = [
|
||
|
|
'expected_json_keys' => 'array',
|
||
|
|
'response_data' => 'array',
|
||
|
|
'last_checked_at' => 'datetime',
|
||
|
|
'is_healthy' => 'boolean',
|
||
|
|
'is_active' => 'boolean',
|
||
|
|
];
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get the site that owns the health check.
|
||
|
|
*/
|
||
|
|
public function site(): BelongsTo
|
||
|
|
{
|
||
|
|
return $this->belongsTo(Site::class);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Scope to filter active health checks.
|
||
|
|
*/
|
||
|
|
public function scopeActive($query)
|
||
|
|
{
|
||
|
|
return $query->where('is_active', true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Scope to filter healthy checks.
|
||
|
|
*/
|
||
|
|
public function scopeHealthy($query)
|
||
|
|
{
|
||
|
|
return $query->where('is_healthy', true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Scope to filter unhealthy checks.
|
||
|
|
*/
|
||
|
|
public function scopeUnhealthy($query)
|
||
|
|
{
|
||
|
|
return $query->where('is_healthy', false);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get the full URL for the health check endpoint.
|
||
|
|
*/
|
||
|
|
public function getFullUrlAttribute(): string
|
||
|
|
{
|
||
|
|
$siteUrl = rtrim($this->site->url, '/');
|
||
|
|
$endpoint = ltrim($this->endpoint, '/');
|
||
|
|
|
||
|
|
return $siteUrl . '/' . $endpoint;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get status badge color.
|
||
|
|
*/
|
||
|
|
public function getStatusColorAttribute(): string
|
||
|
|
{
|
||
|
|
if ($this->is_healthy === null) {
|
||
|
|
return 'secondary';
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->is_healthy ? 'success' : 'danger';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get status text.
|
||
|
|
*/
|
||
|
|
public function getStatusTextAttribute(): string
|
||
|
|
{
|
||
|
|
if ($this->is_healthy === null) {
|
||
|
|
return 'Unknown';
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->is_healthy ? 'Healthy' : 'Unhealthy';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get display name for the endpoint.
|
||
|
|
*/
|
||
|
|
public function getDisplayNameAttribute(): string
|
||
|
|
{
|
||
|
|
return $this->endpoint ?: 'Health Check';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Validate if response contains all expected keys.
|
||
|
|
*/
|
||
|
|
public function validateResponseKeys(array $responseData): bool
|
||
|
|
{
|
||
|
|
if (empty($this->expected_json_keys)) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach ($this->expected_json_keys as $key) {
|
||
|
|
if (!$this->hasNestedKey($responseData, $key)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if nested key exists in array using dot notation.
|
||
|
|
*/
|
||
|
|
protected function hasNestedKey(array $array, string $key): bool
|
||
|
|
{
|
||
|
|
if (array_key_exists($key, $array)) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (explode('.', $key) as $segment) {
|
||
|
|
if (!is_array($array) || !array_key_exists($segment, $array)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
$array = $array[$segment];
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|