mirror of
https://github.com/Dvorinka/beszel.git
synced 2026-07-29 07:13:48 +00:00
feat(hub): improve WHOIS lookup reliability and enhance site UI
Build Docker images / Hub (push) Failing after 52s
Build Docker images / Hub (push) Failing after 52s
Implement enhanced WHOIS lookup strategies, specifically targeting .eu domains through EURid web scraping and alternative services to improve data accuracy for expiry dates. - Add EURid web scraping and alternative WHOIS service support for .eu domains - Increase timeouts for .eu domain lookups in TCP and native WHOIS - Improve domain scheduler to prevent overwriting valid data with zero-value dates - Enhance site UI with subdomain indicators in domain tables - Add filtering capabilities to the calendar view - Implement drag-and-drop reordering for systems table - Add new debug and test utilities for WHOIS and date parsing logic
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func tryAlternativeWHOISService(ctx context.Context, serviceName, url, domainName string) (string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create request for %s: %w", serviceName, err)
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
||||
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
||||
|
||||
client := &http.Client{Timeout: 15 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to fetch %s: %w", serviceName, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("%s returned status %d", serviceName, resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read %s response: %w", serviceName, err)
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func parseAlternativeWHOISHTML(html, domainName, serviceName string) {
|
||||
fmt.Printf("HTML length from %s: %d characters\n", serviceName, len(html))
|
||||
|
||||
// Look for expiry date patterns (common across WHOIS services)
|
||||
expiryPatterns := []string{
|
||||
`Expiry Date:\s*</[^>]*>\s*([^<\n]+)`,
|
||||
`Expiry Date:</[^>]*>\s*([^<\n]+)`,
|
||||
`Expires on:\s*</[^>]*>\s*([^<\n]+)`,
|
||||
`Expires:\s*</[^>]*>\s*([^<\n]+)`,
|
||||
`"expiry":"([^"]+)"`,
|
||||
`"expires":"([^"]+)"`,
|
||||
`data-expiry="([^"]+)"`,
|
||||
`expiry_date["\s]*:\s*"([^"]+)"`,
|
||||
`expires["\s]*:\s*"([^"]+)"`,
|
||||
`\d{4}-\d{2}-\d{2}`, // ISO date pattern
|
||||
`\d{2}/\d{2}/\d{4}`, // DD/MM/YYYY pattern
|
||||
`\d{2}-\d{2}-\d{4}`, // MM-DD-YYYY pattern
|
||||
`202\d-\d{2}-\d{2}`, // 202x-xx-xx pattern
|
||||
}
|
||||
|
||||
fmt.Printf("\n=== Searching for expiry dates on %s ===\n", serviceName)
|
||||
for _, pattern := range expiryPatterns {
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindAllStringSubmatch(html, -1)
|
||||
if len(matches) > 0 {
|
||||
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||
for i, match := range matches {
|
||||
if len(match) > 1 {
|
||||
fmt.Printf(" Match %d: %s\n", i+1, strings.TrimSpace(match[1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for registrar name
|
||||
registrarPatterns := []string{
|
||||
`Registrar:\s*</[^>]*>\s*([^<\n]+)`,
|
||||
`Registrar:</[^>]*>\s*([^<\n]+)`,
|
||||
`Registered through:\s*</[^>]*>\s*([^<\n]+)`,
|
||||
`"registrar":"([^"]+)"`,
|
||||
`data-registrar="([^"]+)"`,
|
||||
}
|
||||
|
||||
fmt.Printf("\n=== Searching for registrar on %s ===\n", serviceName)
|
||||
for _, pattern := range registrarPatterns {
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindAllStringSubmatch(html, -1)
|
||||
if len(matches) > 0 {
|
||||
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||
for i, match := range matches {
|
||||
if len(match) > 1 {
|
||||
fmt.Printf(" Match %d: %s\n", i+1, strings.TrimSpace(match[1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for status
|
||||
statusPatterns := []string{
|
||||
`Status:\s*</[^>]*>\s*([^<\n]+)`,
|
||||
`Status:</[^>]*>\s*([^<\n]+)`,
|
||||
`"status":"([^"]+)"`,
|
||||
`data-status="([^"]+)"`,
|
||||
}
|
||||
|
||||
fmt.Printf("\n=== Searching for status on %s ===\n", serviceName)
|
||||
for _, pattern := range statusPatterns {
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindAllStringSubmatch(html, -1)
|
||||
if len(matches) > 0 {
|
||||
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||
for i, match := range matches {
|
||||
if len(match) > 1 {
|
||||
fmt.Printf(" Match %d: %s\n", i+1, strings.TrimSpace(match[1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show sample HTML
|
||||
fmt.Printf("\n=== Sample HTML from %s (first 1500 chars) ===\n", serviceName)
|
||||
if len(html) > 1500 {
|
||||
fmt.Printf("%s...\n", html[:1500])
|
||||
} else {
|
||||
fmt.Printf("%s\n", html)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
testDomains := []string{"bookra.eu", "sportcreative.eu"}
|
||||
|
||||
for _, domain := range testDomains {
|
||||
fmt.Printf("Testing alternative WHOIS services for: %s\n", domain)
|
||||
fmt.Println("========================================")
|
||||
|
||||
// Try multiple alternative WHOIS services
|
||||
services := []struct {
|
||||
name string
|
||||
url string
|
||||
}{
|
||||
{"whois.com", fmt.Sprintf("https://www.whois.com/whois/%s", domain)},
|
||||
{"who.is", fmt.Sprintf("https://who.is/whois/%s", domain)},
|
||||
{"ip2location.com", fmt.Sprintf("https://www.ip2location.com/whois/%s", domain)},
|
||||
}
|
||||
|
||||
for _, service := range services {
|
||||
fmt.Printf("\n--- Testing %s ---\n", service.name)
|
||||
|
||||
html, err := tryAlternativeWHOISService(context.Background(), service.name, service.url, domain)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
parseAlternativeWHOISHTML(html, domain, service.name)
|
||||
}
|
||||
|
||||
fmt.Println("\n========================================\n")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// parseFlexibleDate parses dates in various formats
|
||||
func parseFlexibleDate(dateString string) string {
|
||||
if dateString == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Remove common separators and normalize
|
||||
normalized := regexp.MustCompile(`[./-]`).ReplaceAllString(dateString, "-")
|
||||
normalized = regexp.MustCompile(`\s+`).ReplaceAllString(normalized, "")
|
||||
|
||||
// Try different date formats
|
||||
formats := []string{
|
||||
// DD.MM.YYYY, DD/MM/YYYY, DD-MM-YYYY
|
||||
`^(\d{2})[-/.](\d{2})[-/.](\d{4})$`,
|
||||
// YYYY-MM-DD, YYYY/MM/DD, YYYY.MM.DD
|
||||
`^(\d{4})[-/.](\d{2})[-/.](\d{2})$`,
|
||||
// MM-DD-YYYY, MM/DD/YYYY, MM.DD.YYYY
|
||||
`^(\d{2})[-/.](\d{2})[-/.](\d{4})$`,
|
||||
}
|
||||
|
||||
for _, format := range formats {
|
||||
re := regexp.MustCompile(format)
|
||||
match := re.FindStringSubmatch(normalized)
|
||||
if match != nil {
|
||||
part1, part2, part3 := match[1], match[2], match[3]
|
||||
|
||||
// Determine if it's DD.MM.YYYY or YYYY.MM.DD format
|
||||
var year, month, day string
|
||||
|
||||
if len(part1) == 4 {
|
||||
// YYYY.MM.DD format
|
||||
year = part1
|
||||
month = part2
|
||||
day = part3
|
||||
} else {
|
||||
// DD.MM.YYYY format (most common)
|
||||
day = part1
|
||||
month = part2
|
||||
year = part3
|
||||
}
|
||||
|
||||
// Validate and format
|
||||
yearNum, _ := strconv.Atoi(year)
|
||||
monthNum, _ := strconv.Atoi(month)
|
||||
dayNum, _ := strconv.Atoi(day)
|
||||
|
||||
if yearNum >= 2000 && yearNum <= 2100 && monthNum >= 1 && monthNum <= 12 && dayNum >= 1 && dayNum <= 31 {
|
||||
return fmt.Sprintf("%s-%s-%s", year, fmt.Sprintf("%02s", month), fmt.Sprintf("%02s", day))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func main() {
|
||||
testDates := []string{
|
||||
"15.06.2026",
|
||||
"13.11.2029",
|
||||
"2026-06-15",
|
||||
"15/06/2026",
|
||||
"13-11-2029",
|
||||
"2026.06.15",
|
||||
"15 06 2026",
|
||||
"invalid-date",
|
||||
"32.13.2026", // Invalid day/month
|
||||
"1999.12.31", // Too old
|
||||
}
|
||||
|
||||
fmt.Println("Testing Flexible Date Parsing")
|
||||
fmt.Println("=============================")
|
||||
|
||||
for _, date := range testDates {
|
||||
result := parseFlexibleDate(date)
|
||||
status := "✅"
|
||||
if result == "" {
|
||||
status = "❌"
|
||||
}
|
||||
fmt.Printf("%s '%s' -> '%s'\n", status, date, result)
|
||||
}
|
||||
|
||||
fmt.Println("\nExample Usage:")
|
||||
fmt.Println("=============")
|
||||
fmt.Println("User can paste: '15.06.2026, 13.11.2029'")
|
||||
fmt.Println("System will parse: '2026-06-15', '2029-11-13'")
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
testDomains := []string{"bookra.eu", "sportcreative.eu"}
|
||||
|
||||
for _, domain := range testDomains {
|
||||
fmt.Printf("Testing EURid web scraping for: %s\n", domain)
|
||||
fmt.Println("----------------------------------------")
|
||||
|
||||
// EURid web WHOIS URL
|
||||
url := fmt.Sprintf("https://www.eurid.eu/en/registrations/search/?domain=%s", domain)
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create request: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
||||
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
||||
|
||||
client := &http.Client{Timeout: 15 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to fetch EURid web page: %v\n", err)
|
||||
continue
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
fmt.Printf("EURid web page returned status %d\n", resp.StatusCode)
|
||||
continue
|
||||
}
|
||||
|
||||
// Read the HTML response
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to read EURid response: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
html := string(body)
|
||||
fmt.Printf("HTML length: %d characters\n", len(html))
|
||||
|
||||
// Look for expiry date patterns
|
||||
expiryPatterns := []string{
|
||||
`Expiry date:\s*</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||
`Expiry date:</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||
`Expiry date</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||
`"expiryDate":"([^"]+)"`,
|
||||
`data-expiry="([^"]+)"`,
|
||||
`\d{2}/\d{2}/\d{4}`, // Generic date pattern
|
||||
}
|
||||
|
||||
fmt.Println("\n=== Searching for expiry dates ===")
|
||||
for _, pattern := range expiryPatterns {
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindAllStringSubmatch(html, -1)
|
||||
if len(matches) > 0 {
|
||||
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||
for i, match := range matches {
|
||||
if len(match) > 1 {
|
||||
fmt.Printf(" Match %d: %s\n", i+1, match[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for registrar patterns
|
||||
registrarPatterns := []string{
|
||||
`Registrar:\s*</strong>\s*([^<\n]+)`,
|
||||
`Registrar:</strong>\s*([^<\n]+)`,
|
||||
`Registrar</strong>\s*([^<\n]+)`,
|
||||
`"registrar":"([^"]+)"`,
|
||||
`data-registrar="([^"]+)"`,
|
||||
}
|
||||
|
||||
fmt.Println("\n=== Searching for registrar ===")
|
||||
for _, pattern := range registrarPatterns {
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindAllStringSubmatch(html, -1)
|
||||
if len(matches) > 0 {
|
||||
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||
for i, match := range matches {
|
||||
if len(match) > 1 {
|
||||
fmt.Printf(" Match %d: %s\n", i+1, strings.TrimSpace(match[1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show some sample HTML to understand the structure
|
||||
fmt.Println("\n=== Sample HTML (first 1000 chars) ===")
|
||||
if len(html) > 1000 {
|
||||
fmt.Printf("%s...\n", html[:1000])
|
||||
} else {
|
||||
fmt.Printf("%s\n", html)
|
||||
}
|
||||
|
||||
fmt.Println("\n========================================\n")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func tryEURidEndpoint(ctx context.Context, url, domainName string) (string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
// Use more realistic browser headers
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
|
||||
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
|
||||
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
|
||||
req.Header.Set("Accept-Encoding", "gzip, deflate, br")
|
||||
req.Header.Set("Cache-Control", "no-cache")
|
||||
req.Header.Set("Pragma", "no-cache")
|
||||
req.Header.Set("Sec-Ch-Ua", "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"")
|
||||
req.Header.Set("Sec-Ch-Ua-Mobile", "?0")
|
||||
req.Header.Set("Sec-Ch-Ua-Platform", "\"Windows\"")
|
||||
req.Header.Set("Sec-Fetch-Dest", "document")
|
||||
req.Header.Set("Sec-Fetch-Mode", "navigate")
|
||||
req.Header.Set("Sec-Fetch-Site", "none")
|
||||
req.Header.Set("Sec-Fetch-User", "?1")
|
||||
req.Header.Set("Upgrade-Insecure-Requests", "1")
|
||||
|
||||
client := &http.Client{Timeout: 20 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to fetch EURid web page: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("EURid web page returned status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Read the HTML response
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read EURid response: %w", err)
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func parseEURidWebHTML(html, domainName string) {
|
||||
fmt.Printf("HTML length: %d characters\n", len(html))
|
||||
|
||||
// Look for expiry date patterns
|
||||
expiryPatterns := []string{
|
||||
`Expiry date:\s*</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||
`Expiry date:</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||
`Expiry date</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||
`"expiryDate":"([^"]+)"`,
|
||||
`data-expiry="([^"]+)"`,
|
||||
`\d{2}/\d{2}/\d{4}`, // Generic date pattern
|
||||
`\d{4}-\d{2}-\d{2}`, // ISO date pattern
|
||||
}
|
||||
|
||||
fmt.Println("\n=== Searching for expiry dates ===")
|
||||
for _, pattern := range expiryPatterns {
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindAllStringSubmatch(html, -1)
|
||||
if len(matches) > 0 {
|
||||
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||
for i, match := range matches {
|
||||
if len(match) > 1 {
|
||||
fmt.Printf(" Match %d: %s\n", i+1, match[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for registrar patterns
|
||||
registrarPatterns := []string{
|
||||
`Registrar:\s*</strong>\s*([^<\n]+)`,
|
||||
`Registrar:</strong>\s*([^<\n]+)`,
|
||||
`Registrar</strong>\s*([^<\n]+)`,
|
||||
`"registrar":"([^"]+)"`,
|
||||
`data-registrar="([^"]+)"`,
|
||||
}
|
||||
|
||||
fmt.Println("\n=== Searching for registrar ===")
|
||||
for _, pattern := range registrarPatterns {
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindAllStringSubmatch(html, -1)
|
||||
if len(matches) > 0 {
|
||||
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||
for i, match := range matches {
|
||||
if len(match) > 1 {
|
||||
fmt.Printf(" Match %d: %s\n", i+1, strings.TrimSpace(match[1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for any JSON data
|
||||
jsonPatterns := []string{
|
||||
`\{[^}]*"expiry[^}]*\}`,
|
||||
`\{[^}]*"registrar[^}]*\}`,
|
||||
`\{[^}]*"domain"[^}]*\}`,
|
||||
}
|
||||
|
||||
fmt.Println("\n=== Searching for JSON data ===")
|
||||
for _, pattern := range jsonPatterns {
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindAllString(html, -1)
|
||||
if len(matches) > 0 {
|
||||
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||
for i, match := range matches {
|
||||
fmt.Printf(" Match %d: %s\n", i+1, match)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show sample HTML
|
||||
fmt.Println("\n=== Sample HTML (first 2000 chars) ===")
|
||||
if len(html) > 2000 {
|
||||
fmt.Printf("%s...\n", html[:2000])
|
||||
} else {
|
||||
fmt.Printf("%s\n", html)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
testDomains := []string{"bookra.eu", "sportcreative.eu"}
|
||||
|
||||
for _, domain := range testDomains {
|
||||
fmt.Printf("Testing EURid web scraping for: %s\n", domain)
|
||||
fmt.Println("========================================")
|
||||
|
||||
// Try multiple EURid endpoints
|
||||
endpoints := []string{
|
||||
fmt.Sprintf("https://whois.eurid.eu/en/?q=%s", domain),
|
||||
fmt.Sprintf("https://whois.eurid.eu/en/search?q=%s", domain),
|
||||
fmt.Sprintf("https://www.eurid.eu/en/whois/?domain=%s", domain),
|
||||
}
|
||||
|
||||
for i, url := range endpoints {
|
||||
fmt.Printf("\n--- Attempt %d: %s ---\n", i+1, url)
|
||||
|
||||
html, err := tryEURidEndpoint(context.Background(), url, domain)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
parseEURidWebHTML(html, domain)
|
||||
break // If we got HTML, don't try other endpoints
|
||||
}
|
||||
|
||||
fmt.Println("\n========================================\n")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Test TCP WHOIS for .eu domains
|
||||
testDomains := []string{"bookra.eu", "sportcreative.eu"}
|
||||
|
||||
for _, domainName := range testDomains {
|
||||
fmt.Printf("Testing TCP WHOIS for: %s\n", domainName)
|
||||
fmt.Println("----------------------------------------")
|
||||
|
||||
// Extract TLD
|
||||
parts := strings.Split(domainName, ".")
|
||||
if len(parts) < 2 {
|
||||
fmt.Printf("Invalid domain format: %s\n", domainName)
|
||||
continue
|
||||
}
|
||||
tld := strings.ToLower(parts[len(parts)-1])
|
||||
|
||||
// WHOIS server for .eu
|
||||
server := "whois.eu"
|
||||
if tld != "eu" {
|
||||
fmt.Printf("Skipping non-eu domain: %s\n", domainName)
|
||||
continue
|
||||
}
|
||||
|
||||
addr := net.JoinHostPort(server, "43")
|
||||
fmt.Printf("Connecting to: %s\n", addr)
|
||||
|
||||
// Use longer timeout for .eu domains
|
||||
timeout := 20 * time.Second
|
||||
dialer := &net.Dialer{Timeout: timeout}
|
||||
|
||||
start := time.Now()
|
||||
conn, err := dialer.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
fmt.Printf("TCP WHOIS dial failed: %v\n", err)
|
||||
continue
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
fmt.Printf("Connected in: %v\n", time.Since(start))
|
||||
|
||||
// Send query
|
||||
query := domainName + "\r\n"
|
||||
fmt.Printf("Sending query: %q\n", query)
|
||||
|
||||
writeStart := time.Now()
|
||||
if _, err := conn.Write([]byte(query)); err != nil {
|
||||
fmt.Printf("TCP WHOIS write failed: %v\n", err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("Write completed in: %v\n", time.Since(writeStart))
|
||||
|
||||
// Set read deadline
|
||||
if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil {
|
||||
fmt.Printf("Failed to set read deadline: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Read response
|
||||
var output strings.Builder
|
||||
buf := make([]byte, 4096)
|
||||
totalRead := 0
|
||||
|
||||
readStart := time.Now()
|
||||
for {
|
||||
n, err := conn.Read(buf)
|
||||
if n > 0 {
|
||||
output.Write(buf[:n])
|
||||
totalRead += n
|
||||
fmt.Printf("Read %d bytes (total: %d)\n", n, totalRead)
|
||||
}
|
||||
if err != nil {
|
||||
if err.Error() != "EOF" {
|
||||
fmt.Printf("Read error: %v\n", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
// Prevent infinite loop
|
||||
if totalRead > 10000 {
|
||||
fmt.Printf("Stopping read after 10KB\n")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Read completed in: %v\n", time.Since(readStart))
|
||||
fmt.Printf("Total bytes read: %d\n", totalRead)
|
||||
|
||||
response := output.String()
|
||||
if len(response) > 0 {
|
||||
fmt.Printf("✅ WHOIS Response (first 500 chars):\n")
|
||||
if len(response) > 500 {
|
||||
fmt.Printf("%s...\n", response[:500])
|
||||
} else {
|
||||
fmt.Printf("%s\n", response)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("❌ No response received\n")
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println("========================================")
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/hub/domains/whois"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a new lookup service with a dummy API key
|
||||
lookupService := whois.NewLookupService("dummy-api-key")
|
||||
|
||||
// Test domains including .eu domain
|
||||
testDomains := []string{
|
||||
"google.com",
|
||||
"bookra.eu", // Your .eu domain
|
||||
"sportcreative.eu", // Another .eu domain
|
||||
"github.com",
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
fmt.Println("=== WHOIS Lookup Test ===")
|
||||
fmt.Println()
|
||||
|
||||
for _, domainName := range testDomains {
|
||||
fmt.Printf("Testing domain: %s\n", domainName)
|
||||
fmt.Println("----------------------------------------")
|
||||
|
||||
start := time.Now()
|
||||
|
||||
// Test WHOIS lookup
|
||||
whoisData, err := lookupService.LookupWHOIS(ctx, domainName)
|
||||
|
||||
duration := time.Since(start)
|
||||
fmt.Printf("Lookup duration: %v\n", duration)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else if whoisData != nil {
|
||||
fmt.Printf("✅ WHOIS Data Found:\n")
|
||||
fmt.Printf(" Domain: %s\n", whoisData.DomainName)
|
||||
fmt.Printf(" Status: %v\n", whoisData.Status)
|
||||
fmt.Printf(" DNSSEC: %s\n", whoisData.DNSSEC)
|
||||
|
||||
if whoisData.Dates.ExpiryDate != nil && !whoisData.Dates.ExpiryDate.IsZero() {
|
||||
fmt.Printf(" Expiry Date: %s\n", whoisData.Dates.ExpiryDate.Format("2006-01-02"))
|
||||
}
|
||||
if whoisData.Dates.CreationDate != nil && !whoisData.Dates.CreationDate.IsZero() {
|
||||
fmt.Printf(" Creation Date: %s\n", whoisData.Dates.CreationDate.Format("2006-01-02"))
|
||||
}
|
||||
if whoisData.Dates.UpdatedDate != nil && !whoisData.Dates.UpdatedDate.IsZero() {
|
||||
fmt.Printf(" Updated Date: %s\n", whoisData.Dates.UpdatedDate.Format("2006-01-02"))
|
||||
}
|
||||
|
||||
if whoisData.Registrar.Name != "" {
|
||||
fmt.Printf(" Registrar: %s\n", whoisData.Registrar.Name)
|
||||
}
|
||||
if whoisData.Registrar.ID != "" {
|
||||
fmt.Printf(" Registrar ID: %s\n", whoisData.Registrar.ID)
|
||||
}
|
||||
|
||||
if whoisData.Registrant.Name != "" || whoisData.Registrant.Organization != "" {
|
||||
fmt.Printf(" Registrant: %s (%s)\n", whoisData.Registrant.Name, whoisData.Registrant.Organization)
|
||||
}
|
||||
if whoisData.Registrant.Country != "" {
|
||||
fmt.Printf(" Registrant Country: %s\n", whoisData.Registrant.Country)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("❌ No WHOIS data returned\n")
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println("=== Full Domain Lookup Test ===")
|
||||
|
||||
// Test full domain lookup
|
||||
fullDomain, err := lookupService.LookupDomain(ctx, domainName)
|
||||
if err != nil {
|
||||
fmt.Printf("Full lookup ERROR: %v\n", err)
|
||||
} else if fullDomain != nil {
|
||||
fmt.Printf("✅ Full Domain Data:\n")
|
||||
fmt.Printf(" Domain: %s\n", fullDomain.DomainName)
|
||||
fmt.Printf(" Status: %s\n", fullDomain.Status)
|
||||
fmt.Printf(" Active: %t\n", fullDomain.Active)
|
||||
if fullDomain.ExpiryDate != nil {
|
||||
fmt.Printf(" Expiry: %s\n", fullDomain.ExpiryDate.Format("2006-01-02"))
|
||||
daysUntil := int(fullDomain.ExpiryDate.Sub(time.Now()).Hours() / 24)
|
||||
fmt.Printf(" Days Until Expiry: %d\n", daysUntil)
|
||||
}
|
||||
fmt.Printf(" Registrar: %s\n", fullDomain.RegistrarName)
|
||||
fmt.Printf(" DNSSEC: %s\n", fullDomain.DNSSEC)
|
||||
|
||||
if len(fullDomain.IPv4Addresses) > 0 {
|
||||
fmt.Printf(" IPv4 Addresses: %v\n", fullDomain.IPv4Addresses)
|
||||
}
|
||||
if len(fullDomain.IPv6Addresses) > 0 {
|
||||
fmt.Printf(" IPv6 Addresses: %v\n", fullDomain.IPv6Addresses)
|
||||
}
|
||||
if len(fullDomain.NameServers) > 0 {
|
||||
fmt.Printf(" Name Servers: %v\n", fullDomain.NameServers)
|
||||
}
|
||||
if len(fullDomain.MXRecords) > 0 {
|
||||
fmt.Printf(" MX Records: %v\n", fullDomain.MXRecords)
|
||||
}
|
||||
|
||||
if fullDomain.SSLValidTo != nil {
|
||||
fmt.Printf(" SSL Valid Until: %s\n", fullDomain.SSLValidTo.Format("2006-01-02"))
|
||||
sslDaysUntil := int(fullDomain.SSLValidTo.Sub(time.Now()).Hours() / 24)
|
||||
fmt.Printf(" SSL Days Until: %d\n", sslDaysUntil)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println("========================================")
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Test WhoisXML API for .eu domains
|
||||
const testWhoisXML = async (domain) => {
|
||||
try {
|
||||
// Note: This would require a real API key to test
|
||||
console.log(`Testing WhoisXML API for: ${domain}`);
|
||||
console.log('Note: WhoisXML API requires an API key to test properly');
|
||||
|
||||
// URL structure they use
|
||||
const url = `https://www.whoisxmlapi.com/whoisserver/WhoisService?apiKey=YOUR_API_KEY&outputFormat=json&domainName=${domain}`;
|
||||
console.log(`WhoisXML URL: ${url}`);
|
||||
|
||||
// Based on their code, WhoisXML should return expiry dates for .eu domains
|
||||
console.log('WhoisXML API typically provides expiry dates for .eu domains that TCP WHOIS does not');
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
testWhoisXML('bookra.eu');
|
||||
testWhoisXML('sportcreative.eu');
|
||||
Reference in New Issue
Block a user