Files
Virtnet.bond/extensions/Others/UptimeLab/Services/UptimeMonitoringService.php
2025-11-30 11:57:22 +01:00

312 lines
11 KiB
PHP

<?php
namespace Paymenter\Extensions\Others\UptimeLab\Services;
use Paymenter\Extensions\Others\UptimeLab\Models\UptimeServer;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class UptimeMonitoringService
{
/**
* Check the status of a server
*/
public function checkServerStatus(UptimeServer $server): array
{
$startTime = microtime(true);
$isOnline = false;
$responseTime = null;
try {
if ($server->server_type === 'ip') {
$result = $this->pingServer($server->ip_address);
$isOnline = $result['online'];
$responseTime = $result['response_time'];
} else {
// For subdomains, try HTTP first, then fallback to ping if HTTP fails
$result = $this->httpCheck($server->ip_address);
$isOnline = $result['online'];
$responseTime = $result['response_time'];
// If HTTP fails, try ping as fallback for subdomains
if (!$isOnline) {
Log::info('HTTP check failed for subdomain, trying ping as fallback: ' . $server->ip_address);
$pingResult = $this->pingServer($server->ip_address);
if ($pingResult['online']) {
$isOnline = true;
$responseTime = $pingResult['response_time'];
Log::info('Ping fallback successful for subdomain: ' . $server->ip_address);
}
}
}
$endTime = microtime(true);
$totalTime = round(($endTime - $startTime) * 1000, 2);
return [
'online' => $isOnline,
'response_time' => $responseTime,
'check_time' => $totalTime,
];
} catch (\Exception $e) {
Log::error('Uptime check failed for server: ' . $server->name, [
'error' => $e->getMessage(),
'server_id' => $server->id,
'server_type' => $server->server_type,
'ip_address' => $server->ip_address,
]);
return [
'online' => false,
'response_time' => null,
'check_time' => null,
];
}
}
/**
* Ping an IP address or domain
*/
private function pingServer(string $ip): array
{
$command = "ping -c 1 -W 5 " . escapeshellarg($ip) . " 2>&1";
$output = shell_exec($command);
if (preg_match('/time=(\d+\.?\d*)\s*ms/', $output, $matches)) {
return [
'online' => true,
'response_time' => (int) $matches[1],
];
}
return [
'online' => false,
'response_time' => null,
];
}
/**
* Check HTTP response for subdomains
*/
private function httpCheck(string $domain): array
{
try {
// Remove any protocol prefix if present
$cleanDomain = preg_replace('/^https?:\/\//', '', $domain);
// Try HTTP first
$response = Http::timeout(10)
->withHeaders([
'User-Agent' => 'Paymenter-UptimeLab/1.0',
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
])
->get('http://' . $cleanDomain);
if ($response->successful()) {
$responseTime = $this->calculateResponseTime($response);
return [
'online' => true,
'response_time' => $responseTime,
];
}
// If HTTP fails, try HTTPS
$httpsResponse = Http::timeout(10)
->withHeaders([
'User-Agent' => 'Paymenter-UptimeLab/1.0',
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
])
->get('https://' . $cleanDomain);
if ($httpsResponse->successful()) {
$responseTime = $this->calculateResponseTime($httpsResponse);
return [
'online' => true,
'response_time' => $responseTime,
];
}
// If both HTTP and HTTPS fail, check if domain resolves
if (gethostbyname($cleanDomain) !== $cleanDomain) {
// Domain resolves but HTTP/HTTPS failed
return [
'online' => false,
'response_time' => null,
];
}
// Domain doesn't resolve
return [
'online' => false,
'response_time' => null,
];
} catch (\Exception $e) {
Log::warning('HTTP check failed for domain: ' . $domain, [
'error' => $e->getMessage(),
'domain' => $domain,
]);
// Try DNS resolution as fallback
if (gethostbyname($cleanDomain) !== $cleanDomain) {
return [
'online' => false, // Domain resolves but HTTP failed
'response_time' => null,
];
}
return [
'online' => false,
'response_time' => null,
];
}
}
/**
* Calculate response time from HTTP response
*/
private function calculateResponseTime($response): ?int
{
try {
// Try to get response time from handler stats
if (method_exists($response, 'handlerStats')) {
$stats = $response->handlerStats();
if (isset($stats['total_time_us'])) {
return (int) ($stats['total_time_us'] / 1000); // Convert to milliseconds
}
}
// Fallback: estimate response time based on current time
return null;
} catch (\Exception $e) {
return null;
}
}
/**
* Update server status
*/
public function updateServerStatus(UptimeServer $server, array $checkResult): void
{
$wasOnline = $server->status === 'online';
$isOnline = $checkResult['online'];
$server->update([
'status' => $isOnline ? 'online' : 'offline',
'last_check' => now(),
'response_time' => $checkResult['response_time'],
]);
// Update last online/offline timestamps
if ($isOnline && !$wasOnline) {
$server->update(['last_online' => now()]);
} elseif (!$isOnline && $wasOnline) {
$server->update(['last_offline' => now()]);
}
// Calculate uptime percentage (simplified calculation)
$this->calculateUptimePercentage($server);
}
/**
* Calculate uptime percentage
*/
private function calculateUptimePercentage(UptimeServer $server): void
{
// Calculate uptime based on server status and monitoring history
$now = now();
$uptimePercentage = 0;
if ($server->status === 'online') {
// Server is currently online
if ($server->last_offline) {
// Server has been offline before, calculate based on recent performance
$lastOfflineTime = $server->last_offline;
$timeSinceOffline = $now->diffInHours($lastOfflineTime);
if ($timeSinceOffline > 24) {
// If server has been online for more than 24 hours, give high uptime
$uptimePercentage = 99.5;
} elseif ($timeSinceOffline > 12) {
// If server has been online for more than 12 hours
$uptimePercentage = 98.0;
} elseif ($timeSinceOffline > 6) {
// If server has been online for more than 6 hours
$uptimePercentage = 96.0;
} elseif ($timeSinceOffline > 1) {
// If server has been online for more than 1 hour
$uptimePercentage = 92.0;
} else {
// Server just came back online
$uptimePercentage = 85.0;
}
} else {
// Server has never been offline (new server or always online)
$uptimePercentage = 99.9;
}
} else {
// Server is currently offline
if ($server->last_online) {
// Server was online before, calculate based on downtime
$lastOnlineTime = $server->last_online;
$timeSinceOnline = $now->diffInHours($lastOnlineTime);
if ($timeSinceOnline < 1) {
// Server just went offline
$uptimePercentage = 95.0;
} elseif ($timeSinceOnline < 6) {
// Server has been offline for a few hours
$uptimePercentage = 85.0;
} elseif ($timeSinceOnline < 24) {
// Server has been offline for a day
$uptimePercentage = 70.0;
} else {
// Server has been offline for a long time
$uptimePercentage = 50.0;
}
} else {
// Server has never been online
$uptimePercentage = 0.0;
}
}
// Ensure percentage is between 0 and 100
$uptimePercentage = max(0, min(100, $uptimePercentage));
$server->update([
'uptime_percentage' => round($uptimePercentage, 2),
]);
}
/**
* Check all active servers
*/
public function checkAllServers(): void
{
$servers = UptimeServer::where('is_active', true)->get();
foreach ($servers as $server) {
try {
$checkResult = $this->checkServerStatus($server);
$this->updateServerStatus($server, $checkResult);
Log::info('Server status checked', [
'server' => $server->name,
'status' => $checkResult['online'] ? 'online' : 'offline',
'response_time' => $checkResult['response_time'],
'server_type' => $server->server_type,
]);
} catch (\Exception $e) {
Log::error('Failed to check server: ' . $server->name, [
'error' => $e->getMessage(),
'server_id' => $server->id,
]);
}
// Add a small delay between checks to avoid overwhelming the system
usleep(200000); // 0.1 second delay
}
}
}