CodePress CMS v1.9.0: visitor statistics with SVG world map and GeoIP
- Add GeoIP class with provider chain: local DB-IP Lite, MaxMind .mmdb, external API - Add built-in pure-PHP MMDBReader so .mmdb works without Composer - Add cli/geoip-update.php to download DB-IP Lite and build a compact binary index - Add cli/generate-world-map.php to generate the world map SVG from Natural Earth TopoJSON - Add Analytics class aggregating stats in admin/storage/stats.json with LOCK_EX - Add admin statistics page with choropleth world map, country list, top pages, daily chart, referrers and a period filter - Add GeoIP and privacy settings with database update and stats reset buttons - Add optional IP anonymization and configurable retention period - Add country field to requests.log (parser accepts 7, 8 or 9 fields) - Add country column to request log and KPI cards to the dashboard - Ignore GeoIP binaries and stats.json in Git - Update TODO.md, guides and version to 1.9.0
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* DB-IP Lite database downloader & binary converter for CodePress CMS
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli' && (!isset($_SESSION['admin_user']))) {
|
||||
// Can also be included from admin handler
|
||||
}
|
||||
|
||||
function updateGeoIPDatabase(): array
|
||||
{
|
||||
$baseDir = dirname(__DIR__) . '/admin/storage/geoip';
|
||||
if (!is_dir($baseDir)) {
|
||||
@mkdir($baseDir, 0755, true);
|
||||
}
|
||||
|
||||
$currentDate = new DateTime('first day of this month');
|
||||
$urls = [];
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$ym = $currentDate->format('Y-m');
|
||||
$urls[] = "https://download.db-ip.com/free/dbip-country-lite-{$ym}.csv.gz";
|
||||
$currentDate->modify('-1 month');
|
||||
}
|
||||
|
||||
$downloadUrl = null;
|
||||
$gzContent = null;
|
||||
|
||||
foreach ($urls as $url) {
|
||||
$ctx = stream_context_create(['http' => ['timeout' => 15, 'user_agent' => 'CodePressCMS/1.9.0']]);
|
||||
$data = @file_get_contents($url, false, $ctx);
|
||||
if ($data !== false && strlen($data) > 1000) {
|
||||
$downloadUrl = $url;
|
||||
$gzContent = $data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$gzContent) {
|
||||
return ['success' => false, 'message' => 'Kon DB-IP Lite database niet downloaden vanaf DB-IP.com.'];
|
||||
}
|
||||
|
||||
$csvData = @gzdecode($gzContent);
|
||||
if (!$csvData) {
|
||||
return ['success' => false, 'message' => 'Kon gecomprimeerde DB-IP database niet uitpakken.'];
|
||||
}
|
||||
|
||||
$ipv4BinPath = $baseDir . '/ipv4.bin';
|
||||
$ipv6BinPath = $baseDir . '/ipv6.bin';
|
||||
$v4Handle = fopen($ipv4BinPath, 'wb');
|
||||
$v6Handle = fopen($ipv6BinPath, 'wb');
|
||||
|
||||
$lines = explode("\n", $csvData);
|
||||
$v4Count = 0;
|
||||
$v6Count = 0;
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if ($line === '' || $line[0] === '#') continue;
|
||||
|
||||
$parts = str_getcsv($line);
|
||||
if (count($parts) < 3) continue;
|
||||
|
||||
$startIp = trim($parts[0]);
|
||||
$endIp = trim($parts[1]);
|
||||
$country = strtoupper(trim($parts[2]));
|
||||
|
||||
if (strlen($country) !== 2) continue;
|
||||
|
||||
if (filter_var($startIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
$startLong = ip2long($startIp);
|
||||
$endLong = ip2long($endIp);
|
||||
if ($startLong !== false && $endLong !== false) {
|
||||
// Pack 4-byte uint32 start, 4-byte uint32 end, 2-byte country code
|
||||
$record = pack('NNa2', $startLong, $endLong, $country);
|
||||
fwrite($v4Handle, $record);
|
||||
$v4Count++;
|
||||
}
|
||||
} elseif (filter_var($startIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$startBin = inet_pton($startIp);
|
||||
$endBin = inet_pton($endIp);
|
||||
if ($startBin !== false && $endBin !== false) {
|
||||
// Pack 16-byte start, 16-byte end, 2-byte country code
|
||||
$record = $startBin . $endBin . $country;
|
||||
fwrite($v6Handle, $record);
|
||||
$v6Count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose($v4Handle);
|
||||
fclose($v6Handle);
|
||||
|
||||
$meta = [
|
||||
'source' => 'DB-IP Lite',
|
||||
'attribution' => 'IP geolocation by DB-IP (https://dbip.com)',
|
||||
'updated' => date('Y-m-d H:i:s'),
|
||||
'url' => $downloadUrl,
|
||||
'ipv4_records' => $v4Count,
|
||||
'ipv6_records' => $v6Count,
|
||||
'ipv4_size' => filesize($ipv4BinPath),
|
||||
'ipv6_size' => filesize($ipv6BinPath),
|
||||
];
|
||||
|
||||
file_put_contents($baseDir . '/meta.json', json_encode($meta, JSON_PRETTY_PRINT));
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => "GeoIP database succesvol bijgewerkt! ({$v4Count} IPv4, {$v6Count} IPv6 records)",
|
||||
'meta' => $meta
|
||||
];
|
||||
}
|
||||
|
||||
if (php_sapi_name() === 'cli' && basename(__FILE__) === basename($_SERVER['SCRIPT_FILENAME'])) {
|
||||
echo "DB-IP Lite database bijwerken...\n";
|
||||
$res = updateGeoIPDatabase();
|
||||
echo $res['message'] . "\n";
|
||||
}
|
||||
Reference in New Issue
Block a user