262 lines
10 KiB
PHP
262 lines
10 KiB
PHP
<?php
|
||
header('Content-Type: application/json');
|
||
require_once __DIR__ . '/../db/config.php';
|
||
require_once __DIR__ . '/../mail/MailService.php';
|
||
|
||
$action = $_GET['action'] ?? (php_sapi_name() === 'cli' ? 'check' : '');
|
||
|
||
function ping($url) {
|
||
$ch = curl_init($url);
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||
curl_setopt($ch, CURLOPT_NOBODY, true); // HEAD request
|
||
|
||
$startTime = microtime(true);
|
||
$response = curl_exec($ch);
|
||
$latency = (microtime(true) - $startTime) * 1000; // ms
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
$error = curl_error($ch);
|
||
curl_close($ch);
|
||
|
||
return [
|
||
'status' => ($httpCode >= 200 && $httpCode < 400) ? 'ok' : 'error',
|
||
'latency' => round($latency, 2),
|
||
'http_code' => $httpCode,
|
||
'error' => $error ?: ($httpCode ? "HTTP $httpCode" : "Connection failed")
|
||
];
|
||
}
|
||
|
||
function notifyStatusChange($url, $oldStatus, $newStatus, $error = null) {
|
||
$recipient = 'yumeecute@aol.com';
|
||
$time = date('Y-m-d H:i:s');
|
||
|
||
if ($oldStatus === 'ok' && $newStatus === 'error') {
|
||
$subject = "๐ด ALERT: Website Down - $url";
|
||
$html = "
|
||
<div style='font-family: Arial, sans-serif; border: 1px solid #ff4d4d; padding: 20px; border-radius: 8px;'>
|
||
<h2 style='color: #ff4d4d;'>Website Down Alert</h2>
|
||
<p>The following website is currently unresponsive:</p>
|
||
<p><strong>URL:</strong> <a href='$url'>$url</a></p>
|
||
<p><strong>Error:</strong> $error</p>
|
||
<p><strong>Time:</strong> $time</p>
|
||
<hr>
|
||
<p style='font-size: 12px; color: #666;'>This is an automated notification from your Uptime Monitor.</p>
|
||
</div>
|
||
";
|
||
} elseif ($oldStatus === 'error' && $newStatus === 'ok') {
|
||
$subject = "๐ข RESOLVED: Website Up - $url";
|
||
$html = "
|
||
<div style='font-family: Arial, sans-serif; border: 1px solid #28a745; padding: 20px; border-radius: 8px;'>
|
||
<h2 style='color: #28a745;'>Website Back Online</h2>
|
||
<p>The following website is responding normally again:</p>
|
||
<p><strong>URL:</strong> <a href='$url'>$url</a></p>
|
||
<p><strong>Time:</strong> $time</p>
|
||
<hr>
|
||
<p style='font-size: 12px; color: #666;'>This is an automated notification from your Uptime Monitor.</p>
|
||
</div>
|
||
";
|
||
} else {
|
||
return; // No notification for other transitions
|
||
}
|
||
|
||
MailService::sendMail($recipient, $subject, $html);
|
||
}
|
||
|
||
function isMonitoringEnabled() {
|
||
try {
|
||
$stmt = db()->prepare("SELECT value FROM settings WHERE `key` = 'monitoring_enabled'");
|
||
$stmt->execute();
|
||
$val = $stmt->fetchColumn();
|
||
return $val === '1';
|
||
} catch (Exception $e) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function generateApiKey() {
|
||
return bin2hex(random_bytes(32));
|
||
}
|
||
|
||
switch ($action) {
|
||
case 'settings':
|
||
echo json_encode(['monitoring_enabled' => isMonitoringEnabled()]);
|
||
break;
|
||
|
||
case 'toggle':
|
||
$data = json_decode(file_get_contents('php://input'), true);
|
||
$enabled = ($data['enabled'] ?? false) ? '1' : '0';
|
||
$stmt = db()->prepare("UPDATE settings SET value = ? WHERE `key` = 'monitoring_enabled'");
|
||
$stmt->execute([$enabled]);
|
||
echo json_encode(['success' => true, 'monitoring_enabled' => $enabled === '1']);
|
||
break;
|
||
|
||
case 'list':
|
||
$stmt = db()->query("SELECT * FROM urls ORDER BY id DESC");
|
||
echo json_encode($stmt->fetchAll());
|
||
break;
|
||
|
||
case 'add':
|
||
$data = json_decode(file_get_contents('php://input'), true);
|
||
$url = $data['url'] ?? '';
|
||
if (filter_var($url, FILTER_VALIDATE_URL)) {
|
||
// Get default team_id or first available
|
||
$team_id = db()->query("SELECT id FROM teams LIMIT 1")->fetchColumn();
|
||
if (!$team_id) {
|
||
// Should not happen with migrations, but safety first
|
||
db()->query("INSERT INTO teams (name, owner_email) VALUES ('Default Team', 'admin@example.com')");
|
||
$team_id = db()->lastInsertId();
|
||
}
|
||
$stmt = db()->prepare("INSERT INTO urls (url, team_id) VALUES (?, ?)");
|
||
$stmt->execute([$url, $team_id]);
|
||
echo json_encode(['success' => true, 'id' => db()->lastInsertId()]);
|
||
} else {
|
||
echo json_encode(['success' => false, 'error' => 'Invalid URL. Use http:// or https://']);
|
||
}
|
||
break;
|
||
|
||
case 'delete':
|
||
$data = json_decode(file_get_contents('php://input'), true);
|
||
$id = $data['id'] ?? 0;
|
||
$stmt = db()->prepare("DELETE FROM urls WHERE id = ?");
|
||
$stmt->execute([$id]);
|
||
echo json_encode(['success' => true]);
|
||
break;
|
||
|
||
case 'check':
|
||
if (!isMonitoringEnabled()) {
|
||
echo json_encode(['info' => 'Monitoring is disabled globally']);
|
||
exit;
|
||
}
|
||
|
||
$stmt = db()->query("SELECT * FROM urls WHERE is_active = 1");
|
||
$urls = $stmt->fetchAll();
|
||
$results = [];
|
||
|
||
foreach ($urls as $row) {
|
||
$res = ping($row['url']);
|
||
|
||
// Notify on change
|
||
notifyStatusChange($row['url'], $row['last_status'], $res['status'], $res['error']);
|
||
|
||
// Update URL status
|
||
$upd = db()->prepare("UPDATE urls SET last_status = ?, last_latency = ?, last_checked_at = NOW() WHERE id = ?");
|
||
$upd->execute([$res['status'], $res['latency'], $row['id']]);
|
||
|
||
// Insert log
|
||
$log = db()->prepare("INSERT INTO logs (url_id, status, latency, error_message) VALUES (?, ?, ?, ?)");
|
||
$log->execute([$row['id'], $res['status'], $res['latency'], $res['status'] === 'error' ? $res['error'] : null]);
|
||
|
||
$results[] = [
|
||
'id' => $row['id'],
|
||
'url' => $row['url'],
|
||
'status' => $res['status'],
|
||
'latency' => $res['latency'],
|
||
'error' => $res['error']
|
||
];
|
||
}
|
||
echo json_encode($results);
|
||
break;
|
||
|
||
case 'logs':
|
||
$url_id = $_GET['url_id'] ?? null;
|
||
if ($url_id) {
|
||
$stmt = db()->prepare("SELECT * FROM logs WHERE url_id = ? ORDER BY id DESC LIMIT 24");
|
||
$stmt->execute([$url_id]);
|
||
} else {
|
||
$stmt = db()->query("SELECT l.*, u.url FROM logs l JOIN urls u ON l.url_id = u.id ORDER BY l.id DESC LIMIT 50");
|
||
}
|
||
echo json_encode($stmt->fetchAll());
|
||
break;
|
||
|
||
case 'stats':
|
||
$res = [
|
||
'total' => db()->query("SELECT COUNT(*) FROM urls")->fetchColumn(),
|
||
'up' => db()->query("SELECT COUNT(*) FROM urls WHERE last_status = 'ok'")->fetchColumn(),
|
||
'down' => db()->query("SELECT COUNT(*) FROM urls WHERE last_status = 'error'")->fetchColumn(),
|
||
'avg_latency' => db()->query("SELECT AVG(last_latency) FROM urls")->fetchColumn() ?: 0,
|
||
'recent_errors' => db()->query("SELECT l.*, u.url FROM logs l JOIN urls u ON l.url_id = u.id WHERE l.status = 'error' ORDER BY l.id DESC LIMIT 10")->fetchAll()
|
||
];
|
||
echo json_encode($res);
|
||
break;
|
||
|
||
case 'teams':
|
||
$stmt = db()->query("SELECT * FROM teams ORDER BY id DESC");
|
||
echo json_encode($stmt->fetchAll());
|
||
break;
|
||
|
||
case 'create_team':
|
||
$data = json_decode(file_get_contents('php://input'), true);
|
||
$name = $data['name'] ?? '';
|
||
$email = $data['email'] ?? 'yumeecute@aol.com';
|
||
if ($name) {
|
||
$stmt = db()->prepare("INSERT INTO teams (name, owner_email) VALUES (?, ?)");
|
||
$stmt->execute([$name, $email]);
|
||
echo json_encode(['success' => true, 'id' => db()->lastInsertId()]);
|
||
} else {
|
||
echo json_encode(['success' => false, 'error' => 'Team name required']);
|
||
}
|
||
break;
|
||
|
||
case 'members':
|
||
$team_id = $_GET['team_id'] ?? null;
|
||
if (!$team_id) {
|
||
$team_id = db()->query("SELECT id FROM teams LIMIT 1")->fetchColumn();
|
||
}
|
||
if ($team_id) {
|
||
$stmt = db()->prepare("SELECT * FROM team_members WHERE team_id = ?");
|
||
$stmt->execute([$team_id]);
|
||
echo json_encode($stmt->fetchAll());
|
||
} else {
|
||
echo json_encode([]);
|
||
}
|
||
break;
|
||
|
||
case 'invite':
|
||
$data = json_decode(file_get_contents('php://input'), true);
|
||
$email = $data['email'] ?? '';
|
||
$team_id = $data['team_id'] ?? db()->query("SELECT id FROM teams LIMIT 1")->fetchColumn();
|
||
if (filter_var($email, FILTER_VALIDATE_EMAIL) && $team_id) {
|
||
$stmt = db()->prepare("INSERT INTO team_members (team_id, email, status) VALUES (?, ?, 'invited')");
|
||
$stmt->execute([$team_id, $email]);
|
||
|
||
// Send invite email
|
||
$subject = "Invitation to join Team on ๐๐จ๐ง๐ข๐ญ๐จ๐ซ ๐๐ฉ๐ญ๐ข๐ฆ๐ ๐๐ฒ ๐๐ฎ๐ฆ๐๐";
|
||
$html = "<p>You have been invited to join a team.</p><p><a href='http://".$_SERVER['HTTP_HOST']."'>Accept Invitation</a></p>";
|
||
MailService::sendMail($email, $subject, $html);
|
||
|
||
echo json_encode(['success' => true]);
|
||
} else {
|
||
echo json_encode(['success' => false, 'error' => 'Invalid email or team ID']);
|
||
}
|
||
break;
|
||
|
||
case 'keys':
|
||
$team_id = $_GET['team_id'] ?? db()->query("SELECT id FROM teams LIMIT 1")->fetchColumn();
|
||
if ($team_id) {
|
||
$stmt = db()->prepare("SELECT * FROM api_keys WHERE team_id = ?");
|
||
$stmt->execute([$team_id]);
|
||
echo json_encode($stmt->fetchAll());
|
||
} else {
|
||
echo json_encode([]);
|
||
}
|
||
break;
|
||
|
||
case 'generate_key':
|
||
$data = json_decode(file_get_contents('php://input'), true);
|
||
$label = $data['label'] ?? 'New Key';
|
||
$team_id = $data['team_id'] ?? db()->query("SELECT id FROM teams LIMIT 1")->fetchColumn();
|
||
if ($team_id) {
|
||
$key = generateApiKey();
|
||
$stmt = db()->prepare("INSERT INTO api_keys (team_id, api_key, label) VALUES (?, ?, ?)");
|
||
$stmt->execute([$team_id, $key, $label]);
|
||
echo json_encode(['success' => true, 'key' => $key]);
|
||
} else {
|
||
echo json_encode(['success' => false, 'error' => 'No team available']);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
echo json_encode(['error' => 'Unknown action']);
|
||
break;
|
||
} |