137 lines
4.1 KiB
Go
Raw Normal View History

2026-03-12 20:51:38 -07:00
package hostinger
import (
"fmt"
"net/http"
"net/url"
"setec-manager/internal/hosting"
)
// hostingerDNSRecord is the Hostinger API representation of a DNS record.
type hostingerDNSRecord struct {
Type string `json:"type"`
Name string `json:"name"`
Content string `json:"content"`
TTL int `json:"ttl"`
Priority *int `json:"priority,omitempty"`
}
// hostingerDNSUpdateRequest is the request body for updating DNS records.
type hostingerDNSUpdateRequest struct {
Records []hostingerDNSRecord `json:"records"`
Overwrite bool `json:"overwrite"`
}
// hostingerDNSValidateRequest is the request body for validating DNS records.
type hostingerDNSValidateRequest struct {
Records []hostingerDNSRecord `json:"records"`
}
// ListDNSRecords retrieves all DNS records for the given domain.
func (c *Client) ListDNSRecords(domain string) ([]hosting.DNSRecord, error) {
path := fmt.Sprintf("/api/dns/v1/zones/%s", url.PathEscape(domain))
var apiRecords []hostingerDNSRecord
if err := c.doRequest(http.MethodGet, path, nil, &apiRecords); err != nil {
return nil, fmt.Errorf("list DNS records for %s: %w", domain, err)
}
records := make([]hosting.DNSRecord, 0, len(apiRecords))
for _, r := range apiRecords {
records = append(records, toGenericDNSRecord(r))
}
return records, nil
}
// UpdateDNSRecords updates DNS records for the given domain.
// If overwrite is true, existing records are replaced entirely.
func (c *Client) UpdateDNSRecords(domain string, records []hosting.DNSRecord, overwrite bool) error {
path := fmt.Sprintf("/api/dns/v1/zones/%s", url.PathEscape(domain))
hostingerRecords := make([]hostingerDNSRecord, 0, len(records))
for _, r := range records {
hostingerRecords = append(hostingerRecords, toHostingerDNSRecord(r))
}
// Validate first.
validatePath := fmt.Sprintf("/api/dns/v1/zones/%s/validate", url.PathEscape(domain))
validateReq := hostingerDNSValidateRequest{Records: hostingerRecords}
if err := c.doRequest(http.MethodPost, validatePath, validateReq, nil); err != nil {
return fmt.Errorf("validate DNS records for %s: %w", domain, err)
}
req := hostingerDNSUpdateRequest{
Records: hostingerRecords,
Overwrite: overwrite,
}
if err := c.doRequest(http.MethodPut, path, req, nil); err != nil {
return fmt.Errorf("update DNS records for %s: %w", domain, err)
}
return nil
}
// CreateDNSRecord adds a single DNS record to the domain without overwriting.
func (c *Client) CreateDNSRecord(domain string, record hosting.DNSRecord) error {
return c.UpdateDNSRecords(domain, []hosting.DNSRecord{record}, false)
}
// DeleteDNSRecord removes DNS records matching the given filter.
func (c *Client) DeleteDNSRecord(domain string, filter hosting.DNSRecordFilter) error {
path := fmt.Sprintf("/api/dns/v1/zones/%s", url.PathEscape(domain))
params := url.Values{}
if filter.Name != "" {
params.Set("name", filter.Name)
}
if filter.Type != "" {
params.Set("type", filter.Type)
}
if len(params) > 0 {
path += "?" + params.Encode()
}
if err := c.doRequest(http.MethodDelete, path, nil, nil); err != nil {
return fmt.Errorf("delete DNS record %s/%s for %s: %w", filter.Name, filter.Type, domain, err)
}
return nil
}
// ResetDNSRecords resets the domain's DNS zone to default records.
func (c *Client) ResetDNSRecords(domain string) error {
path := fmt.Sprintf("/api/dns/v1/zones/%s/reset", url.PathEscape(domain))
if err := c.doRequest(http.MethodPost, path, nil, nil); err != nil {
return fmt.Errorf("reset DNS records for %s: %w", domain, err)
}
return nil
}
// toGenericDNSRecord converts a Hostinger DNS record to the generic type.
func toGenericDNSRecord(r hostingerDNSRecord) hosting.DNSRecord {
rec := hosting.DNSRecord{
Type: r.Type,
Name: r.Name,
Content: r.Content,
TTL: r.TTL,
}
if r.Priority != nil {
rec.Priority = *r.Priority
}
return rec
}
// toHostingerDNSRecord converts a generic DNS record to the Hostinger format.
func toHostingerDNSRecord(r hosting.DNSRecord) hostingerDNSRecord {
rec := hostingerDNSRecord{
Type: r.Type,
Name: r.Name,
Content: r.Content,
TTL: r.TTL,
}
if r.Priority != 0 {
p := r.Priority
rec.Priority = &p
}
return rec
}