From f341efe0565b678ff9d41b788c5d31a829ba1aec Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 22 Aug 2026 20:25:18 +0200 Subject: [PATCH] first --- .gitignore | 1 + monip/docker-compose.yml | 29 ++++++ monip/nginx/conf.d/default.conf | 27 +++++ monip/src/assets/style.css | 128 +++++++++++++++++++++++ monip/src/functions.php | 173 ++++++++++++++++++++++++++++++++ monip/src/index.php | 140 ++++++++++++++++++++++++++ 6 files changed, 498 insertions(+) create mode 100644 .gitignore create mode 100644 monip/docker-compose.yml create mode 100644 monip/nginx/conf.d/default.conf create mode 100644 monip/src/assets/style.css create mode 100644 monip/src/functions.php create mode 100644 monip/src/index.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3fd66b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +monip/.env diff --git a/monip/docker-compose.yml b/monip/docker-compose.yml new file mode 100644 index 0000000..f0232ff --- /dev/null +++ b/monip/docker-compose.yml @@ -0,0 +1,29 @@ +services: + fpm-monip: + image: php:8.2-fpm-alpine + container_name: fpm-monip + restart: unless-stopped + volumes: + - ./src:/var/www/html + environment: + TZ: "Europe/Paris" + IPINFO_TOKEN: ${IPINFO_TOKEN} + networks: + - traefik-net + monip: + image: nginx:alpine + container_name: monip + restart: unless-stopped +# ports: +# - "8030:80" + volumes: + - ./src:/var/www/html + - ./nginx/conf.d:/etc/nginx/conf.d + environment: + TZ: "Europe/Paris" + networks: + - traefik-net + +networks: + traefik-net: + external: true diff --git a/monip/nginx/conf.d/default.conf b/monip/nginx/conf.d/default.conf new file mode 100644 index 0000000..ee5ab41 --- /dev/null +++ b/monip/nginx/conf.d/default.conf @@ -0,0 +1,27 @@ +server { + listen 80; + server_name localhost; + root /var/www/html; + index index.php; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + # Gestion des fichiers PHP via PHP-FPM + location ~ \.php$ { + fastcgi_pass fpm-monip:9000; # Nom du service PHP-FPM dans docker-compose + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param REMOTE_ADDR $remote_addr; + fastcgi_param HTTP_X_REAL_IP $remote_addr; + fastcgi_param HTTP_X_FORWARDED_FOR $proxy_add_x_forwarded_for; + } + + # Cache des assets statiques + location ~* \.(css|js|png|jpg|jpeg|gif|ico)$ { + expires 30d; + add_header Cache-Control "public, no-transform"; + } +} diff --git a/monip/src/assets/style.css b/monip/src/assets/style.css new file mode 100644 index 0000000..f598036 --- /dev/null +++ b/monip/src/assets/style.css @@ -0,0 +1,128 @@ +@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Share+Tech+Mono&family=VT323&display=swap'); + +:root { + --matrix-bg: #020403; + --matrix-surface: #071009; + --matrix-surface-2: #0a140c; + --accent-primary: #00ff41; + --accent-soft: color-mix(in srgb, var(--accent-primary) 35%, white); + --accent-line: color-mix(in srgb, var(--accent-primary) 32%, transparent); + --accent-glow: color-mix(in srgb, var(--accent-primary) 32%, transparent); + --matrix-alert: #ff5a36; + --matrix-warn: #d8e7c4; + --matrix-muted: #6c8b73; +} + +* { box-sizing: border-box; } + +html { background: var(--matrix-bg); } + +body.matrix-mode { + position: relative; + min-height: 100vh; + margin: 0; + overflow-x: hidden; + background: var(--matrix-bg); + color: var(--accent-soft); + font-family: 'Share Tech Mono', monospace; + font-size: .78rem; + letter-spacing: .025em; +} + +body.matrix-mode::before { + position: fixed; + inset: 0; + z-index: -2; + pointer-events: none; + content: ""; + background: radial-gradient(ellipse at 50% -10%, color-mix(in srgb, var(--accent-primary) 8.5%, transparent), transparent 58%), var(--matrix-bg); +} + +body.matrix-mode::after { + position: fixed; + inset: 0; + z-index: 10; + pointer-events: none; + content: ""; + opacity: .12; + background: repeating-linear-gradient(to bottom, transparent 0, transparent 3px, color-mix(in srgb, var(--accent-primary) 16%, transparent) 4px, transparent 5px); +} + +.container { position: relative; z-index: 1; max-width: 1080px; } + +.card { + color: var(--accent-soft); + background: rgba(7, 16, 9, .92); + border: 1px solid var(--accent-line); + border-radius: 0; + box-shadow: 0 0 18px color-mix(in srgb, var(--accent-primary) 10%, transparent); +} + +.card:hover { + border-color: color-mix(in srgb, var(--accent-primary) 68%, transparent); + box-shadow: 0 0 22px color-mix(in srgb, var(--accent-primary) 15%, transparent); +} + +.card-header, .card-footer { border-color: var(--accent-line); border-radius: 0 !important; } +.card-header { padding: .6rem .78rem .48rem; font-family: 'VT323', monospace; font-size: .85rem; letter-spacing: .09em; text-transform: uppercase; } +.card-body { padding: .78rem; font-size: .78rem; } +.card-footer { padding: .55rem .78rem; background: rgba(0, 0, 0, .28) !important; color: var(--matrix-muted) !important; font-size: .65rem; } + +.header-main { color: var(--matrix-bg); background: var(--accent-primary); border-color: var(--accent-primary); } +.header-main h2 { color: var(--matrix-bg); text-shadow: none; font-size: 1.05rem; } +.header-alt { color: var(--accent-primary); background: color-mix(in srgb, var(--accent-primary) 10%, transparent); border-bottom: 1px solid var(--accent-primary); } +.header-alert { color: #ff9a72; background: rgba(120, 24, 12, .55); border-bottom: 1px solid var(--matrix-alert); } +.header-warn { color: var(--matrix-warn); background: rgba(140, 165, 98, .12); border-bottom: 1px solid rgba(216, 231, 196, .7); } +.header-debug { color: #b7c0b9; background: rgba(86, 99, 90, .27); border-bottom: 1px solid rgba(183, 192, 185, .55); } + +h1, h2, h3, h4, h5, h6 { color: var(--accent-primary); text-shadow: 0 0 8px color-mix(in srgb, var(--accent-primary) 32%, transparent); } +.header-main h2 { text-shadow: none; } +strong { color: color-mix(in srgb, var(--accent-primary) 20%, white); font-weight: normal; } +p { line-height: 1.4; } +.text-muted { color: var(--matrix-muted) !important; } +.text-danger { color: var(--matrix-alert) !important; text-shadow: 0 0 6px rgba(255, 90, 54, .3); } +.text-success { color: var(--accent-primary) !important; } + +pre { + max-height: 26rem; + margin: 0; + overflow: auto; + padding: .78rem; + color: var(--accent-primary); + background: #010201; + border: 1px solid var(--accent-line); + border-radius: 0; + font-family: 'JetBrains Mono', 'Menlo', 'Consolas', monospace; + font-size: .68rem; + line-height: 1.3; + white-space: pre-wrap; + word-break: break-word; +} + +.py-4 { padding-top: 2rem !important; padding-bottom: 2rem !important; } +.shadow-sm { box-shadow: 0 0 26px color-mix(in srgb, var(--accent-primary) 11%, transparent) !important; } + +.hue-toggle { + position: fixed; + right: .8rem; + bottom: .65rem; + z-index: 20; + padding: .18rem .35rem; + color: var(--accent-primary); + background: rgba(2, 4, 3, .82); + border: 1px solid var(--accent-line); + border-radius: 0; + font: .68rem 'Share Tech Mono', monospace; + letter-spacing: .04em; + cursor: pointer; + opacity: .72; +} + +.hue-toggle:hover, .hue-toggle:focus-visible { opacity: 1; color: var(--accent-primary); box-shadow: 0 0 10px color-mix(in srgb, var(--accent-primary) 22%, transparent); outline: none; } + +@media (max-width: 575.98px) { + .py-4 { padding-top: 1.25rem !important; padding-bottom: 1.25rem !important; } + .card-header { font-size: .85rem; } + .card-body { padding: .65rem; } +} + diff --git a/monip/src/functions.php b/monip/src/functions.php new file mode 100644 index 0000000..304f5aa --- /dev/null +++ b/monip/src/functions.php @@ -0,0 +1,173 @@ + true, + CURLOPT_TIMEOUT => 5, + ]); + $response = curl_exec($ch); + curl_close($ch); + $data = $response ? json_decode($response, true) : []; + return is_array($data) ? $data : []; +} + +function getRealClientIp(): string +{ + $trustedProxies = [ + '172.16.0.0/12', + '10.0.0.0/8', + '192.168.0.0/16', + '127.0.0.1', + ]; + $remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; + if (!ipInRanges($remoteAddr, $trustedProxies)) { + return $remoteAddr; + } + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $ips = array_map('trim', explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])); + foreach ($ips as $ip) { + if (filter_var($ip, FILTER_VALIDATE_IP)) { + return $ip; + } + } + } + if (!empty($_SERVER['HTTP_X_REAL_IP']) && filter_var($_SERVER['HTTP_X_REAL_IP'], FILTER_VALIDATE_IP)) { + return $_SERVER['HTTP_X_REAL_IP']; + } + return $remoteAddr; +} + +function ipInRanges(string $ip, array $ranges): bool +{ + foreach ($ranges as $range) { + if (ipInRange($ip, $range)) { + return true; + } + } + return false; +} + +function ipInRange(string $ip, string $cidr): bool +{ + if (strpos($cidr, '/') === false) { + return $ip === $cidr; + } + [$subnet, $bits] = explode('/', $cidr); + $ipLong = ip2long($ip); + $subnetLong = ip2long($subnet); + if ($ipLong === false || $subnetLong === false) { + return false; + } + $mask = -1 << (32 - (int)$bits); + $subnetLong &= $mask; + return ($ipLong & $mask) === $subnetLong; +} + +function getWhoisServer(string $ip): string +{ + // Convertir l'IP en nombre pour comparaison + $ipLong = ip2long($ip); + if ($ipLong === false) { + return 'whois.iana.org'; // Fallback + } + + // Plages IP simplifiées (à adapter si besoin) + if ($ipLong >= ip2long('192.0.0.0') && $ipLong <= ip2long('223.255.255.255')) { + return 'whois.apnic.net'; // Asie-Pacifique + } elseif ($ipLong >= ip2long('128.0.0.0') && $ipLong <= ip2long('191.255.255.255')) { + return 'whois.arin.net'; // Amérique + } elseif ($ipLong >= ip2long('185.0.0.0') && $ipLong <= ip2long('191.255.255.255')) { + return 'whois.ripe.net'; // Europe (étendue) + } elseif ($ipLong >= ip2long('200.0.0.0') && $ipLong <= ip2long('200.255.255.255')) { + return 'whois.lacnic.net'; // Amérique Latine + } else { + return 'whois.ripe.net'; // Europe par défaut (pour ton cas) + } +} + +function fetchLocalWhois(string $ip, int $timeout = 5): array +{ + if (!filter_var($ip, FILTER_VALIDATE_IP)) { + return ['raw' => '', 'error' => 'IP invalide']; + } + + $escapedIp = escapeshellarg($ip); + + // 1. Convertir l'IP en format in-addr.arpa (ex: 82.64.199.86 → 86.199.64.82.in-addr.arpa) + $parts = explode('.', $ip); + $reverseIp = implode('.', array_reverse($parts)) . '.in-addr.arpa'; + $escapedReverseIp = escapeshellarg($reverseIp); + + // 2. Essayer avec whois.ripe.net + in-addr.arpa (méthode la plus fiable) + $command = "timeout {$timeout} whois -h whois.ripe.net {$escapedReverseIp} 2>&1"; + $output = shell_exec($command); + + // 3. Fallback : Essayer sans -h (utilise la config par défaut) + if (empty(trim($output ?? '')) || strpos($output, 'no entries found') !== false) { + $command = "timeout {$timeout} whois {$escapedReverseIp} 2>&1"; + $output = shell_exec($command); + } + + // 4. Fallback ultime : Essayer avec l'IP directe (si le client whois est bien configuré) + if (empty(trim($output ?? '')) || strpos($output, 'no entries found') !== false) { + $command = "timeout {$timeout} whois {$escapedIp} 2>&1"; + $output = shell_exec($command); + } + + return [ + 'raw' => $output !== null ? trim($output) : '', + 'source' => 'local' + ]; +} + +function fetchWhois2(string $ip): array +{ + if (!function_exists('curl_init')) { + return []; + } + $ch = curl_init("https://ipwhois.app/json/{$ip}"); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 5, + CURLOPT_CONNECTTIMEOUT => 3, + CURLOPT_SSL_VERIFYPEER => true, + ]); + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + if ($response === false || $httpCode !== 200) { + return []; + } + $data = json_decode($response, true); + return is_array($data) ? $data : []; +} + +function countryName(string $code): string +{ + $countries = [ + 'FR' => 'France', + 'US' => 'États-Unis', + 'GB' => 'Royaume-Uni', + 'DE' => 'Allemagne', + 'ES' => 'Espagne', + 'IT' => 'Italie', + 'BE' => 'Belgique', + 'CH' => 'Suisse', + 'CA' => 'Canada', + 'PT' => 'Portugal', + 'NL' => 'Pays-Bas', + 'LU' => 'Luxembourg', + ]; + + $normalizedCode = strtoupper(trim($code)); + return $countries[$normalizedCode] ?? $code; +} + diff --git a/monip/src/index.php b/monip/src/index.php new file mode 100644 index 0000000..a399dc2 --- /dev/null +++ b/monip/src/index.php @@ -0,0 +1,140 @@ + $whoisData]; +?> + + + + +MONIP - <?= htmlspecialchars((string)$ip, ENT_QUOTES, 'UTF-8'); ?> + + + + +
+
+

> VBLOG.IO IP TRACER

+
+
+
> ADRESSE IP SOURCE
+
+

IPv4:

+

REVERSE DNS:

+

HOSTNAME:

+
+
> GÉOLOCALISATION
+
+ +

PAYS:

+

RÉGION:

+

VILLE:

+

FUSEAU HORAIRE:

+

COORDONNÉES:

+

DONNÉES GÉO INDISPONIBLES.

+
+
+
+
> FOURNISSEUR (ISP)
+
+ +

ISP:

+

ORGANISATION:

+

AS:

+

DONNÉES ISP INDISPONIBLES.

+
+
> SÉCURITÉ
+
+ +

PROXY: OUI' : 'Non'; ?>

+

HÉBERGÉ: OUI' : 'Non'; ?>

+

MOBILE: OUI' : 'Non'; ?>

+
+
+
> WHOIS / ASN
+
+ +
> EN-TÊTES HTTP
+
+ +
> NAVIGATEUR & SYSTÈME
+
+

USER AGENT:

+

RÉFÉRENT:

+

HOST:

+

DATE LOCALE:

+
+
+ +
+
+ + + + +