Update cloudflare_service.go
This commit is contained in:
@@ -79,8 +79,9 @@ func (c *CloudflareClient) get(path string, dest interface{}) error {
|
||||
// ─── Tipos de error ──────────────────────────────────────────────────────────
|
||||
|
||||
type CFError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
DocumentationURL string `json:"documentation_url,omitempty"`
|
||||
}
|
||||
|
||||
// ─── Tipos de respuesta ──────────────────────────────────────────────────────
|
||||
@@ -94,18 +95,26 @@ type CFUser struct {
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// CFZoneAccount representa la cuenta propietaria de la zona.
|
||||
type CFZoneAccount struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// CFZone representa una zona (dominio) en Cloudflare.
|
||||
type CFZone struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Paused bool `json:"paused"`
|
||||
Type string `json:"type"`
|
||||
NameServers []string `json:"name_servers"`
|
||||
OriginalNS []string `json:"original_name_servers"`
|
||||
ModifiedOn string `json:"modified_on"`
|
||||
ActivatedOn string `json:"activated_on"`
|
||||
Plan CFPlan `json:"plan"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Paused bool `json:"paused"`
|
||||
Type string `json:"type"`
|
||||
NameServers []string `json:"name_servers"`
|
||||
OriginalNS []string `json:"original_name_servers"`
|
||||
CreatedOn string `json:"created_on"`
|
||||
ModifiedOn string `json:"modified_on"`
|
||||
ActivatedOn string `json:"activated_on"`
|
||||
Account CFZoneAccount `json:"account"`
|
||||
Plan CFPlan `json:"plan"`
|
||||
}
|
||||
|
||||
// CFPlan representa el plan de una zona.
|
||||
@@ -114,18 +123,30 @@ type CFPlan struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// CFDNSRecordSettings configuraciones adicionales de un registro DNS.
|
||||
type CFDNSRecordSettings struct {
|
||||
IPv4Only bool `json:"ipv4_only,omitempty"`
|
||||
IPv6Only bool `json:"ipv6_only,omitempty"`
|
||||
}
|
||||
|
||||
// CFDNSRecord representa un registro DNS.
|
||||
type CFDNSRecord struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Content string `json:"content"`
|
||||
Proxied bool `json:"proxied"`
|
||||
Proxiable bool `json:"proxiable"`
|
||||
TTL int `json:"ttl"`
|
||||
Priority int `json:"priority,omitempty"`
|
||||
CreatedOn string `json:"created_on"`
|
||||
ModifiedOn string `json:"modified_on"`
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Content string `json:"content"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Settings CFDNSRecordSettings `json:"settings,omitempty"`
|
||||
PrivateRouting bool `json:"private_routing,omitempty"`
|
||||
Proxied bool `json:"proxied"`
|
||||
Proxiable bool `json:"proxiable"`
|
||||
TTL int `json:"ttl"`
|
||||
Priority int `json:"priority,omitempty"`
|
||||
CreatedOn string `json:"created_on"`
|
||||
ModifiedOn string `json:"modified_on"`
|
||||
CommentModifiedOn string `json:"comment_modified_on,omitempty"`
|
||||
TagsModifiedOn string `json:"tags_modified_on,omitempty"`
|
||||
}
|
||||
|
||||
// CFSSLStatus representa el estado SSL de una zona.
|
||||
@@ -222,24 +243,105 @@ func (c *CloudflareClient) GetTokenDetail(tokenID string) (*CFTokenDetail, error
|
||||
return &detail, nil
|
||||
}
|
||||
|
||||
// GetZones lista todas las zonas de la cuenta.
|
||||
func (c *CloudflareClient) GetZones() ([]CFZone, error) {
|
||||
var zones []CFZone
|
||||
// pagina 1, 50 por página (máximo)
|
||||
if err := c.get("/zones?per_page=50&page=1", &zones); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return zones, nil
|
||||
// cfResultInfo contiene metadatos de paginación de la API de Cloudflare.
|
||||
type cfResultInfo struct {
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"per_page"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
Count int `json:"count"`
|
||||
TotalCount int `json:"total_count"`
|
||||
}
|
||||
|
||||
// GetDNSRecords devuelve los registros DNS de una zona.
|
||||
func (c *CloudflareClient) GetDNSRecords(zoneID string) ([]CFDNSRecord, error) {
|
||||
var records []CFDNSRecord
|
||||
path := fmt.Sprintf("/zones/%s/dns_records?per_page=100&page=1", zoneID)
|
||||
if err := c.get(path, &records); err != nil {
|
||||
return nil, err
|
||||
// getWithInfo realiza un GET y decodifica también result_info para paginación.
|
||||
func (c *CloudflareClient) getWithInfo(path string, dest interface{}) (cfResultInfo, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, cloudflareBaseURL+path, nil)
|
||||
if err != nil {
|
||||
return cfResultInfo{}, fmt.Errorf("cloudflare: crear request: %w", err)
|
||||
}
|
||||
return records, nil
|
||||
req.Header.Set("Authorization", "Bearer "+c.token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return cfResultInfo{}, fmt.Errorf("cloudflare: ejecutar request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return cfResultInfo{}, fmt.Errorf("cloudflare: leer body: %w", err)
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return cfResultInfo{}, fmt.Errorf("cloudflare: status %d – %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var wrapper struct {
|
||||
Success bool `json:"success"`
|
||||
Errors []CFError `json:"errors"`
|
||||
Result json.RawMessage `json:"result"`
|
||||
ResultInfo cfResultInfo `json:"result_info"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &wrapper); err != nil {
|
||||
return cfResultInfo{}, fmt.Errorf("cloudflare: decodificar envelope: %w", err)
|
||||
}
|
||||
if !wrapper.Success {
|
||||
if len(wrapper.Errors) > 0 {
|
||||
return cfResultInfo{}, fmt.Errorf("cloudflare API error %d: %s", wrapper.Errors[0].Code, wrapper.Errors[0].Message)
|
||||
}
|
||||
return cfResultInfo{}, fmt.Errorf("cloudflare: respuesta no exitosa")
|
||||
}
|
||||
if dest != nil && wrapper.Result != nil {
|
||||
if err := json.Unmarshal(wrapper.Result, dest); err != nil {
|
||||
return cfResultInfo{}, fmt.Errorf("cloudflare: decodificar result: %w", err)
|
||||
}
|
||||
}
|
||||
return wrapper.ResultInfo, nil
|
||||
}
|
||||
|
||||
// GetZones lista todas las zonas de la cuenta, iterando páginas si es necesario.
|
||||
// Si el cliente fue creado con accountID, filtra por esa cuenta.
|
||||
func (c *CloudflareClient) GetZones() ([]CFZone, error) {
|
||||
const perPage = 50
|
||||
var all []CFZone
|
||||
|
||||
accountFilter := ""
|
||||
if c.accountID != "" {
|
||||
accountFilter = "&account.id=" + c.accountID
|
||||
}
|
||||
|
||||
for page := 1; ; page++ {
|
||||
path := fmt.Sprintf("/zones?per_page=%d&page=%d%s", perPage, page, accountFilter)
|
||||
var zones []CFZone
|
||||
info, err := c.getWithInfo(path, &zones)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
all = append(all, zones...)
|
||||
if page >= info.TotalPages || info.TotalPages == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// GetDNSRecords devuelve todos los registros DNS de una zona, paginando automáticamente.
|
||||
func (c *CloudflareClient) GetDNSRecords(zoneID string) ([]CFDNSRecord, error) {
|
||||
const perPage = 5000
|
||||
var all []CFDNSRecord
|
||||
|
||||
for page := 1; ; page++ {
|
||||
path := fmt.Sprintf("/zones/%s/dns_records?per_page=%d&page=%d", zoneID, perPage, page)
|
||||
var records []CFDNSRecord
|
||||
info, err := c.getWithInfo(path, &records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
all = append(all, records...)
|
||||
if page >= info.TotalPages || info.TotalPages == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// GetSSLCertificates devuelve los certificate packs de una zona.
|
||||
@@ -328,12 +430,15 @@ func (c *CloudflareClient) doRequest(method, path string, payload interface{}, d
|
||||
|
||||
// CFDNSRecordInput es el payload para crear o actualizar un registro DNS.
|
||||
type CFDNSRecordInput struct {
|
||||
Type string `json:"type"` // A, AAAA, CNAME, TXT, MX, NS, SRV, CAA…
|
||||
Name string `json:"name"` // Nombre del registro (ej. "www" o "@")
|
||||
Content string `json:"content"` // Valor del registro
|
||||
TTL int `json:"ttl"` // 1 = automático, o segundos (min 60)
|
||||
Proxied bool `json:"proxied"` // true = nube naranja
|
||||
Priority int `json:"priority,omitempty"` // Solo para MX / SRV
|
||||
Type string `json:"type"` // A, AAAA, CNAME, TXT, MX, NS, SRV, CAA…
|
||||
Name string `json:"name"` // Nombre del registro (ej. "www" o "@")
|
||||
Content string `json:"content"` // Valor del registro
|
||||
TTL int `json:"ttl"` // 1 = automático, o segundos (min 60)
|
||||
Proxied bool `json:"proxied"` // true = nube naranja
|
||||
Priority int `json:"priority,omitempty"` // Solo para MX / SRV
|
||||
Comment string `json:"comment,omitempty"` // Comentario descriptivo
|
||||
Tags []string `json:"tags,omitempty"` // Etiquetas (ej. ["owner:team"])
|
||||
Settings *CFDNSRecordSettings `json:"settings,omitempty"` // Configuraciones adicionales
|
||||
}
|
||||
|
||||
// ─── DNS CRUD ────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user