feat(hostinger): VPS actions, DNS editing, domain lock/privacy, nameservers, auto-renewal
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -54,6 +55,37 @@ func (c *HostingerClient) getRaw(ctx context.Context, path string) ([]byte, erro
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// writeRaw envía una petición HTTP autenticada con método y body opcionales.
|
||||
func (c *HostingerClient) writeRaw(ctx context.Context, method, path string, payload interface{}) ([]byte, error) {
|
||||
var reqBody io.Reader
|
||||
if payload != nil {
|
||||
b, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("hostinger: serializar body: %w", err)
|
||||
}
|
||||
reqBody = bytes.NewReader(b)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, method, hostingerBaseURL+path, reqBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("hostinger: crear request: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+c.token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("hostinger: ejecutar request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("hostinger: leer body: %w", err)
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return nil, fmt.Errorf("hostinger: status %d – %s", resp.StatusCode, string(body))
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// get realiza una petición GET autenticada y decodifica el cuerpo en dest.
|
||||
func (c *HostingerClient) get(path string, dest interface{}) error {
|
||||
body, err := c.getRaw(context.Background(), path)
|
||||
@@ -115,6 +147,19 @@ type HostingerNameServers struct {
|
||||
NS4 string `json:"ns4"`
|
||||
}
|
||||
|
||||
// HostingerDNSZoneInput es un grupo de registros para PUT /dns/v1/zones/{domain}.
|
||||
type HostingerDNSZoneInput struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
TTL int `json:"ttl,omitempty"`
|
||||
Records []HostingerDNSZoneValue `json:"records"`
|
||||
}
|
||||
|
||||
// HostingerDNSZoneValue es el valor de contenido de un registro DNS.
|
||||
type HostingerDNSZoneValue struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// HostingerSubscription representa una suscripción de facturación.
|
||||
type HostingerSubscription struct {
|
||||
ID string `json:"id"`
|
||||
@@ -263,6 +308,122 @@ func (c *HostingerClient) GetOrders() ([]HostingerSubscription, error) {
|
||||
return arr, nil
|
||||
}
|
||||
|
||||
// ─── DNS: escritura ─────────────────────────────────────────────────────────
|
||||
|
||||
// UpdateDNSZone agrega/actualiza registros en la zona DNS.
|
||||
// overwrite=true reemplaza registros del mismo tipo/nombre; false los agrega.
|
||||
func (c *HostingerClient) UpdateDNSZone(domain string, overwrite bool, zone []HostingerDNSZoneInput) error {
|
||||
path := fmt.Sprintf("/dns/v1/zones/%s", domain)
|
||||
_, err := c.writeRaw(context.Background(), http.MethodPut, path,
|
||||
map[string]interface{}{"overwrite": overwrite, "zone": zone})
|
||||
return err
|
||||
}
|
||||
|
||||
// ResetDNS restaura la zona DNS de un dominio a sus valores por defecto.
|
||||
func (c *HostingerClient) ResetDNS(domain string) error {
|
||||
path := fmt.Sprintf("/dns/v1/zones/%s/reset", domain)
|
||||
_, err := c.writeRaw(context.Background(), http.MethodPost, path, map[string]bool{"sync": true})
|
||||
return err
|
||||
}
|
||||
|
||||
// ─── Domains: escritura ──────────────────────────────────────────────────────
|
||||
|
||||
// UpdateNameservers actualiza los nameservers de un dominio.
|
||||
func (c *HostingerClient) UpdateNameservers(domain, ns1, ns2, ns3, ns4 string) error {
|
||||
path := fmt.Sprintf("/domains/v1/portfolio/%s/nameservers", domain)
|
||||
_, err := c.writeRaw(context.Background(), http.MethodPut, path,
|
||||
map[string]string{"ns1": ns1, "ns2": ns2, "ns3": ns3, "ns4": ns4})
|
||||
return err
|
||||
}
|
||||
|
||||
// SetDomainLock activa (PUT) o desactiva (DELETE) el bloqueo del dominio.
|
||||
func (c *HostingerClient) SetDomainLock(domain string, enable bool) error {
|
||||
path := fmt.Sprintf("/domains/v1/portfolio/%s/domain-lock", domain)
|
||||
method := http.MethodPut
|
||||
if !enable {
|
||||
method = http.MethodDelete
|
||||
}
|
||||
_, err := c.writeRaw(context.Background(), method, path, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetPrivacyProtection activa (PUT) o desactiva (DELETE) la protección WHOIS.
|
||||
func (c *HostingerClient) SetPrivacyProtection(domain string, enable bool) error {
|
||||
path := fmt.Sprintf("/domains/v1/portfolio/%s/privacy-protection", domain)
|
||||
method := http.MethodPut
|
||||
if !enable {
|
||||
method = http.MethodDelete
|
||||
}
|
||||
_, err := c.writeRaw(context.Background(), method, path, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// ─── VPS: acciones ───────────────────────────────────────────────────────────
|
||||
|
||||
// VPSAction ejecuta una acción en una VM: start | stop | restart.
|
||||
func (c *HostingerClient) VPSAction(vmID int, action string) error {
|
||||
path := fmt.Sprintf("/vps/v1/virtual-machines/%d/%s", vmID, action)
|
||||
_, err := c.writeRaw(context.Background(), http.MethodPost, path, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetVPSRootPassword cambia la contraseña root de una VM.
|
||||
func (c *HostingerClient) SetVPSRootPassword(vmID int, password string) error {
|
||||
path := fmt.Sprintf("/vps/v1/virtual-machines/%d/root-password", vmID)
|
||||
_, err := c.writeRaw(context.Background(), http.MethodPut, path, map[string]string{"password": password})
|
||||
return err
|
||||
}
|
||||
|
||||
// SetVPSHostname cambia el hostname de una VM.
|
||||
func (c *HostingerClient) SetVPSHostname(vmID int, hostname string) error {
|
||||
path := fmt.Sprintf("/vps/v1/virtual-machines/%d/hostname", vmID)
|
||||
_, err := c.writeRaw(context.Background(), http.MethodPut, path, map[string]string{"hostname": hostname})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetVPSMetrics obtiene las métricas de uso de una VM.
|
||||
func (c *HostingerClient) GetVPSMetrics(vmID int) (json.RawMessage, error) {
|
||||
path := fmt.Sprintf("/vps/v1/virtual-machines/%d/metrics", vmID)
|
||||
body, err := c.getRaw(context.Background(), path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.RawMessage(body), nil
|
||||
}
|
||||
|
||||
// GetVPSBackups obtiene los backups de una VM.
|
||||
func (c *HostingerClient) GetVPSBackups(vmID int) (json.RawMessage, error) {
|
||||
path := fmt.Sprintf("/vps/v1/virtual-machines/%d/backups", vmID)
|
||||
body, err := c.getRaw(context.Background(), path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.RawMessage(body), nil
|
||||
}
|
||||
|
||||
// ─── Billing: acciones ───────────────────────────────────────────────────────
|
||||
|
||||
// ToggleAutoRenewal activa (true) o desactiva la autorenovación de una suscripción.
|
||||
func (c *HostingerClient) ToggleAutoRenewal(subscriptionID string, enable bool) (*HostingerSubscription, error) {
|
||||
var path, method string
|
||||
if enable {
|
||||
path = fmt.Sprintf("/billing/v1/subscriptions/%s/auto-renewal/enable", subscriptionID)
|
||||
method = http.MethodPatch
|
||||
} else {
|
||||
path = fmt.Sprintf("/billing/v1/subscriptions/%s/auto-renewal/disable", subscriptionID)
|
||||
method = http.MethodDelete
|
||||
}
|
||||
body, err := c.writeRaw(context.Background(), method, path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var sub HostingerSubscription
|
||||
if err := json.Unmarshal(body, &sub); err != nil {
|
||||
return nil, fmt.Errorf("hostinger: no se pudo interpretar respuesta: %w", err)
|
||||
}
|
||||
return &sub, nil
|
||||
}
|
||||
|
||||
// GetHostingAccounts obtiene las cuentas de hosting.
|
||||
// Endpoint: GET /hosting/v1/websites
|
||||
func (c *HostingerClient) GetHostingAccounts() ([]HostingerHosting, error) {
|
||||
|
||||
Reference in New Issue
Block a user