| Current Path : /home/www/hallgroupsolutions.com/home/ |
| Current File : /home/www/hallgroupsolutions.com/home/admin.php |
<?php
// =============================================
// admin.php - VERSI FINAL (AJAX JSON murni, dashboard rapi)
// =============================================
// Bersihkan buffer output sejak awal
if (ob_get_length()) ob_end_clean();
// Header anti-cache global
header('Cache-Control: no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: 0');
// Konfigurasi session lebih kompatibel
ini_set('session.use_strict_mode', 0);
ini_set('session.cookie_samesite', 'Lax');
session_start();
// ==================== PASSWORD ADMIN ====================
// GANTI PASSWORD INI SEKARANG JUGA!
$ADMIN_PASS = 'Cacad2424'; // ← GANTI DI SINI menjadi password kamu sendiri
// =============================================
// AJAX ENDPOINT - DIJALANKAN PERTAMA (sebelum output HTML)
// =============================================
if (isset($_GET['ajax']) && $_GET['ajax'] === '1') {
header('Content-Type: application/json; charset=utf-8');
if (empty($_SESSION['admin_logged_in'])) {
echo json_encode(['error' => 'Session expired. Silakan login ulang.']);
exit;
}
try {
$db = new PDO('sqlite:visitors.db');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$total = $db->query("SELECT COUNT(*) FROM visits")->fetchColumn() ?: 0;
$bots = $db->query("SELECT COUNT(*) FROM visits WHERE is_bot = 1")->fetchColumn() ?: 0;
$humans = $total - $bots;
$stmt = $db->query("SELECT * FROM visits ORDER BY timestamp DESC LIMIT 500");
$rows = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$bot_label = $row['is_bot'] ? '<span class="badge bg-danger">YA</span>' : '<span class="badge bg-success">Tidak</span>';
$params = $row['query_params'] && $row['query_params'] !== '[]'
? htmlspecialchars(substr($row['query_params'], 0, 150)) . (strlen($row['query_params']) > 150 ? '...' : '')
: '-';
$rows[] = [
'timestamp' => $row['timestamp'] ?? '-',
'ip' => $row['ip'] ?? '-',
'user_agent' => htmlspecialchars($row['user_agent'] ?? '-'),
'bot_label' => $bot_label,
'request_uri' => htmlspecialchars($row['request_uri'] ?? '-'),
'params' => $params
];
}
echo json_encode(compact('total', 'bots', 'humans', 'rows'));
} catch (Exception $e) {
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}
exit; // PASTIKAN EXIT DI SINI - AGAR TIDAK CETAK HTML
}
// =============================================
// HANDLE DOWNLOAD TXT
// =============================================
if (isset($_GET['download']) && !empty($_SESSION['admin_logged_in'])) {
$type = $_GET['download'];
$filename_prefix = ($type === 'human') ? 'visitor_nyata' : 'bot_cloud';
try {
$db = new PDO('sqlite:visitors.db');
$where = ($type === 'human') ? "WHERE is_bot = 0" : "WHERE is_bot = 1";
$stmt = $db->query("SELECT * FROM visits $where ORDER BY timestamp DESC");
$total_filtered = $db->query("SELECT COUNT(*) FROM visits $where")->fetchColumn() ?: 0;
header('Content-Type: text/plain; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $filename_prefix . '_log_' . date('Y-m-d_H-i-s') . '.txt"');
header('Cache-Control: no-cache, no-store, must-revalidate');
echo "=== Log " . (($type === 'human') ? 'Visitor Nyata (Manusia)' : 'Bot & Cloud ISP') . " ===\n";
echo "Generated: " . date('Y-m-d H:i:s') . " WIB\n";
echo "Total records: $total_filtered\n\n";
echo str_pad("Waktu", 20) . " | " .
str_pad("IP", 18) . " | " .
str_pad("User-Agent", 70) . " | " .
"Link Diklik | Parameter\n";
echo str_repeat("-", 140) . "\n";
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$ua = $row['user_agent'] ?? '-';
$params = $row['query_params'] && $row['query_params'] !== '[]'
? substr($row['query_params'], 0, 60) . (strlen($row['query_params']) > 60 ? '...' : '')
: '-';
echo str_pad($row['timestamp'], 20) . " | " .
str_pad($row['ip'], 18) . " | " .
str_pad(substr($ua, 0, 67) . (strlen($ua) > 67 ? '...' : ''), 70) . " | " .
$row['request_uri'] . " | " .
$params . "\n";
}
exit;
} catch (Exception $e) {
die("Error generate file: " . htmlspecialchars($e->getMessage()));
}
}
// =============================================
// LOGIN, LOGOUT, RESET
// =============================================
if (isset($_POST['password'])) {
if ($_POST['password'] === $ADMIN_PASS) {
$_SESSION['admin_logged_in'] = true;
} else {
$error = "Password salah!";
}
}
if (isset($_GET['logout'])) {
session_destroy();
header("Location: admin.php");
exit;
}
if (isset($_GET['reset']) && $_GET['reset'] === 'confirm' && !empty($_SESSION['admin_logged_in'])) {
try {
$db = new PDO('sqlite:visitors.db');
$db->exec("DELETE FROM visits");
$db->exec("VACUUM");
$reset_message = "<div class='alert alert-success'>Semua log berhasil dihapus!</div>";
} catch (Exception $e) {
$reset_message = "<div class='alert alert-danger'>Gagal reset: " . htmlspecialchars($e->getMessage()) . "</div>";
}
}
// Jika belum login → tampilkan form login
if (empty($_SESSION['admin_logged_in'])) {
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Admin Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-4">
<div class="card shadow">
<div class="card-body">
<h3 class="text-center mb-4">Admin Panel</h3>
<?php if (isset($error)) echo "<div class='alert alert-danger'>$error</div>"; ?>
<form method="POST">
<div class="mb-3">
<input type="password" name="password" class="form-control" placeholder="Password" required autofocus>
</div>
<button type="submit" class="btn btn-primary w-100">Masuk</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
<?php
exit;
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Admin - Statistik Visitor</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body { background:#f8f9fa; font-family: system-ui, sans-serif; }
.card-header { background:#0d6efd; color:white; }
.ua-column { max-width: 380px; overflow-x: auto; white-space: nowrap; }
#loading { display:none; position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); z-index:9999; }
.table th { text-align: center; vertical-align: middle; }
.table td { vertical-align: middle; }
</style>
</head>
<body>
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="mb-0">📊 Statistik Visitor & Bot</h2>
<div>
<a href="#" onclick="location.reload(); return false;" class="btn btn-outline-primary me-2">Home</a>
<a href="?download=human" class="btn btn-outline-success me-2">Download Visitor (.txt)</a>
<a href="?download=bot" class="btn btn-outline-warning me-2">Download Bot (.txt)</a>
<a href="?reset=confirm" class="btn btn-outline-danger me-2" onclick="return confirm('Yakin hapus semua log?');">Reset Log</a>
<a href="?logout=1" class="btn btn-outline-secondary">Logout</a>
</div>
</div>
<?php if (isset($reset_message)) echo $reset_message; ?>
<div class="card shadow">
<div class="card-header text-center">
<h5 class="mb-0">Data Kunjungan Terbaru</h5>
</div>
<div class="card-body position-relative">
<div id="stats" class="row text-center mb-4 g-3"></div>
<div class="table-responsive">
<table class="table table-hover table-bordered align-middle">
<thead class="table-dark">
<tr>
<th>Waktu</th>
<th>IP</th>
<th class="ua-column">User-Agent</th>
<th>Bot?</th>
<th>Link Diklik</th>
<th>Parameter</th>
</tr>
</thead>
<tbody id="tableBody">
<tr><td colspan="6" class="text-center py-4">Memuat data...</td></tr>
</tbody>
</table>
</div>
<div id="loading" class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div class="card-footer text-muted text-center small">
Update otomatis setiap 15 detik | Bot termasuk deteksi reverse DNS (amazon, azure, google, oracle, dll.)
</div>
</div>
</div>
<script>
function loadData() {
document.getElementById('loading').style.display = 'block';
fetch('admin.php?ajax=1', {
credentials: 'same-origin',
cache: 'no-cache',
headers: { 'Cache-Control': 'no-cache' }
})
.then(r => {
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.json();
})
.then(data => {
if (data.error) {
document.getElementById('tableBody').innerHTML = `<tr><td colspan="6" class="text-danger text-center">${data.error}</td></tr>`;
console.error('Server error:', data.error);
} else {
document.getElementById('stats').innerHTML = `
<div class="col-md-4"><div class="card border-primary"><div class="card-body text-center"><h4 class="text-primary mb-0">${data.total || 0}</h4><small>Total</small></div></div></div>
<div class="col-md-4"><div class="card border-danger"><div class="card-body text-center"><h4 class="text-danger mb-0">${data.bots || 0}</h4><small>Bot/Cloud</small></div></div></div>
<div class="col-md-4"><div class="card border-success"><div class="card-body text-center"><h4 class="text-success mb-0">${data.humans || 0}</h4><small>Manusia</small></div></div></div>
`;
let tbody = document.getElementById('tableBody');
tbody.innerHTML = '';
(data.rows || []).forEach(row => {
let tr = document.createElement('tr');
tr.innerHTML = `
<td>${row.timestamp}</td>
<td><code>${row.ip}</code></td>
<td class="ua-column" title="${row.user_agent}">${row.user_agent}</td>
<td>${row.bot_label}</td>
<td><code>${row.request_uri}</code></td>
<td><small>${row.params}</small></td>
`;
tbody.appendChild(tr);
});
}
document.getElementById('loading').style.display = 'none';
})
.catch(err => {
console.error('Fetch error:', err);
document.getElementById('tableBody').innerHTML = '<tr><td colspan="6" class="text-danger text-center">Gagal memuat data realtime. Coba refresh (F5).</td></tr>';
document.getElementById('loading').style.display = 'none';
});
}
loadData();
setInterval(loadData, 15000);
</script>
</body>
</html>