mirror of
https://github.com/Dvorinka/beszel.git
synced 2026-06-03 21:02:56 +00:00
fe5c7eaa95
Build Docker images / Hub (push) Failing after 1m35s
Implement comprehensive domain data collection including provider detection (DNS, hosting, email, CA), HTTP headers, TLS certificate chains, and SEO metadata. Added PageSpeed Insights integration for monitors to track Core Web Vitals. - **hub**: - Add provider detection logic for DNS, email, and hosting. - Expand `Domain` entity to include SEO, headers, certificates, and enhanced registration details. - Implement automated collection of TLD, WHOIS raw data, and host country codes. - Update scheduler to track changes in providers and security settings (privacy/transfer lock). - Add PageSpeed check endpoint to monitor API. - **site**: - Update domain table and detail views to display new intelligence (providers, headers, SEO). - Implement PageSpeed metrics visualization with Core Web Vitals status indicators. - Add display options for provider information in the domain list. - **db**: - Add migration for new domain collection fields.
348 lines
11 KiB
Go
348 lines
11 KiB
Go
package domain
|
|
|
|
import "time"
|
|
|
|
// Domain represents a tracked domain with WHOIS and DNS information
|
|
type Domain struct {
|
|
ID string `json:"id" db:"id"`
|
|
DomainName string `json:"domain_name" db:"domain_name"`
|
|
Status string `json:"status" db:"status"`
|
|
Active bool `json:"active" db:"active"`
|
|
|
|
// Dates
|
|
ExpiryDate *time.Time `json:"expiry_date" db:"expiry_date"`
|
|
CreationDate *time.Time `json:"creation_date" db:"creation_date"`
|
|
UpdatedDate *time.Time `json:"updated_date" db:"updated_date"`
|
|
|
|
// Registrar
|
|
RegistrarName string `json:"registrar_name" db:"registrar_name"`
|
|
RegistrarID string `json:"registrar_id" db:"registrar_id"`
|
|
RegistrarURL string `json:"registrar_url" db:"registrar_url"`
|
|
RegistryDomainID string `json:"registry_domain_id" db:"registry_domain_id"`
|
|
|
|
// DNS
|
|
DNSSEC string `json:"dnssec" db:"dnssec"`
|
|
NameServers []string `json:"name_servers" db:"name_servers"`
|
|
MXRecords []string `json:"mx_records" db:"mx_records"`
|
|
TXTRecords []string `json:"txt_records" db:"txt_records"`
|
|
CNAMERecord string `json:"cname_record" db:"cname_record"`
|
|
SRVRecords []string `json:"srv_records" db:"srv_records"`
|
|
|
|
// IP Addresses
|
|
IPv4Addresses []string `json:"ipv4_addresses" db:"ipv4_addresses"`
|
|
IPv6Addresses []string `json:"ipv6_addresses" db:"ipv6_addresses"`
|
|
|
|
// SSL Certificate
|
|
SSLIssuer string `json:"ssl_issuer" db:"ssl_issuer"`
|
|
SSLIssuerCountry string `json:"ssl_issuer_country" db:"ssl_issuer_country"`
|
|
SSLValidFrom *time.Time `json:"ssl_valid_from" db:"ssl_valid_from"`
|
|
SSLValidTo *time.Time `json:"ssl_valid_to" db:"ssl_valid_to"`
|
|
SSLSubject string `json:"ssl_subject" db:"ssl_subject"`
|
|
SSLFingerprint string `json:"ssl_fingerprint" db:"ssl_fingerprint"`
|
|
SSLKeySize int `json:"ssl_key_size" db:"ssl_key_size"`
|
|
SSLSignatureAlgo string `json:"ssl_signature_algo" db:"ssl_signature_algo"`
|
|
|
|
// Host Info
|
|
HostCountry string `json:"host_country" db:"host_country"`
|
|
HostRegion string `json:"host_region" db:"host_region"`
|
|
HostCity string `json:"host_city" db:"host_city"`
|
|
HostISP string `json:"host_isp" db:"host_isp"`
|
|
HostOrg string `json:"host_org" db:"host_org"`
|
|
HostAS string `json:"host_as" db:"host_as"`
|
|
HostLat float64 `json:"host_lat" db:"host_lat"`
|
|
HostLon float64 `json:"host_lon" db:"host_lon"`
|
|
|
|
// Valuation
|
|
PurchasePrice float64 `json:"purchase_price" db:"purchase_price"`
|
|
CurrentValue float64 `json:"current_value" db:"current_value"`
|
|
RenewalCost float64 `json:"renewal_cost" db:"renewal_cost"`
|
|
AutoRenew bool `json:"auto_renew" db:"auto_renew"`
|
|
|
|
// Registrant Contact (from WHOIS)
|
|
RegistrantName string `json:"registrant_name" db:"registrant_name"`
|
|
RegistrantOrg string `json:"registrant_org" db:"registrant_org"`
|
|
RegistrantStreet string `json:"registrant_street" db:"registrant_street"`
|
|
RegistrantCity string `json:"registrant_city" db:"registrant_city"`
|
|
RegistrantState string `json:"registrant_state" db:"registrant_state"`
|
|
RegistrantCountry string `json:"registrant_country" db:"registrant_country"`
|
|
RegistrantPostal string `json:"registrant_postal" db:"registrant_postal"`
|
|
|
|
// Abuse Contact (from WHOIS)
|
|
AbuseEmail string `json:"abuse_email" db:"abuse_email"`
|
|
AbusePhone string `json:"abuse_phone" db:"abuse_phone"`
|
|
|
|
// Provider Detection
|
|
DNSProvider string `json:"dns_provider" db:"dns_provider"`
|
|
HostingProvider string `json:"hosting_provider" db:"hosting_provider"`
|
|
EmailProvider string `json:"email_provider" db:"email_provider"`
|
|
CAProvider string `json:"ca_provider" db:"ca_provider"`
|
|
|
|
// HTTP Headers
|
|
Headers []Header `json:"headers" db:"headers"`
|
|
|
|
// Certificate Chain
|
|
Certificates []Certificate `json:"certificates" db:"certificates"`
|
|
|
|
// SEO Metadata
|
|
SEOMeta *SEOMeta `json:"seo_meta" db:"seo_meta"`
|
|
|
|
// Raw WHOIS Response
|
|
WHOISRaw string `json:"whois_raw" db:"whois_raw"`
|
|
|
|
// Registration Details
|
|
PrivacyEnabled bool `json:"privacy_enabled" db:"privacy_enabled"`
|
|
TransferLock bool `json:"transfer_lock" db:"transfer_lock"`
|
|
TLD string `json:"tld" db:"tld"`
|
|
DomainStatuses []string `json:"domain_statuses" db:"domain_statuses"`
|
|
|
|
// Enhanced Geo
|
|
HostCountryCode string `json:"host_country_code" db:"host_country_code"`
|
|
|
|
// Metadata
|
|
Tags []string `json:"tags" db:"tags"`
|
|
Notes string `json:"notes" db:"notes"`
|
|
FaviconURL string `json:"favicon_url" db:"favicon_url"`
|
|
|
|
// Ownership
|
|
UserID string `json:"user" db:"user"`
|
|
Created time.Time `json:"created" db:"created"`
|
|
Updated time.Time `json:"updated" db:"updated"`
|
|
LastChecked time.Time `json:"last_checked" db:"last_checked"`
|
|
|
|
// Alert settings
|
|
AlertDaysBefore int `json:"alert_days_before" db:"alert_days_before"` // Days before expiry to alert
|
|
SSLAlertEnabled bool `json:"ssl_alert_enabled" db:"ssl_alert_enabled"`
|
|
}
|
|
|
|
// DomainHistory tracks changes to a domain over time
|
|
type DomainHistory struct {
|
|
ID string `json:"id" db:"id"`
|
|
DomainID string `json:"domain" db:"domain"`
|
|
ChangeType string `json:"change_type" db:"change_type"` // expiry, ssl, dns, registrar, ip, etc.
|
|
FieldName string `json:"field_name" db:"field_name"`
|
|
OldValue string `json:"old_value" db:"old_value"`
|
|
NewValue string `json:"new_value" db:"new_value"`
|
|
UserID string `json:"user" db:"user"`
|
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
|
}
|
|
|
|
// WHOISData represents parsed WHOIS information
|
|
type WHOISData struct {
|
|
DomainName string `json:"domain_name"`
|
|
Status []string `json:"status"`
|
|
DNSSEC string `json:"dnssec"`
|
|
Dates WHOISDates `json:"dates"`
|
|
Registrar WHOISRegistrar `json:"registrar"`
|
|
Registrant WHOISContact `json:"registrant"`
|
|
Abuse WHOISAbuse `json:"abuse"`
|
|
}
|
|
|
|
type WHOISDates struct {
|
|
ExpiryDate *time.Time `json:"expiry_date"`
|
|
CreationDate *time.Time `json:"creation_date"`
|
|
UpdatedDate *time.Time `json:"updated_date"`
|
|
}
|
|
|
|
type WHOISRegistrar struct {
|
|
Name string `json:"name"`
|
|
ID string `json:"id"`
|
|
URL string `json:"url"`
|
|
RegistryDomainID string `json:"registry_domain_id"`
|
|
}
|
|
|
|
type WHOISContact struct {
|
|
Name string `json:"name"`
|
|
Organization string `json:"organization"`
|
|
Street string `json:"street"`
|
|
City string `json:"city"`
|
|
State string `json:"state"`
|
|
Country string `json:"country"`
|
|
PostalCode string `json:"postal_code"`
|
|
}
|
|
|
|
type WHOISAbuse struct {
|
|
Email string `json:"email"`
|
|
Phone string `json:"phone"`
|
|
}
|
|
|
|
// SSLInfo represents SSL certificate information
|
|
type SSLInfo struct {
|
|
Issuer string `json:"issuer"`
|
|
IssuerCountry string `json:"issuer_country"`
|
|
ValidFrom time.Time `json:"valid_from"`
|
|
ValidTo time.Time `json:"valid_to"`
|
|
Subject string `json:"subject"`
|
|
Fingerprint string `json:"fingerprint"`
|
|
KeySize int `json:"key_size"`
|
|
SignatureAlgo string `json:"signature_algo"`
|
|
}
|
|
|
|
// DNSInfo represents DNS records
|
|
type DNSInfo struct {
|
|
NameServers []string `json:"name_servers"`
|
|
MXRecords []string `json:"mx_records"`
|
|
TXTRecords []string `json:"txt_records"`
|
|
DNSSEC string `json:"dnssec"`
|
|
}
|
|
|
|
// HostInfo represents host/geolocation information
|
|
type HostInfo struct {
|
|
Country string `json:"country"`
|
|
Region string `json:"region"`
|
|
City string `json:"city"`
|
|
Lat float64 `json:"lat"`
|
|
Lon float64 `json:"lon"`
|
|
ISP string `json:"isp"`
|
|
Org string `json:"org"`
|
|
ASNumber string `json:"as_number"`
|
|
}
|
|
|
|
// IPInfo represents IP address information
|
|
type IPInfo struct {
|
|
IPv4 []string `json:"ipv4"`
|
|
IPv6 []string `json:"ipv6"`
|
|
}
|
|
|
|
// Header represents an HTTP response header
|
|
type Header struct {
|
|
Name string `json:"name"`
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
// Certificate represents a TLS certificate in the chain
|
|
type Certificate struct {
|
|
Issuer string `json:"issuer"`
|
|
Subject string `json:"subject"`
|
|
AltNames []string `json:"alt_names"`
|
|
ValidFrom time.Time `json:"valid_from"`
|
|
ValidTo time.Time `json:"valid_to"`
|
|
CAProvider string `json:"ca_provider"`
|
|
}
|
|
|
|
// OpenGraphMeta represents Open Graph metadata
|
|
type OpenGraphMeta struct {
|
|
URL string `json:"url"`
|
|
Type string `json:"type"`
|
|
Title string `json:"title"`
|
|
Images []string `json:"images"`
|
|
Description string `json:"description"`
|
|
}
|
|
|
|
// TwitterMeta represents Twitter card metadata
|
|
type TwitterMeta struct {
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Image string `json:"image"`
|
|
Card string `json:"card"`
|
|
}
|
|
|
|
// GeneralMeta represents general HTML meta tags
|
|
type GeneralMeta struct {
|
|
Title string `json:"title"`
|
|
Author string `json:"author"`
|
|
Robots string `json:"robots"`
|
|
Keywords string `json:"keywords"`
|
|
Canonical string `json:"canonical"`
|
|
Description string `json:"description"`
|
|
}
|
|
|
|
// RobotsTxt represents parsed robots.txt data
|
|
type RobotsTxt struct {
|
|
Fetched bool `json:"fetched"`
|
|
Groups []RobotsGroup `json:"groups"`
|
|
Sitemaps []string `json:"sitemaps"`
|
|
}
|
|
|
|
// RobotsGroup represents a user-agent group in robots.txt
|
|
type RobotsGroup struct {
|
|
UserAgents []string `json:"userAgents"`
|
|
Rules []RobotsRule `json:"rules"`
|
|
}
|
|
|
|
// RobotsRule represents a single rule in robots.txt
|
|
type RobotsRule struct {
|
|
Type string `json:"type"`
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
// SEOMeta represents all SEO-related metadata
|
|
type SEOMeta struct {
|
|
OpenGraph OpenGraphMeta `json:"openGraph"`
|
|
Twitter TwitterMeta `json:"twitter"`
|
|
General GeneralMeta `json:"general"`
|
|
Robots RobotsTxt `json:"robots"`
|
|
}
|
|
|
|
// ChangeType constants for domain history
|
|
const (
|
|
ChangeTypeExpiry = "expiry"
|
|
ChangeTypeSSL = "ssl"
|
|
ChangeTypeDNS = "dns"
|
|
ChangeTypeRegistrar = "registrar"
|
|
ChangeTypeIP = "ip"
|
|
ChangeTypeHost = "host"
|
|
ChangeTypeStatus = "status"
|
|
ChangeTypeProvider = "provider"
|
|
ChangeTypeSecurity = "security"
|
|
ChangeTypeSEO = "seo"
|
|
)
|
|
|
|
// Domain status constants
|
|
const (
|
|
DomainStatusActive = "active"
|
|
DomainStatusExpiring = "expiring" // Within alert_days_before
|
|
DomainStatusExpired = "expired"
|
|
DomainStatusUnknown = "unknown"
|
|
DomainStatusPaused = "paused"
|
|
)
|
|
|
|
// IsExpiring returns true if the domain is expiring within the alert window
|
|
func (d *Domain) IsExpiring() bool {
|
|
if d.ExpiryDate == nil || d.AlertDaysBefore <= 0 {
|
|
return false
|
|
}
|
|
alertDate := time.Now().AddDate(0, 0, d.AlertDaysBefore)
|
|
return d.ExpiryDate.Before(alertDate) && d.ExpiryDate.After(time.Now())
|
|
}
|
|
|
|
// IsExpired returns true if the domain has expired
|
|
func (d *Domain) IsExpired() bool {
|
|
if d.ExpiryDate == nil {
|
|
return false
|
|
}
|
|
return d.ExpiryDate.Before(time.Now())
|
|
}
|
|
|
|
// DaysUntilExpiry returns the number of days until expiry
|
|
func (d *Domain) DaysUntilExpiry() int {
|
|
if d.ExpiryDate == nil {
|
|
return -1
|
|
}
|
|
return int(time.Until(*d.ExpiryDate).Hours() / 24)
|
|
}
|
|
|
|
// SSLDaysUntilExpiry returns days until SSL expiry
|
|
func (d *Domain) SSLDaysUntilExpiry() int {
|
|
if d.SSLValidTo == nil {
|
|
return -1
|
|
}
|
|
return int(time.Until(*d.SSLValidTo).Hours() / 24)
|
|
}
|
|
|
|
// GetStatus returns the current domain status
|
|
func (d *Domain) GetStatus() string {
|
|
if !d.Active {
|
|
return DomainStatusPaused
|
|
}
|
|
if d.IsExpired() {
|
|
return DomainStatusExpired
|
|
}
|
|
if d.IsExpiring() {
|
|
return DomainStatusExpiring
|
|
}
|
|
if d.ExpiryDate == nil {
|
|
return DomainStatusUnknown
|
|
}
|
|
return DomainStatusActive
|
|
}
|