first commit
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
/**
|
||||
* Genereert een SVG wereldkaart voor de syslog dashboard.
|
||||
* Gebaseerd op Natural Earth data via TopoJSON world-atlas.
|
||||
*/
|
||||
|
||||
$url = 'https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json';
|
||||
$outputFile = __DIR__ . '/../assets/img/world-map.svg';
|
||||
|
||||
echo "Downloaden van wereldkaart data...\n";
|
||||
$json = @file_get_contents($url);
|
||||
if (!$json) {
|
||||
echo "Fout: kan wereldkaart data niet downloaden van {$url}\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$data = json_decode($json, true);
|
||||
if (!$data || !isset($data['objects']['countries'])) {
|
||||
echo "Fout: ongeldige TopoJSON data\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// ISO numeric -> alpha-2 mapping
|
||||
$isoNumericToAlpha2 = [
|
||||
4 => 'AF', 8 => 'AL', 12 => 'DZ', 20 => 'EG', 24 => 'AO',
|
||||
31 => 'AZ', 32 => 'AR', 36 => 'AU', 40 => 'AT', 50 => 'BD',
|
||||
51 => 'AM', 56 => 'BE', 64 => 'BT', 68 => 'BO', 70 => 'BA',
|
||||
72 => 'BW', 76 => 'BR', 84 => 'BZ', 90 => 'SB', 96 => 'BN',
|
||||
100 => 'BG', 104 => 'MM', 108 => 'BI', 112 => 'BY', 116 => 'KH',
|
||||
120 => 'CM', 124 => 'CA', 132 => 'CV', 140 => 'CF', 144 => 'LK',
|
||||
148 => 'TD', 152 => 'CL', 156 => 'CN', 158 => 'TW', 162 => 'CX',
|
||||
166 => 'CC', 170 => 'CO', 174 => 'KM', 178 => 'CG', 180 => 'CD',
|
||||
184 => 'CK', 188 => 'CR', 191 => 'HR', 192 => 'CU', 196 => 'CY',
|
||||
203 => 'CZ', 204 => 'BJ', 208 => 'DK', 212 => 'DM', 214 => 'DO',
|
||||
218 => 'EC', 222 => 'SV', 226 => 'GQ', 231 => 'ET', 232 => 'ER',
|
||||
233 => 'EE', 234 => 'FO', 238 => 'FK', 239 => 'GS', 242 => 'FJ',
|
||||
246 => 'FI', 248 => 'AX', 250 => 'FR', 254 => 'GF', 258 => 'PF',
|
||||
260 => 'TF', 262 => 'DJ', 266 => 'GA', 268 => 'GE', 270 => 'GM',
|
||||
276 => 'DE', 288 => 'GH', 292 => 'GI', 296 => 'KI', 300 => 'GR',
|
||||
304 => 'GL', 308 => 'GD', 312 => 'GP', 316 => 'GU', 320 => 'GT',
|
||||
324 => 'GN', 328 => 'GY', 332 => 'HT', 334 => 'HM', 336 => 'VA',
|
||||
340 => 'HN', 344 => 'HK', 348 => 'HU', 352 => 'IS', 356 => 'IN',
|
||||
360 => 'ID', 364 => 'IR', 368 => 'IQ', 372 => 'IE', 376 => 'IL',
|
||||
380 => 'IT', 384 => 'CI', 388 => 'JM', 392 => 'JP', 398 => 'KZ',
|
||||
400 => 'JO', 404 => 'KE', 408 => 'KP', 410 => 'KR', 414 => 'KW',
|
||||
417 => 'KG', 418 => 'LA', 422 => 'LB', 426 => 'LS', 428 => 'LV',
|
||||
430 => 'LR', 434 => 'LY', 438 => 'LI', 440 => 'LT', 442 => 'LU',
|
||||
446 => 'MO', 450 => 'MG', 454 => 'MW', 458 => 'MY', 462 => 'MV',
|
||||
466 => 'ML', 470 => 'MT', 478 => 'MR', 480 => 'MU', 484 => 'MX',
|
||||
492 => 'MC', 496 => 'MN', 498 => 'MD', 499 => 'ME', 504 => 'MA',
|
||||
508 => 'MZ', 512 => 'OM', 516 => 'NA', 520 => 'NR', 524 => 'NP',
|
||||
528 => 'NL', 540 => 'NC', 548 => 'VU', 554 => 'NZ', 558 => 'NI',
|
||||
562 => 'NE', 566 => 'NG', 570 => 'NU', 574 => 'NF', 578 => 'NO',
|
||||
580 => 'MP', 583 => 'FM', 584 => 'MH', 585 => 'PW', 586 => 'PK',
|
||||
591 => 'PA', 598 => 'PG', 600 => 'PY', 604 => 'PE', 608 => 'PH',
|
||||
612 => 'PN', 616 => 'PL', 620 => 'PT', 624 => 'GW', 626 => 'TL',
|
||||
630 => 'PR', 634 => 'QA', 634 => 'QA', 638 => 'RE', 642 => 'RO',
|
||||
643 => 'RU', 646 => 'RW', 652 => 'BL', 654 => 'SH', 659 => 'KN',
|
||||
660 => 'AI', 662 => 'LC', 663 => 'MF', 666 => 'PM', 670 => 'VC',
|
||||
674 => 'SM', 678 => 'ST', 682 => 'SA', 686 => 'SN', 688 => 'RS',
|
||||
690 => 'SC', 694 => 'SL', 702 => 'SG', 703 => 'SK', 704 => 'VN',
|
||||
705 => 'SI', 706 => 'SO', 710 => 'ZA', 716 => 'ZW', 724 => 'ES',
|
||||
728 => 'SS', 729 => 'SD', 732 => 'EH', 740 => 'SR', 748 => 'SZ',
|
||||
752 => 'SE', 756 => 'CH', 760 => 'SY', 762 => 'TJ', 764 => 'TH',
|
||||
768 => 'TG', 772 => 'TK', 776 => 'TO', 780 => 'TT', 784 => 'AE',
|
||||
788 => 'TN', 792 => 'TR', 795 => 'TM', 796 => 'TC', 798 => 'TV',
|
||||
800 => 'UG', 804 => 'UA', 807 => 'MK', 818 => 'EG', 826 => 'GB',
|
||||
831 => 'GG', 832 => 'JE', 833 => 'IM', 834 => 'TZ', 840 => 'US',
|
||||
854 => 'BF', 858 => 'UY', 860 => 'UZ', 862 => 'VE', 887 => 'YE',
|
||||
894 => 'ZM',
|
||||
];
|
||||
|
||||
$width = 1000;
|
||||
$height = 400;
|
||||
$t = $data['transform'];
|
||||
$scale = $t['scale'];
|
||||
$translate = $t['translate'];
|
||||
$arcs = $data['arcs'];
|
||||
|
||||
function decodeArc($idx, &$arcs, $scale, $translate) {
|
||||
$reverse = false;
|
||||
if ($idx < 0) {
|
||||
$reverse = true;
|
||||
$idx = -$idx - 1;
|
||||
}
|
||||
$arc = $arcs[$idx];
|
||||
$points = [];
|
||||
$x = 0; $y = 0;
|
||||
foreach ($arc as $delta) {
|
||||
$x += $delta[0];
|
||||
$y += $delta[1];
|
||||
$lon = $x * $scale[0] + $translate[0];
|
||||
$lat = $y * $scale[1] + $translate[1];
|
||||
$points[] = [$lon, $lat];
|
||||
}
|
||||
if ($reverse) $points = array_reverse($points);
|
||||
return $points;
|
||||
}
|
||||
|
||||
function ringToSVG($points, $w, $h) {
|
||||
if (count($points) < 3) return '';
|
||||
$parts = [];
|
||||
foreach ($points as $i => $p) {
|
||||
$px = ($p[0] + 180) / 360 * $w;
|
||||
$py = (90 - $p[1]) / 180 * $h;
|
||||
$parts[] = ($i === 0 ? 'M' : 'L') . round($px, 2) . ',' . round($py, 2);
|
||||
}
|
||||
$parts[] = 'Z';
|
||||
return implode(' ', $parts);
|
||||
}
|
||||
|
||||
$svgPaths = [];
|
||||
|
||||
foreach ($data['objects']['countries']['geometries'] as $geom) {
|
||||
$numId = (int)$geom['id'];
|
||||
$isoAlpha2 = $isoNumericToAlpha2[$numId] ?? '';
|
||||
$name = $geom['properties']['name'] ?? 'Unknown';
|
||||
if ($isoAlpha2 === 'AQ') continue;
|
||||
|
||||
$polygons = [];
|
||||
if ($geom['type'] === 'Polygon') {
|
||||
$polygons[] = $geom['arcs'];
|
||||
} elseif ($geom['type'] === 'MultiPolygon') {
|
||||
$polygons = $geom['arcs'];
|
||||
}
|
||||
|
||||
foreach ($polygons as $rings) {
|
||||
$allPoints = [];
|
||||
foreach ($rings as $ring) {
|
||||
foreach ($ring as $arcIdx) {
|
||||
$pts = decodeArc($arcIdx, $arcs, $scale, $translate);
|
||||
$allPoints = array_merge($allPoints, $pts);
|
||||
}
|
||||
break; // outer ring only, skip holes
|
||||
}
|
||||
$d = ringToSVG($allPoints, $width, $height);
|
||||
if ($d) {
|
||||
$svgPaths[] = ['iso' => $isoAlpha2, 'name' => $name, 'd' => $d];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' . $width . ' ' . $height . '" class="codepress-world-map" width="100%" height="100%">
|
||||
<style>
|
||||
.codepress-world-map { display: block; background: #eaf2fb; }
|
||||
.codepress-world-map .country { fill: #dfe4ea; stroke: #ffffff; stroke-width: 0.5; stroke-linejoin: round; vector-effect: non-scaling-stroke; transition: fill .15s ease; }
|
||||
.codepress-world-map .country:hover { fill: #ffc107; }
|
||||
</style>
|
||||
<g class="countries">';
|
||||
|
||||
foreach ($svgPaths as $c) {
|
||||
$svg .= "\n" . ' <path class="country" id="' . htmlspecialchars($c['iso']) . '" data-name="' . htmlspecialchars($c['name']) . '" d="' . $c['d'] . '" />';
|
||||
}
|
||||
|
||||
$svg .= '
|
||||
</g>
|
||||
</svg>';
|
||||
|
||||
file_put_contents($outputFile, $svg);
|
||||
echo "Wereldkaart opgeslagen: {$outputFile}\n";
|
||||
echo count($svgPaths) . " landpaden gegenereerd.\n";
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* Download de DB-IP Country Lite database voor GeoIP lookups.
|
||||
* Dit is een gratis database met wekelijkse updates.
|
||||
*/
|
||||
|
||||
$outputDir = __DIR__ . '/../storage/geoip';
|
||||
|
||||
if (!is_dir($outputDir)) {
|
||||
mkdir($outputDir, 0755, true);
|
||||
}
|
||||
|
||||
$yearMonth = date('Y-m');
|
||||
$url = "https://download.db-ip.com/free/dbip-country-lite-{$yearMonth}.csv.gz";
|
||||
$gzFile = $outputDir . '/dbip-country-lite.csv.gz';
|
||||
$csvFile = $outputDir . '/dbip-country-lite.csv';
|
||||
|
||||
echo "Downloaden GeoIP database van {$url}...\n";
|
||||
|
||||
$fp = fopen($gzFile, 'w');
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_FILE => $fp,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_TIMEOUT => 120,
|
||||
CURLOPT_USERAGENT => 'SyslogDash/1.0',
|
||||
]);
|
||||
curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
unlink($gzFile);
|
||||
echo "Fout: HTTP {$httpCode} bij downloaden GeoIP database\n";
|
||||
echo "Tip: bezoek https://www.db-ip.com/db/lite.php voor handmatige download\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Uitpakken...\n";
|
||||
$gz = gzopen($gzFile, 'r');
|
||||
$csv = fopen($csvFile, 'w');
|
||||
while ($line = gzgets($gz)) {
|
||||
fputs($csv, $line);
|
||||
}
|
||||
gzclose($gz);
|
||||
fclose($csv);
|
||||
unlink($gzFile);
|
||||
|
||||
// Convert to binary format for faster lookups
|
||||
$ipv4File = $outputDir . '/ipv4.bin';
|
||||
$ipv6File = $outputDir . '/ipv6.bin';
|
||||
|
||||
$fh4 = fopen($ipv4File, 'w');
|
||||
$fh6 = fopen($ipv6File, 'w');
|
||||
$count4 = 0;
|
||||
$count6 = 0;
|
||||
|
||||
$csv = fopen($csvFile, 'r');
|
||||
while (($row = fgetcsv($csv)) !== false) {
|
||||
if (count($row) < 3) continue;
|
||||
$start = trim($row[0]);
|
||||
$end = trim($row[1]);
|
||||
$code = trim($row[2]);
|
||||
if (strlen($code) !== 2) continue;
|
||||
|
||||
if (filter_var($start, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
$startLong = ip2long($start);
|
||||
$endLong = ip2long($end);
|
||||
if ($startLong === false || $endLong === false) continue;
|
||||
fwrite($fh4, pack('NNa2', $startLong, $endLong, $code));
|
||||
$count4++;
|
||||
} elseif (filter_var($start, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$startBin = inet_pton($start);
|
||||
$endBin = inet_pton($end);
|
||||
if ($startBin === false || $endBin === false) continue;
|
||||
fwrite($fh6, $startBin . $endBin . $code);
|
||||
$count6++;
|
||||
}
|
||||
}
|
||||
fclose($csv);
|
||||
fclose($fh4);
|
||||
fclose($fh6);
|
||||
|
||||
unlink($csvFile);
|
||||
|
||||
echo "GeoIP database bijgewerkt:\n";
|
||||
echo " IPv4: {$count4} ranges\n";
|
||||
echo " IPv6: {$count6} ranges\n";
|
||||
echo " Bestanden: {$ipv4File}, {$ipv6File}\n";
|
||||
Reference in New Issue
Block a user