288 lines
9.1 KiB
Go
288 lines
9.1 KiB
Go
|
|
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
|
||
|
|
}
|