v1.6.31 : analyse complète des logs + détection bots

- AccessLogParser : tous chemins/statuts pour IPs publiques (ipAllPaths, ipAllDays, ipAgents)
- Détection bots par patterns (data/bots.json, ~50 patterns initiaux)
- Section « Agents détectés » en bas de page admin/stats avec badge 🤖
- Panneau d'édition des patterns bots (formulaire avec CSRF)
- Drill-down IP : section « Autres chemins » (hors articles/livres)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 21:33:47 +02:00
parent 68a44d19d1
commit d53b5da31a
6 changed files with 288 additions and 77 deletions
+45
View File
@@ -2768,6 +2768,37 @@ switch ($action) {
$adminData['as_groups'] = asGroups();
$adminData['stats_pages_by_day'] = $statsRaw['pages_by_day'] ?? [];
$adminData['stats_ip_data'] = $statsRaw['ip_data'] ?? [];
// Patterns de bots — initialisation si absent
$botsFile = DATA_PATH . '/bots.json';
if (!file_exists($botsFile)) {
$defaultBots = [
'Googlebot','Googlebot-Image','Google-InspectionTool','Google-Extended',
'bingbot','BingPreview','msnbot',
'DuckDuckBot','DuckDuckGo-Favicons-Bot',
'Baiduspider','YandexBot','YandexImages','YandexMetrika',
'Applebot',
'facebookexternalhit','facebot',
'Twitterbot','LinkedInBot','Slackbot','TelegramBot','WhatsApp','Discordbot',
'PetalBot','Bytespider','SogouSpider','SeznamBot','Exabot',
'AhrefsBot','SemrushBot','MJ12bot','DotBot','rogerbot','BLEXBot','DataForSeoBot',
'Screaming Frog SEO Spider',
'ClaudeBot','GPTBot','Google-Extended','PerplexityBot','cohere-ai','anthropic-ai',
'meta-externalagent','OAI-SearchBot','Amazonbot',
'CCBot','ia_archiver','archive.org_bot',
'NetcraftSurveyAgent',
'python-requests','python-urllib','Python/',
'curl/','wget/','Wget/',
'Go-http-client/1','Java/','Apache-HttpClient','okhttp/',
'Scrapy','HeadlessChrome','PhantomJS','Puppeteer','Playwright','Selenium',
'UptimeRobot','Pingdom','StatusCake','Site24x7','GTmetrix',
'Chrome-Lighthouse','PageSpeed','Zabbix','check_http',
'libwww-perl','GuzzleHttp','masscan','zgrab','nuclei',
];
@mkdir(dirname($botsFile), 0755, true);
@file_put_contents($botsFile, json_encode($defaultBots, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
$adminData['bot_patterns'] = json_decode((string) file_get_contents($botsFile), true) ?: [];
}
if ($tab === 'categories') {
@@ -3206,6 +3237,20 @@ switch ($action) {
header('Location: /admin/stats?' . ($ok ? 'saved=1' : 'error=write'));
exit;
case 'admin_save_bots':
requireAuth();
if (!isAdmin() || $_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(403);
exit;
}
$botsFile = DATA_PATH . '/bots.json';
$patterns = array_values(array_unique(array_filter(
array_map('trim', explode("\n", (string) ($_POST['bot_patterns'] ?? '')))
)));
$ok = @file_put_contents($botsFile, json_encode($patterns, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)) !== false;
header('Location: /admin/stats?' . ($ok ? 'saved=1' : 'error=write'));
exit;
case 'admin_create_role':
requireAuth();
if (!isAdmin() || $_SERVER['REQUEST_METHOD'] !== 'POST') {