288 lines
9.1 KiB
Go
Raw Normal View History

2026-03-12 20:51:38 -07:00
package hosting
import (
"errors"
"sort"
"sync"
"time"
)
// ErrNotSupported is returned when a provider does not support a given operation.
var ErrNotSupported = errors.New("operation not supported by this provider")
// Provider is the interface all hosting service integrations must implement.
// Not all providers support all features -- methods should return ErrNotSupported
// for unsupported operations.
type Provider interface {
// Name returns the provider identifier (e.g. "hostinger", "digitalocean").
Name() string
// DisplayName returns a human-readable provider name.
DisplayName() string
// --- Authentication ---
// Configure applies the given configuration to the provider.
Configure(cfg ProviderConfig) error
// TestConnection verifies that the provider credentials are valid.
TestConnection() error
// --- DNS Management ---
// ListDNSRecords returns all DNS records for a domain.
ListDNSRecords(domain string) ([]DNSRecord, error)
// CreateDNSRecord adds a single DNS record to a domain.
CreateDNSRecord(domain string, record DNSRecord) error
// UpdateDNSRecords replaces DNS records for a domain. If overwrite is true,
// all existing records are removed first.
UpdateDNSRecords(domain string, records []DNSRecord, overwrite bool) error
// DeleteDNSRecord removes DNS records matching the filter.
DeleteDNSRecord(domain string, filter DNSRecordFilter) error
// ResetDNSRecords restores the default DNS records for a domain.
ResetDNSRecords(domain string) error
// --- Domain Management ---
// ListDomains returns all domains on the account.
ListDomains() ([]Domain, error)
// GetDomain returns detailed information about a single domain.
GetDomain(domain string) (*DomainDetail, error)
// CheckDomainAvailability checks registration availability across TLDs.
CheckDomainAvailability(domain string, tlds []string) ([]DomainAvailability, error)
// PurchaseDomain registers a new domain.
PurchaseDomain(req DomainPurchaseRequest) (*OrderResult, error)
// SetNameservers configures the nameservers for a domain.
SetNameservers(domain string, nameservers []string) error
// EnableDomainLock enables the registrar lock on a domain.
EnableDomainLock(domain string) error
// DisableDomainLock disables the registrar lock on a domain.
DisableDomainLock(domain string) error
// EnablePrivacyProtection enables WHOIS privacy for a domain.
EnablePrivacyProtection(domain string) error
// DisablePrivacyProtection disables WHOIS privacy for a domain.
DisablePrivacyProtection(domain string) error
// --- VPS Management ---
// ListVMs returns all virtual machines on the account.
ListVMs() ([]VirtualMachine, error)
// GetVM returns details for a single virtual machine.
GetVM(id string) (*VirtualMachine, error)
// CreateVM provisions a new virtual machine.
CreateVM(req VMCreateRequest) (*OrderResult, error)
// ListDataCenters returns available data center locations.
ListDataCenters() ([]DataCenter, error)
// --- SSH Keys ---
// ListSSHKeys returns all SSH keys on the account.
ListSSHKeys() ([]SSHKey, error)
// AddSSHKey uploads a new SSH public key.
AddSSHKey(name, publicKey string) (*SSHKey, error)
// DeleteSSHKey removes an SSH key by ID.
DeleteSSHKey(id string) error
// --- Billing ---
// ListSubscriptions returns all active subscriptions.
ListSubscriptions() ([]Subscription, error)
// GetCatalog returns available products in a category.
GetCatalog(category string) ([]CatalogItem, error)
}
// ---------------------------------------------------------------------------
// Model types
// ---------------------------------------------------------------------------
// ProviderConfig holds the credentials and settings needed to connect to a
// hosting provider.
type ProviderConfig struct {
APIKey string `json:"api_key"`
APISecret string `json:"api_secret,omitempty"`
BaseURL string `json:"base_url,omitempty"`
Extra map[string]string `json:"extra,omitempty"`
}
// DNSRecord represents a single DNS record.
type DNSRecord struct {
Name string `json:"name"`
Type string `json:"type"` // A, AAAA, CNAME, MX, TXT, etc.
Content string `json:"content"`
TTL int `json:"ttl,omitempty"` // seconds; 0 means provider default
Priority int `json:"priority,omitempty"` // used by MX, SRV
}
// DNSRecordFilter identifies DNS records to match for deletion or lookup.
type DNSRecordFilter struct {
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
}
// Domain is a summary of a domain on the account.
type Domain struct {
Name string `json:"name"`
Status string `json:"status"`
ExpiresAt time.Time `json:"expires_at"`
}
// DomainDetail contains full information about a domain registration.
type DomainDetail struct {
Name string `json:"name"`
Status string `json:"status"`
Registrar string `json:"registrar"`
RegisteredAt time.Time `json:"registered_at"`
ExpiresAt time.Time `json:"expires_at"`
AutoRenew bool `json:"auto_renew"`
Locked bool `json:"locked"`
PrivacyProtection bool `json:"privacy_protection"`
Nameservers []string `json:"nameservers"`
}
// DomainAvailability reports whether a domain + TLD combination can be
// registered and its price.
type DomainAvailability struct {
Domain string `json:"domain"`
TLD string `json:"tld"`
Available bool `json:"available"`
Price float64 `json:"price"` // in the provider's default currency
Currency string `json:"currency"`
}
// DomainPurchaseRequest contains everything needed to register a domain.
type DomainPurchaseRequest struct {
Domain string `json:"domain"`
Years int `json:"years"`
AutoRenew bool `json:"auto_renew"`
Privacy bool `json:"privacy"`
PaymentMethod string `json:"payment_method,omitempty"`
}
// OrderResult is returned after a purchase or provisioning request.
type OrderResult struct {
OrderID string `json:"order_id"`
Status string `json:"status"` // e.g. "pending", "completed", "failed"
Message string `json:"message,omitempty"`
}
// VirtualMachine represents a VPS instance.
type VirtualMachine struct {
ID string `json:"id"`
Hostname string `json:"hostname"`
IPAddress string `json:"ip_address"`
IPv6 string `json:"ipv6,omitempty"`
Status string `json:"status"` // running, stopped, provisioning, etc.
Plan string `json:"plan"`
DataCenter string `json:"data_center"`
OS string `json:"os"`
CPUs int `json:"cpus"`
RAMBytes int64 `json:"ram_bytes"`
DiskBytes int64 `json:"disk_bytes"`
CreatedAt time.Time `json:"created_at"`
}
// VMCreateRequest contains everything needed to provision a new VPS.
type VMCreateRequest struct {
Hostname string `json:"hostname"`
Plan string `json:"plan"`
DataCenterID string `json:"data_center_id"`
OS string `json:"os"`
SSHKeyID string `json:"ssh_key_id,omitempty"`
Password string `json:"password,omitempty"`
}
// DataCenter represents a physical hosting location.
type DataCenter struct {
ID string `json:"id"`
Name string `json:"name"`
Location string `json:"location"` // city or region
Country string `json:"country"`
}
// SSHKey is a stored SSH public key.
type SSHKey struct {
ID string `json:"id"`
Name string `json:"name"`
PublicKey string `json:"public_key"`
CreatedAt time.Time `json:"created_at"`
}
// Subscription represents a billing subscription.
type Subscription struct {
ID string `json:"id"`
Name string `json:"name"`
Status string `json:"status"` // active, cancelled, expired
RenewsAt time.Time `json:"renews_at"`
Price float64 `json:"price"`
Currency string `json:"currency"`
}
// CatalogItem is a purchasable product or plan.
type CatalogItem struct {
ID string `json:"id"`
Name string `json:"name"`
Category string `json:"category"`
Price float64 `json:"price"`
Currency string `json:"currency"`
Features map[string]string `json:"features,omitempty"`
}
// ---------------------------------------------------------------------------
// Provider registry
// ---------------------------------------------------------------------------
var (
mu sync.RWMutex
providers = map[string]Provider{}
)
// Register adds a provider to the global registry. It panics if a provider
// with the same name is already registered.
func Register(p Provider) {
mu.Lock()
defer mu.Unlock()
name := p.Name()
if _, exists := providers[name]; exists {
panic("hosting: provider already registered: " + name)
}
providers[name] = p
}
// Get returns a registered provider by name.
func Get(name string) (Provider, bool) {
mu.RLock()
defer mu.RUnlock()
p, ok := providers[name]
return p, ok
}
// List returns the names of all registered providers, sorted alphabetically.
func List() []string {
mu.RLock()
defer mu.RUnlock()
names := make([]string, 0, len(providers))
for name := range providers {
names = append(names, name)
}
sort.Strings(names)
return names
}