package main import ( "bytes" "encoding/json" "fmt" "io" "log" "net/http" ) type IpResponse struct { Ip string `json:"ip"` } type DnsRecord struct { Id string `json:"id"` Name string `json:"name"` Type string `json:"type"` Content string `json:"content"` Ttl int `json:"ttl"` Proxied bool `json:"proxied"` } type DnsRecords struct { Result []DnsRecord `json:"result"` } func getIp() (string, error) { // Get IP from ipify and return it as a IpResponse ipUrl := "https://api.ipify.org?format=json" resp, err := http.Get(ipUrl) if err != nil { return "", err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return "", err } var ipResponse IpResponse if err := json.Unmarshal(body, &ipResponse); err != nil { return "", err } return ipResponse.Ip, nil } type DnsUpdater struct { config *Config } func (d *DnsUpdater) CheckIfRecordsExist(recordName string) (DnsRecord, error) { cloudFlareUrl := "https://api.cloudflare.com/client/v4/zones/" + d.config.ZoneID + "/dns_records?name=" + recordName + "&type=A" log.Printf("CloudFlare URL: %s", cloudFlareUrl) req, err := http.NewRequest("GET", cloudFlareUrl, nil) if err != nil { return DnsRecord{}, err } req.Header.Set("X-Auth-Email", d.config.Email) req.Header.Set("Authorization", "Bearer "+d.config.AuthKey) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { return DnsRecord{}, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return DnsRecord{}, err } log.Printf("Body: %s", string(body)) var dnsRecords DnsRecords if err := json.Unmarshal(body, &dnsRecords); err != nil { return DnsRecord{}, err } log.Printf("DNS Records: %+v", dnsRecords) // Check if the record exists for _, record := range dnsRecords.Result { if record.Name == recordName && record.Type == "A" { return record, nil } } return DnsRecord{}, nil } func (d *DnsUpdater) Update(record DnsRecord) error { ip, err := getIp() if err != nil { return err } if d.config.ForceIP != "" { ip = d.config.ForceIP } if record.Content == ip { return nil } record.Content = ip record.Ttl = 3600 cloudFlareUrl := "https://api.cloudflare.com/client/v4/zones/" + d.config.ZoneID + "/dns_records/" + record.Id body, err := json.Marshal(record) if err != nil { return err } req, err := http.NewRequest("PATCH", cloudFlareUrl, bytes.NewBuffer(body)) if err != nil { return err } req.Header.Set("X-Auth-Email", d.config.Email) req.Header.Set("Authorization", "Bearer "+d.config.AuthKey) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() body, err = io.ReadAll(resp.Body) if err != nil { return err } if resp.StatusCode != 200 { return fmt.Errorf("failed to update DNS: %s", string(body)) } log.Printf("Updated DNS: %s", string(body)) return nil } func (d *DnsUpdater) Create(recordName string) error { ip, err := getIp() if err != nil { return err } if d.config.ForceIP != "" { ip = d.config.ForceIP } record := DnsRecord{ Name: recordName, Type: "A", Content: ip, Ttl: 3600, Proxied: false, } cloudFlareUrl := "https://api.cloudflare.com/client/v4/zones/" + d.config.ZoneID + "/dns_records" body, err := json.Marshal(record) if err != nil { return err } req, err := http.NewRequest("POST", cloudFlareUrl, bytes.NewBuffer(body)) if err != nil { return err } req.Header.Set("X-Auth-Email", d.config.Email) req.Header.Set("Authorization", "Bearer "+d.config.AuthKey) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() body, err = io.ReadAll(resp.Body) if err != nil { return err } if resp.StatusCode != 200 { return fmt.Errorf("failed to create DNS: %s", string(body)) } log.Printf("Created DNS: %s", string(body)) return nil }