up
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -212,3 +213,115 @@ func (c *CloudflareClient) GetFirewallRules(zoneID string) ([]CFFirewallRule, er
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
// ─── Helpers de escritura ────────────────────────────────────────────────────
|
||||
|
||||
// doRequest realiza una petición con body JSON y decodifica la respuesta.
|
||||
func (c *CloudflareClient) doRequest(method, path string, payload interface{}, dest interface{}) error {
|
||||
var bodyReader io.Reader
|
||||
if payload != nil {
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cloudflare: serializar payload: %w", err)
|
||||
}
|
||||
bodyReader = bytes.NewReader(data)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, cloudflareBaseURL+path, bodyReader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cloudflare: 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 fmt.Errorf("cloudflare: ejecutar request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cloudflare: leer body: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("cloudflare: status %d – %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
// Para DELETE la respuesta puede ser vacía o tener solo {"success":true,"result":{"id":"..."}}
|
||||
if len(body) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var wrapper struct {
|
||||
Success bool `json:"success"`
|
||||
Errors []CFError `json:"errors"`
|
||||
Result json.RawMessage `json:"result"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &wrapper); err != nil {
|
||||
return fmt.Errorf("cloudflare: decodificar envelope: %w", err)
|
||||
}
|
||||
if !wrapper.Success {
|
||||
if len(wrapper.Errors) > 0 {
|
||||
return fmt.Errorf("cloudflare API error %d: %s", wrapper.Errors[0].Code, wrapper.Errors[0].Message)
|
||||
}
|
||||
return fmt.Errorf("cloudflare: respuesta no exitosa")
|
||||
}
|
||||
if dest != nil && wrapper.Result != nil {
|
||||
if err := json.Unmarshal(wrapper.Result, dest); err != nil {
|
||||
return fmt.Errorf("cloudflare: decodificar result: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ─── Tipos de entrada para DNS ───────────────────────────────────────────────
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ─── DNS CRUD ────────────────────────────────────────────────────────────────
|
||||
|
||||
// CreateDNSRecord crea un nuevo registro DNS en la zona indicada.
|
||||
func (c *CloudflareClient) CreateDNSRecord(zoneID string, input CFDNSRecordInput) (*CFDNSRecord, error) {
|
||||
var record CFDNSRecord
|
||||
path := fmt.Sprintf("/zones/%s/dns_records", zoneID)
|
||||
if err := c.doRequest(http.MethodPost, path, input, &record); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
// UpdateDNSRecord actualiza (PUT completo) un registro DNS existente.
|
||||
func (c *CloudflareClient) UpdateDNSRecord(zoneID, recordID string, input CFDNSRecordInput) (*CFDNSRecord, error) {
|
||||
var record CFDNSRecord
|
||||
path := fmt.Sprintf("/zones/%s/dns_records/%s", zoneID, recordID)
|
||||
if err := c.doRequest(http.MethodPut, path, input, &record); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
// PatchDNSRecord actualiza parcialmente (PATCH) un registro DNS existente.
|
||||
func (c *CloudflareClient) PatchDNSRecord(zoneID, recordID string, input CFDNSRecordInput) (*CFDNSRecord, error) {
|
||||
var record CFDNSRecord
|
||||
path := fmt.Sprintf("/zones/%s/dns_records/%s", zoneID, recordID)
|
||||
if err := c.doRequest(http.MethodPatch, path, input, &record); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
// DeleteDNSRecord elimina un registro DNS por su ID.
|
||||
func (c *CloudflareClient) DeleteDNSRecord(zoneID, recordID string) error {
|
||||
path := fmt.Sprintf("/zones/%s/dns_records/%s", zoneID, recordID)
|
||||
return c.doRequest(http.MethodDelete, path, nil, nil)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user