package commands import ( "bytes" "encoding/json" "fmt" "io" "net/http" ) type SetAcmeRecordArgs struct { APIToken string ZoneID string RecordName string RecordValue string } type cfDNSRecordRequest struct { Type string `json:"type"` Name string `json:"name"` Content string `json:"content"` TTL int `json:"ttl"` } type cfDNSRecord struct { ID string `json:"id"` Name string `json:"name"` Content string `json:"content"` } type cfDNSRecordResponse struct { Success bool `json:"success"` Errors []struct { Message string `json:"message"` } `json:"errors"` Result cfDNSRecord `json:"result"` } type cfDNSListResponse struct { Success bool `json:"success"` Errors []struct { Message string `json:"message"` } `json:"errors"` Result []cfDNSRecord `json:"result"` } func cfRequest(method, url, token string, body []byte) (*http.Response, error) { req, err := http.NewRequest(method, url, bytes.NewReader(body)) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") return (&http.Client{}).Do(req) } func SetAcmeRecord(args SetAcmeRecordArgs, stdout, stderr io.Writer) error { listURL := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records?type=TXT&name=%s", args.ZoneID, args.RecordName) resp, err := cfRequest("GET", listURL, args.APIToken, nil) if err != nil { return fmt.Errorf("error listing DNS records: %w", err) } defer resp.Body.Close() var listResult cfDNSListResponse if err := json.NewDecoder(resp.Body).Decode(&listResult); err != nil { return fmt.Errorf("error decoding list response: %w", err) } if !listResult.Success { msg := "Cloudflare API error:" for _, e := range listResult.Errors { msg += " " + e.Message } return fmt.Errorf("%s", msg) } record := cfDNSRecordRequest{ Type: "TXT", Name: args.RecordName, Content: args.RecordValue, TTL: 1, } body, err := json.Marshal(record) if err != nil { return fmt.Errorf("error marshaling request: %w", err) } var method, recordURL, action string if len(listResult.Result) > 0 { method = "PUT" recordURL = fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records/%s", args.ZoneID, listResult.Result[0].ID) action = "Updated" } else { method = "POST" recordURL = fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records", args.ZoneID) action = "Created" } resp2, err := cfRequest(method, recordURL, args.APIToken, body) if err != nil { return fmt.Errorf("error calling Cloudflare API: %w", err) } defer resp2.Body.Close() var result cfDNSRecordResponse if err := json.NewDecoder(resp2.Body).Decode(&result); err != nil { return fmt.Errorf("error decoding response: %w", err) } if !result.Success { msg := "Cloudflare API error:" for _, e := range result.Errors { msg += " " + e.Message } return fmt.Errorf("%s", msg) } fmt.Fprintf(stdout, "%s TXT record:\n ID: %s\n Name: %s\n Content: %s\n", action, result.Result.ID, result.Result.Name, result.Result.Content) return nil }