mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-09-05 20:54:22 +08:00
574 lines
19 KiB
Go
574 lines
19 KiB
Go
package cloudflare
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"golang.org/x/net/idna"
|
|
|
|
"github.com/StackExchange/dnscontrol/v4/models"
|
|
"github.com/StackExchange/dnscontrol/v4/providers/cloudflare/rtypes/cfsingleredirect"
|
|
"github.com/cloudflare/cloudflare-go"
|
|
)
|
|
|
|
// get list of domains for account. Cache so the ids can be looked up from domain name
|
|
// The caller must do all locking.
|
|
func (c *cloudflareProvider) cacheDomainList() error {
|
|
if c.domainIndex != nil {
|
|
return nil
|
|
}
|
|
|
|
c.domainIndex = map[string]string{}
|
|
c.nameservers = map[string][]string{}
|
|
//fmt.Printf("DEBUG: CLOUDFLARE POPULATING CACHE\n")
|
|
zones, err := c.cfClient.ListZones(context.Background())
|
|
if err != nil {
|
|
return fmt.Errorf("failed fetching domain list from cloudflare(%q): %s", c.cfClient.APIEmail, err)
|
|
}
|
|
|
|
for _, zone := range zones {
|
|
if encoded, err := idna.ToASCII(zone.Name); err == nil && encoded != zone.Name {
|
|
if _, ok := c.domainIndex[encoded]; ok {
|
|
fmt.Printf("WARNING: Zone %q appears twice in this cloudflare account\n", encoded)
|
|
}
|
|
c.domainIndex[encoded] = zone.ID
|
|
c.nameservers[encoded] = zone.NameServers
|
|
}
|
|
if _, ok := c.domainIndex[zone.Name]; ok {
|
|
fmt.Printf("WARNING: Zone %q appears twice in this cloudflare account\n", zone.Name)
|
|
}
|
|
c.domainIndex[zone.Name] = zone.ID
|
|
c.nameservers[zone.Name] = zone.NameServers
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// get all records for a domain
|
|
func (c *cloudflareProvider) getRecordsForDomain(id string, domain string) ([]*models.RecordConfig, error) {
|
|
records := []*models.RecordConfig{}
|
|
rrs, _, err := c.cfClient.ListDNSRecords(context.Background(), cloudflare.ZoneIdentifier(id), cloudflare.ListDNSRecordsParams{})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed fetching record list from cloudflare(%q): %w", c.cfClient.APIEmail, err)
|
|
}
|
|
for _, rec := range rrs {
|
|
rt, err := c.nativeToRecord(domain, rec)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
records = append(records, rt)
|
|
}
|
|
return records, nil
|
|
}
|
|
|
|
func (c *cloudflareProvider) deleteDNSRecord(rec cloudflare.DNSRecord, domainID string) error {
|
|
return c.cfClient.DeleteDNSRecord(context.Background(), cloudflare.ZoneIdentifier(domainID), rec.ID)
|
|
}
|
|
|
|
func (c *cloudflareProvider) createZone(domainName string) (string, error) {
|
|
zone, err := c.cfClient.CreateZone(context.Background(), domainName, false, cloudflare.Account{ID: c.accountID}, "full")
|
|
return zone.ID, err
|
|
}
|
|
|
|
func cfDnskeyData(rec *models.RecordConfig) *cfRecData {
|
|
return &cfRecData{
|
|
Algorithm: rec.DnskeyAlgorithm,
|
|
Flags: rec.DnskeyFlags,
|
|
Protocol: rec.DnskeyProtocol,
|
|
PublicKey: rec.DnskeyPublicKey,
|
|
}
|
|
}
|
|
|
|
func cfDSData(rec *models.RecordConfig) *cfRecData {
|
|
return &cfRecData{
|
|
KeyTag: rec.DsKeyTag,
|
|
Algorithm: rec.DsAlgorithm,
|
|
DigestType: rec.DsDigestType,
|
|
Digest: rec.DsDigest,
|
|
}
|
|
}
|
|
|
|
func cfSrvData(rec *models.RecordConfig) *cfRecData {
|
|
serverParts := strings.Split(rec.GetLabelFQDN(), ".")
|
|
c := &cfRecData{
|
|
Service: serverParts[0],
|
|
Proto: serverParts[1],
|
|
Name: strings.Join(serverParts[2:], "."),
|
|
Port: rec.SrvPort,
|
|
Priority: rec.SrvPriority,
|
|
Weight: rec.SrvWeight,
|
|
}
|
|
c.Target = cfTarget(rec.GetTargetField())
|
|
return c
|
|
}
|
|
|
|
func cfCaaData(rec *models.RecordConfig) *cfRecData {
|
|
return &cfRecData{
|
|
Tag: rec.CaaTag,
|
|
Flags: uint16(rec.CaaFlag),
|
|
Value: rec.GetTargetField(),
|
|
}
|
|
}
|
|
|
|
func cfTlsaData(rec *models.RecordConfig) *cfRecData {
|
|
return &cfRecData{
|
|
Usage: rec.TlsaUsage,
|
|
Selector: rec.TlsaSelector,
|
|
MatchingType: rec.TlsaMatchingType,
|
|
Certificate: rec.GetTargetField(),
|
|
}
|
|
}
|
|
|
|
func cfSshfpData(rec *models.RecordConfig) *cfRecData {
|
|
return &cfRecData{
|
|
Algorithm: rec.SshfpAlgorithm,
|
|
HashType: rec.SshfpFingerprint,
|
|
Fingerprint: rec.GetTargetField(),
|
|
}
|
|
}
|
|
|
|
func cfSvcbData(rec *models.RecordConfig) *cfRecData {
|
|
return &cfRecData{
|
|
Priority: rec.SvcPriority,
|
|
Target: cfTarget(rec.GetTargetField()),
|
|
Value: rec.SvcParams,
|
|
}
|
|
}
|
|
|
|
func cfNaptrData(rec *models.RecordConfig) *cfNaptrRecData {
|
|
return &cfNaptrRecData{
|
|
Flags: rec.NaptrFlags,
|
|
Order: rec.NaptrOrder,
|
|
Preference: rec.NaptrPreference,
|
|
Regex: rec.NaptrRegexp,
|
|
Replacement: rec.GetTargetField(),
|
|
Service: rec.NaptrService,
|
|
}
|
|
}
|
|
|
|
func (c *cloudflareProvider) createRecDiff2(rec *models.RecordConfig, domainID string, msg string) []*models.Correction {
|
|
|
|
content := rec.GetTargetField()
|
|
if rec.Metadata[metaOriginalIP] != "" {
|
|
content = rec.Metadata[metaOriginalIP]
|
|
}
|
|
prio := ""
|
|
if rec.Type == "MX" {
|
|
prio = fmt.Sprintf(" %d ", rec.MxPreference)
|
|
}
|
|
if rec.Type == "TXT" {
|
|
content = rec.GetTargetTXTJoined()
|
|
}
|
|
if rec.Type == "DS" {
|
|
content = fmt.Sprintf("%d %d %d %s", rec.DsKeyTag, rec.DsAlgorithm, rec.DsDigestType, rec.DsDigest)
|
|
}
|
|
if msg == "" {
|
|
msg = fmt.Sprintf("CREATE record: %s %s %d%s %s", rec.GetLabel(), rec.Type, rec.TTL, prio, content)
|
|
}
|
|
if rec.Metadata[metaProxy] == "on" || rec.Metadata[metaProxy] == "full" {
|
|
msg = msg + fmt.Sprintf("\nACTIVATE PROXY for new record %s %s %d %s", rec.GetLabel(), rec.Type, rec.TTL, rec.GetTargetField())
|
|
}
|
|
arr := []*models.Correction{{
|
|
Msg: msg,
|
|
F: func() error {
|
|
cf := cloudflare.CreateDNSRecordParams{
|
|
Name: rec.GetLabel(),
|
|
Type: rec.Type,
|
|
TTL: int(rec.TTL),
|
|
Content: content,
|
|
Priority: &rec.MxPreference,
|
|
}
|
|
if rec.Type == "SRV" {
|
|
cf.Data = cfSrvData(rec)
|
|
cf.Name = rec.GetLabelFQDN()
|
|
} else if rec.Type == "CAA" {
|
|
cf.Data = cfCaaData(rec)
|
|
cf.Name = rec.GetLabelFQDN()
|
|
cf.Content = ""
|
|
} else if rec.Type == "TLSA" {
|
|
cf.Data = cfTlsaData(rec)
|
|
cf.Name = rec.GetLabelFQDN()
|
|
} else if rec.Type == "SSHFP" {
|
|
cf.Data = cfSshfpData(rec)
|
|
cf.Name = rec.GetLabelFQDN()
|
|
} else if rec.Type == "DNSKEY" {
|
|
cf.Data = cfDnskeyData(rec)
|
|
} else if rec.Type == "DS" {
|
|
cf.Data = cfDSData(rec)
|
|
} else if rec.Type == "NAPTR" {
|
|
cf.Data = cfNaptrData(rec)
|
|
cf.Name = rec.GetLabelFQDN()
|
|
} else if rec.Type == "HTTPS" || rec.Type == "SVCB" {
|
|
cf.Data = cfSvcbData(rec)
|
|
}
|
|
resp, err := c.cfClient.CreateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(domainID), cf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Records are created with the proxy off. If proxy should be
|
|
// enabled, we do a second API call.
|
|
resultID := resp.ID
|
|
if rec.Metadata[metaProxy] == "on" || rec.Metadata[metaProxy] == "full" {
|
|
return c.modifyRecord(domainID, resultID, true, rec)
|
|
}
|
|
return nil
|
|
},
|
|
}}
|
|
return arr
|
|
}
|
|
|
|
func (c *cloudflareProvider) modifyRecord(domainID, recID string, proxied bool, rec *models.RecordConfig) error {
|
|
if domainID == "" || recID == "" {
|
|
return fmt.Errorf("cannot modify record if domain or record id are empty")
|
|
}
|
|
|
|
r := cloudflare.UpdateDNSRecordParams{
|
|
ID: recID,
|
|
Proxied: &proxied,
|
|
Name: rec.GetLabel(),
|
|
Type: rec.Type,
|
|
Content: rec.GetTargetField(),
|
|
Priority: &rec.MxPreference,
|
|
TTL: int(rec.TTL),
|
|
}
|
|
if rec.Type == "TXT" {
|
|
r.Content = rec.GetTargetTXTJoined()
|
|
}
|
|
if rec.Type == "SRV" {
|
|
r.Data = cfSrvData(rec)
|
|
r.Name = rec.GetLabelFQDN()
|
|
} else if rec.Type == "CAA" {
|
|
r.Data = cfCaaData(rec)
|
|
r.Name = rec.GetLabelFQDN()
|
|
r.Content = ""
|
|
} else if rec.Type == "TLSA" {
|
|
r.Data = cfTlsaData(rec)
|
|
r.Name = rec.GetLabelFQDN()
|
|
} else if rec.Type == "SSHFP" {
|
|
r.Data = cfSshfpData(rec)
|
|
r.Name = rec.GetLabelFQDN()
|
|
} else if rec.Type == "DNSKEY" {
|
|
r.Data = cfDnskeyData(rec)
|
|
r.Content = ""
|
|
} else if rec.Type == "DS" {
|
|
r.Data = cfDSData(rec)
|
|
r.Content = ""
|
|
} else if rec.Type == "NAPTR" {
|
|
r.Data = cfNaptrData(rec)
|
|
r.Name = rec.GetLabelFQDN()
|
|
} else if rec.Type == "HTTPS" || rec.Type == "SVCB" {
|
|
r.Data = cfSvcbData(rec)
|
|
}
|
|
_, err := c.cfClient.UpdateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(domainID), r)
|
|
return err
|
|
}
|
|
|
|
// change universal ssl state
|
|
func (c *cloudflareProvider) changeUniversalSSL(domainID string, state bool) error {
|
|
_, err := c.cfClient.EditUniversalSSLSetting(context.Background(), domainID, cloudflare.UniversalSSLSetting{Enabled: state})
|
|
return err
|
|
}
|
|
|
|
// get universal ssl state
|
|
func (c *cloudflareProvider) getUniversalSSL(domainID string) (bool, error) {
|
|
result, err := c.cfClient.UniversalSSLSettingDetails(context.Background(), domainID)
|
|
return result.Enabled, err
|
|
}
|
|
|
|
func (c *cloudflareProvider) getSingleRedirects(id string, domain string) ([]*models.RecordConfig, error) {
|
|
rules, err := c.cfClient.GetEntrypointRuleset(context.Background(), cloudflare.ZoneIdentifier(id), "http_request_dynamic_redirect")
|
|
if err != nil {
|
|
var e *cloudflare.NotFoundError
|
|
if errors.As(err, &e) {
|
|
return []*models.RecordConfig{}, nil
|
|
}
|
|
return nil, fmt.Errorf("failed fetching redirect rule list cloudflare: %s (%T)", err, err)
|
|
}
|
|
//var rulelist []cloudflare.RulesetRule
|
|
//rulelist = rules.Rules
|
|
//rulelist := rules.Rules
|
|
|
|
//printer.Printf("DEBUG: rules %+v\n", rules)
|
|
recs := []*models.RecordConfig{}
|
|
for _, pr := range rules.Rules {
|
|
//printer.Printf("DEBUG: %+v\n", pr)
|
|
|
|
var thisPr = pr
|
|
r := &models.RecordConfig{
|
|
Type: "CLOUDFLAREAPI_SINGLE_REDIRECT",
|
|
Original: thisPr,
|
|
TTL: 1,
|
|
}
|
|
r.SetLabel("@", domain)
|
|
|
|
// Extract the valuables from the rule, use it to make the sr:
|
|
srWhen := pr.Expression
|
|
srThen := pr.ActionParameters.FromValue.TargetURL.Expression
|
|
code := int(pr.ActionParameters.FromValue.StatusCode)
|
|
sr := cfsingleredirect.FromAPIData(srWhen, srThen, code)
|
|
//sr.SRRRuleList = rulelist
|
|
//printer.Printf("DEBUG: DESCRIPTION = %v\n", pr.Description)
|
|
sr.SRDisplay = pr.Description
|
|
// printer.Printf("DEBUG: PR = %+v\n", pr)
|
|
// printer.Printf("DEBUG: rules = %+v\n", rules)
|
|
sr.SRRRulesetID = rules.ID
|
|
sr.SRRRulesetRuleID = pr.ID //correct
|
|
|
|
r.CloudflareRedirect = sr
|
|
r.SetTarget(pr.Description)
|
|
|
|
recs = append(recs, r)
|
|
}
|
|
|
|
return recs, nil
|
|
}
|
|
|
|
func (c *cloudflareProvider) createSingleRedirect(domainID string, cfr models.CloudflareSingleRedirectConfig) error {
|
|
|
|
//printer.Printf("DEBUG: createSingleRedir: d=%v crf=%+v\n", domainID, cfr)
|
|
// Asumption for target:
|
|
|
|
newSingleRedirectRulesActionParameters := cloudflare.RulesetRuleActionParameters{}
|
|
newSingleRedirectRule := cloudflare.RulesetRule{}
|
|
newSingleRedirectRules := []cloudflare.RulesetRule{}
|
|
newSingleRedirectRules = append(newSingleRedirectRules, newSingleRedirectRule)
|
|
newSingleRedirect := cloudflare.UpdateEntrypointRulesetParams{}
|
|
|
|
// Preserve query string if there isn't one in the replacement.
|
|
preserveQueryString := !strings.Contains(cfr.SRThen, "?")
|
|
|
|
newSingleRedirectRulesActionParameters.FromValue = &cloudflare.RulesetRuleActionParametersFromValue{}
|
|
// Redirect status code
|
|
newSingleRedirectRulesActionParameters.FromValue.StatusCode = uint16(cfr.Code)
|
|
// Incoming request expression
|
|
newSingleRedirectRules[0].Expression = cfr.SRWhen
|
|
// Redirect expression
|
|
newSingleRedirectRulesActionParameters.FromValue.TargetURL.Expression = cfr.SRThen
|
|
// Redirect name
|
|
newSingleRedirectRules[0].Description = cfr.SRDisplay
|
|
// Rule action, should always be redirect in this case
|
|
newSingleRedirectRules[0].Action = "redirect"
|
|
// Phase should always be http_request_dynamic_redirect
|
|
newSingleRedirect.Phase = "http_request_dynamic_redirect"
|
|
|
|
// Assigns the values in the nested structs
|
|
newSingleRedirectRulesActionParameters.FromValue.PreserveQueryString = &preserveQueryString
|
|
newSingleRedirectRules[0].ActionParameters = &newSingleRedirectRulesActionParameters
|
|
|
|
// Get a list of current redirects so that the new redirect get appended to it
|
|
rules, err := c.cfClient.GetEntrypointRuleset(context.Background(), cloudflare.ZoneIdentifier(domainID), "http_request_dynamic_redirect")
|
|
var e *cloudflare.NotFoundError
|
|
if err != nil && !errors.As(err, &e) {
|
|
return fmt.Errorf("failed fetching redirect rule list cloudflare: %s", err)
|
|
}
|
|
newSingleRedirect.Rules = newSingleRedirectRules
|
|
newSingleRedirect.Rules = append(newSingleRedirect.Rules, rules.Rules...)
|
|
|
|
_, err = c.cfClient.UpdateEntrypointRuleset(context.Background(), cloudflare.ZoneIdentifier(domainID), newSingleRedirect)
|
|
|
|
return err
|
|
}
|
|
|
|
func (c *cloudflareProvider) deleteSingleRedirects(domainID string, cfr models.CloudflareSingleRedirectConfig) error {
|
|
|
|
// This block should delete rules using the as is Cloudflare Golang lib in theory, need to debug why it isn't
|
|
// updatedRuleset := cloudflare.UpdateEntrypointRulesetParams{}
|
|
// updatedRulesetRules := []cloudflare.RulesetRule{}
|
|
|
|
// rules, err := c.cfClient.GetEntrypointRuleset(context.Background(), cloudflare.ZoneIdentifier(domainID), "http_request_dynamic_redirect")
|
|
// if err != nil {
|
|
// return fmt.Errorf("failed fetching redirect rule list cloudflare: %s", err)
|
|
// }
|
|
|
|
// for _, rule := range rules.Rules {
|
|
// if rule.ID != cfr.SRRRulesetRuleID {
|
|
// updatedRulesetRules = append(updatedRulesetRules, rule)
|
|
// } else {
|
|
// printer.Printf("DEBUG: MATCH %v : %v\n", rule.ID, cfr.SRRRulesetRuleID)
|
|
// }
|
|
// }
|
|
// updatedRuleset.Rules = updatedRulesetRules
|
|
// _, err = c.cfClient.UpdateEntrypointRuleset(context.Background(), cloudflare.ZoneIdentifier(domainID), updatedRuleset)
|
|
|
|
// Old Code
|
|
|
|
// rules, err := c.cfClient.GetEntrypointRuleset(context.Background(), cloudflare.ZoneIdentifier(domainID), "http_request_dynamic_redirect")
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
//printer.Printf("DEBUG: CALLING API DeleteRulesetRule: SRRRulesetID=%v, cfr.SRRRulesetRuleID=%v\n", cfr.SRRRulesetID, cfr.SRRRulesetRuleID)
|
|
|
|
err := c.cfClient.DeleteRulesetRule(context.Background(), cloudflare.ZoneIdentifier(domainID), cfr.SRRRulesetID, cfr.SRRRulesetRuleID)
|
|
// TODO(tlim): This is terrible. It returns an error even when it is successful.
|
|
if strings.Contains(err.Error(), `"success": true,`) {
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (c *cloudflareProvider) updateSingleRedirect(domainID string, oldrec, newrec *models.RecordConfig) error {
|
|
// rulesetID := cfr.SRRRulesetID
|
|
// rulesetRuleID := cfr.SRRRulesetRuleID
|
|
//printer.Printf("DEBUG: UPDATE-DEL domID=%v sr=%+v\n", domainID, cfr)
|
|
if err := c.deleteSingleRedirects(domainID, *oldrec.CloudflareRedirect); err != nil {
|
|
return err
|
|
}
|
|
//printer.Printf("DEBUG: UPDATE-CREATE domID=%v sr=%+v\n", domainID, newrec.CloudflareRedirect)
|
|
return c.createSingleRedirect(domainID, *newrec.CloudflareRedirect)
|
|
}
|
|
|
|
func (c *cloudflareProvider) getPageRules(id string, domain string) ([]*models.RecordConfig, error) {
|
|
rules, err := c.cfClient.ListPageRules(context.Background(), id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed fetching page rule list cloudflare: %s", err)
|
|
}
|
|
recs := []*models.RecordConfig{}
|
|
for _, pr := range rules {
|
|
// only interested in forwarding rules. Lets be very specific, and skip anything else
|
|
if len(pr.Actions) != 1 || len(pr.Targets) != 1 {
|
|
continue
|
|
}
|
|
if pr.Actions[0].ID != "forwarding_url" {
|
|
continue
|
|
}
|
|
value := pr.Actions[0].Value.(map[string]interface{})
|
|
var thisPr = pr
|
|
r := &models.RecordConfig{
|
|
Type: "PAGE_RULE",
|
|
Original: thisPr,
|
|
TTL: 1,
|
|
}
|
|
r.SetLabel("@", domain)
|
|
code := intZero(value["status_code"])
|
|
raw := fmt.Sprintf("%s,%s,%d,%d", // $FROM,$TO,$PRIO,$CODE
|
|
pr.Targets[0].Constraint.Value,
|
|
value["url"],
|
|
pr.Priority,
|
|
code)
|
|
r.SetTarget(raw)
|
|
|
|
cr, err := cfsingleredirect.FromUserInput(raw, code, pr.Priority)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
r.CloudflareRedirect = cr
|
|
|
|
recs = append(recs, r)
|
|
}
|
|
return recs, nil
|
|
}
|
|
|
|
func (c *cloudflareProvider) deletePageRule(recordID, domainID string) error {
|
|
return c.cfClient.DeletePageRule(context.Background(), domainID, recordID)
|
|
}
|
|
|
|
func (c *cloudflareProvider) updatePageRule(recordID, domainID string, cfr models.CloudflareSingleRedirectConfig) error {
|
|
// maybe someday?
|
|
//c.apiProvider.UpdatePageRule(context.Background(), domainId, recordID, )
|
|
if err := c.deletePageRule(recordID, domainID); err != nil {
|
|
return err
|
|
}
|
|
return c.createPageRule(domainID, cfr)
|
|
}
|
|
|
|
func (c *cloudflareProvider) createPageRule(domainID string, cfr models.CloudflareSingleRedirectConfig) error {
|
|
//printer.Printf("DEBUG: called createPageRule(%s, %+v)\n", domainID, cfr)
|
|
// from to priority code
|
|
// parts := strings.Split(target, ",")
|
|
// priority, _ := strconv.Atoi(parts[2])
|
|
// code, _ := strconv.Atoi(parts[3])
|
|
// printer.Printf("DEBUG: pr.PageRule target = %v\n", target)
|
|
// printer.Printf("DEBUG: pr.PageRule target = %v\n", parts[0])
|
|
// printer.Printf("DEBUG: pr.PageRule url = %v\n", parts[1])
|
|
// printer.Printf("DEBUG: pr.PageRule code = %v\n", code)
|
|
priority := cfr.PRPriority
|
|
code := cfr.Code
|
|
prWhen := cfr.PRWhen
|
|
prThen := cfr.PRThen
|
|
pr := cloudflare.PageRule{
|
|
Status: "active",
|
|
Priority: priority,
|
|
Targets: []cloudflare.PageRuleTarget{
|
|
{Target: "url", Constraint: pageRuleConstraint{Operator: "matches", Value: prWhen}},
|
|
},
|
|
Actions: []cloudflare.PageRuleAction{
|
|
{ID: "forwarding_url", Value: &pageRuleFwdInfo{
|
|
StatusCode: code,
|
|
URL: prThen,
|
|
}},
|
|
},
|
|
}
|
|
//printer.Printf("DEBUG: createPageRule pr=%+v\n", pr)
|
|
_, err := c.cfClient.CreatePageRule(context.Background(), domainID, pr)
|
|
return err
|
|
}
|
|
|
|
func (c *cloudflareProvider) getWorkerRoutes(id string, domain string) ([]*models.RecordConfig, error) {
|
|
res, err := c.cfClient.ListWorkerRoutes(context.Background(), cloudflare.ZoneIdentifier(id), cloudflare.ListWorkerRoutesParams{})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed fetching worker route list cloudflare: %s", err)
|
|
}
|
|
|
|
recs := []*models.RecordConfig{}
|
|
for _, pr := range res.Routes {
|
|
var thisPr = pr
|
|
r := &models.RecordConfig{
|
|
Type: "WORKER_ROUTE",
|
|
Original: thisPr,
|
|
TTL: 1,
|
|
}
|
|
r.SetLabel("@", domain)
|
|
r.SetTarget(fmt.Sprintf("%s,%s", // $PATTERN,$SCRIPT
|
|
pr.Pattern,
|
|
pr.ScriptName))
|
|
recs = append(recs, r)
|
|
}
|
|
return recs, nil
|
|
}
|
|
|
|
func (c *cloudflareProvider) deleteWorkerRoute(recordID, domainID string) error {
|
|
_, err := c.cfClient.DeleteWorkerRoute(context.Background(), cloudflare.ZoneIdentifier(domainID), recordID)
|
|
return err
|
|
}
|
|
|
|
func (c *cloudflareProvider) updateWorkerRoute(recordID, domainID string, target string) error {
|
|
// Causing Stack Overflow (!?)
|
|
// return c.updateWorkerRoute(recordID, domainID, target)
|
|
|
|
if err := c.deleteWorkerRoute(recordID, domainID); err != nil {
|
|
return err
|
|
}
|
|
return c.createWorkerRoute(domainID, target)
|
|
}
|
|
|
|
func (c *cloudflareProvider) createWorkerRoute(domainID string, target string) error {
|
|
// $PATTERN,$SCRIPT
|
|
parts := strings.Split(target, ",")
|
|
if len(parts) != 2 {
|
|
return fmt.Errorf("unexpected target: '%s' (expected: 'PATTERN,SCRIPT')", target)
|
|
}
|
|
wr := cloudflare.CreateWorkerRouteParams{
|
|
Pattern: parts[0],
|
|
Script: parts[1],
|
|
}
|
|
|
|
_, err := c.cfClient.CreateWorkerRoute(context.Background(), cloudflare.ZoneIdentifier(domainID), wr)
|
|
return err
|
|
}
|
|
|
|
// https://github.com/dominikh/go-tools/issues/1137 which is a dup of
|
|
// https://github.com/dominikh/go-tools/issues/810
|
|
//
|
|
//lint:ignore U1000 false positive due to
|
|
type pageRuleConstraint struct {
|
|
Operator string `json:"operator"`
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
type pageRuleFwdInfo struct {
|
|
URL string `json:"url"`
|
|
StatusCode int `json:"status_code"`
|
|
}
|