Fix newest homepage detection, file creation dates, real IP, and request log visitor type
- Fix detectNewestPage() to search subdirectories recursively and handle language prefixes - Fix getFileInfo() to preserve frontmatter created date or ctime instead of overwriting with mtime - Automatically store created date in frontmatter when creating/editing files - Add RequestLogger::getClientIp() with proxy and Cloudflare header support - Replace domain column in request log with visitor/bot type badges (Human, AI, Search, Scraper) - Update 'Activiteitenlog' to 'Activiteiten log' in UI and guide
This commit is contained in:
@@ -23,6 +23,114 @@ class RequestLogger
|
||||
@file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
public static function getClientIp(): string
|
||||
{
|
||||
$headers = [
|
||||
'HTTP_CF_CONNECTING_IP',
|
||||
'HTTP_X_REAL_IP',
|
||||
'HTTP_CLIENT_IP',
|
||||
'HTTP_X_FORWARDED_FOR',
|
||||
'REMOTE_ADDR',
|
||||
];
|
||||
|
||||
foreach ($headers as $header) {
|
||||
if (!empty($_SERVER[$header])) {
|
||||
$rawIp = $_SERVER[$header];
|
||||
if (str_contains($rawIp, ',')) {
|
||||
$ips = explode(',', $rawIp);
|
||||
$rawIp = trim($ips[0]);
|
||||
}
|
||||
$rawIp = trim($rawIp);
|
||||
if (filter_var($rawIp, FILTER_VALIDATE_IP) !== false) {
|
||||
return $rawIp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $_SERVER['REMOTE_ADDR'] ?? 'cli';
|
||||
}
|
||||
|
||||
public static function detectVisitorInfo(string $ua, string $user = ''): array
|
||||
{
|
||||
if (empty($ua)) {
|
||||
$userLabel = ($user && $user !== 'Gast') ? $user : 'Onbekend';
|
||||
return ['type' => 'unknown', 'label' => $userLabel, 'badge' => 'secondary', 'icon' => 'bi-question-circle'];
|
||||
}
|
||||
|
||||
$bots = [
|
||||
'AI' => [
|
||||
'GPTBot' => 'AI (GPTBot)',
|
||||
'ChatGPT-User' => 'AI (ChatGPT)',
|
||||
'Claude-Web' => 'AI (Claude)',
|
||||
'ClaudeBot' => 'AI (ClaudeBot)',
|
||||
'anthropic-ai' => 'AI (Anthropic)',
|
||||
'Google-Extended' => 'AI (Gemini/Google)',
|
||||
'CCBot' => 'AI (CommonCrawl)',
|
||||
'PerplexityBot' => 'AI (Perplexity)',
|
||||
'Amazonbot' => 'AI (Amazon)',
|
||||
'cohere-ai' => 'AI (Cohere)',
|
||||
'OAI-SearchBot' => 'AI (OpenAI)',
|
||||
'Bytespider' => 'AI (ByteDance)',
|
||||
'FacebookBot' => 'AI (Meta/FB)',
|
||||
'Applebot-Extended' => 'AI (Apple)',
|
||||
],
|
||||
'Search' => [
|
||||
'Googlebot' => 'Zoekmachine (Google)',
|
||||
'Bingbot' => 'Zoekmachine (Bing)',
|
||||
'BingPreview' => 'Zoekmachine (Bing)',
|
||||
'Slurp' => 'Zoekmachine (Yahoo)',
|
||||
'DuckDuckBot' => 'Zoekmachine (DuckDuckGo)',
|
||||
'Baiduspider' => 'Zoekmachine (Baidu)',
|
||||
'YandexBot' => 'Zoekmachine (Yandex)',
|
||||
'Sogou' => 'Zoekmachine (Sogou)',
|
||||
'Exabot' => 'Zoekmachine (Exabot)',
|
||||
'facebot' => 'Zoekmachine (Facebook)',
|
||||
],
|
||||
'Scraper' => [
|
||||
'HTTrack' => 'Scraper (HTTrack)',
|
||||
'Scrapy' => 'Scraper (Scrapy)',
|
||||
'PhantomJS' => 'Scraper (PhantomJS)',
|
||||
'HeadlessChrome' => 'Scraper (Headless)',
|
||||
'curl' => 'Scraper (cURL)',
|
||||
'wget' => 'Scraper (Wget)',
|
||||
'python' => 'Scraper (Python)',
|
||||
'Postman' => 'Scraper (Postman)',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($bots['AI'] as $pattern => $label) {
|
||||
if (stripos($ua, $pattern) !== false) {
|
||||
return ['type' => 'ai', 'label' => $label, 'badge' => 'danger', 'icon' => 'bi-robot'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($bots['Search'] as $pattern => $label) {
|
||||
if (stripos($ua, $pattern) !== false) {
|
||||
return ['type' => 'search', 'label' => $label, 'badge' => 'primary', 'icon' => 'bi-search'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($bots['Scraper'] as $pattern => $label) {
|
||||
if (stripos($ua, $pattern) !== false) {
|
||||
return ['type' => 'scraper', 'label' => $label, 'badge' => 'warning text-dark', 'icon' => 'bi-bug'];
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/(bot|crawler|spider|slurp)/i', $ua)) {
|
||||
return ['type' => 'bot', 'label' => 'Bot', 'badge' => 'secondary', 'icon' => 'bi-robot'];
|
||||
}
|
||||
|
||||
$userPrefix = ($user && $user !== 'Gast') ? htmlspecialchars($user) . ' (' : '';
|
||||
$userSuffix = ($user && $user !== 'Gast') ? ')' : '';
|
||||
|
||||
return [
|
||||
'type' => 'human',
|
||||
'label' => $userPrefix . 'Mens' . $userSuffix,
|
||||
'badge' => 'success',
|
||||
'icon' => 'bi-person-check',
|
||||
];
|
||||
}
|
||||
|
||||
public static function detectBot(): ?string
|
||||
{
|
||||
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
@@ -69,10 +177,17 @@ class RequestLogger
|
||||
|
||||
foreach ($content as $line) {
|
||||
if (preg_match('/^\[([^\]]+)\] \[([^\]]+)\] \[([^\]]+)\] \[([^\]]*)\] \[([^\]]+)\] \[([^\]]*)\] \[([^\]]*)\]$/', trim($line), $m)) {
|
||||
$user = $m[3];
|
||||
// Handle backwards compatibility where host (e.g. localhost:8080 or domain.com) was logged
|
||||
if (str_contains($user, '.') || str_contains($user, ':') || $user === 'cli') {
|
||||
$user = 'Gast';
|
||||
}
|
||||
$visitorInfo = self::detectVisitorInfo($m[6], $user);
|
||||
$logs[] = [
|
||||
'time' => $m[1],
|
||||
'ip' => $m[2],
|
||||
'host' => $m[3],
|
||||
'user' => $user,
|
||||
'visitor_info' => $visitorInfo,
|
||||
'lang' => $m[4],
|
||||
'page' => $m[5],
|
||||
'ua' => $m[6],
|
||||
|
||||
Reference in New Issue
Block a user