diff --git a/api.php b/api.php index c8ea37e..78c2037 100644 --- a/api.php +++ b/api.php @@ -18,6 +18,71 @@ try { $action = $_GET['action'] ?? 'domains'; +function dh_strip_html_text(string $html): string { + $html = preg_replace('/]*>[\s\S]*?<\/script>/i', ' ', $html); + $html = preg_replace('/]*>[\s\S]*?<\/style>/i', ' ', $html); + $text = strip_tags($html ?? ''); + $text = preg_replace('/\s+/u', ' ', $text ?? ''); + return trim($text ?? ''); +} + +function dh_fetch_text(string $url): ?string { + $ctx = stream_context_create([ + 'http' => [ + 'timeout' => 12, + 'user_agent' => 'Mozilla/5.0 DomainHunterPanel/1.0', + 'follow_location' => 1, + ], + 'ssl' => [ + 'verify_peer' => true, + 'verify_peer_name' => true, + ] + ]); + $raw = @file_get_contents($url, false, $ctx); + if ($raw === false) return null; + return dh_strip_html_text($raw); +} + +function dh_find_price(string $text, string $tld, string $kind): ?float { + $hintsRegister = ['rejestr', 'rejestracja', 'new', 'first year', '1 rok']; + $hintsRenew = ['odnow', 'renew', 'renewal', 'kolejny', 'next year', '2 rok']; + $hints = $kind === 'renew' ? $hintsRenew : $hintsRegister; + + $token = '.' . strtolower($tld); + $len = mb_strlen($text, 'UTF-8'); + + $pos = 0; + while (($idx = mb_stripos($text, $token, $pos, 'UTF-8')) !== false) { + $start = max(0, $idx - 240); + $chunk = mb_substr($text, $start, 520, 'UTF-8'); + $chunkLow = mb_strtolower($chunk, 'UTF-8'); + $ok = false; + foreach ($hints as $h) { + if (mb_strpos($chunkLow, $h, 0, 'UTF-8') !== false) { $ok = true; break; } + } + if ($ok && preg_match('/(\d{1,4}(?:[\.,]\d{1,2})?)\s*(zł|pln|eur|€|usd|\$)/iu', $chunkLow, $m)) { + return floatval(str_replace(',', '.', $m[1])); + } + $pos = $idx + mb_strlen($token, 'UTF-8'); + if ($pos >= $len) break; + } + + foreach ($hints as $h) { + $pos = 0; + while (($idx = mb_stripos($text, $h, $pos, 'UTF-8')) !== false) { + $start = max(0, $idx - 100); + $chunk = mb_substr($text, $start, 320, 'UTF-8'); + if (preg_match('/(\d{1,4}(?:[\.,]\d{1,2})?)\s*(zł|pln|eur|€|usd|\$)/iu', $chunk, $m)) { + return floatval(str_replace(',', '.', $m[1])); + } + $pos = $idx + mb_strlen($h, 'UTF-8'); + if ($pos >= $len) break; + } + } + + return null; +} + if ($action === 'domains') { $q = trim($_GET['q'] ?? ''); $tld = trim($_GET['tld'] ?? ''); @@ -82,6 +147,89 @@ if ($action === 'domains') { exit; } +if ($action === 'live_prices') { + $domain = trim($_GET['domain'] ?? ''); + $tld = strtolower(trim($_GET['tld'] ?? '')); + if ($domain !== '' && $tld === '' && strpos($domain, '.') !== false) { + $parts = explode('.', $domain); + $tld = strtolower(end($parts)); + } + + if ($tld === '') { + http_response_code(400); + echo json_encode(['ok' => false, 'error' => 'domain or tld required']); + exit; + } + + $regPath = __DIR__ . '/data/registrars.json'; + if (!file_exists($regPath)) { + http_response_code(500); + echo json_encode(['ok' => false, 'error' => 'registrars.json missing']); + exit; + } + + $regData = json_decode(file_get_contents($regPath), true) ?: []; + $cachePath = __DIR__ . '/data/live-price-cache.json'; + $cacheTtl = 1800; + $cache = []; + if (file_exists($cachePath)) { + $cache = json_decode(file_get_contents($cachePath), true) ?: []; + } + + $now = time(); + $items = []; + + foreach (($regData['registrars'] ?? []) as $reg) { + $name = $reg['name'] ?? ''; + if ($name === '') continue; + $autoUrl = $reg['autoPricing'][$tld]['url'] ?? null; + $baseUrl = $reg['url'] ?? null; + $key = strtolower($name) . '|' . $tld; + + $entry = $cache[$key] ?? null; + if (!$entry || (($entry['ts'] ?? 0) + $cacheTtl < $now)) { + $register = null; + $renew = null; + $sourceUrl = $autoUrl ?: $baseUrl; + if ($sourceUrl) { + $text = dh_fetch_text($sourceUrl); + if ($text !== null) { + $register = dh_find_price($text, $tld, 'register'); + $renew = dh_find_price($text, $tld, 'renew'); + } + } + $entry = [ + 'register_price' => $register, + 'renew_price' => $renew, + 'source_url' => $sourceUrl, + 'ts' => $now, + ]; + $cache[$key] = $entry; + } + + $items[] = [ + 'registrar' => $name, + 'tld' => $tld, + 'register_price' => $entry['register_price'] ?? null, + 'renew_price' => $entry['renew_price'] ?? null, + 'source_url' => $entry['source_url'] ?? null, + 'live' => true, + 'checked_at' => gmdate('c', intval($entry['ts'] ?? $now)), + ]; + } + + @file_put_contents($cachePath, json_encode($cache, JSON_UNESCAPED_UNICODE)); + + echo json_encode([ + 'ok' => true, + 'domain' => $domain, + 'tld' => $tld, + 'cached_seconds' => $cacheTtl, + 'items' => $items, + ], JSON_UNESCAPED_UNICODE); + exit; +} + if ($action === 'prices') { if ($_SERVER['REQUEST_METHOD'] === 'POST') { $raw = json_decode(file_get_contents('php://input'), true) ?: []; diff --git a/index.html b/index.html index ebdc03b..6e42153 100644 --- a/index.html +++ b/index.html @@ -34,7 +34,7 @@

Domain Hunter Panel

-

Dynamicznie z DB: domeny z Mongo + status nowości w kolejnych skanach.

+

Domeny z Mongo + ceny live per TLD pobierane dynamicznie z rejestratorów (z krótkim cache).

Filtr domen

@@ -63,7 +63,7 @@