38753-vm/api/uptime.php
2026-02-25 01:15:30 +00:00

318 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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) {
$time = date('Y-m-d H:i:s');
$statusEmoji = ($newStatus === 'error') ? '๐Ÿšจ' : '๐ŸŸข';
$statusText = strtoupper($newStatus);
// 1. Send Telegram Push to Phone (24/7 real-time forcing)
try {
$stmt = db()->query("SELECT value FROM settings WHERE `key` = 'telegram_token'");
$token = $stmt->fetchColumn();
$stmt2 = db()->query("SELECT value FROM settings WHERE `key` = 'telegram_chat_id'");
$chatId = $stmt2->fetchColumn();
if ($token && $chatId) {
$msg = "$statusEmoji *MONITOR ALERT*
URL: $url
Status: *$statusText*
Time: $time
" . ($error ? "Error: $error" : "");
$tgUrl = "https://api.telegram.org/bot$token/sendMessage";
$data = ['chat_id' => $chatId, 'text' => $msg, 'parse_mode' => 'Markdown'];
$options = [
'http' => [
'header' => "Content-type: application/x-www-form-urlencoded
",
'method' => 'POST',
'content' => http_build_query($data),
],
];
file_get_contents($tgUrl, false, stream_context_create($options));
}
} catch (Exception $e) {
// Ignore errors if telegram not setup
}
// 2. Original Email logic
$recipient = 'yumeecute@aol.com';
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 'save_telegram':
$data = json_decode(file_get_contents('php://input'), true);
$token = $data['telegram_token'] ?? '';
$chatId = $data['telegram_chat_id'] ?? '';
$stmt = db()->prepare("INSERT INTO settings (`key`, value) VALUES ('telegram_token', ?) ON DUPLICATE KEY UPDATE value = VALUES(value)");
$stmt->execute([$token]);
$stmt = db()->prepare("INSERT INTO settings (`key`, value) VALUES ('telegram_chat_id', ?) ON DUPLICATE KEY UPDATE value = VALUES(value)");
$stmt->execute([$chatId]);
echo json_encode(['success' => true]);
break;
case 'get_telegram':
$stmt = db()->query("SELECT `key`, value FROM settings WHERE `key` IN ('telegram_token', 'telegram_chat_id')");
$res = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
echo json_encode([
'telegram_token' => $res['telegram_token'] ?? '',
'telegram_chat_id' => $res['telegram_chat_id'] ?? ''
]);
break;
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']."/?invite_email=".urlencode($email)."'>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;
}