From abecb9b3054cfd4541feab4457bd4747861f1ef9 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Thu, 14 May 2026 20:17:45 -0500 Subject: [PATCH] fix(hostinger): DNS aplanado + nameservers, facturacion precios formateados, subscriptions endpoint --- pkg/services/hostinger_service.go | 81 +++++++++++++++++++----- resources/views/hostinger.html | 26 ++++++-- rest/controllers/hostinger_controller.go | 8 ++- 3 files changed, 88 insertions(+), 27 deletions(-) diff --git a/pkg/services/hostinger_service.go b/pkg/services/hostinger_service.go index 8b8a40e..f627dd6 100644 --- a/pkg/services/hostinger_service.go +++ b/pkg/services/hostinger_service.go @@ -99,14 +99,20 @@ type HostingerDomain struct { RegisteredAt string `json:"registered_at"` } -// HostingerDNSRecord representa un registro DNS. +// HostingerDNSRecord representa un registro DNS aplanado para la vista. type HostingerDNSRecord struct { - ID int `json:"id"` - Type string `json:"type"` - Name string `json:"name"` - Content string `json:"content"` - TTL int `json:"ttl"` - Priority int `json:"priority,omitempty"` + Type string `json:"type"` + Name string `json:"name"` + Content string `json:"content"` + TTL int `json:"ttl"` +} + +// HostingerNameServers contiene los nameservers de un dominio. +type HostingerNameServers struct { + NS1 string `json:"ns1"` + NS2 string `json:"ns2"` + NS3 string `json:"ns3"` + NS4 string `json:"ns4"` } // HostingerSubscription representa una suscripción de facturación. @@ -178,23 +184,64 @@ func (c *HostingerClient) GetDomains() ([]HostingerDomain, error) { return arr, nil } -// hostingerDNSZone es la estructura interna de la respuesta de zona DNS. -type hostingerDNSZone struct { - Records []HostingerDNSRecord `json:"records"` +// hostingerDNSContent es el valor individual de un registro DNS. +type hostingerDNSContent struct { + Content string `json:"content"` + IsDisabled bool `json:"is_disabled"` } -// GetDNSRecords obtiene los registros DNS de un dominio. -// Endpoint: GET /dns/v1/zones/{domain} — devuelve un array de zonas con records embebidos. +// hostingerDNSEntry es un grupo de registros del mismo tipo/nombre desde la API. +type hostingerDNSEntry struct { + Name string `json:"name"` + TTL int `json:"ttl"` + Type string `json:"type"` + Records []hostingerDNSContent `json:"records"` +} + +// GetDNSRecords obtiene los registros DNS de un dominio y los aplana para la vista. +// Endpoint: GET /dns/v1/zones/{domain} — array de {name, ttl, type, records:[{content}]} func (c *HostingerClient) GetDNSRecords(domain string) ([]HostingerDNSRecord, error) { path := fmt.Sprintf("/dns/v1/zones/%s", domain) - var zones []hostingerDNSZone - if err := c.get(path, &zones); err != nil { + body, err := c.getRaw(context.Background(), path) + if err != nil { return nil, err } - if len(zones) > 0 { - return zones[0].Records, nil + var entries []hostingerDNSEntry + if err := json.Unmarshal(body, &entries); err != nil { + return nil, fmt.Errorf("hostinger: no se pudo interpretar respuesta DNS: %w", err) } - return []HostingerDNSRecord{}, nil + var result []HostingerDNSRecord + for _, e := range entries { + for _, r := range e.Records { + result = append(result, HostingerDNSRecord{ + Type: e.Type, + Name: e.Name, + Content: r.Content, + TTL: e.TTL, + }) + } + } + if result == nil { + result = []HostingerDNSRecord{} + } + return result, nil +} + +// GetDomainNameServers obtiene los nameservers del dominio desde el portafolio. +// Endpoint: GET /domains/v1/portfolio/{domain} +func (c *HostingerClient) GetDomainNameServers(domain string) (*HostingerNameServers, error) { + path := fmt.Sprintf("/domains/v1/portfolio/%s", domain) + body, err := c.getRaw(context.Background(), path) + if err != nil { + return nil, err + } + var resp struct { + NameServers HostingerNameServers `json:"name_servers"` + } + if err := json.Unmarshal(body, &resp); err != nil { + return nil, fmt.Errorf("hostinger: no se pudo interpretar nameservers: %w", err) + } + return &resp.NameServers, nil } // GetOrders obtiene las suscripciones de facturación. diff --git a/resources/views/hostinger.html b/resources/views/hostinger.html index 05f787b..ff1ac2c 100644 --- a/resources/views/hostinger.html +++ b/resources/views/hostinger.html @@ -164,8 +164,8 @@
Nameservers
+| @@ -283,6 +292,7 @@ document.addEventListener('alpine:init', () => { orders: [], dnsRecords: [], dnsDomain: '', + dnsNameServers: null, dnsModal: false, configModal: false, cfgForm: { id: 0, token: '', nota: '' }, @@ -329,9 +339,11 @@ document.addEventListener('alpine:init', () => { this.loading = true; this.dnsDomain = domain; this.dnsRecords = []; + this.dnsNameServers = null; try { const res = await axios.get('/app/hostinger/dns/' + encodeURIComponent(domain)); this.dnsRecords = res.data.data || []; + this.dnsNameServers = res.data.name_servers || null; this.dnsModal = true; } catch (e) { this.showToast(e.response?.data?.error || 'Error al cargar DNS', 'error'); diff --git a/rest/controllers/hostinger_controller.go b/rest/controllers/hostinger_controller.go index f5397e8..9c8d120 100644 --- a/rest/controllers/hostinger_controller.go +++ b/rest/controllers/hostinger_controller.go @@ -91,7 +91,7 @@ func GetHostingerDomains(c *fiber.Ctx) error { return c.JSON(fiber.Map{"data": data}) } -// GetHostingerDNS devuelve los registros DNS de un dominio (:domain). +// GetHostingerDNS devuelve los registros DNS y los nameservers de un dominio (:domain). func GetHostingerDNS(c *fiber.Ctx) error { domain := c.Params("domain") if domain == "" { @@ -103,11 +103,13 @@ func GetHostingerDNS(c *fiber.Ctx) error { "error": "No se encontró configuración activa de Hostinger", }) } - data, err := client.GetDNSRecords(domain) + records, err := client.GetDNSRecords(domain) if err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } - return c.JSON(fiber.Map{"data": data, "domain": domain}) + // Nameservers son opcionales: si falla (ej. VPS hostnames no están en portafolio) se ignora. + ns, _ := client.GetDomainNameServers(domain) + return c.JSON(fiber.Map{"data": records, "name_servers": ns, "domain": domain}) } // GetHostingerOrders devuelve las órdenes de facturación. |