p2p/dnsdisc: add implementation of EIP-1459 (#20094)
This adds an implementation of node discovery via DNS TXT records to the go-ethereum library. The implementation doesn't match EIP-1459 exactly, the main difference being that this implementation uses separate merkle trees for tree links and ENRs. The EIP will be updated to match p2p/dnsdisc. To maintain DNS trees, cmd/devp2p provides a frontend for the p2p/dnsdisc library. The new 'dns' subcommands can be used to create, sign and deploy DNS discovery trees.
This commit is contained in:
		
							parent
							
								
									32b07e8b1f
								
							
						
					
					
						commit
						0568e81701
					
				| @ -19,10 +19,10 @@ package main | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/discover" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enode" | ||||
| @ -38,24 +38,35 @@ var ( | ||||
| 			discv4PingCommand, | ||||
| 			discv4RequestRecordCommand, | ||||
| 			discv4ResolveCommand, | ||||
| 			discv4ResolveJSONCommand, | ||||
| 		}, | ||||
| 	} | ||||
| 	discv4PingCommand = cli.Command{ | ||||
| 		Name:      "ping", | ||||
| 		Usage:     "Sends ping to a node", | ||||
| 		Action:    discv4Ping, | ||||
| 		ArgsUsage: "<node>", | ||||
| 	} | ||||
| 	discv4RequestRecordCommand = cli.Command{ | ||||
| 		Name:      "requestenr", | ||||
| 		Usage:     "Requests a node record using EIP-868 enrRequest", | ||||
| 		Action:    discv4RequestRecord, | ||||
| 		ArgsUsage: "<node>", | ||||
| 	} | ||||
| 	discv4ResolveCommand = cli.Command{ | ||||
| 		Name:      "resolve", | ||||
| 		Usage:     "Finds a node in the DHT", | ||||
| 		Action:    discv4Resolve, | ||||
| 		ArgsUsage: "<node>", | ||||
| 		Flags:     []cli.Flag{bootnodesFlag}, | ||||
| 	} | ||||
| 	discv4ResolveJSONCommand = cli.Command{ | ||||
| 		Name:      "resolve-json", | ||||
| 		Usage:     "Re-resolves nodes in a nodes.json file", | ||||
| 		Action:    discv4ResolveJSON, | ||||
| 		Flags:     []cli.Flag{bootnodesFlag}, | ||||
| 		ArgsUsage: "<nodes.json file>", | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| var bootnodesFlag = cli.StringFlag{ | ||||
| @ -64,10 +75,8 @@ var bootnodesFlag = cli.StringFlag{ | ||||
| } | ||||
| 
 | ||||
| func discv4Ping(ctx *cli.Context) error { | ||||
| 	n, disc, err := getNodeArgAndStartV4(ctx) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	n := getNodeArg(ctx) | ||||
| 	disc := startV4(ctx) | ||||
| 	defer disc.Close() | ||||
| 
 | ||||
| 	start := time.Now() | ||||
| @ -79,10 +88,8 @@ func discv4Ping(ctx *cli.Context) error { | ||||
| } | ||||
| 
 | ||||
| func discv4RequestRecord(ctx *cli.Context) error { | ||||
| 	n, disc, err := getNodeArgAndStartV4(ctx) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	n := getNodeArg(ctx) | ||||
| 	disc := startV4(ctx) | ||||
| 	defer disc.Close() | ||||
| 
 | ||||
| 	respN, err := disc.RequestENR(n) | ||||
| @ -94,33 +101,43 @@ func discv4RequestRecord(ctx *cli.Context) error { | ||||
| } | ||||
| 
 | ||||
| func discv4Resolve(ctx *cli.Context) error { | ||||
| 	n, disc, err := getNodeArgAndStartV4(ctx) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	n := getNodeArg(ctx) | ||||
| 	disc := startV4(ctx) | ||||
| 	defer disc.Close() | ||||
| 
 | ||||
| 	fmt.Println(disc.Resolve(n).String()) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func getNodeArgAndStartV4(ctx *cli.Context) (*enode.Node, *discover.UDPv4, error) { | ||||
| 	if ctx.NArg() != 1 { | ||||
| 		return nil, nil, fmt.Errorf("missing node as command-line argument") | ||||
| func discv4ResolveJSON(ctx *cli.Context) error { | ||||
| 	if ctx.NArg() < 1 { | ||||
| 		return fmt.Errorf("need nodes file as argument") | ||||
| 	} | ||||
| 	n, err := parseNode(ctx.Args()[0]) | ||||
| 	disc := startV4(ctx) | ||||
| 	defer disc.Close() | ||||
| 	file := ctx.Args().Get(0) | ||||
| 
 | ||||
| 	// Load existing nodes in file.
 | ||||
| 	var nodes []*enode.Node | ||||
| 	if common.FileExist(file) { | ||||
| 		nodes = loadNodesJSON(file).nodes() | ||||
| 	} | ||||
| 	// Add nodes from command line arguments.
 | ||||
| 	for i := 1; i < ctx.NArg(); i++ { | ||||
| 		n, err := parseNode(ctx.Args().Get(i)) | ||||
| 		if err != nil { | ||||
| 		return nil, nil, err | ||||
| 			exit(err) | ||||
| 		} | ||||
| 	var bootnodes []*enode.Node | ||||
| 	if commandHasFlag(ctx, bootnodesFlag) { | ||||
| 		bootnodes, err = parseBootnodes(ctx) | ||||
| 		if err != nil { | ||||
| 			return nil, nil, err | ||||
| 		nodes = append(nodes, n) | ||||
| 	} | ||||
| 
 | ||||
| 	result := make(nodeSet, len(nodes)) | ||||
| 	for _, n := range nodes { | ||||
| 		n = disc.Resolve(n) | ||||
| 		result[n.ID()] = nodeJSON{Seq: n.Seq(), N: n} | ||||
| 	} | ||||
| 	disc, err := startV4(bootnodes) | ||||
| 	return n, disc, err | ||||
| 	writeNodesJSON(file, result) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) { | ||||
| @ -139,28 +156,39 @@ func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) { | ||||
| 	return nodes, nil | ||||
| } | ||||
| 
 | ||||
| // commandHasFlag returns true if the current command supports the given flag.
 | ||||
| func commandHasFlag(ctx *cli.Context, flag cli.Flag) bool { | ||||
| 	flags := ctx.FlagNames() | ||||
| 	sort.Strings(flags) | ||||
| 	i := sort.SearchStrings(flags, flag.GetName()) | ||||
| 	return i != len(flags) && flags[i] == flag.GetName() | ||||
| // startV4 starts an ephemeral discovery V4 node.
 | ||||
| func startV4(ctx *cli.Context) *discover.UDPv4 { | ||||
| 	socket, ln, cfg, err := listen() | ||||
| 	if err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| 	if commandHasFlag(ctx, bootnodesFlag) { | ||||
| 		bn, err := parseBootnodes(ctx) | ||||
| 		if err != nil { | ||||
| 			exit(err) | ||||
| 		} | ||||
| 		cfg.Bootnodes = bn | ||||
| 	} | ||||
| 	disc, err := discover.ListenV4(socket, ln, cfg) | ||||
| 	if err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| 	return disc | ||||
| } | ||||
| 
 | ||||
| // startV4 starts an ephemeral discovery V4 node.
 | ||||
| func startV4(bootnodes []*enode.Node) (*discover.UDPv4, error) { | ||||
| func listen() (*net.UDPConn, *enode.LocalNode, discover.Config, error) { | ||||
| 	var cfg discover.Config | ||||
| 	cfg.Bootnodes = bootnodes | ||||
| 	cfg.PrivateKey, _ = crypto.GenerateKey() | ||||
| 	db, _ := enode.OpenDB("") | ||||
| 	ln := enode.NewLocalNode(db, cfg.PrivateKey) | ||||
| 
 | ||||
| 	socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{0, 0, 0, 0}}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		db.Close() | ||||
| 		return nil, nil, cfg, err | ||||
| 	} | ||||
| 	addr := socket.LocalAddr().(*net.UDPAddr) | ||||
| 	ln.SetFallbackIP(net.IP{127, 0, 0, 1}) | ||||
| 	ln.SetFallbackUDP(addr.Port) | ||||
| 	return discover.ListenUDP(socket, ln, cfg) | ||||
| 	return socket, ln, cfg, nil | ||||
| } | ||||
|  | ||||
							
								
								
									
										163
									
								
								cmd/devp2p/dns_cloudflare.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								cmd/devp2p/dns_cloudflare.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,163 @@ | ||||
| // Copyright 2019 The go-ethereum Authors
 | ||||
| // This file is part of go-ethereum.
 | ||||
| //
 | ||||
| // go-ethereum is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // go-ethereum is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/cloudflare/cloudflare-go" | ||||
| 	"github.com/ethereum/go-ethereum/log" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/dnsdisc" | ||||
| 	"gopkg.in/urfave/cli.v1" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	cloudflareTokenFlag = cli.StringFlag{ | ||||
| 		Name:   "token", | ||||
| 		Usage:  "CloudFlare API token", | ||||
| 		EnvVar: "CLOUDFLARE_API_TOKEN", | ||||
| 	} | ||||
| 	cloudflareZoneIDFlag = cli.StringFlag{ | ||||
| 		Name:  "zoneid", | ||||
| 		Usage: "CloudFlare Zone ID (optional)", | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| type cloudflareClient struct { | ||||
| 	*cloudflare.API | ||||
| 	zoneID string | ||||
| } | ||||
| 
 | ||||
| // newCloudflareClient sets up a CloudFlare API client from command line flags.
 | ||||
| func newCloudflareClient(ctx *cli.Context) *cloudflareClient { | ||||
| 	token := ctx.String(cloudflareTokenFlag.Name) | ||||
| 	if token == "" { | ||||
| 		exit(fmt.Errorf("need cloudflare API token to proceed")) | ||||
| 	} | ||||
| 	api, err := cloudflare.NewWithAPIToken(token) | ||||
| 	if err != nil { | ||||
| 		exit(fmt.Errorf("can't create Cloudflare client: %v", err)) | ||||
| 	} | ||||
| 	return &cloudflareClient{ | ||||
| 		API:    api, | ||||
| 		zoneID: ctx.String(cloudflareZoneIDFlag.Name), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // deploy uploads the given tree to CloudFlare DNS.
 | ||||
| func (c *cloudflareClient) deploy(name string, t *dnsdisc.Tree) error { | ||||
| 	if err := c.checkZone(name); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	records := t.ToTXT(name) | ||||
| 	return c.uploadRecords(name, records) | ||||
| } | ||||
| 
 | ||||
| // checkZone verifies permissions on the CloudFlare DNS Zone for name.
 | ||||
| func (c *cloudflareClient) checkZone(name string) error { | ||||
| 	if c.zoneID == "" { | ||||
| 		log.Info(fmt.Sprintf("Finding CloudFlare zone ID for %s", name)) | ||||
| 		id, err := c.ZoneIDByName(name) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		c.zoneID = id | ||||
| 	} | ||||
| 	log.Info(fmt.Sprintf("Checking Permissions on zone %s", c.zoneID)) | ||||
| 	zone, err := c.ZoneDetails(c.zoneID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if !strings.HasSuffix(name, "."+zone.Name) { | ||||
| 		return fmt.Errorf("CloudFlare zone name %q does not match name %q to be deployed", zone.Name, name) | ||||
| 	} | ||||
| 	needPerms := map[string]bool{"#zone:edit": false, "#zone:read": false} | ||||
| 	for _, perm := range zone.Permissions { | ||||
| 		if _, ok := needPerms[perm]; ok { | ||||
| 			needPerms[perm] = true | ||||
| 		} | ||||
| 	} | ||||
| 	for _, ok := range needPerms { | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("wrong permissions on zone %s: %v", c.zoneID, needPerms) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // uploadRecords updates the TXT records at a particular subdomain. All non-root records
 | ||||
| // will have a TTL of "infinity" and all existing records not in the new map will be
 | ||||
| // nuked!
 | ||||
| func (c *cloudflareClient) uploadRecords(name string, records map[string]string) error { | ||||
| 	// Convert all names to lowercase.
 | ||||
| 	lrecords := make(map[string]string, len(records)) | ||||
| 	for name, r := range records { | ||||
| 		lrecords[strings.ToLower(name)] = r | ||||
| 	} | ||||
| 	records = lrecords | ||||
| 
 | ||||
| 	log.Info(fmt.Sprintf("Retrieving existing TXT records on %s", name)) | ||||
| 	entries, err := c.DNSRecords(c.zoneID, cloudflare.DNSRecord{Type: "TXT"}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	existing := make(map[string]cloudflare.DNSRecord) | ||||
| 	for _, entry := range entries { | ||||
| 		if !strings.HasSuffix(entry.Name, name) { | ||||
| 			continue | ||||
| 		} | ||||
| 		existing[strings.ToLower(entry.Name)] = entry | ||||
| 	} | ||||
| 
 | ||||
| 	// Iterate over the new records and inject anything missing.
 | ||||
| 	for path, val := range records { | ||||
| 		old, exists := existing[path] | ||||
| 		if !exists { | ||||
| 			// Entry is unknown, push a new one to Cloudflare.
 | ||||
| 			log.Info(fmt.Sprintf("Creating %s = %q", path, val)) | ||||
| 			ttl := 1 | ||||
| 			if path != name { | ||||
| 				ttl = 2147483647 // Max TTL permitted by Cloudflare
 | ||||
| 			} | ||||
| 			_, err = c.CreateDNSRecord(c.zoneID, cloudflare.DNSRecord{Type: "TXT", Name: path, Content: val, TTL: ttl}) | ||||
| 		} else if old.Content != val { | ||||
| 			// Entry already exists, only change its content.
 | ||||
| 			log.Info(fmt.Sprintf("Updating %s from %q to %q", path, old.Content, val)) | ||||
| 			old.Content = val | ||||
| 			err = c.UpdateDNSRecord(c.zoneID, old.ID, old) | ||||
| 		} else { | ||||
| 			log.Info(fmt.Sprintf("Skipping %s = %q", path, val)) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to publish %s: %v", path, err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Iterate over the old records and delete anything stale.
 | ||||
| 	for path, entry := range existing { | ||||
| 		if _, ok := records[path]; ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		// Stale entry, nuke it.
 | ||||
| 		log.Info(fmt.Sprintf("Deleting %s = %q", path, entry.Content)) | ||||
| 		if err := c.DeleteDNSRecord(c.zoneID, entry.ID); err != nil { | ||||
| 			return fmt.Errorf("failed to delete %s: %v", path, err) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										358
									
								
								cmd/devp2p/dnscmd.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								cmd/devp2p/dnscmd.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,358 @@ | ||||
| // Copyright 2018 The go-ethereum Authors
 | ||||
| // This file is part of go-ethereum.
 | ||||
| //
 | ||||
| // go-ethereum is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // go-ethereum is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/ecdsa" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/accounts/keystore" | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/console" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/dnsdisc" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enode" | ||||
| 	cli "gopkg.in/urfave/cli.v1" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	dnsCommand = cli.Command{ | ||||
| 		Name:  "dns", | ||||
| 		Usage: "DNS Discovery Commands", | ||||
| 		Subcommands: []cli.Command{ | ||||
| 			dnsSyncCommand, | ||||
| 			dnsSignCommand, | ||||
| 			dnsTXTCommand, | ||||
| 			dnsCloudflareCommand, | ||||
| 		}, | ||||
| 	} | ||||
| 	dnsSyncCommand = cli.Command{ | ||||
| 		Name:      "sync", | ||||
| 		Usage:     "Download a DNS discovery tree", | ||||
| 		ArgsUsage: "<url> [ <directory> ]", | ||||
| 		Action:    dnsSync, | ||||
| 		Flags:     []cli.Flag{dnsTimeoutFlag}, | ||||
| 	} | ||||
| 	dnsSignCommand = cli.Command{ | ||||
| 		Name:      "sign", | ||||
| 		Usage:     "Sign a DNS discovery tree", | ||||
| 		ArgsUsage: "<tree-directory> <key-file>", | ||||
| 		Action:    dnsSign, | ||||
| 		Flags:     []cli.Flag{dnsDomainFlag, dnsSeqFlag}, | ||||
| 	} | ||||
| 	dnsTXTCommand = cli.Command{ | ||||
| 		Name:      "to-txt", | ||||
| 		Usage:     "Create a DNS TXT records for a discovery tree", | ||||
| 		ArgsUsage: "<tree-directory> <output-file>", | ||||
| 		Action:    dnsToTXT, | ||||
| 	} | ||||
| 	dnsCloudflareCommand = cli.Command{ | ||||
| 		Name:      "to-cloudflare", | ||||
| 		Usage:     "Deploy DNS TXT records to cloudflare", | ||||
| 		ArgsUsage: "<tree-directory>", | ||||
| 		Action:    dnsToCloudflare, | ||||
| 		Flags:     []cli.Flag{cloudflareTokenFlag, cloudflareZoneIDFlag}, | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	dnsTimeoutFlag = cli.DurationFlag{ | ||||
| 		Name:  "timeout", | ||||
| 		Usage: "Timeout for DNS lookups", | ||||
| 	} | ||||
| 	dnsDomainFlag = cli.StringFlag{ | ||||
| 		Name:  "domain", | ||||
| 		Usage: "Domain name of the tree", | ||||
| 	} | ||||
| 	dnsSeqFlag = cli.UintFlag{ | ||||
| 		Name:  "seq", | ||||
| 		Usage: "New sequence number of the tree", | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| // dnsSync performs dnsSyncCommand.
 | ||||
| func dnsSync(ctx *cli.Context) error { | ||||
| 	var ( | ||||
| 		c      = dnsClient(ctx) | ||||
| 		url    = ctx.Args().Get(0) | ||||
| 		outdir = ctx.Args().Get(1) | ||||
| 	) | ||||
| 	domain, _, err := dnsdisc.ParseURL(url) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if outdir == "" { | ||||
| 		outdir = domain | ||||
| 	} | ||||
| 
 | ||||
| 	t, err := c.SyncTree(url) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	def := treeToDefinition(url, t) | ||||
| 	def.Meta.LastModified = time.Now() | ||||
| 	writeTreeDefinition(outdir, def) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func dnsSign(ctx *cli.Context) error { | ||||
| 	if ctx.NArg() < 2 { | ||||
| 		return fmt.Errorf("need tree definition directory and key file as arguments") | ||||
| 	} | ||||
| 	var ( | ||||
| 		defdir  = ctx.Args().Get(0) | ||||
| 		keyfile = ctx.Args().Get(1) | ||||
| 		def     = loadTreeDefinition(defdir) | ||||
| 		domain  = directoryName(defdir) | ||||
| 	) | ||||
| 	if def.Meta.URL != "" { | ||||
| 		d, _, err := dnsdisc.ParseURL(def.Meta.URL) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("invalid 'url' field: %v", err) | ||||
| 		} | ||||
| 		domain = d | ||||
| 	} | ||||
| 	if ctx.IsSet(dnsDomainFlag.Name) { | ||||
| 		domain = ctx.String(dnsDomainFlag.Name) | ||||
| 	} | ||||
| 	if ctx.IsSet(dnsSeqFlag.Name) { | ||||
| 		def.Meta.Seq = ctx.Uint(dnsSeqFlag.Name) | ||||
| 	} else { | ||||
| 		def.Meta.Seq++ // Auto-bump sequence number if not supplied via flag.
 | ||||
| 	} | ||||
| 	t, err := dnsdisc.MakeTree(def.Meta.Seq, def.Nodes, def.Meta.Links) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	key := loadSigningKey(keyfile) | ||||
| 	url, err := t.Sign(key, domain) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("can't sign: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	def = treeToDefinition(url, t) | ||||
| 	def.Meta.LastModified = time.Now() | ||||
| 	writeTreeDefinition(defdir, def) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func directoryName(dir string) string { | ||||
| 	abs, err := filepath.Abs(dir) | ||||
| 	if err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| 	return filepath.Base(abs) | ||||
| } | ||||
| 
 | ||||
| // dnsToTXT peforms dnsTXTCommand.
 | ||||
| func dnsToTXT(ctx *cli.Context) error { | ||||
| 	if ctx.NArg() < 1 { | ||||
| 		return fmt.Errorf("need tree definition directory as argument") | ||||
| 	} | ||||
| 	output := ctx.Args().Get(1) | ||||
| 	if output == "" { | ||||
| 		output = "-" // default to stdout
 | ||||
| 	} | ||||
| 	domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	writeTXTJSON(output, t.ToTXT(domain)) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // dnsToCloudflare peforms dnsCloudflareCommand.
 | ||||
| func dnsToCloudflare(ctx *cli.Context) error { | ||||
| 	if ctx.NArg() < 1 { | ||||
| 		return fmt.Errorf("need tree definition directory as argument") | ||||
| 	} | ||||
| 	domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	client := newCloudflareClient(ctx) | ||||
| 	return client.deploy(domain, t) | ||||
| } | ||||
| 
 | ||||
| // loadSigningKey loads a private key in Ethereum keystore format.
 | ||||
| func loadSigningKey(keyfile string) *ecdsa.PrivateKey { | ||||
| 	keyjson, err := ioutil.ReadFile(keyfile) | ||||
| 	if err != nil { | ||||
| 		exit(fmt.Errorf("failed to read the keyfile at '%s': %v", keyfile, err)) | ||||
| 	} | ||||
| 	password, _ := console.Stdin.PromptPassword("Please enter the password for '" + keyfile + "': ") | ||||
| 	key, err := keystore.DecryptKey(keyjson, password) | ||||
| 	if err != nil { | ||||
| 		exit(fmt.Errorf("error decrypting key: %v", err)) | ||||
| 	} | ||||
| 	return key.PrivateKey | ||||
| } | ||||
| 
 | ||||
| // dnsClient configures the DNS discovery client from command line flags.
 | ||||
| func dnsClient(ctx *cli.Context) *dnsdisc.Client { | ||||
| 	var cfg dnsdisc.Config | ||||
| 	if commandHasFlag(ctx, dnsTimeoutFlag) { | ||||
| 		cfg.Timeout = ctx.Duration(dnsTimeoutFlag.Name) | ||||
| 	} | ||||
| 	c, _ := dnsdisc.NewClient(cfg) // cannot fail because no URLs given
 | ||||
| 	return c | ||||
| } | ||||
| 
 | ||||
| // There are two file formats for DNS node trees on disk:
 | ||||
| //
 | ||||
| // The 'TXT' format is a single JSON file containing DNS TXT records
 | ||||
| // as a JSON object where the keys are names and the values are objects
 | ||||
| // containing the value of the record.
 | ||||
| //
 | ||||
| // The 'definition' format is a directory containing two files:
 | ||||
| //
 | ||||
| //      enrtree-info.json    -- contains sequence number & links to other trees
 | ||||
| //      nodes.json           -- contains the nodes as a JSON array.
 | ||||
| //
 | ||||
| // This format exists because it's convenient to edit. nodes.json can be generated
 | ||||
| // in multiple ways: it may be written by a DHT crawler or compiled by a human.
 | ||||
| 
 | ||||
| type dnsDefinition struct { | ||||
| 	Meta  dnsMetaJSON | ||||
| 	Nodes []*enode.Node | ||||
| } | ||||
| 
 | ||||
| type dnsMetaJSON struct { | ||||
| 	URL          string    `json:"url,omitempty"` | ||||
| 	Seq          uint      `json:"seq"` | ||||
| 	Sig          string    `json:"signature,omitempty"` | ||||
| 	Links        []string  `json:"links"` | ||||
| 	LastModified time.Time `json:"lastModified"` | ||||
| } | ||||
| 
 | ||||
| func treeToDefinition(url string, t *dnsdisc.Tree) *dnsDefinition { | ||||
| 	meta := dnsMetaJSON{ | ||||
| 		URL:   url, | ||||
| 		Seq:   t.Seq(), | ||||
| 		Sig:   t.Signature(), | ||||
| 		Links: t.Links(), | ||||
| 	} | ||||
| 	if meta.Links == nil { | ||||
| 		meta.Links = []string{} | ||||
| 	} | ||||
| 	return &dnsDefinition{Meta: meta, Nodes: t.Nodes()} | ||||
| } | ||||
| 
 | ||||
| // loadTreeDefinition loads a directory in 'definition' format.
 | ||||
| func loadTreeDefinition(directory string) *dnsDefinition { | ||||
| 	metaFile, nodesFile := treeDefinitionFiles(directory) | ||||
| 	var def dnsDefinition | ||||
| 	err := common.LoadJSON(metaFile, &def.Meta) | ||||
| 	if err != nil && !os.IsNotExist(err) { | ||||
| 		exit(err) | ||||
| 	} | ||||
| 	if def.Meta.Links == nil { | ||||
| 		def.Meta.Links = []string{} | ||||
| 	} | ||||
| 	// Check link syntax.
 | ||||
| 	for _, link := range def.Meta.Links { | ||||
| 		if _, _, err := dnsdisc.ParseURL(link); err != nil { | ||||
| 			exit(fmt.Errorf("invalid link %q: %v", link, err)) | ||||
| 		} | ||||
| 	} | ||||
| 	// Check/convert nodes.
 | ||||
| 	nodes := loadNodesJSON(nodesFile) | ||||
| 	if err := nodes.verify(); err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| 	def.Nodes = nodes.nodes() | ||||
| 	return &def | ||||
| } | ||||
| 
 | ||||
| // loadTreeDefinitionForExport loads a DNS tree and ensures it is signed.
 | ||||
| func loadTreeDefinitionForExport(dir string) (domain string, t *dnsdisc.Tree, err error) { | ||||
| 	metaFile, _ := treeDefinitionFiles(dir) | ||||
| 	def := loadTreeDefinition(dir) | ||||
| 	if def.Meta.URL == "" { | ||||
| 		return "", nil, fmt.Errorf("missing 'url' field in %v", metaFile) | ||||
| 	} | ||||
| 	domain, pubkey, err := dnsdisc.ParseURL(def.Meta.URL) | ||||
| 	if err != nil { | ||||
| 		return "", nil, fmt.Errorf("invalid 'url' field in %v: %v", metaFile, err) | ||||
| 	} | ||||
| 	if t, err = dnsdisc.MakeTree(def.Meta.Seq, def.Nodes, def.Meta.Links); err != nil { | ||||
| 		return "", nil, err | ||||
| 	} | ||||
| 	if err := ensureValidTreeSignature(t, pubkey, def.Meta.Sig); err != nil { | ||||
| 		return "", nil, err | ||||
| 	} | ||||
| 	return domain, t, nil | ||||
| } | ||||
| 
 | ||||
| // ensureValidTreeSignature checks that sig is valid for tree and assigns it as the
 | ||||
| // tree's signature if valid.
 | ||||
| func ensureValidTreeSignature(t *dnsdisc.Tree, pubkey *ecdsa.PublicKey, sig string) error { | ||||
| 	if sig == "" { | ||||
| 		return fmt.Errorf("missing signature, run 'devp2p dns sign' first") | ||||
| 	} | ||||
| 	if err := t.SetSignature(pubkey, sig); err != nil { | ||||
| 		return fmt.Errorf("invalid signature on tree, run 'devp2p dns sign' to update it") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // writeTreeDefinition writes a DNS node tree definition to the given directory.
 | ||||
| func writeTreeDefinition(directory string, def *dnsDefinition) { | ||||
| 	metaJSON, err := json.MarshalIndent(&def.Meta, "", jsonIndent) | ||||
| 	if err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| 	// Convert nodes.
 | ||||
| 	nodes := make(nodeSet, len(def.Nodes)) | ||||
| 	nodes.add(def.Nodes...) | ||||
| 	// Write.
 | ||||
| 	if err := os.Mkdir(directory, 0744); err != nil && !os.IsExist(err) { | ||||
| 		exit(err) | ||||
| 	} | ||||
| 	metaFile, nodesFile := treeDefinitionFiles(directory) | ||||
| 	writeNodesJSON(nodesFile, nodes) | ||||
| 	if err := ioutil.WriteFile(metaFile, metaJSON, 0644); err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func treeDefinitionFiles(directory string) (string, string) { | ||||
| 	meta := filepath.Join(directory, "enrtree-info.json") | ||||
| 	nodes := filepath.Join(directory, "nodes.json") | ||||
| 	return meta, nodes | ||||
| } | ||||
| 
 | ||||
| // writeTXTJSON writes TXT records in JSON format.
 | ||||
| func writeTXTJSON(file string, txt map[string]string) { | ||||
| 	txtJSON, err := json.MarshalIndent(txt, "", jsonIndent) | ||||
| 	if err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| 	if file == "-" { | ||||
| 		os.Stdout.Write(txtJSON) | ||||
| 		fmt.Println() | ||||
| 		return | ||||
| 	} | ||||
| 	if err := ioutil.WriteFile(file, txtJSON, 0644); err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| } | ||||
| @ -20,8 +20,10 @@ import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sort" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/internal/debug" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enode" | ||||
| 	"github.com/ethereum/go-ethereum/params" | ||||
| 	"gopkg.in/urfave/cli.v1" | ||||
| ) | ||||
| @ -57,12 +59,38 @@ func init() { | ||||
| 	app.Commands = []cli.Command{ | ||||
| 		enrdumpCommand, | ||||
| 		discv4Command, | ||||
| 		dnsCommand, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	if err := app.Run(os.Args); err != nil { | ||||
| 	exit(app.Run(os.Args)) | ||||
| } | ||||
| 
 | ||||
| // commandHasFlag returns true if the current command supports the given flag.
 | ||||
| func commandHasFlag(ctx *cli.Context, flag cli.Flag) bool { | ||||
| 	flags := ctx.FlagNames() | ||||
| 	sort.Strings(flags) | ||||
| 	i := sort.SearchStrings(flags, flag.GetName()) | ||||
| 	return i != len(flags) && flags[i] == flag.GetName() | ||||
| } | ||||
| 
 | ||||
| // getNodeArg handles the common case of a single node descriptor argument.
 | ||||
| func getNodeArg(ctx *cli.Context) *enode.Node { | ||||
| 	if ctx.NArg() != 1 { | ||||
| 		exit("missing node as command-line argument") | ||||
| 	} | ||||
| 	n, err := parseNode(ctx.Args()[0]) | ||||
| 	if err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func exit(err interface{}) { | ||||
| 	if err == nil { | ||||
| 		os.Exit(0) | ||||
| 	} | ||||
| 	fmt.Fprintln(os.Stderr, err) | ||||
| 	os.Exit(1) | ||||
| } | ||||
| } | ||||
|  | ||||
							
								
								
									
										87
									
								
								cmd/devp2p/nodeset.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								cmd/devp2p/nodeset.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | ||||
| // Copyright 2019 The go-ethereum Authors
 | ||||
| // This file is part of go-ethereum.
 | ||||
| //
 | ||||
| // go-ethereum is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // go-ethereum is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"sort" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enode" | ||||
| ) | ||||
| 
 | ||||
| const jsonIndent = "    " | ||||
| 
 | ||||
| // nodeSet is the nodes.json file format. It holds a set of node records
 | ||||
| // as a JSON object.
 | ||||
| type nodeSet map[enode.ID]nodeJSON | ||||
| 
 | ||||
| type nodeJSON struct { | ||||
| 	Seq uint64      `json:"seq"` | ||||
| 	N   *enode.Node `json:"record"` | ||||
| } | ||||
| 
 | ||||
| func loadNodesJSON(file string) nodeSet { | ||||
| 	var nodes nodeSet | ||||
| 	if err := common.LoadJSON(file, &nodes); err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| 	return nodes | ||||
| } | ||||
| 
 | ||||
| func writeNodesJSON(file string, nodes nodeSet) { | ||||
| 	nodesJSON, err := json.MarshalIndent(nodes, "", jsonIndent) | ||||
| 	if err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| 	if err := ioutil.WriteFile(file, nodesJSON, 0644); err != nil { | ||||
| 		exit(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (ns nodeSet) nodes() []*enode.Node { | ||||
| 	result := make([]*enode.Node, 0, len(ns)) | ||||
| 	for _, n := range ns { | ||||
| 		result = append(result, n.N) | ||||
| 	} | ||||
| 	// Sort by ID.
 | ||||
| 	sort.Slice(result, func(i, j int) bool { | ||||
| 		return bytes.Compare(result[i].ID().Bytes(), result[j].ID().Bytes()) < 0 | ||||
| 	}) | ||||
| 	return result | ||||
| } | ||||
| 
 | ||||
| func (ns nodeSet) add(nodes ...*enode.Node) { | ||||
| 	for _, n := range nodes { | ||||
| 		ns[n.ID()] = nodeJSON{Seq: n.Seq(), N: n} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (ns nodeSet) verify() error { | ||||
| 	for id, n := range ns { | ||||
| 		if n.N.ID() != id { | ||||
| 			return fmt.Errorf("invalid node %v: ID does not match ID %v in record", id, n.N.ID()) | ||||
| 		} | ||||
| 		if n.N.Seq() != n.Seq { | ||||
| 			return fmt.Errorf("invalid node %v: 'seq' does not match seq %d from record", id, n.N.Seq()) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										260
									
								
								p2p/dnsdisc/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								p2p/dnsdisc/client.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,260 @@ | ||||
| // Copyright 2018 The go-ethereum Authors
 | ||||
| // This file is part of the go-ethereum library.
 | ||||
| //
 | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU Lesser General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // The go-ethereum library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU Lesser General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU Lesser General Public License
 | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package dnsdisc | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"math/rand" | ||||
| 	"net" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/common/mclock" | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| 	"github.com/ethereum/go-ethereum/log" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enode" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enr" | ||||
| 	lru "github.com/hashicorp/golang-lru" | ||||
| ) | ||||
| 
 | ||||
| // Client discovers nodes by querying DNS servers.
 | ||||
| type Client struct { | ||||
| 	cfg       Config | ||||
| 	clock     mclock.Clock | ||||
| 	linkCache linkCache | ||||
| 	trees     map[string]*clientTree | ||||
| 
 | ||||
| 	entries *lru.Cache | ||||
| } | ||||
| 
 | ||||
| // Config holds configuration options for the client.
 | ||||
| type Config struct { | ||||
| 	Timeout         time.Duration      // timeout used for DNS lookups (default 5s)
 | ||||
| 	RecheckInterval time.Duration      // time between tree root update checks (default 30min)
 | ||||
| 	CacheLimit      int                // maximum number of cached records (default 1000)
 | ||||
| 	ValidSchemes    enr.IdentityScheme // acceptable ENR identity schemes (default enode.ValidSchemes)
 | ||||
| 	Resolver        Resolver           // the DNS resolver to use (defaults to system DNS)
 | ||||
| 	Logger          log.Logger         // destination of client log messages (defaults to root logger)
 | ||||
| } | ||||
| 
 | ||||
| // Resolver is a DNS resolver that can query TXT records.
 | ||||
| type Resolver interface { | ||||
| 	LookupTXT(ctx context.Context, domain string) ([]string, error) | ||||
| } | ||||
| 
 | ||||
| func (cfg Config) withDefaults() Config { | ||||
| 	const ( | ||||
| 		defaultTimeout = 5 * time.Second | ||||
| 		defaultRecheck = 30 * time.Minute | ||||
| 		defaultCache   = 1000 | ||||
| 	) | ||||
| 	if cfg.Timeout == 0 { | ||||
| 		cfg.Timeout = defaultTimeout | ||||
| 	} | ||||
| 	if cfg.RecheckInterval == 0 { | ||||
| 		cfg.RecheckInterval = defaultRecheck | ||||
| 	} | ||||
| 	if cfg.CacheLimit == 0 { | ||||
| 		cfg.CacheLimit = defaultCache | ||||
| 	} | ||||
| 	if cfg.ValidSchemes == nil { | ||||
| 		cfg.ValidSchemes = enode.ValidSchemes | ||||
| 	} | ||||
| 	if cfg.Resolver == nil { | ||||
| 		cfg.Resolver = new(net.Resolver) | ||||
| 	} | ||||
| 	if cfg.Logger == nil { | ||||
| 		cfg.Logger = log.Root() | ||||
| 	} | ||||
| 	return cfg | ||||
| } | ||||
| 
 | ||||
| // NewClient creates a client.
 | ||||
| func NewClient(cfg Config, urls ...string) (*Client, error) { | ||||
| 	c := &Client{ | ||||
| 		cfg:   cfg.withDefaults(), | ||||
| 		clock: mclock.System{}, | ||||
| 		trees: make(map[string]*clientTree), | ||||
| 	} | ||||
| 	var err error | ||||
| 	if c.entries, err = lru.New(c.cfg.CacheLimit); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	for _, url := range urls { | ||||
| 		if err := c.AddTree(url); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| // SyncTree downloads the entire node tree at the given URL. This doesn't add the tree for
 | ||||
| // later use, but any previously-synced entries are reused.
 | ||||
| func (c *Client) SyncTree(url string) (*Tree, error) { | ||||
| 	le, err := parseURL(url) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("invalid enrtree URL: %v", err) | ||||
| 	} | ||||
| 	ct := newClientTree(c, le) | ||||
| 	t := &Tree{entries: make(map[string]entry)} | ||||
| 	if err := ct.syncAll(t.entries); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	t.root = ct.root | ||||
| 	return t, nil | ||||
| } | ||||
| 
 | ||||
| // AddTree adds a enrtree:// URL to crawl.
 | ||||
| func (c *Client) AddTree(url string) error { | ||||
| 	le, err := parseURL(url) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("invalid enrtree URL: %v", err) | ||||
| 	} | ||||
| 	ct, err := c.ensureTree(le) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	c.linkCache.add(ct) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (c *Client) ensureTree(le *linkEntry) (*clientTree, error) { | ||||
| 	if tree, ok := c.trees[le.domain]; ok { | ||||
| 		if !tree.matchPubkey(le.pubkey) { | ||||
| 			return nil, fmt.Errorf("conflicting public keys for domain %q", le.domain) | ||||
| 		} | ||||
| 		return tree, nil | ||||
| 	} | ||||
| 	ct := newClientTree(c, le) | ||||
| 	c.trees[le.domain] = ct | ||||
| 	return ct, nil | ||||
| } | ||||
| 
 | ||||
| // RandomNode retrieves the next random node.
 | ||||
| func (c *Client) RandomNode(ctx context.Context) *enode.Node { | ||||
| 	for { | ||||
| 		ct := c.randomTree() | ||||
| 		if ct == nil { | ||||
| 			return nil | ||||
| 		} | ||||
| 		n, err := ct.syncRandom(ctx) | ||||
| 		if err != nil { | ||||
| 			if err == ctx.Err() { | ||||
| 				return nil // context canceled.
 | ||||
| 			} | ||||
| 			c.cfg.Logger.Debug("Error in DNS random node sync", "tree", ct.loc.domain, "err", err) | ||||
| 			continue | ||||
| 		} | ||||
| 		if n != nil { | ||||
| 			return n | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // randomTree returns a random tree.
 | ||||
| func (c *Client) randomTree() *clientTree { | ||||
| 	if !c.linkCache.valid() { | ||||
| 		c.gcTrees() | ||||
| 	} | ||||
| 	limit := rand.Intn(len(c.trees)) | ||||
| 	for _, ct := range c.trees { | ||||
| 		if limit == 0 { | ||||
| 			return ct | ||||
| 		} | ||||
| 		limit-- | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // gcTrees rebuilds the 'trees' map.
 | ||||
| func (c *Client) gcTrees() { | ||||
| 	trees := make(map[string]*clientTree) | ||||
| 	for t := range c.linkCache.all() { | ||||
| 		trees[t.loc.domain] = t | ||||
| 	} | ||||
| 	c.trees = trees | ||||
| } | ||||
| 
 | ||||
| // resolveRoot retrieves a root entry via DNS.
 | ||||
| func (c *Client) resolveRoot(ctx context.Context, loc *linkEntry) (rootEntry, error) { | ||||
| 	txts, err := c.cfg.Resolver.LookupTXT(ctx, loc.domain) | ||||
| 	c.cfg.Logger.Trace("Updating DNS discovery root", "tree", loc.domain, "err", err) | ||||
| 	if err != nil { | ||||
| 		return rootEntry{}, err | ||||
| 	} | ||||
| 	for _, txt := range txts { | ||||
| 		if strings.HasPrefix(txt, rootPrefix) { | ||||
| 			return parseAndVerifyRoot(txt, loc) | ||||
| 		} | ||||
| 	} | ||||
| 	return rootEntry{}, nameError{loc.domain, errNoRoot} | ||||
| } | ||||
| 
 | ||||
| func parseAndVerifyRoot(txt string, loc *linkEntry) (rootEntry, error) { | ||||
| 	e, err := parseRoot(txt) | ||||
| 	if err != nil { | ||||
| 		return e, err | ||||
| 	} | ||||
| 	if !e.verifySignature(loc.pubkey) { | ||||
| 		return e, entryError{typ: "root", err: errInvalidSig} | ||||
| 	} | ||||
| 	return e, nil | ||||
| } | ||||
| 
 | ||||
| // resolveEntry retrieves an entry from the cache or fetches it from the network
 | ||||
| // if it isn't cached.
 | ||||
| func (c *Client) resolveEntry(ctx context.Context, domain, hash string) (entry, error) { | ||||
| 	cacheKey := truncateHash(hash) | ||||
| 	if e, ok := c.entries.Get(cacheKey); ok { | ||||
| 		return e.(entry), nil | ||||
| 	} | ||||
| 	e, err := c.doResolveEntry(ctx, domain, hash) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c.entries.Add(cacheKey, e) | ||||
| 	return e, nil | ||||
| } | ||||
| 
 | ||||
| // doResolveEntry fetches an entry via DNS.
 | ||||
| func (c *Client) doResolveEntry(ctx context.Context, domain, hash string) (entry, error) { | ||||
| 	wantHash, err := b32format.DecodeString(hash) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("invalid base32 hash") | ||||
| 	} | ||||
| 	name := hash + "." + domain | ||||
| 	txts, err := c.cfg.Resolver.LookupTXT(ctx, hash+"."+domain) | ||||
| 	c.cfg.Logger.Trace("DNS discovery lookup", "name", name, "err", err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	for _, txt := range txts { | ||||
| 		e, err := parseEntry(txt, c.cfg.ValidSchemes) | ||||
| 		if err == errUnknownEntry { | ||||
| 			continue | ||||
| 		} | ||||
| 		if !bytes.HasPrefix(crypto.Keccak256([]byte(txt)), wantHash) { | ||||
| 			err = nameError{name, errHashMismatch} | ||||
| 		} else if err != nil { | ||||
| 			err = nameError{name, err} | ||||
| 		} | ||||
| 		return e, err | ||||
| 	} | ||||
| 	return nil, nameError{name, errNoEntry} | ||||
| } | ||||
							
								
								
									
										306
									
								
								p2p/dnsdisc/client_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								p2p/dnsdisc/client_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,306 @@ | ||||
| // Copyright 2018 The go-ethereum Authors
 | ||||
| // This file is part of the go-ethereum library.
 | ||||
| //
 | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU Lesser General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // The go-ethereum library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU Lesser General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU Lesser General Public License
 | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package dnsdisc | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/ecdsa" | ||||
| 	"math/rand" | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/davecgh/go-spew/spew" | ||||
| 	"github.com/ethereum/go-ethereum/common/mclock" | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| 	"github.com/ethereum/go-ethereum/internal/testlog" | ||||
| 	"github.com/ethereum/go-ethereum/log" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enode" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enr" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	signingKeySeed = 0x111111 | ||||
| 	nodesSeed1     = 0x2945237 | ||||
| 	nodesSeed2     = 0x4567299 | ||||
| ) | ||||
| 
 | ||||
| func TestClientSyncTree(t *testing.T) { | ||||
| 	r := mapResolver{ | ||||
| 		"3CA2MBMUQ55ZCT74YEEQLANJDI.n": "enr=-HW4QAggRauloj2SDLtIHN1XBkvhFZ1vtf1raYQp9TBW2RD5EEawDzbtSmlXUfnaHcvwOizhVYLtr7e6vw7NAf6mTuoCgmlkgnY0iXNlY3AyNTZrMaECjrXI8TLNXU0f8cthpAMxEshUyQlK-AM0PW2wfrnacNI=", | ||||
| 		"53HBTPGGZ4I76UEPCNQGZWIPTQ.n": "enr=-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA=", | ||||
| 		"BG7SVUBUAJ3UAWD2ATEBLMRNEE.n": "enrtree=53HBTPGGZ4I76UEPCNQGZWIPTQ,3CA2MBMUQ55ZCT74YEEQLANJDI,HNHR6UTVZF5TJKK3FV27ZI76P4", | ||||
| 		"HNHR6UTVZF5TJKK3FV27ZI76P4.n": "enr=-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o=", | ||||
| 		"JGUFMSAGI7KZYB3P7IZW4S5Y3A.n": "enrtree-link=AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org", | ||||
| 		"n":                            "enrtree-root=v1 e=BG7SVUBUAJ3UAWD2ATEBLMRNEE l=JGUFMSAGI7KZYB3P7IZW4S5Y3A seq=1 sig=gacuU0nTy9duIdu1IFDyF5Lv9CFHqHiNcj91n0frw70tZo3tZZsCVkE3j1ILYyVOHRLWGBmawo_SEkThZ9PgcQE=", | ||||
| 	} | ||||
| 	var ( | ||||
| 		wantNodes = testNodes(0x29452, 3) | ||||
| 		wantLinks = []string{"enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org"} | ||||
| 		wantSeq   = uint(1) | ||||
| 	) | ||||
| 
 | ||||
| 	c, _ := NewClient(Config{Resolver: r, Logger: testlog.Logger(t, log.LvlTrace)}) | ||||
| 	stree, err := c.SyncTree("enrtree://AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@n") | ||||
| 	if err != nil { | ||||
| 		t.Fatal("sync error:", err) | ||||
| 	} | ||||
| 	if !reflect.DeepEqual(sortByID(stree.Nodes()), sortByID(wantNodes)) { | ||||
| 		t.Errorf("wrong nodes in synced tree:\nhave %v\nwant %v", spew.Sdump(stree.Nodes()), spew.Sdump(wantNodes)) | ||||
| 	} | ||||
| 	if !reflect.DeepEqual(stree.Links(), wantLinks) { | ||||
| 		t.Errorf("wrong links in synced tree: %v", stree.Links()) | ||||
| 	} | ||||
| 	if stree.Seq() != wantSeq { | ||||
| 		t.Errorf("synced tree has wrong seq: %d", stree.Seq()) | ||||
| 	} | ||||
| 	if len(c.trees) > 0 { | ||||
| 		t.Errorf("tree from SyncTree added to client") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // In this test, syncing the tree fails because it contains an invalid ENR entry.
 | ||||
| func TestClientSyncTreeBadNode(t *testing.T) { | ||||
| 	r := mapResolver{ | ||||
| 		"n":                            "enrtree-root=v1 e=ZFJZDQKSOMJRYYQSZKJZC54HCF l=JGUFMSAGI7KZYB3P7IZW4S5Y3A seq=3 sig=WEy8JTZ2dHmXM2qeBZ7D2ECK7SGbnurl1ge_S_5GQBAqnADk0gLTcg8Lm5QNqLHZjJKGAb443p996idlMcBqEQA=", | ||||
| 		"JGUFMSAGI7KZYB3P7IZW4S5Y3A.n": "enrtree-link=AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org", | ||||
| 		"ZFJZDQKSOMJRYYQSZKJZC54HCF.n": "enr=gggggggggggggg=", | ||||
| 	} | ||||
| 
 | ||||
| 	c, _ := NewClient(Config{Resolver: r, Logger: testlog.Logger(t, log.LvlTrace)}) | ||||
| 	_, err := c.SyncTree("enrtree://APFGGTFOBVE2ZNAB3CSMNNX6RRK3ODIRLP2AA5U4YFAA6MSYZUYTQ@n") | ||||
| 	wantErr := nameError{name: "ZFJZDQKSOMJRYYQSZKJZC54HCF.n", err: entryError{typ: "enr", err: errInvalidENR}} | ||||
| 	if err != wantErr { | ||||
| 		t.Fatalf("expected sync error %q, got %q", wantErr, err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // This test checks that RandomNode hits all entries.
 | ||||
| func TestClientRandomNode(t *testing.T) { | ||||
| 	nodes := testNodes(nodesSeed1, 30) | ||||
| 	tree, url := makeTestTree("n", nodes, nil) | ||||
| 	r := mapResolver(tree.ToTXT("n")) | ||||
| 	c, _ := NewClient(Config{Resolver: r, Logger: testlog.Logger(t, log.LvlTrace)}) | ||||
| 	if err := c.AddTree(url); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	checkRandomNode(t, c, nodes) | ||||
| } | ||||
| 
 | ||||
| // This test checks that RandomNode traverses linked trees as well as explicitly added trees.
 | ||||
| func TestClientRandomNodeLinks(t *testing.T) { | ||||
| 	nodes := testNodes(nodesSeed1, 40) | ||||
| 	tree1, url1 := makeTestTree("t1", nodes[:10], nil) | ||||
| 	tree2, url2 := makeTestTree("t2", nodes[10:], []string{url1}) | ||||
| 	cfg := Config{ | ||||
| 		Resolver: newMapResolver(tree1.ToTXT("t1"), tree2.ToTXT("t2")), | ||||
| 		Logger:   testlog.Logger(t, log.LvlTrace), | ||||
| 	} | ||||
| 	c, _ := NewClient(cfg) | ||||
| 	if err := c.AddTree(url2); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	checkRandomNode(t, c, nodes) | ||||
| } | ||||
| 
 | ||||
| // This test verifies that RandomNode re-checks the root of the tree to catch
 | ||||
| // updates to nodes.
 | ||||
| func TestClientRandomNodeUpdates(t *testing.T) { | ||||
| 	var ( | ||||
| 		clock    = new(mclock.Simulated) | ||||
| 		nodes    = testNodes(nodesSeed1, 30) | ||||
| 		resolver = newMapResolver() | ||||
| 		cfg      = Config{ | ||||
| 			Resolver:        resolver, | ||||
| 			Logger:          testlog.Logger(t, log.LvlTrace), | ||||
| 			RecheckInterval: 20 * time.Minute, | ||||
| 		} | ||||
| 		c, _ = NewClient(cfg) | ||||
| 	) | ||||
| 	c.clock = clock | ||||
| 	tree1, url := makeTestTree("n", nodes[:25], nil) | ||||
| 
 | ||||
| 	// Sync the original tree.
 | ||||
| 	resolver.add(tree1.ToTXT("n")) | ||||
| 	c.AddTree(url) | ||||
| 	checkRandomNode(t, c, nodes[:25]) | ||||
| 
 | ||||
| 	// Update some nodes and ensure RandomNode returns the new nodes as well.
 | ||||
| 	keys := testKeys(nodesSeed1, len(nodes)) | ||||
| 	for i, n := range nodes[:len(nodes)/2] { | ||||
| 		r := n.Record() | ||||
| 		r.Set(enr.IP{127, 0, 0, 1}) | ||||
| 		r.SetSeq(55) | ||||
| 		enode.SignV4(r, keys[i]) | ||||
| 		n2, _ := enode.New(enode.ValidSchemes, r) | ||||
| 		nodes[i] = n2 | ||||
| 	} | ||||
| 	tree2, _ := makeTestTree("n", nodes, nil) | ||||
| 	clock.Run(cfg.RecheckInterval + 1*time.Second) | ||||
| 	resolver.clear() | ||||
| 	resolver.add(tree2.ToTXT("n")) | ||||
| 	checkRandomNode(t, c, nodes) | ||||
| } | ||||
| 
 | ||||
| // This test verifies that RandomNode re-checks the root of the tree to catch
 | ||||
| // updates to links.
 | ||||
| func TestClientRandomNodeLinkUpdates(t *testing.T) { | ||||
| 	var ( | ||||
| 		clock    = new(mclock.Simulated) | ||||
| 		nodes    = testNodes(nodesSeed1, 30) | ||||
| 		resolver = newMapResolver() | ||||
| 		cfg      = Config{ | ||||
| 			Resolver:        resolver, | ||||
| 			Logger:          testlog.Logger(t, log.LvlTrace), | ||||
| 			RecheckInterval: 20 * time.Minute, | ||||
| 		} | ||||
| 		c, _ = NewClient(cfg) | ||||
| 	) | ||||
| 	c.clock = clock | ||||
| 	tree3, url3 := makeTestTree("t3", nodes[20:30], nil) | ||||
| 	tree2, url2 := makeTestTree("t2", nodes[10:20], nil) | ||||
| 	tree1, url1 := makeTestTree("t1", nodes[0:10], []string{url2}) | ||||
| 	resolver.add(tree1.ToTXT("t1")) | ||||
| 	resolver.add(tree2.ToTXT("t2")) | ||||
| 	resolver.add(tree3.ToTXT("t3")) | ||||
| 
 | ||||
| 	// Sync tree1 using RandomNode.
 | ||||
| 	c.AddTree(url1) | ||||
| 	checkRandomNode(t, c, nodes[:20]) | ||||
| 
 | ||||
| 	// Add link to tree3, remove link to tree2.
 | ||||
| 	tree1, _ = makeTestTree("t1", nodes[:10], []string{url3}) | ||||
| 	resolver.add(tree1.ToTXT("t1")) | ||||
| 	clock.Run(cfg.RecheckInterval + 1*time.Second) | ||||
| 	t.Log("tree1 updated") | ||||
| 
 | ||||
| 	var wantNodes []*enode.Node | ||||
| 	wantNodes = append(wantNodes, tree1.Nodes()...) | ||||
| 	wantNodes = append(wantNodes, tree3.Nodes()...) | ||||
| 	checkRandomNode(t, c, wantNodes) | ||||
| 
 | ||||
| 	// Check that linked trees are GCed when they're no longer referenced.
 | ||||
| 	if len(c.trees) != 2 { | ||||
| 		t.Errorf("client knows %d trees, want 2", len(c.trees)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func checkRandomNode(t *testing.T, c *Client, wantNodes []*enode.Node) { | ||||
| 	t.Helper() | ||||
| 
 | ||||
| 	var ( | ||||
| 		want     = make(map[enode.ID]*enode.Node) | ||||
| 		maxCalls = len(wantNodes) * 2 | ||||
| 		calls    = 0 | ||||
| 		ctx      = context.Background() | ||||
| 	) | ||||
| 	for _, n := range wantNodes { | ||||
| 		want[n.ID()] = n | ||||
| 	} | ||||
| 	for ; len(want) > 0 && calls < maxCalls; calls++ { | ||||
| 		n := c.RandomNode(ctx) | ||||
| 		if n == nil { | ||||
| 			t.Fatalf("RandomNode returned nil (call %d)", calls) | ||||
| 		} | ||||
| 		delete(want, n.ID()) | ||||
| 	} | ||||
| 	t.Logf("checkRandomNode called RandomNode %d times to find %d nodes", calls, len(wantNodes)) | ||||
| 	for _, n := range want { | ||||
| 		t.Errorf("RandomNode didn't discover node %v", n.ID()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func makeTestTree(domain string, nodes []*enode.Node, links []string) (*Tree, string) { | ||||
| 	tree, err := MakeTree(1, nodes, links) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	url, err := tree.Sign(testKey(signingKeySeed), domain) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return tree, url | ||||
| } | ||||
| 
 | ||||
| // testKeys creates deterministic private keys for testing.
 | ||||
| func testKeys(seed int64, n int) []*ecdsa.PrivateKey { | ||||
| 	rand := rand.New(rand.NewSource(seed)) | ||||
| 	keys := make([]*ecdsa.PrivateKey, n) | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		key, err := ecdsa.GenerateKey(crypto.S256(), rand) | ||||
| 		if err != nil { | ||||
| 			panic("can't generate key: " + err.Error()) | ||||
| 		} | ||||
| 		keys[i] = key | ||||
| 	} | ||||
| 	return keys | ||||
| } | ||||
| 
 | ||||
| func testKey(seed int64) *ecdsa.PrivateKey { | ||||
| 	return testKeys(seed, 1)[0] | ||||
| } | ||||
| 
 | ||||
| func testNodes(seed int64, n int) []*enode.Node { | ||||
| 	keys := testKeys(seed, n) | ||||
| 	nodes := make([]*enode.Node, n) | ||||
| 	for i, key := range keys { | ||||
| 		record := new(enr.Record) | ||||
| 		record.SetSeq(uint64(i)) | ||||
| 		enode.SignV4(record, key) | ||||
| 		n, err := enode.New(enode.ValidSchemes, record) | ||||
| 		if err != nil { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 		nodes[i] = n | ||||
| 	} | ||||
| 	return nodes | ||||
| } | ||||
| 
 | ||||
| func testNode(seed int64) *enode.Node { | ||||
| 	return testNodes(seed, 1)[0] | ||||
| } | ||||
| 
 | ||||
| type mapResolver map[string]string | ||||
| 
 | ||||
| func newMapResolver(maps ...map[string]string) mapResolver { | ||||
| 	mr := make(mapResolver) | ||||
| 	for _, m := range maps { | ||||
| 		mr.add(m) | ||||
| 	} | ||||
| 	return mr | ||||
| } | ||||
| 
 | ||||
| func (mr mapResolver) clear() { | ||||
| 	for k := range mr { | ||||
| 		delete(mr, k) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (mr mapResolver) add(m map[string]string) { | ||||
| 	for k, v := range m { | ||||
| 		mr[k] = v | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (mr mapResolver) LookupTXT(ctx context.Context, name string) ([]string, error) { | ||||
| 	if record, ok := mr[name]; ok { | ||||
| 		return []string{record}, nil | ||||
| 	} | ||||
| 	return nil, nil | ||||
| } | ||||
							
								
								
									
										18
									
								
								p2p/dnsdisc/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								p2p/dnsdisc/doc.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| // Copyright 2018 The go-ethereum Authors
 | ||||
| // This file is part of the go-ethereum library.
 | ||||
| //
 | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU Lesser General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // The go-ethereum library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU Lesser General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU Lesser General Public License
 | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| // Package dnsdisc implements node discovery via DNS (EIP-1459).
 | ||||
| package dnsdisc | ||||
							
								
								
									
										63
									
								
								p2p/dnsdisc/error.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								p2p/dnsdisc/error.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | ||||
| // Copyright 2018 The go-ethereum Authors
 | ||||
| // This file is part of the go-ethereum library.
 | ||||
| //
 | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU Lesser General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // The go-ethereum library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU Lesser General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU Lesser General Public License
 | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package dnsdisc | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| ) | ||||
| 
 | ||||
| // Entry parse errors.
 | ||||
| var ( | ||||
| 	errUnknownEntry = errors.New("unknown entry type") | ||||
| 	errNoPubkey     = errors.New("missing public key") | ||||
| 	errBadPubkey    = errors.New("invalid public key") | ||||
| 	errInvalidENR   = errors.New("invalid node record") | ||||
| 	errInvalidChild = errors.New("invalid child hash") | ||||
| 	errInvalidSig   = errors.New("invalid base64 signature") | ||||
| 	errSyntax       = errors.New("invalid syntax") | ||||
| ) | ||||
| 
 | ||||
| // Resolver/sync errors
 | ||||
| var ( | ||||
| 	errNoRoot        = errors.New("no valid root found") | ||||
| 	errNoEntry       = errors.New("no valid tree entry found") | ||||
| 	errHashMismatch  = errors.New("hash mismatch") | ||||
| 	errENRInLinkTree = errors.New("enr entry in link tree") | ||||
| 	errLinkInENRTree = errors.New("link entry in ENR tree") | ||||
| ) | ||||
| 
 | ||||
| type nameError struct { | ||||
| 	name string | ||||
| 	err  error | ||||
| } | ||||
| 
 | ||||
| func (err nameError) Error() string { | ||||
| 	if ee, ok := err.err.(entryError); ok { | ||||
| 		return fmt.Sprintf("invalid %s entry at %s: %v", ee.typ, err.name, ee.err) | ||||
| 	} | ||||
| 	return err.name + ": " + err.err.Error() | ||||
| } | ||||
| 
 | ||||
| type entryError struct { | ||||
| 	typ string | ||||
| 	err error | ||||
| } | ||||
| 
 | ||||
| func (err entryError) Error() string { | ||||
| 	return fmt.Sprintf("invalid %s entry: %v", err.typ, err.err) | ||||
| } | ||||
							
								
								
									
										277
									
								
								p2p/dnsdisc/sync.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								p2p/dnsdisc/sync.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,277 @@ | ||||
| // Copyright 2019 The go-ethereum Authors
 | ||||
| // This file is part of the go-ethereum library.
 | ||||
| //
 | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU Lesser General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // The go-ethereum library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU Lesser General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU Lesser General Public License
 | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package dnsdisc | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/ecdsa" | ||||
| 	"math/rand" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/common/mclock" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enode" | ||||
| ) | ||||
| 
 | ||||
| // clientTree is a full tree being synced.
 | ||||
| type clientTree struct { | ||||
| 	c             *Client | ||||
| 	loc           *linkEntry | ||||
| 	root          *rootEntry | ||||
| 	lastRootCheck mclock.AbsTime // last revalidation of root
 | ||||
| 	enrs          *subtreeSync | ||||
| 	links         *subtreeSync | ||||
| 	linkCache     linkCache | ||||
| } | ||||
| 
 | ||||
| func newClientTree(c *Client, loc *linkEntry) *clientTree { | ||||
| 	ct := &clientTree{c: c, loc: loc} | ||||
| 	ct.linkCache.self = ct | ||||
| 	return ct | ||||
| } | ||||
| 
 | ||||
| func (ct *clientTree) matchPubkey(key *ecdsa.PublicKey) bool { | ||||
| 	return keysEqual(ct.loc.pubkey, key) | ||||
| } | ||||
| 
 | ||||
| func keysEqual(k1, k2 *ecdsa.PublicKey) bool { | ||||
| 	return k1.Curve == k2.Curve && k1.X.Cmp(k2.X) == 0 && k1.Y.Cmp(k2.Y) == 0 | ||||
| } | ||||
| 
 | ||||
| // syncAll retrieves all entries of the tree.
 | ||||
| func (ct *clientTree) syncAll(dest map[string]entry) error { | ||||
| 	if err := ct.updateRoot(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := ct.links.resolveAll(dest); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := ct.enrs.resolveAll(dest); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // syncRandom retrieves a single entry of the tree. The Node return value
 | ||||
| // is non-nil if the entry was a node.
 | ||||
| func (ct *clientTree) syncRandom(ctx context.Context) (*enode.Node, error) { | ||||
| 	if ct.rootUpdateDue() { | ||||
| 		if err := ct.updateRoot(); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	// Link tree sync has priority, run it to completion before syncing ENRs.
 | ||||
| 	if !ct.links.done() { | ||||
| 		err := ct.syncNextLink(ctx) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Sync next random entry in ENR tree. Once every node has been visited, we simply
 | ||||
| 	// start over. This is fine because entries are cached.
 | ||||
| 	if ct.enrs.done() { | ||||
| 		ct.enrs = newSubtreeSync(ct.c, ct.loc, ct.root.eroot, false) | ||||
| 	} | ||||
| 	return ct.syncNextRandomENR(ctx) | ||||
| } | ||||
| 
 | ||||
| func (ct *clientTree) syncNextLink(ctx context.Context) error { | ||||
| 	hash := ct.links.missing[0] | ||||
| 	e, err := ct.links.resolveNext(ctx, hash) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	ct.links.missing = ct.links.missing[1:] | ||||
| 
 | ||||
| 	if le, ok := e.(*linkEntry); ok { | ||||
| 		lt, err := ct.c.ensureTree(le) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		ct.linkCache.add(lt) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (ct *clientTree) syncNextRandomENR(ctx context.Context) (*enode.Node, error) { | ||||
| 	index := rand.Intn(len(ct.enrs.missing)) | ||||
| 	hash := ct.enrs.missing[index] | ||||
| 	e, err := ct.enrs.resolveNext(ctx, hash) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	ct.enrs.missing = removeHash(ct.enrs.missing, index) | ||||
| 	if ee, ok := e.(*enrEntry); ok { | ||||
| 		return ee.node, nil | ||||
| 	} | ||||
| 	return nil, nil | ||||
| } | ||||
| 
 | ||||
| func (ct *clientTree) String() string { | ||||
| 	return ct.loc.url() | ||||
| } | ||||
| 
 | ||||
| // removeHash removes the element at index from h.
 | ||||
| func removeHash(h []string, index int) []string { | ||||
| 	if len(h) == 1 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	last := len(h) - 1 | ||||
| 	if index < last { | ||||
| 		h[index] = h[last] | ||||
| 		h[last] = "" | ||||
| 	} | ||||
| 	return h[:last] | ||||
| } | ||||
| 
 | ||||
| // updateRoot ensures that the given tree has an up-to-date root.
 | ||||
| func (ct *clientTree) updateRoot() error { | ||||
| 	ct.lastRootCheck = ct.c.clock.Now() | ||||
| 	ctx, cancel := context.WithTimeout(context.Background(), ct.c.cfg.Timeout) | ||||
| 	defer cancel() | ||||
| 	root, err := ct.c.resolveRoot(ctx, ct.loc) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	ct.root = &root | ||||
| 
 | ||||
| 	// Invalidate subtrees if changed.
 | ||||
| 	if ct.links == nil || root.lroot != ct.links.root { | ||||
| 		ct.links = newSubtreeSync(ct.c, ct.loc, root.lroot, true) | ||||
| 		ct.linkCache.reset() | ||||
| 	} | ||||
| 	if ct.enrs == nil || root.eroot != ct.enrs.root { | ||||
| 		ct.enrs = newSubtreeSync(ct.c, ct.loc, root.eroot, false) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // rootUpdateDue returns true when a root update is needed.
 | ||||
| func (ct *clientTree) rootUpdateDue() bool { | ||||
| 	return ct.root == nil || time.Duration(ct.c.clock.Now()-ct.lastRootCheck) > ct.c.cfg.RecheckInterval | ||||
| } | ||||
| 
 | ||||
| // subtreeSync is the sync of an ENR or link subtree.
 | ||||
| type subtreeSync struct { | ||||
| 	c       *Client | ||||
| 	loc     *linkEntry | ||||
| 	root    string | ||||
| 	missing []string // missing tree node hashes
 | ||||
| 	link    bool     // true if this sync is for the link tree
 | ||||
| } | ||||
| 
 | ||||
| func newSubtreeSync(c *Client, loc *linkEntry, root string, link bool) *subtreeSync { | ||||
| 	return &subtreeSync{c, loc, root, []string{root}, link} | ||||
| } | ||||
| 
 | ||||
| func (ts *subtreeSync) done() bool { | ||||
| 	return len(ts.missing) == 0 | ||||
| } | ||||
| 
 | ||||
| func (ts *subtreeSync) resolveAll(dest map[string]entry) error { | ||||
| 	for !ts.done() { | ||||
| 		hash := ts.missing[0] | ||||
| 		ctx, cancel := context.WithTimeout(context.Background(), ts.c.cfg.Timeout) | ||||
| 		e, err := ts.resolveNext(ctx, hash) | ||||
| 		cancel() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		dest[hash] = e | ||||
| 		ts.missing = ts.missing[1:] | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (ts *subtreeSync) resolveNext(ctx context.Context, hash string) (entry, error) { | ||||
| 	e, err := ts.c.resolveEntry(ctx, ts.loc.domain, hash) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	switch e := e.(type) { | ||||
| 	case *enrEntry: | ||||
| 		if ts.link { | ||||
| 			return nil, errENRInLinkTree | ||||
| 		} | ||||
| 	case *linkEntry: | ||||
| 		if !ts.link { | ||||
| 			return nil, errLinkInENRTree | ||||
| 		} | ||||
| 	case *subtreeEntry: | ||||
| 		ts.missing = append(ts.missing, e.children...) | ||||
| 	} | ||||
| 	return e, nil | ||||
| } | ||||
| 
 | ||||
| // linkCache tracks the links of a tree.
 | ||||
| type linkCache struct { | ||||
| 	self    *clientTree | ||||
| 	directM map[*clientTree]struct{} // direct links
 | ||||
| 	allM    map[*clientTree]struct{} // direct & transitive links
 | ||||
| } | ||||
| 
 | ||||
| // reset clears the cache.
 | ||||
| func (lc *linkCache) reset() { | ||||
| 	lc.directM = nil | ||||
| 	lc.allM = nil | ||||
| } | ||||
| 
 | ||||
| // add adds a direct link to the cache.
 | ||||
| func (lc *linkCache) add(ct *clientTree) { | ||||
| 	if lc.directM == nil { | ||||
| 		lc.directM = make(map[*clientTree]struct{}) | ||||
| 	} | ||||
| 	if _, ok := lc.directM[ct]; !ok { | ||||
| 		lc.invalidate() | ||||
| 	} | ||||
| 	lc.directM[ct] = struct{}{} | ||||
| } | ||||
| 
 | ||||
| // invalidate resets the cache of transitive links.
 | ||||
| func (lc *linkCache) invalidate() { | ||||
| 	lc.allM = nil | ||||
| } | ||||
| 
 | ||||
| // valid returns true when the cache of transitive links is up-to-date.
 | ||||
| func (lc *linkCache) valid() bool { | ||||
| 	// Re-check validity of child caches to catch updates.
 | ||||
| 	for ct := range lc.allM { | ||||
| 		if ct != lc.self && !ct.linkCache.valid() { | ||||
| 			lc.allM = nil | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return lc.allM != nil | ||||
| } | ||||
| 
 | ||||
| // all returns all trees reachable through the cache.
 | ||||
| func (lc *linkCache) all() map[*clientTree]struct{} { | ||||
| 	if lc.valid() { | ||||
| 		return lc.allM | ||||
| 	} | ||||
| 	// Remake lc.allM it by taking the union of all() across children.
 | ||||
| 	m := make(map[*clientTree]struct{}) | ||||
| 	if lc.self != nil { | ||||
| 		m[lc.self] = struct{}{} | ||||
| 	} | ||||
| 	for ct := range lc.directM { | ||||
| 		m[ct] = struct{}{} | ||||
| 		for lt := range ct.linkCache.all() { | ||||
| 			m[lt] = struct{}{} | ||||
| 		} | ||||
| 	} | ||||
| 	lc.allM = m | ||||
| 	return m | ||||
| } | ||||
							
								
								
									
										384
									
								
								p2p/dnsdisc/tree.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								p2p/dnsdisc/tree.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,384 @@ | ||||
| // Copyright 2018 The go-ethereum Authors
 | ||||
| // This file is part of the go-ethereum library.
 | ||||
| //
 | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU Lesser General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // The go-ethereum library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU Lesser General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU Lesser General Public License
 | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package dnsdisc | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/ecdsa" | ||||
| 	"encoding/base32" | ||||
| 	"encoding/base64" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enode" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enr" | ||||
| 	"github.com/ethereum/go-ethereum/rlp" | ||||
| 	"golang.org/x/crypto/sha3" | ||||
| ) | ||||
| 
 | ||||
| // Tree is a merkle tree of node records.
 | ||||
| type Tree struct { | ||||
| 	root    *rootEntry | ||||
| 	entries map[string]entry | ||||
| } | ||||
| 
 | ||||
| // Sign signs the tree with the given private key and sets the sequence number.
 | ||||
| func (t *Tree) Sign(key *ecdsa.PrivateKey, domain string) (url string, err error) { | ||||
| 	root := *t.root | ||||
| 	sig, err := crypto.Sign(root.sigHash(), key) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	root.sig = sig | ||||
| 	t.root = &root | ||||
| 	link := &linkEntry{domain, &key.PublicKey} | ||||
| 	return link.url(), nil | ||||
| } | ||||
| 
 | ||||
| // SetSignature verifies the given signature and assigns it as the tree's current
 | ||||
| // signature if valid.
 | ||||
| func (t *Tree) SetSignature(pubkey *ecdsa.PublicKey, signature string) error { | ||||
| 	sig, err := b64format.DecodeString(signature) | ||||
| 	if err != nil || len(sig) != crypto.SignatureLength { | ||||
| 		return errInvalidSig | ||||
| 	} | ||||
| 	root := *t.root | ||||
| 	root.sig = sig | ||||
| 	if !root.verifySignature(pubkey) { | ||||
| 		return errInvalidSig | ||||
| 	} | ||||
| 	t.root = &root | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Seq returns the sequence number of the tree.
 | ||||
| func (t *Tree) Seq() uint { | ||||
| 	return t.root.seq | ||||
| } | ||||
| 
 | ||||
| // Signature returns the signature of the tree.
 | ||||
| func (t *Tree) Signature() string { | ||||
| 	return b64format.EncodeToString(t.root.sig) | ||||
| } | ||||
| 
 | ||||
| // ToTXT returns all DNS TXT records required for the tree.
 | ||||
| func (t *Tree) ToTXT(domain string) map[string]string { | ||||
| 	records := map[string]string{domain: t.root.String()} | ||||
| 	for _, e := range t.entries { | ||||
| 		sd := subdomain(e) | ||||
| 		if domain != "" { | ||||
| 			sd = sd + "." + domain | ||||
| 		} | ||||
| 		records[sd] = e.String() | ||||
| 	} | ||||
| 	return records | ||||
| } | ||||
| 
 | ||||
| // Links returns all links contained in the tree.
 | ||||
| func (t *Tree) Links() []string { | ||||
| 	var links []string | ||||
| 	for _, e := range t.entries { | ||||
| 		if le, ok := e.(*linkEntry); ok { | ||||
| 			links = append(links, le.url()) | ||||
| 		} | ||||
| 	} | ||||
| 	return links | ||||
| } | ||||
| 
 | ||||
| // Nodes returns all nodes contained in the tree.
 | ||||
| func (t *Tree) Nodes() []*enode.Node { | ||||
| 	var nodes []*enode.Node | ||||
| 	for _, e := range t.entries { | ||||
| 		if ee, ok := e.(*enrEntry); ok { | ||||
| 			nodes = append(nodes, ee.node) | ||||
| 		} | ||||
| 	} | ||||
| 	return nodes | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	hashAbbrev    = 16 | ||||
| 	maxChildren   = 300 / (hashAbbrev * (13 / 8)) | ||||
| 	minHashLength = 12 | ||||
| 	rootPrefix    = "enrtree-root=v1" | ||||
| ) | ||||
| 
 | ||||
| // MakeTree creates a tree containing the given nodes and links.
 | ||||
| func MakeTree(seq uint, nodes []*enode.Node, links []string) (*Tree, error) { | ||||
| 	// Sort records by ID and ensure all nodes have a valid record.
 | ||||
| 	records := make([]*enode.Node, len(nodes)) | ||||
| 	copy(records, nodes) | ||||
| 	sortByID(records) | ||||
| 	for _, n := range records { | ||||
| 		if len(n.Record().Signature()) == 0 { | ||||
| 			return nil, fmt.Errorf("can't add node %v: unsigned node record", n.ID()) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Create the leaf list.
 | ||||
| 	enrEntries := make([]entry, len(records)) | ||||
| 	for i, r := range records { | ||||
| 		enrEntries[i] = &enrEntry{r} | ||||
| 	} | ||||
| 	linkEntries := make([]entry, len(links)) | ||||
| 	for i, l := range links { | ||||
| 		le, err := parseURL(l) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		linkEntries[i] = le | ||||
| 	} | ||||
| 
 | ||||
| 	// Create intermediate nodes.
 | ||||
| 	t := &Tree{entries: make(map[string]entry)} | ||||
| 	eroot := t.build(enrEntries) | ||||
| 	t.entries[subdomain(eroot)] = eroot | ||||
| 	lroot := t.build(linkEntries) | ||||
| 	t.entries[subdomain(lroot)] = lroot | ||||
| 	t.root = &rootEntry{seq: seq, eroot: subdomain(eroot), lroot: subdomain(lroot)} | ||||
| 	return t, nil | ||||
| } | ||||
| 
 | ||||
| func (t *Tree) build(entries []entry) entry { | ||||
| 	if len(entries) == 1 { | ||||
| 		return entries[0] | ||||
| 	} | ||||
| 	if len(entries) <= maxChildren { | ||||
| 		hashes := make([]string, len(entries)) | ||||
| 		for i, e := range entries { | ||||
| 			hashes[i] = subdomain(e) | ||||
| 			t.entries[hashes[i]] = e | ||||
| 		} | ||||
| 		return &subtreeEntry{hashes} | ||||
| 	} | ||||
| 	var subtrees []entry | ||||
| 	for len(entries) > 0 { | ||||
| 		n := maxChildren | ||||
| 		if len(entries) < n { | ||||
| 			n = len(entries) | ||||
| 		} | ||||
| 		sub := t.build(entries[:n]) | ||||
| 		entries = entries[n:] | ||||
| 		subtrees = append(subtrees, sub) | ||||
| 		t.entries[subdomain(sub)] = sub | ||||
| 	} | ||||
| 	return t.build(subtrees) | ||||
| } | ||||
| 
 | ||||
| func sortByID(nodes []*enode.Node) []*enode.Node { | ||||
| 	sort.Slice(nodes, func(i, j int) bool { | ||||
| 		return bytes.Compare(nodes[i].ID().Bytes(), nodes[j].ID().Bytes()) < 0 | ||||
| 	}) | ||||
| 	return nodes | ||||
| } | ||||
| 
 | ||||
| // Entry Types
 | ||||
| 
 | ||||
| type entry interface { | ||||
| 	fmt.Stringer | ||||
| } | ||||
| 
 | ||||
| type ( | ||||
| 	rootEntry struct { | ||||
| 		eroot string | ||||
| 		lroot string | ||||
| 		seq   uint | ||||
| 		sig   []byte | ||||
| 	} | ||||
| 	subtreeEntry struct { | ||||
| 		children []string | ||||
| 	} | ||||
| 	enrEntry struct { | ||||
| 		node *enode.Node | ||||
| 	} | ||||
| 	linkEntry struct { | ||||
| 		domain string | ||||
| 		pubkey *ecdsa.PublicKey | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| // Entry Encoding
 | ||||
| 
 | ||||
| var ( | ||||
| 	b32format = base32.StdEncoding.WithPadding(base32.NoPadding) | ||||
| 	b64format = base64.URLEncoding | ||||
| ) | ||||
| 
 | ||||
| func subdomain(e entry) string { | ||||
| 	h := sha3.NewLegacyKeccak256() | ||||
| 	io.WriteString(h, e.String()) | ||||
| 	return b32format.EncodeToString(h.Sum(nil)[:16]) | ||||
| } | ||||
| 
 | ||||
| func (e *rootEntry) String() string { | ||||
| 	return fmt.Sprintf(rootPrefix+" e=%s l=%s seq=%d sig=%s", e.eroot, e.lroot, e.seq, b64format.EncodeToString(e.sig)) | ||||
| } | ||||
| 
 | ||||
| func (e *rootEntry) sigHash() []byte { | ||||
| 	h := sha3.NewLegacyKeccak256() | ||||
| 	fmt.Fprintf(h, rootPrefix+" e=%s l=%s seq=%d", e.eroot, e.lroot, e.seq) | ||||
| 	return h.Sum(nil) | ||||
| } | ||||
| 
 | ||||
| func (e *rootEntry) verifySignature(pubkey *ecdsa.PublicKey) bool { | ||||
| 	sig := e.sig[:crypto.RecoveryIDOffset] // remove recovery id
 | ||||
| 	return crypto.VerifySignature(crypto.FromECDSAPub(pubkey), e.sigHash(), sig) | ||||
| } | ||||
| 
 | ||||
| func (e *subtreeEntry) String() string { | ||||
| 	return "enrtree=" + strings.Join(e.children, ",") | ||||
| } | ||||
| 
 | ||||
| func (e *enrEntry) String() string { | ||||
| 	enc, _ := rlp.EncodeToBytes(e.node.Record()) | ||||
| 	return "enr=" + b64format.EncodeToString(enc) | ||||
| } | ||||
| 
 | ||||
| func (e *linkEntry) String() string { | ||||
| 	return "enrtree-link=" + e.link() | ||||
| } | ||||
| 
 | ||||
| func (e *linkEntry) url() string { | ||||
| 	return "enrtree://" + e.link() | ||||
| } | ||||
| 
 | ||||
| func (e *linkEntry) link() string { | ||||
| 	return fmt.Sprintf("%s@%s", b32format.EncodeToString(crypto.CompressPubkey(e.pubkey)), e.domain) | ||||
| } | ||||
| 
 | ||||
| // Entry Parsing
 | ||||
| 
 | ||||
| func parseEntry(e string, validSchemes enr.IdentityScheme) (entry, error) { | ||||
| 	switch { | ||||
| 	case strings.HasPrefix(e, "enrtree-link="): | ||||
| 		return parseLink(e[13:]) | ||||
| 	case strings.HasPrefix(e, "enrtree="): | ||||
| 		return parseSubtree(e[8:]) | ||||
| 	case strings.HasPrefix(e, "enr="): | ||||
| 		return parseENR(e[4:], validSchemes) | ||||
| 	default: | ||||
| 		return nil, errUnknownEntry | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func parseRoot(e string) (rootEntry, error) { | ||||
| 	var eroot, lroot, sig string | ||||
| 	var seq uint | ||||
| 	if _, err := fmt.Sscanf(e, rootPrefix+" e=%s l=%s seq=%d sig=%s", &eroot, &lroot, &seq, &sig); err != nil { | ||||
| 		return rootEntry{}, entryError{"root", errSyntax} | ||||
| 	} | ||||
| 	if !isValidHash(eroot) || !isValidHash(lroot) { | ||||
| 		return rootEntry{}, entryError{"root", errInvalidChild} | ||||
| 	} | ||||
| 	sigb, err := b64format.DecodeString(sig) | ||||
| 	if err != nil || len(sigb) != crypto.SignatureLength { | ||||
| 		return rootEntry{}, entryError{"root", errInvalidSig} | ||||
| 	} | ||||
| 	return rootEntry{eroot, lroot, seq, sigb}, nil | ||||
| } | ||||
| 
 | ||||
| func parseLink(e string) (entry, error) { | ||||
| 	pos := strings.IndexByte(e, '@') | ||||
| 	if pos == -1 { | ||||
| 		return nil, entryError{"link", errNoPubkey} | ||||
| 	} | ||||
| 	keystring, domain := e[:pos], e[pos+1:] | ||||
| 	keybytes, err := b32format.DecodeString(keystring) | ||||
| 	if err != nil { | ||||
| 		return nil, entryError{"link", errBadPubkey} | ||||
| 	} | ||||
| 	key, err := crypto.DecompressPubkey(keybytes) | ||||
| 	if err != nil { | ||||
| 		return nil, entryError{"link", errBadPubkey} | ||||
| 	} | ||||
| 	return &linkEntry{domain, key}, nil | ||||
| } | ||||
| 
 | ||||
| func parseSubtree(e string) (entry, error) { | ||||
| 	if e == "" { | ||||
| 		return &subtreeEntry{}, nil // empty entry is OK
 | ||||
| 	} | ||||
| 	hashes := make([]string, 0, strings.Count(e, ",")) | ||||
| 	for _, c := range strings.Split(e, ",") { | ||||
| 		if !isValidHash(c) { | ||||
| 			return nil, entryError{"subtree", errInvalidChild} | ||||
| 		} | ||||
| 		hashes = append(hashes, c) | ||||
| 	} | ||||
| 	return &subtreeEntry{hashes}, nil | ||||
| } | ||||
| 
 | ||||
| func parseENR(e string, validSchemes enr.IdentityScheme) (entry, error) { | ||||
| 	enc, err := b64format.DecodeString(e) | ||||
| 	if err != nil { | ||||
| 		return nil, entryError{"enr", errInvalidENR} | ||||
| 	} | ||||
| 	var rec enr.Record | ||||
| 	if err := rlp.DecodeBytes(enc, &rec); err != nil { | ||||
| 		return nil, entryError{"enr", err} | ||||
| 	} | ||||
| 	n, err := enode.New(validSchemes, &rec) | ||||
| 	if err != nil { | ||||
| 		return nil, entryError{"enr", err} | ||||
| 	} | ||||
| 	return &enrEntry{n}, nil | ||||
| } | ||||
| 
 | ||||
| func isValidHash(s string) bool { | ||||
| 	dlen := b32format.DecodedLen(len(s)) | ||||
| 	if dlen < minHashLength || dlen > 32 || strings.ContainsAny(s, "\n\r") { | ||||
| 		return false | ||||
| 	} | ||||
| 	buf := make([]byte, 32) | ||||
| 	_, err := b32format.Decode(buf, []byte(s)) | ||||
| 	return err == nil | ||||
| } | ||||
| 
 | ||||
| // truncateHash truncates the given base32 hash string to the minimum acceptable length.
 | ||||
| func truncateHash(hash string) string { | ||||
| 	maxLen := b32format.EncodedLen(minHashLength) | ||||
| 	if len(hash) < maxLen { | ||||
| 		panic(fmt.Errorf("dnsdisc: hash %q is too short", hash)) | ||||
| 	} | ||||
| 	return hash[:maxLen] | ||||
| } | ||||
| 
 | ||||
| // URL encoding
 | ||||
| 
 | ||||
| // ParseURL parses an enrtree:// URL and returns its components.
 | ||||
| func ParseURL(url string) (domain string, pubkey *ecdsa.PublicKey, err error) { | ||||
| 	le, err := parseURL(url) | ||||
| 	if err != nil { | ||||
| 		return "", nil, err | ||||
| 	} | ||||
| 	return le.domain, le.pubkey, nil | ||||
| } | ||||
| 
 | ||||
| func parseURL(url string) (*linkEntry, error) { | ||||
| 	const scheme = "enrtree://" | ||||
| 	if !strings.HasPrefix(url, scheme) { | ||||
| 		return nil, fmt.Errorf("wrong/missing scheme 'enrtree' in URL") | ||||
| 	} | ||||
| 	le, err := parseLink(url[len(scheme):]) | ||||
| 	if err != nil { | ||||
| 		return nil, err.(entryError).err | ||||
| 	} | ||||
| 	return le.(*linkEntry), nil | ||||
| } | ||||
							
								
								
									
										144
									
								
								p2p/dnsdisc/tree_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								p2p/dnsdisc/tree_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,144 @@ | ||||
| // Copyright 2018 The go-ethereum Authors
 | ||||
| // This file is part of the go-ethereum library.
 | ||||
| //
 | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU Lesser General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // The go-ethereum library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU Lesser General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU Lesser General Public License
 | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package dnsdisc | ||||
| 
 | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/davecgh/go-spew/spew" | ||||
| 	"github.com/ethereum/go-ethereum/common/hexutil" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enode" | ||||
| ) | ||||
| 
 | ||||
| func TestParseRoot(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		input string | ||||
| 		e     rootEntry | ||||
| 		err   error | ||||
| 	}{ | ||||
| 		{ | ||||
| 			input: "enrtree-root=v1 e=TO4Q75OQ2N7DX4EOOR7X66A6OM seq=3 sig=N-YY6UB9xD0hFx1Gmnt7v0RfSxch5tKyry2SRDoLx7B4GfPXagwLxQqyf7gAMvApFn_ORwZQekMWa_pXrcGCtw=", | ||||
| 			err:   entryError{"root", errSyntax}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			input: "enrtree-root=v1 e=TO4Q75OQ2N7DX4EOOR7X66A6OM l=TO4Q75OQ2N7DX4EOOR7X66A6OM seq=3 sig=N-YY6UB9xD0hFx1Gmnt7v0RfSxch5tKyry2SRDoLx7B4GfPXagwLxQqyf7gAMvApFn_ORwZQekMWa_pXrcGCtw=", | ||||
| 			err:   entryError{"root", errInvalidSig}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			input: "enrtree-root=v1 e=QFT4PBCRX4XQCV3VUYJ6BTCEPU l=JGUFMSAGI7KZYB3P7IZW4S5Y3A seq=3 sig=3FmXuVwpa8Y7OstZTx9PIb1mt8FrW7VpDOFv4AaGCsZ2EIHmhraWhe4NxYhQDlw5MjeFXYMbJjsPeKlHzmJREQE=", | ||||
| 			e: rootEntry{ | ||||
| 				eroot: "QFT4PBCRX4XQCV3VUYJ6BTCEPU", | ||||
| 				lroot: "JGUFMSAGI7KZYB3P7IZW4S5Y3A", | ||||
| 				seq:   3, | ||||
| 				sig:   hexutil.MustDecode("0xdc5997b95c296bc63b3acb594f1f4f21bd66b7c16b5bb5690ce16fe006860ac6761081e686b69685ee0dc588500e5c393237855d831b263b0f78a947ce62511101"), | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	for i, test := range tests { | ||||
| 		e, err := parseRoot(test.input) | ||||
| 		if !reflect.DeepEqual(e, test.e) { | ||||
| 			t.Errorf("test %d: wrong entry %s, want %s", i, spew.Sdump(e), spew.Sdump(test.e)) | ||||
| 		} | ||||
| 		if err != test.err { | ||||
| 			t.Errorf("test %d: wrong error %q, want %q", i, err, test.err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestParseEntry(t *testing.T) { | ||||
| 	testkey := testKey(signingKeySeed) | ||||
| 	tests := []struct { | ||||
| 		input string | ||||
| 		e     entry | ||||
| 		err   error | ||||
| 	}{ | ||||
| 		// Subtrees:
 | ||||
| 		{ | ||||
| 			input: "enrtree=1,2", | ||||
| 			err:   entryError{"subtree", errInvalidChild}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			input: "enrtree=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", | ||||
| 			err:   entryError{"subtree", errInvalidChild}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			input: "enrtree=", | ||||
| 			e:     &subtreeEntry{}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			input: "enrtree=AAAAAAAAAAAAAAAAAAAA", | ||||
| 			e:     &subtreeEntry{[]string{"AAAAAAAAAAAAAAAAAAAA"}}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			input: "enrtree=AAAAAAAAAAAAAAAAAAAA,BBBBBBBBBBBBBBBBBBBB", | ||||
| 			e:     &subtreeEntry{[]string{"AAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBB"}}, | ||||
| 		}, | ||||
| 		// Links
 | ||||
| 		{ | ||||
| 			input: "enrtree-link=AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@nodes.example.org", | ||||
| 			e:     &linkEntry{"nodes.example.org", &testkey.PublicKey}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			input: "enrtree-link=nodes.example.org", | ||||
| 			err:   entryError{"link", errNoPubkey}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			input: "enrtree-link=AP62DT7WOTEQZGQZOU474PP3KMEGVTTE7A7NPRXKX3DUD57@nodes.example.org", | ||||
| 			err:   entryError{"link", errBadPubkey}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			input: "enrtree-link=AP62DT7WONEQZGQZOU474PP3KMEGVTTE7A7NPRXKX3DUD57TQHGIA@nodes.example.org", | ||||
| 			err:   entryError{"link", errBadPubkey}, | ||||
| 		}, | ||||
| 		// ENRs
 | ||||
| 		{ | ||||
| 			input: "enr=-HW4QES8QIeXTYlDzbfr1WEzE-XKY4f8gJFJzjJL-9D7TC9lJb4Z3JPRRz1lP4pL_N_QpT6rGQjAU9Apnc-C1iMP36OAgmlkgnY0iXNlY3AyNTZrMaED5IdwfMxdmR8W37HqSFdQLjDkIwBd4Q_MjxgZifgKSdM=", | ||||
| 			e:     &enrEntry{node: testNode(nodesSeed1)}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			input: "enr=-HW4QLZHjM4vZXkbp-5xJoHsKSbE7W39FPC8283X-y8oHcHPTnDDlIlzL5ArvDUlHZVDPgmFASrh7cWgLOLxj4wprRkHgmlkgnY0iXNlY3AyNTZrMaEC3t2jLMhDpCDX5mbSEwDn4L3iUfyXzoO8G28XvjGRkrAg=", | ||||
| 			err:   entryError{"enr", errInvalidENR}, | ||||
| 		}, | ||||
| 		// Invalid:
 | ||||
| 		{input: "", err: errUnknownEntry}, | ||||
| 		{input: "foo", err: errUnknownEntry}, | ||||
| 		{input: "enrtree", err: errUnknownEntry}, | ||||
| 		{input: "enrtree-x=", err: errUnknownEntry}, | ||||
| 	} | ||||
| 	for i, test := range tests { | ||||
| 		e, err := parseEntry(test.input, enode.ValidSchemes) | ||||
| 		if !reflect.DeepEqual(e, test.e) { | ||||
| 			t.Errorf("test %d: wrong entry %s, want %s", i, spew.Sdump(e), spew.Sdump(test.e)) | ||||
| 		} | ||||
| 		if err != test.err { | ||||
| 			t.Errorf("test %d: wrong error %q, want %q", i, err, test.err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestMakeTree(t *testing.T) { | ||||
| 	nodes := testNodes(nodesSeed2, 50) | ||||
| 	tree, err := MakeTree(2, nodes, nil) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	txt := tree.ToTXT("") | ||||
| 	if len(txt) < len(nodes)+1 { | ||||
| 		t.Fatal("too few TXT records in output") | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										77
									
								
								vendor/github.com/cloudflare/cloudflare-go/CODE_OF_CONDUCT.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								vendor/github.com/cloudflare/cloudflare-go/CODE_OF_CONDUCT.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| # Contributor Covenant Code of Conduct | ||||
| 
 | ||||
| ## Our Pledge | ||||
| 
 | ||||
| In the interest of fostering an open and welcoming environment, we as | ||||
| contributors and maintainers pledge to making participation in our project and | ||||
| our community a harassment-free experience for everyone, regardless of age, body | ||||
| size, disability, ethnicity, sex characteristics, gender identity and expression, | ||||
| level of experience, education, socio-economic status, nationality, personal | ||||
| appearance, race, religion, or sexual identity and orientation. | ||||
| 
 | ||||
| ## Our Standards | ||||
| 
 | ||||
| Examples of behavior that contributes to creating a positive environment | ||||
| include: | ||||
| 
 | ||||
| * Using welcoming and inclusive language | ||||
| * Being respectful of differing viewpoints and experiences | ||||
| * Gracefully accepting constructive criticism | ||||
| * Focusing on what is best for the community | ||||
| * Showing empathy towards other community members | ||||
| 
 | ||||
| Examples of unacceptable behavior by participants include: | ||||
| 
 | ||||
| * The use of sexualized language or imagery and unwelcome sexual attention or | ||||
|   advances | ||||
| * Trolling, insulting/derogatory comments, and personal or political attacks | ||||
| * Public or private harassment | ||||
| * Publishing others' private information, such as a physical or electronic | ||||
|   address, without explicit permission | ||||
| * Other conduct which could reasonably be considered inappropriate in a | ||||
|   professional setting | ||||
| 
 | ||||
| ## Our Responsibilities | ||||
| 
 | ||||
| Project maintainers are responsible for clarifying the standards of acceptable | ||||
| behavior and are expected to take appropriate and fair corrective action in | ||||
| response to any instances of unacceptable behavior. | ||||
| 
 | ||||
| Project maintainers have the right and responsibility to remove, edit, or | ||||
| reject comments, commits, code, wiki edits, issues, and other contributions | ||||
| that are not aligned to this Code of Conduct, or to ban temporarily or | ||||
| permanently any contributor for other behaviors that they deem inappropriate, | ||||
| threatening, offensive, or harmful. | ||||
| 
 | ||||
| ## Scope | ||||
| 
 | ||||
| This Code of Conduct applies both within project spaces and in public spaces | ||||
| when an individual is representing the project or its community. Examples of | ||||
| representing a project or community include using an official project e-mail | ||||
| address, posting via an official social media account, or acting as an appointed | ||||
| representative at an online or offline event. Representation of a project may be | ||||
| further defined and clarified by project maintainers. | ||||
| 
 | ||||
| ## Enforcement | ||||
| 
 | ||||
| Instances of abusive, harassing, or otherwise unacceptable behavior may be | ||||
| reported by contacting the project team at ggalow@cloudflare.com. All | ||||
| complaints will be reviewed and investigated and will result in a response that | ||||
| is deemed necessary and appropriate to the circumstances. The project team is | ||||
| obligated to maintain confidentiality with regard to the reporter of an incident. | ||||
| Further details of specific enforcement policies may be posted separately. | ||||
| 
 | ||||
| Project maintainers who do not follow or enforce the Code of Conduct in good | ||||
| faith may face temporary or permanent repercussions as determined by other | ||||
| members of the project's leadership. | ||||
| 
 | ||||
| ## Attribution | ||||
| 
 | ||||
| This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, | ||||
| available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html | ||||
| 
 | ||||
| [homepage]: https://www.contributor-covenant.org | ||||
| 
 | ||||
| For answers to common questions about this code of conduct, see | ||||
| https://www.contributor-covenant.org/faq | ||||
| 
 | ||||
							
								
								
									
										26
									
								
								vendor/github.com/cloudflare/cloudflare-go/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/cloudflare/cloudflare-go/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| Copyright (c) 2015-2019, Cloudflare. All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without modification, | ||||
| are permitted provided that the following conditions are met: | ||||
| 
 | ||||
| 1. Redistributions of source code must retain the above copyright notice, this | ||||
| list of conditions and the following disclaimer. | ||||
| 
 | ||||
| 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
| this list of conditions and the following disclaimer in the documentation and/or | ||||
| other materials provided with the distribution. | ||||
| 
 | ||||
| 3. Neither the name of the copyright holder nor the names of its contributors | ||||
| may be used to endorse or promote products derived from this software without | ||||
| specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | ||||
| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||||
| ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										107
									
								
								vendor/github.com/cloudflare/cloudflare-go/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								vendor/github.com/cloudflare/cloudflare-go/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | ||||
| # cloudflare-go | ||||
| 
 | ||||
| [](https://godoc.org/github.com/cloudflare/cloudflare-go) | ||||
| [](https://travis-ci.org/cloudflare/cloudflare-go) | ||||
| [](https://goreportcard.com/report/github.com/cloudflare/cloudflare-go) | ||||
| 
 | ||||
| > **Note**: This library is under active development as we expand it to cover | ||||
| > our (expanding!) API. Consider the public API of this package a little | ||||
| > unstable as we work towards a v1.0. | ||||
| 
 | ||||
| A Go library for interacting with | ||||
| [Cloudflare's API v4](https://api.cloudflare.com/). This library allows you to: | ||||
| 
 | ||||
| * Manage and automate changes to your DNS records within Cloudflare | ||||
| * Manage and automate changes to your zones (domains) on Cloudflare, including | ||||
|   adding new zones to your account | ||||
| * List and modify the status of WAF (Web Application Firewall) rules for your | ||||
|   zones | ||||
| * Fetch Cloudflare's IP ranges for automating your firewall whitelisting | ||||
| 
 | ||||
| A command-line client, [flarectl](cmd/flarectl), is also available as part of | ||||
| this project. | ||||
| 
 | ||||
| ## Features | ||||
| 
 | ||||
| The current feature list includes: | ||||
| 
 | ||||
| * [x] Cache purging | ||||
| * [x] Cloudflare IPs | ||||
| * [x] Custom hostnames | ||||
| * [x] DNS Records | ||||
| * [x] Firewall (partial) | ||||
| * [ ] [Keyless SSL](https://blog.cloudflare.com/keyless-ssl-the-nitty-gritty-technical-details/) | ||||
| * [x] [Load Balancing](https://blog.cloudflare.com/introducing-load-balancing-intelligent-failover-with-cloudflare/) | ||||
| * [x] [Logpush Jobs](https://developers.cloudflare.com/logs/logpush/) | ||||
| * [ ] Organization Administration | ||||
| * [x] [Origin CA](https://blog.cloudflare.com/universal-ssl-encryption-all-the-way-to-the-origin-for-free/) | ||||
| * [x] [Railgun](https://www.cloudflare.com/railgun/) administration | ||||
| * [x] Rate Limiting | ||||
| * [x] User Administration (partial) | ||||
| * [x] Virtual DNS Management | ||||
| * [x] Web Application Firewall (WAF) | ||||
| * [x] Zone Lockdown and User-Agent Block rules | ||||
| * [x] Zones | ||||
| 
 | ||||
| Pull Requests are welcome, but please open an issue (or comment in an existing | ||||
| issue) to discuss any non-trivial changes before submitting code. | ||||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
| You need a working Go environment. | ||||
| 
 | ||||
| ``` | ||||
| go get github.com/cloudflare/cloudflare-go | ||||
| ``` | ||||
| 
 | ||||
| ## Getting Started | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"github.com/cloudflare/cloudflare-go" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	// Construct a new API object | ||||
| 	api, err := cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL")) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Fetch user details on the account | ||||
| 	u, err := api.UserDetails() | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	// Print user details | ||||
| 	fmt.Println(u) | ||||
| 
 | ||||
| 	// Fetch the zone ID | ||||
| 	id, err := api.ZoneIDByName("example.com") // Assuming example.com exists in your Cloudflare account already | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Fetch zone details | ||||
| 	zone, err := api.ZoneDetails(id) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	// Print zone details | ||||
| 	fmt.Println(zone) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Also refer to the | ||||
| [API documentation](https://godoc.org/github.com/cloudflare/cloudflare-go) for | ||||
| how to use this package in-depth. | ||||
| 
 | ||||
| # License | ||||
| 
 | ||||
| BSD licensed. See the [LICENSE](LICENSE) file for details. | ||||
							
								
								
									
										180
									
								
								vendor/github.com/cloudflare/cloudflare-go/access_application.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								vendor/github.com/cloudflare/cloudflare-go/access_application.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,180 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // AccessApplication represents an Access application.
 | ||||
| type AccessApplication struct { | ||||
| 	ID              string     `json:"id,omitempty"` | ||||
| 	CreatedAt       *time.Time `json:"created_at,omitempty"` | ||||
| 	UpdatedAt       *time.Time `json:"updated_at,omitempty"` | ||||
| 	AUD             string     `json:"aud,omitempty"` | ||||
| 	Name            string     `json:"name"` | ||||
| 	Domain          string     `json:"domain"` | ||||
| 	SessionDuration string     `json:"session_duration,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // AccessApplicationListResponse represents the response from the list
 | ||||
| // access applications endpoint.
 | ||||
| type AccessApplicationListResponse struct { | ||||
| 	Result []AccessApplication `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // AccessApplicationDetailResponse is the API response, containing a single
 | ||||
| // access application.
 | ||||
| type AccessApplicationDetailResponse struct { | ||||
| 	Success  bool              `json:"success"` | ||||
| 	Errors   []string          `json:"errors"` | ||||
| 	Messages []string          `json:"messages"` | ||||
| 	Result   AccessApplication `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // AccessApplications returns all applications within a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-applications-list-access-applications
 | ||||
| func (api *API) AccessApplications(zoneID string, pageOpts PaginationOptions) ([]AccessApplication, ResultInfo, error) { | ||||
| 	v := url.Values{} | ||||
| 	if pageOpts.PerPage > 0 { | ||||
| 		v.Set("per_page", strconv.Itoa(pageOpts.PerPage)) | ||||
| 	} | ||||
| 	if pageOpts.Page > 0 { | ||||
| 		v.Set("page", strconv.Itoa(pageOpts.Page)) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/access/apps" | ||||
| 	if len(v) > 0 { | ||||
| 		uri = uri + "?" + v.Encode() | ||||
| 	} | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []AccessApplication{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessApplicationListResponse AccessApplicationListResponse | ||||
| 	err = json.Unmarshal(res, &accessApplicationListResponse) | ||||
| 	if err != nil { | ||||
| 		return []AccessApplication{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessApplicationListResponse.Result, accessApplicationListResponse.ResultInfo, nil | ||||
| } | ||||
| 
 | ||||
| // AccessApplication returns a single application based on the
 | ||||
| // application ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-applications-access-applications-details
 | ||||
| func (api *API) AccessApplication(zoneID, applicationID string) (AccessApplication, error) { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/access/apps/%s", | ||||
| 		zoneID, | ||||
| 		applicationID, | ||||
| 	) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return AccessApplication{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessApplicationDetailResponse AccessApplicationDetailResponse | ||||
| 	err = json.Unmarshal(res, &accessApplicationDetailResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessApplication{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessApplicationDetailResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // CreateAccessApplication creates a new access application.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-applications-create-access-application
 | ||||
| func (api *API) CreateAccessApplication(zoneID string, accessApplication AccessApplication) (AccessApplication, error) { | ||||
| 	uri := "/zones/" + zoneID + "/access/apps" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", uri, accessApplication) | ||||
| 	if err != nil { | ||||
| 		return AccessApplication{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessApplicationDetailResponse AccessApplicationDetailResponse | ||||
| 	err = json.Unmarshal(res, &accessApplicationDetailResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessApplication{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessApplicationDetailResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateAccessApplication updates an existing access application.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-applications-update-access-application
 | ||||
| func (api *API) UpdateAccessApplication(zoneID string, accessApplication AccessApplication) (AccessApplication, error) { | ||||
| 	if accessApplication.ID == "" { | ||||
| 		return AccessApplication{}, errors.Errorf("access application ID cannot be empty") | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/access/apps/%s", | ||||
| 		zoneID, | ||||
| 		accessApplication.ID, | ||||
| 	) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, accessApplication) | ||||
| 	if err != nil { | ||||
| 		return AccessApplication{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessApplicationDetailResponse AccessApplicationDetailResponse | ||||
| 	err = json.Unmarshal(res, &accessApplicationDetailResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessApplication{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessApplicationDetailResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteAccessApplication deletes an access application.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-applications-delete-access-application
 | ||||
| func (api *API) DeleteAccessApplication(zoneID, applicationID string) error { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/access/apps/%s", | ||||
| 		zoneID, | ||||
| 		applicationID, | ||||
| 	) | ||||
| 
 | ||||
| 	_, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // RevokeAccessApplicationTokens revokes tokens associated with an
 | ||||
| // access application.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-applications-revoke-access-tokens
 | ||||
| func (api *API) RevokeAccessApplicationTokens(zoneID, applicationID string) error { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/access/apps/%s/revoke-tokens", | ||||
| 		zoneID, | ||||
| 		applicationID, | ||||
| 	) | ||||
| 
 | ||||
| 	_, err := api.makeRequest("POST", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										331
									
								
								vendor/github.com/cloudflare/cloudflare-go/access_identity_provider.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								vendor/github.com/cloudflare/cloudflare-go/access_identity_provider.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,331 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // AccessIdentityProvider is the structure of the provider object.
 | ||||
| type AccessIdentityProvider struct { | ||||
| 	ID     string      `json:"id,omitemtpy"` | ||||
| 	Name   string      `json:"name"` | ||||
| 	Type   string      `json:"type"` | ||||
| 	Config interface{} `json:"config"` | ||||
| } | ||||
| 
 | ||||
| // AccessAzureADConfiguration is the representation of the Azure AD identity
 | ||||
| // provider.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/azuread/
 | ||||
| type AccessAzureADConfiguration struct { | ||||
| 	ClientID      string `json:"client_id"` | ||||
| 	ClientSecret  string `json:"client_secret"` | ||||
| 	DirectoryID   string `json:"directory_id"` | ||||
| 	SupportGroups bool   `json:"support_groups"` | ||||
| } | ||||
| 
 | ||||
| // AccessCentrifyConfiguration is the representation of the Centrify identity
 | ||||
| // provider.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/centrify/
 | ||||
| type AccessCentrifyConfiguration struct { | ||||
| 	ClientID        string `json:"client_id"` | ||||
| 	ClientSecret    string `json:"client_secret"` | ||||
| 	CentrifyAccount string `json:"centrify_account"` | ||||
| 	CentrifyAppID   string `json:"centrify_app_id"` | ||||
| } | ||||
| 
 | ||||
| // AccessCentrifySAMLConfiguration is the representation of the Centrify
 | ||||
| // identity provider using SAML.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/saml-centrify/
 | ||||
| type AccessCentrifySAMLConfiguration struct { | ||||
| 	IssuerURL          string   `json:"issuer_url"` | ||||
| 	SsoTargetURL       string   `json:"sso_target_url"` | ||||
| 	Attributes         []string `json:"attributes"` | ||||
| 	EmailAttributeName string   `json:"email_attribute_name"` | ||||
| 	SignRequest        bool     `json:"sign_request"` | ||||
| 	IdpPublicCert      string   `json:"idp_public_cert"` | ||||
| } | ||||
| 
 | ||||
| // AccessFacebookConfiguration is the representation of the Facebook identity
 | ||||
| // provider.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/facebook-login/
 | ||||
| type AccessFacebookConfiguration struct { | ||||
| 	ClientID     string `json:"client_id"` | ||||
| 	ClientSecret string `json:"client_secret"` | ||||
| } | ||||
| 
 | ||||
| // AccessGSuiteConfiguration is the representation of the GSuite identity
 | ||||
| // provider.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/gsuite/
 | ||||
| type AccessGSuiteConfiguration struct { | ||||
| 	ClientID     string `json:"client_id"` | ||||
| 	ClientSecret string `json:"client_secret"` | ||||
| 	AppsDomain   string `json:"apps_domain"` | ||||
| } | ||||
| 
 | ||||
| // AccessGenericOIDCConfiguration is the representation of the generic OpenID
 | ||||
| // Connect (OIDC) connector.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/generic-oidc/
 | ||||
| type AccessGenericOIDCConfiguration struct { | ||||
| 	ClientID     string `json:"client_id"` | ||||
| 	ClientSecret string `json:"client_secret"` | ||||
| 	AuthURL      string `json:"auth_url"` | ||||
| 	TokenURL     string `json:"token_url"` | ||||
| 	CertsURL     string `json:"certs_url"` | ||||
| } | ||||
| 
 | ||||
| // AccessGitHubConfiguration is the representation of the GitHub identity
 | ||||
| // provider.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/github/
 | ||||
| type AccessGitHubConfiguration struct { | ||||
| 	ClientID     string `json:"client_id"` | ||||
| 	ClientSecret string `json:"client_secret"` | ||||
| } | ||||
| 
 | ||||
| // AccessGoogleConfiguration is the representation of the Google identity
 | ||||
| // provider.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/google/
 | ||||
| type AccessGoogleConfiguration struct { | ||||
| 	ClientID     string `json:"client_id"` | ||||
| 	ClientSecret string `json:"client_secret"` | ||||
| } | ||||
| 
 | ||||
| // AccessJumpCloudSAMLConfiguration is the representation of the Jump Cloud
 | ||||
| // identity provider using SAML.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/jumpcloud-saml/
 | ||||
| type AccessJumpCloudSAMLConfiguration struct { | ||||
| 	IssuerURL          string   `json:"issuer_url"` | ||||
| 	SsoTargetURL       string   `json:"sso_target_url"` | ||||
| 	Attributes         []string `json:"attributes"` | ||||
| 	EmailAttributeName string   `json:"email_attribute_name"` | ||||
| 	SignRequest        bool     `json:"sign_request"` | ||||
| 	IdpPublicCert      string   `json:"idp_public_cert"` | ||||
| } | ||||
| 
 | ||||
| // AccessOktaConfiguration is the representation of the Okta identity provider.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/okta/
 | ||||
| type AccessOktaConfiguration struct { | ||||
| 	ClientID     string `json:"client_id"` | ||||
| 	ClientSecret string `json:"client_secret"` | ||||
| 	OktaAccount  string `json:"okta_account"` | ||||
| } | ||||
| 
 | ||||
| // AccessOktaSAMLConfiguration is the representation of the Okta identity
 | ||||
| // provider using SAML.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/saml-okta/
 | ||||
| type AccessOktaSAMLConfiguration struct { | ||||
| 	IssuerURL          string   `json:"issuer_url"` | ||||
| 	SsoTargetURL       string   `json:"sso_target_url"` | ||||
| 	Attributes         []string `json:"attributes"` | ||||
| 	EmailAttributeName string   `json:"email_attribute_name"` | ||||
| 	SignRequest        bool     `json:"sign_request"` | ||||
| 	IdpPublicCert      string   `json:"idp_public_cert"` | ||||
| } | ||||
| 
 | ||||
| // AccessOneTimePinConfiguration is the representation of the default One Time
 | ||||
| // Pin identity provider.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/one-time-pin/
 | ||||
| type AccessOneTimePinConfiguration struct{} | ||||
| 
 | ||||
| // AccessOneLoginOIDCConfiguration is the representation of the OneLogin
 | ||||
| // OpenID connector as an identity provider.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/onelogin-oidc/
 | ||||
| type AccessOneLoginOIDCConfiguration struct { | ||||
| 	ClientID        string `json:"client_id"` | ||||
| 	ClientSecret    string `json:"client_secret"` | ||||
| 	OneloginAccount string `json:"onelogin_account"` | ||||
| } | ||||
| 
 | ||||
| // AccessOneLoginSAMLConfiguration is the representation of the OneLogin
 | ||||
| // identity provider using SAML.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/onelogin-saml/
 | ||||
| type AccessOneLoginSAMLConfiguration struct { | ||||
| 	IssuerURL          string   `json:"issuer_url"` | ||||
| 	SsoTargetURL       string   `json:"sso_target_url"` | ||||
| 	Attributes         []string `json:"attributes"` | ||||
| 	EmailAttributeName string   `json:"email_attribute_name"` | ||||
| 	SignRequest        bool     `json:"sign_request"` | ||||
| 	IdpPublicCert      string   `json:"idp_public_cert"` | ||||
| } | ||||
| 
 | ||||
| // AccessPingSAMLConfiguration is the representation of the Ping identity
 | ||||
| // provider using SAML.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/ping-saml/
 | ||||
| type AccessPingSAMLConfiguration struct { | ||||
| 	IssuerURL          string   `json:"issuer_url"` | ||||
| 	SsoTargetURL       string   `json:"sso_target_url"` | ||||
| 	Attributes         []string `json:"attributes"` | ||||
| 	EmailAttributeName string   `json:"email_attribute_name"` | ||||
| 	SignRequest        bool     `json:"sign_request"` | ||||
| 	IdpPublicCert      string   `json:"idp_public_cert"` | ||||
| } | ||||
| 
 | ||||
| // AccessYandexConfiguration is the representation of the Yandex identity provider.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/yandex/
 | ||||
| type AccessYandexConfiguration struct { | ||||
| 	ClientID     string `json:"client_id"` | ||||
| 	ClientSecret string `json:"client_secret"` | ||||
| } | ||||
| 
 | ||||
| // AccessADSAMLConfiguration is the representation of the Active Directory
 | ||||
| // identity provider using SAML.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/access/configuring-identity-providers/adfs/
 | ||||
| type AccessADSAMLConfiguration struct { | ||||
| 	IssuerURL          string   `json:"issuer_url"` | ||||
| 	SsoTargetURL       string   `json:"sso_target_url"` | ||||
| 	Attributes         []string `json:"attributes"` | ||||
| 	EmailAttributeName string   `json:"email_attribute_name"` | ||||
| 	SignRequest        bool     `json:"sign_request"` | ||||
| 	IdpPublicCert      string   `json:"idp_public_cert"` | ||||
| } | ||||
| 
 | ||||
| // AccessIdentityProvidersListResponse is the API response for multiple
 | ||||
| // Access Identity Providers.
 | ||||
| type AccessIdentityProvidersListResponse struct { | ||||
| 	Success  bool                     `json:"success"` | ||||
| 	Errors   []string                 `json:"errors"` | ||||
| 	Messages []string                 `json:"messages"` | ||||
| 	Result   []AccessIdentityProvider `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // AccessIdentityProviderListResponse is the API response for a single
 | ||||
| // Access Identity Provider.
 | ||||
| type AccessIdentityProviderListResponse struct { | ||||
| 	Success  bool                   `json:"success"` | ||||
| 	Errors   []string               `json:"errors"` | ||||
| 	Messages []string               `json:"messages"` | ||||
| 	Result   AccessIdentityProvider `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // AccessIdentityProviders returns all Access Identity Providers for an
 | ||||
| // account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-identity-providers-list-access-identity-providers
 | ||||
| func (api *API) AccessIdentityProviders(accountID string) ([]AccessIdentityProvider, error) { | ||||
| 	uri := "/accounts/" + accountID + "/access/identity_providers" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []AccessIdentityProvider{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessIdentityProviderResponse AccessIdentityProvidersListResponse | ||||
| 	err = json.Unmarshal(res, &accessIdentityProviderResponse) | ||||
| 	if err != nil { | ||||
| 		return []AccessIdentityProvider{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessIdentityProviderResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // AccessIdentityProviderDetails returns a single Access Identity
 | ||||
| // Provider for an account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-identity-providers-access-identity-providers-details
 | ||||
| func (api *API) AccessIdentityProviderDetails(accountID, identityProviderID string) (AccessIdentityProvider, error) { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/accounts/%s/access/identity_providers/%s", | ||||
| 		accountID, | ||||
| 		identityProviderID, | ||||
| 	) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return AccessIdentityProvider{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessIdentityProviderResponse AccessIdentityProviderListResponse | ||||
| 	err = json.Unmarshal(res, &accessIdentityProviderResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessIdentityProvider{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessIdentityProviderResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // CreateAccessIdentityProvider creates a new Access Identity Provider.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-identity-providers-create-access-identity-provider
 | ||||
| func (api *API) CreateAccessIdentityProvider(accountID string, identityProviderConfiguration AccessIdentityProvider) (AccessIdentityProvider, error) { | ||||
| 	uri := "/accounts/" + accountID + "/access/identity_providers" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", uri, identityProviderConfiguration) | ||||
| 	if err != nil { | ||||
| 		return AccessIdentityProvider{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessIdentityProviderResponse AccessIdentityProviderListResponse | ||||
| 	err = json.Unmarshal(res, &accessIdentityProviderResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessIdentityProvider{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessIdentityProviderResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateAccessIdentityProvider updates an existing Access Identity
 | ||||
| // Provider.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-identity-providers-create-access-identity-provider
 | ||||
| func (api *API) UpdateAccessIdentityProvider(accountID, identityProviderUUID string, identityProviderConfiguration AccessIdentityProvider) (AccessIdentityProvider, error) { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/accounts/%s/access/identity_providers/%s", | ||||
| 		accountID, | ||||
| 		identityProviderUUID, | ||||
| 	) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, identityProviderConfiguration) | ||||
| 	if err != nil { | ||||
| 		return AccessIdentityProvider{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessIdentityProviderResponse AccessIdentityProviderListResponse | ||||
| 	err = json.Unmarshal(res, &accessIdentityProviderResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessIdentityProvider{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessIdentityProviderResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteAccessIdentityProvider deletes an Access Identity Provider.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-identity-providers-create-access-identity-provider
 | ||||
| func (api *API) DeleteAccessIdentityProvider(accountID, identityProviderUUID string) (AccessIdentityProvider, error) { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/accounts/%s/access/identity_providers/%s", | ||||
| 		accountID, | ||||
| 		identityProviderUUID, | ||||
| 	) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return AccessIdentityProvider{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessIdentityProviderResponse AccessIdentityProviderListResponse | ||||
| 	err = json.Unmarshal(res, &accessIdentityProviderResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessIdentityProvider{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessIdentityProviderResponse.Result, nil | ||||
| } | ||||
							
								
								
									
										101
									
								
								vendor/github.com/cloudflare/cloudflare-go/access_organization.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								vendor/github.com/cloudflare/cloudflare-go/access_organization.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,101 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // AccessOrganization represents an Access organization.
 | ||||
| type AccessOrganization struct { | ||||
| 	CreatedAt   *time.Time                    `json:"created_at"` | ||||
| 	UpdatedAt   *time.Time                    `json:"updated_at"` | ||||
| 	Name        string                        `json:"name"` | ||||
| 	AuthDomain  string                        `json:"auth_domain"` | ||||
| 	LoginDesign AccessOrganizationLoginDesign `json:"login_design"` | ||||
| } | ||||
| 
 | ||||
| // AccessOrganizationLoginDesign represents the login design options.
 | ||||
| type AccessOrganizationLoginDesign struct { | ||||
| 	BackgroundColor string `json:"background_color"` | ||||
| 	TextColor       string `json:"text_color"` | ||||
| 	LogoPath        string `json:"logo_path"` | ||||
| } | ||||
| 
 | ||||
| // AccessOrganizationListResponse represents the response from the list
 | ||||
| // access organization endpoint.
 | ||||
| type AccessOrganizationListResponse struct { | ||||
| 	Result AccessOrganization `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // AccessOrganizationDetailResponse is the API response, containing a
 | ||||
| // single access organization.
 | ||||
| type AccessOrganizationDetailResponse struct { | ||||
| 	Success  bool               `json:"success"` | ||||
| 	Errors   []string           `json:"errors"` | ||||
| 	Messages []string           `json:"messages"` | ||||
| 	Result   AccessOrganization `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // AccessOrganization returns the Access organisation details.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-organizations-access-organization-details
 | ||||
| func (api *API) AccessOrganization(accountID string) (AccessOrganization, ResultInfo, error) { | ||||
| 	uri := "/accounts/" + accountID + "/access/organizations" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return AccessOrganization{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessOrganizationListResponse AccessOrganizationListResponse | ||||
| 	err = json.Unmarshal(res, &accessOrganizationListResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessOrganization{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessOrganizationListResponse.Result, accessOrganizationListResponse.ResultInfo, nil | ||||
| } | ||||
| 
 | ||||
| // CreateAccessOrganization creates the Access organisation details.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-organizations-create-access-organization
 | ||||
| func (api *API) CreateAccessOrganization(accountID string, accessOrganization AccessOrganization) (AccessOrganization, error) { | ||||
| 	uri := "/accounts/" + accountID + "/access/organizations" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", uri, accessOrganization) | ||||
| 	if err != nil { | ||||
| 		return AccessOrganization{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessOrganizationDetailResponse AccessOrganizationDetailResponse | ||||
| 	err = json.Unmarshal(res, &accessOrganizationDetailResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessOrganization{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessOrganizationDetailResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateAccessOrganization creates the Access organisation details.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-organizations-update-access-organization
 | ||||
| func (api *API) UpdateAccessOrganization(accountID string, accessOrganization AccessOrganization) (AccessOrganization, error) { | ||||
| 	uri := "/accounts/" + accountID + "/access/organizations" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, accessOrganization) | ||||
| 	if err != nil { | ||||
| 		return AccessOrganization{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessOrganizationDetailResponse AccessOrganizationDetailResponse | ||||
| 	err = json.Unmarshal(res, &accessOrganizationDetailResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessOrganization{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessOrganizationDetailResponse.Result, nil | ||||
| } | ||||
							
								
								
									
										221
									
								
								vendor/github.com/cloudflare/cloudflare-go/access_policy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								vendor/github.com/cloudflare/cloudflare-go/access_policy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,221 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // AccessPolicy defines a policy for allowing or disallowing access to
 | ||||
| // one or more Access applications.
 | ||||
| type AccessPolicy struct { | ||||
| 	ID         string     `json:"id,omitempty"` | ||||
| 	Precedence int        `json:"precedence"` | ||||
| 	Decision   string     `json:"decision"` | ||||
| 	CreatedAt  *time.Time `json:"created_at"` | ||||
| 	UpdatedAt  *time.Time `json:"updated_at"` | ||||
| 	Name       string     `json:"name"` | ||||
| 
 | ||||
| 	// The include policy works like an OR logical operator. The user must
 | ||||
| 	// satisfy one of the rules.
 | ||||
| 	Include []interface{} `json:"include"` | ||||
| 
 | ||||
| 	// The exclude policy works like a NOT logical operator. The user must
 | ||||
| 	// not satisfy all of the rules in exclude.
 | ||||
| 	Exclude []interface{} `json:"exclude"` | ||||
| 
 | ||||
| 	// The require policy works like a AND logical operator. The user must
 | ||||
| 	// satisfy all of the rules in require.
 | ||||
| 	Require []interface{} `json:"require"` | ||||
| } | ||||
| 
 | ||||
| // AccessPolicyEmail is used for managing access based on the email.
 | ||||
| // For example, restrict access to users with the email addresses
 | ||||
| // `test@example.com` or `someone@example.com`.
 | ||||
| type AccessPolicyEmail struct { | ||||
| 	Email struct { | ||||
| 		Email string `json:"email"` | ||||
| 	} `json:"email"` | ||||
| } | ||||
| 
 | ||||
| // AccessPolicyEmailDomain is used for managing access based on an email
 | ||||
| // domain domain such as `example.com` instead of individual addresses.
 | ||||
| type AccessPolicyEmailDomain struct { | ||||
| 	EmailDomain struct { | ||||
| 		Domain string `json:"domain"` | ||||
| 	} `json:"email_domain"` | ||||
| } | ||||
| 
 | ||||
| // AccessPolicyIP is used for managing access based in the IP. It
 | ||||
| // accepts individual IPs or CIDRs.
 | ||||
| type AccessPolicyIP struct { | ||||
| 	IP struct { | ||||
| 		IP string `json:"ip"` | ||||
| 	} `json:"ip"` | ||||
| } | ||||
| 
 | ||||
| // AccessPolicyEveryone is used for managing access to everyone.
 | ||||
| type AccessPolicyEveryone struct { | ||||
| 	Everyone struct{} `json:"everyone"` | ||||
| } | ||||
| 
 | ||||
| // AccessPolicyAccessGroup is used for managing access based on an
 | ||||
| // access group.
 | ||||
| type AccessPolicyAccessGroup struct { | ||||
| 	Group struct { | ||||
| 		ID string `json:"id"` | ||||
| 	} `json:"group"` | ||||
| } | ||||
| 
 | ||||
| // AccessPolicyListResponse represents the response from the list
 | ||||
| // access polciies endpoint.
 | ||||
| type AccessPolicyListResponse struct { | ||||
| 	Result []AccessPolicy `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // AccessPolicyDetailResponse is the API response, containing a single
 | ||||
| // access policy.
 | ||||
| type AccessPolicyDetailResponse struct { | ||||
| 	Success  bool         `json:"success"` | ||||
| 	Errors   []string     `json:"errors"` | ||||
| 	Messages []string     `json:"messages"` | ||||
| 	Result   AccessPolicy `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // AccessPolicies returns all access policies for an access application.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-policy-list-access-policies
 | ||||
| func (api *API) AccessPolicies(zoneID, applicationID string, pageOpts PaginationOptions) ([]AccessPolicy, ResultInfo, error) { | ||||
| 	v := url.Values{} | ||||
| 	if pageOpts.PerPage > 0 { | ||||
| 		v.Set("per_page", strconv.Itoa(pageOpts.PerPage)) | ||||
| 	} | ||||
| 	if pageOpts.Page > 0 { | ||||
| 		v.Set("page", strconv.Itoa(pageOpts.Page)) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/access/apps/%s/policies", | ||||
| 		zoneID, | ||||
| 		applicationID, | ||||
| 	) | ||||
| 
 | ||||
| 	if len(v) > 0 { | ||||
| 		uri = uri + "?" + v.Encode() | ||||
| 	} | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []AccessPolicy{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessPolicyListResponse AccessPolicyListResponse | ||||
| 	err = json.Unmarshal(res, &accessPolicyListResponse) | ||||
| 	if err != nil { | ||||
| 		return []AccessPolicy{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessPolicyListResponse.Result, accessPolicyListResponse.ResultInfo, nil | ||||
| } | ||||
| 
 | ||||
| // AccessPolicy returns a single policy based on the policy ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-policy-access-policy-details
 | ||||
| func (api *API) AccessPolicy(zoneID, applicationID, policyID string) (AccessPolicy, error) { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/access/apps/%s/policies/%s", | ||||
| 		zoneID, | ||||
| 		applicationID, | ||||
| 		policyID, | ||||
| 	) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return AccessPolicy{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessPolicyDetailResponse AccessPolicyDetailResponse | ||||
| 	err = json.Unmarshal(res, &accessPolicyDetailResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessPolicy{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessPolicyDetailResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // CreateAccessPolicy creates a new access policy.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-policy-create-access-policy
 | ||||
| func (api *API) CreateAccessPolicy(zoneID, applicationID string, accessPolicy AccessPolicy) (AccessPolicy, error) { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/access/apps/%s/policies", | ||||
| 		zoneID, | ||||
| 		applicationID, | ||||
| 	) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", uri, accessPolicy) | ||||
| 	if err != nil { | ||||
| 		return AccessPolicy{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessPolicyDetailResponse AccessPolicyDetailResponse | ||||
| 	err = json.Unmarshal(res, &accessPolicyDetailResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessPolicy{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessPolicyDetailResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateAccessPolicy updates an existing access policy.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-policy-update-access-policy
 | ||||
| func (api *API) UpdateAccessPolicy(zoneID, applicationID string, accessPolicy AccessPolicy) (AccessPolicy, error) { | ||||
| 	if accessPolicy.ID == "" { | ||||
| 		return AccessPolicy{}, errors.Errorf("access policy ID cannot be empty") | ||||
| 	} | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/access/apps/%s/policies/%s", | ||||
| 		zoneID, | ||||
| 		applicationID, | ||||
| 		accessPolicy.ID, | ||||
| 	) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, accessPolicy) | ||||
| 	if err != nil { | ||||
| 		return AccessPolicy{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessPolicyDetailResponse AccessPolicyDetailResponse | ||||
| 	err = json.Unmarshal(res, &accessPolicyDetailResponse) | ||||
| 	if err != nil { | ||||
| 		return AccessPolicy{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessPolicyDetailResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteAccessPolicy deletes an access policy.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-policy-update-access-policy
 | ||||
| func (api *API) DeleteAccessPolicy(zoneID, applicationID, accessPolicyID string) error { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/access/apps/%s/policies/%s", | ||||
| 		zoneID, | ||||
| 		applicationID, | ||||
| 		accessPolicyID, | ||||
| 	) | ||||
| 
 | ||||
| 	_, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										167
									
								
								vendor/github.com/cloudflare/cloudflare-go/access_service_tokens.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								vendor/github.com/cloudflare/cloudflare-go/access_service_tokens.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,167 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // AccessServiceToken represents an Access Service Token.
 | ||||
| type AccessServiceToken struct { | ||||
| 	ClientID  string     `json:"client_id"` | ||||
| 	CreatedAt *time.Time `json:"created_at"` | ||||
| 	ExpiresAt *time.Time `json:"expires_at"` | ||||
| 	ID        string     `json:"id"` | ||||
| 	Name      string     `json:"name"` | ||||
| 	UpdatedAt *time.Time `json:"updated_at"` | ||||
| } | ||||
| 
 | ||||
| // AccessServiceTokenUpdateResponse represents the response from the API
 | ||||
| // when a new Service Token is updated. This base struct is also used in the
 | ||||
| // Create as they are very similar responses.
 | ||||
| type AccessServiceTokenUpdateResponse struct { | ||||
| 	CreatedAt *time.Time `json:"created_at"` | ||||
| 	UpdatedAt *time.Time `json:"updated_at"` | ||||
| 	ID        string     `json:"id"` | ||||
| 	Name      string     `json:"name"` | ||||
| 	ClientID  string     `json:"client_id"` | ||||
| } | ||||
| 
 | ||||
| // AccessServiceTokenCreateResponse is the same API response as the Update
 | ||||
| // operation with the exception that the `ClientSecret` is present in a
 | ||||
| // Create operation.
 | ||||
| type AccessServiceTokenCreateResponse struct { | ||||
| 	CreatedAt    *time.Time `json:"created_at"` | ||||
| 	UpdatedAt    *time.Time `json:"updated_at"` | ||||
| 	ID           string     `json:"id"` | ||||
| 	Name         string     `json:"name"` | ||||
| 	ClientID     string     `json:"client_id"` | ||||
| 	ClientSecret string     `json:"client_secret"` | ||||
| } | ||||
| 
 | ||||
| // AccessServiceTokensListResponse represents the response from the list
 | ||||
| // Access Service Tokens endpoint.
 | ||||
| type AccessServiceTokensListResponse struct { | ||||
| 	Result []AccessServiceToken `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // AccessServiceTokensDetailResponse is the API response, containing a single
 | ||||
| // Access Service Token.
 | ||||
| type AccessServiceTokensDetailResponse struct { | ||||
| 	Success  bool               `json:"success"` | ||||
| 	Errors   []string           `json:"errors"` | ||||
| 	Messages []string           `json:"messages"` | ||||
| 	Result   AccessServiceToken `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // AccessServiceTokensCreationDetailResponse is the API response, containing a
 | ||||
| // single Access Service Token.
 | ||||
| type AccessServiceTokensCreationDetailResponse struct { | ||||
| 	Success  bool                             `json:"success"` | ||||
| 	Errors   []string                         `json:"errors"` | ||||
| 	Messages []string                         `json:"messages"` | ||||
| 	Result   AccessServiceTokenCreateResponse `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // AccessServiceTokensUpdateDetailResponse is the API response, containing a
 | ||||
| // single Access Service Token.
 | ||||
| type AccessServiceTokensUpdateDetailResponse struct { | ||||
| 	Success  bool                             `json:"success"` | ||||
| 	Errors   []string                         `json:"errors"` | ||||
| 	Messages []string                         `json:"messages"` | ||||
| 	Result   AccessServiceTokenUpdateResponse `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // AccessServiceTokens returns all Access Service Tokens for an account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-service-tokens-list-access-service-tokens
 | ||||
| func (api *API) AccessServiceTokens(accountID string) ([]AccessServiceToken, ResultInfo, error) { | ||||
| 	uri := "/accounts/" + accountID + "/access/service_tokens" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []AccessServiceToken{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessServiceTokensListResponse AccessServiceTokensListResponse | ||||
| 	err = json.Unmarshal(res, &accessServiceTokensListResponse) | ||||
| 	if err != nil { | ||||
| 		return []AccessServiceToken{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessServiceTokensListResponse.Result, accessServiceTokensListResponse.ResultInfo, nil | ||||
| } | ||||
| 
 | ||||
| // CreateAccessServiceToken creates a new Access Service Token for an account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-service-tokens-create-access-service-token
 | ||||
| func (api *API) CreateAccessServiceToken(accountID, name string) (AccessServiceTokenCreateResponse, error) { | ||||
| 	uri := "/accounts/" + accountID + "/access/service_tokens" | ||||
| 	marshalledName, _ := json.Marshal(struct { | ||||
| 		Name string `json:"name"` | ||||
| 	}{name}) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", uri, marshalledName) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return AccessServiceTokenCreateResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessServiceTokenCreation AccessServiceTokensCreationDetailResponse | ||||
| 	err = json.Unmarshal(res, &accessServiceTokenCreation) | ||||
| 	if err != nil { | ||||
| 		return AccessServiceTokenCreateResponse{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessServiceTokenCreation.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateAccessServiceToken updates an existing Access Service Token for an
 | ||||
| // account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-service-tokens-update-access-service-token
 | ||||
| func (api *API) UpdateAccessServiceToken(accountID, uuid, name string) (AccessServiceTokenUpdateResponse, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/access/service_tokens/%s", accountID, uuid) | ||||
| 
 | ||||
| 	marshalledName, _ := json.Marshal(struct { | ||||
| 		Name string `json:"name"` | ||||
| 	}{name}) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, marshalledName) | ||||
| 	if err != nil { | ||||
| 		return AccessServiceTokenUpdateResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessServiceTokenUpdate AccessServiceTokensUpdateDetailResponse | ||||
| 	err = json.Unmarshal(res, &accessServiceTokenUpdate) | ||||
| 	if err != nil { | ||||
| 		return AccessServiceTokenUpdateResponse{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessServiceTokenUpdate.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteAccessServiceToken removes an existing Access Service Token for an
 | ||||
| // account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#access-service-tokens-delete-access-service-token
 | ||||
| func (api *API) DeleteAccessServiceToken(accountID, uuid string) (AccessServiceTokenUpdateResponse, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/access/service_tokens/%s", accountID, uuid) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return AccessServiceTokenUpdateResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accessServiceTokenUpdate AccessServiceTokensUpdateDetailResponse | ||||
| 	err = json.Unmarshal(res, &accessServiceTokenUpdate) | ||||
| 	if err != nil { | ||||
| 		return AccessServiceTokenUpdateResponse{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accessServiceTokenUpdate.Result, nil | ||||
| } | ||||
							
								
								
									
										186
									
								
								vendor/github.com/cloudflare/cloudflare-go/account_members.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								vendor/github.com/cloudflare/cloudflare-go/account_members.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,186 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // AccountMember is the definition of a member of an account.
 | ||||
| type AccountMember struct { | ||||
| 	ID     string                   `json:"id"` | ||||
| 	Code   string                   `json:"code"` | ||||
| 	User   AccountMemberUserDetails `json:"user"` | ||||
| 	Status string                   `json:"status"` | ||||
| 	Roles  []AccountRole            `json:"roles"` | ||||
| } | ||||
| 
 | ||||
| // AccountMemberUserDetails outlines all the personal information about
 | ||||
| // a member.
 | ||||
| type AccountMemberUserDetails struct { | ||||
| 	ID                             string `json:"id"` | ||||
| 	FirstName                      string `json:"first_name"` | ||||
| 	LastName                       string `json:"last_name"` | ||||
| 	Email                          string `json:"email"` | ||||
| 	TwoFactorAuthenticationEnabled bool | ||||
| } | ||||
| 
 | ||||
| // AccountMembersListResponse represents the response from the list
 | ||||
| // account members endpoint.
 | ||||
| type AccountMembersListResponse struct { | ||||
| 	Result []AccountMember `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // AccountMemberDetailResponse is the API response, containing a single
 | ||||
| // account member.
 | ||||
| type AccountMemberDetailResponse struct { | ||||
| 	Success  bool          `json:"success"` | ||||
| 	Errors   []string      `json:"errors"` | ||||
| 	Messages []string      `json:"messages"` | ||||
| 	Result   AccountMember `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // AccountMemberInvitation represents the invitation for a new member to
 | ||||
| // the account.
 | ||||
| type AccountMemberInvitation struct { | ||||
| 	Email string   `json:"email"` | ||||
| 	Roles []string `json:"roles"` | ||||
| } | ||||
| 
 | ||||
| // AccountMembers returns all members of an account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#accounts-list-accounts
 | ||||
| func (api *API) AccountMembers(accountID string, pageOpts PaginationOptions) ([]AccountMember, ResultInfo, error) { | ||||
| 	if accountID == "" { | ||||
| 		return []AccountMember{}, ResultInfo{}, errors.New(errMissingAccountID) | ||||
| 	} | ||||
| 
 | ||||
| 	v := url.Values{} | ||||
| 	if pageOpts.PerPage > 0 { | ||||
| 		v.Set("per_page", strconv.Itoa(pageOpts.PerPage)) | ||||
| 	} | ||||
| 	if pageOpts.Page > 0 { | ||||
| 		v.Set("page", strconv.Itoa(pageOpts.Page)) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := "/accounts/" + accountID + "/members" | ||||
| 	if len(v) > 0 { | ||||
| 		uri = uri + "?" + v.Encode() | ||||
| 	} | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []AccountMember{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accountMemberListresponse AccountMembersListResponse | ||||
| 	err = json.Unmarshal(res, &accountMemberListresponse) | ||||
| 	if err != nil { | ||||
| 		return []AccountMember{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accountMemberListresponse.Result, accountMemberListresponse.ResultInfo, nil | ||||
| } | ||||
| 
 | ||||
| // CreateAccountMember invites a new member to join an account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#account-members-add-member
 | ||||
| func (api *API) CreateAccountMember(accountID string, emailAddress string, roles []string) (AccountMember, error) { | ||||
| 	if accountID == "" { | ||||
| 		return AccountMember{}, errors.New(errMissingAccountID) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := "/accounts/" + accountID + "/members" | ||||
| 
 | ||||
| 	var newMember = AccountMemberInvitation{ | ||||
| 		Email: emailAddress, | ||||
| 		Roles: roles, | ||||
| 	} | ||||
| 	res, err := api.makeRequest("POST", uri, newMember) | ||||
| 	if err != nil { | ||||
| 		return AccountMember{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accountMemberListResponse AccountMemberDetailResponse | ||||
| 	err = json.Unmarshal(res, &accountMemberListResponse) | ||||
| 	if err != nil { | ||||
| 		return AccountMember{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accountMemberListResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteAccountMember removes a member from an account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#account-members-remove-member
 | ||||
| func (api *API) DeleteAccountMember(accountID string, userID string) error { | ||||
| 	if accountID == "" { | ||||
| 		return errors.New(errMissingAccountID) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf("/accounts/%s/members/%s", accountID, userID) | ||||
| 
 | ||||
| 	_, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // UpdateAccountMember modifies an existing account member.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#account-members-update-member
 | ||||
| func (api *API) UpdateAccountMember(accountID string, userID string, member AccountMember) (AccountMember, error) { | ||||
| 	if accountID == "" { | ||||
| 		return AccountMember{}, errors.New(errMissingAccountID) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf("/accounts/%s/members/%s", accountID, userID) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, member) | ||||
| 	if err != nil { | ||||
| 		return AccountMember{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accountMemberListResponse AccountMemberDetailResponse | ||||
| 	err = json.Unmarshal(res, &accountMemberListResponse) | ||||
| 	if err != nil { | ||||
| 		return AccountMember{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accountMemberListResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // AccountMember returns details of a single account member.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#account-members-member-details
 | ||||
| func (api *API) AccountMember(accountID string, memberID string) (AccountMember, error) { | ||||
| 	if accountID == "" { | ||||
| 		return AccountMember{}, errors.New(errMissingAccountID) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/accounts/%s/members/%s", | ||||
| 		accountID, | ||||
| 		memberID, | ||||
| 	) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return AccountMember{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accountMemberResponse AccountMemberDetailResponse | ||||
| 	err = json.Unmarshal(res, &accountMemberResponse) | ||||
| 	if err != nil { | ||||
| 		return AccountMember{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accountMemberResponse.Result, nil | ||||
| } | ||||
							
								
								
									
										80
									
								
								vendor/github.com/cloudflare/cloudflare-go/account_roles.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								vendor/github.com/cloudflare/cloudflare-go/account_roles.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // AccountRole defines the roles that a member can have attached.
 | ||||
| type AccountRole struct { | ||||
| 	ID          string                           `json:"id"` | ||||
| 	Name        string                           `json:"name"` | ||||
| 	Description string                           `json:"description"` | ||||
| 	Permissions map[string]AccountRolePermission `json:"permissions"` | ||||
| } | ||||
| 
 | ||||
| // AccountRolePermission is the shared structure for all permissions
 | ||||
| // that can be assigned to a member.
 | ||||
| type AccountRolePermission struct { | ||||
| 	Read bool `json:"read"` | ||||
| 	Edit bool `json:"edit"` | ||||
| } | ||||
| 
 | ||||
| // AccountRolesListResponse represents the list response from the
 | ||||
| // account roles.
 | ||||
| type AccountRolesListResponse struct { | ||||
| 	Result []AccountRole `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // AccountRoleDetailResponse is the API response, containing a single
 | ||||
| // account role.
 | ||||
| type AccountRoleDetailResponse struct { | ||||
| 	Success  bool        `json:"success"` | ||||
| 	Errors   []string    `json:"errors"` | ||||
| 	Messages []string    `json:"messages"` | ||||
| 	Result   AccountRole `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // AccountRoles returns all roles of an account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#account-roles-list-roles
 | ||||
| func (api *API) AccountRoles(accountID string) ([]AccountRole, error) { | ||||
| 	uri := "/accounts/" + accountID + "/roles" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []AccountRole{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accountRolesListResponse AccountRolesListResponse | ||||
| 	err = json.Unmarshal(res, &accountRolesListResponse) | ||||
| 	if err != nil { | ||||
| 		return []AccountRole{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accountRolesListResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // AccountRole returns the details of a single account role.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#account-roles-role-details
 | ||||
| func (api *API) AccountRole(accountID string, roleID string) (AccountRole, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/roles/%s", accountID, roleID) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return AccountRole{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accountRole AccountRoleDetailResponse | ||||
| 	err = json.Unmarshal(res, &accountRole) | ||||
| 	if err != nil { | ||||
| 		return AccountRole{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accountRole.Result, nil | ||||
| } | ||||
							
								
								
									
										114
									
								
								vendor/github.com/cloudflare/cloudflare-go/accounts.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								vendor/github.com/cloudflare/cloudflare-go/accounts.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // AccountSettings outlines the available options for an account.
 | ||||
| type AccountSettings struct { | ||||
| 	EnforceTwoFactor bool `json:"enforce_twofactor"` | ||||
| } | ||||
| 
 | ||||
| // Account represents the root object that owns resources.
 | ||||
| type Account struct { | ||||
| 	ID       string           `json:"id,omitempty"` | ||||
| 	Name     string           `json:"name,omitempty"` | ||||
| 	Settings *AccountSettings `json:"settings"` | ||||
| } | ||||
| 
 | ||||
| // AccountResponse represents the response from the accounts endpoint for a
 | ||||
| // single account ID.
 | ||||
| type AccountResponse struct { | ||||
| 	Result Account `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // AccountListResponse represents the response from the list accounts endpoint.
 | ||||
| type AccountListResponse struct { | ||||
| 	Result []Account `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // AccountDetailResponse is the API response, containing a single Account.
 | ||||
| type AccountDetailResponse struct { | ||||
| 	Success  bool     `json:"success"` | ||||
| 	Errors   []string `json:"errors"` | ||||
| 	Messages []string `json:"messages"` | ||||
| 	Result   Account  `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // Accounts returns all accounts the logged in user has access to.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#accounts-list-accounts
 | ||||
| func (api *API) Accounts(pageOpts PaginationOptions) ([]Account, ResultInfo, error) { | ||||
| 	v := url.Values{} | ||||
| 	if pageOpts.PerPage > 0 { | ||||
| 		v.Set("per_page", strconv.Itoa(pageOpts.PerPage)) | ||||
| 	} | ||||
| 	if pageOpts.Page > 0 { | ||||
| 		v.Set("page", strconv.Itoa(pageOpts.Page)) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := "/accounts" | ||||
| 	if len(v) > 0 { | ||||
| 		uri = uri + "?" + v.Encode() | ||||
| 	} | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []Account{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accListResponse AccountListResponse | ||||
| 	err = json.Unmarshal(res, &accListResponse) | ||||
| 	if err != nil { | ||||
| 		return []Account{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return accListResponse.Result, accListResponse.ResultInfo, nil | ||||
| } | ||||
| 
 | ||||
| // Account returns a single account based on the ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#accounts-account-details
 | ||||
| func (api *API) Account(accountID string) (Account, ResultInfo, error) { | ||||
| 	uri := "/accounts/" + accountID | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return Account{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var accResponse AccountResponse | ||||
| 	err = json.Unmarshal(res, &accResponse) | ||||
| 	if err != nil { | ||||
| 		return Account{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return accResponse.Result, accResponse.ResultInfo, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateAccount allows management of an account using the account ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#accounts-update-account
 | ||||
| func (api *API) UpdateAccount(accountID string, account Account) (Account, error) { | ||||
| 	uri := "/accounts/" + accountID | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, account) | ||||
| 	if err != nil { | ||||
| 		return Account{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var a AccountDetailResponse | ||||
| 	err = json.Unmarshal(res, &a) | ||||
| 	if err != nil { | ||||
| 		return Account{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return a.Result, nil | ||||
| } | ||||
							
								
								
									
										120
									
								
								vendor/github.com/cloudflare/cloudflare-go/argo.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								vendor/github.com/cloudflare/cloudflare-go/argo.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| var validSettingValues = []string{"on", "off"} | ||||
| 
 | ||||
| // ArgoFeatureSetting is the structure of the API object for the
 | ||||
| // argo smart routing and tiered caching settings.
 | ||||
| type ArgoFeatureSetting struct { | ||||
| 	Editable   bool      `json:"editable,omitempty"` | ||||
| 	ID         string    `json:"id,omitempty"` | ||||
| 	ModifiedOn time.Time `json:"modified_on,omitempty"` | ||||
| 	Value      string    `json:"value"` | ||||
| } | ||||
| 
 | ||||
| // ArgoDetailsResponse is the API response for the argo smart routing
 | ||||
| // and tiered caching response.
 | ||||
| type ArgoDetailsResponse struct { | ||||
| 	Result ArgoFeatureSetting `json:"result"` | ||||
| 	Response | ||||
| } | ||||
| 
 | ||||
| // ArgoSmartRouting returns the current settings for smart routing.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#argo-smart-routing-get-argo-smart-routing-setting
 | ||||
| func (api *API) ArgoSmartRouting(zoneID string) (ArgoFeatureSetting, error) { | ||||
| 	uri := "/zones/" + zoneID + "/argo/smart_routing" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return ArgoFeatureSetting{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var argoDetailsResponse ArgoDetailsResponse | ||||
| 	err = json.Unmarshal(res, &argoDetailsResponse) | ||||
| 	if err != nil { | ||||
| 		return ArgoFeatureSetting{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return argoDetailsResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateArgoSmartRouting updates the setting for smart routing.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#argo-smart-routing-patch-argo-smart-routing-setting
 | ||||
| func (api *API) UpdateArgoSmartRouting(zoneID, settingValue string) (ArgoFeatureSetting, error) { | ||||
| 	if !contains(validSettingValues, settingValue) { | ||||
| 		return ArgoFeatureSetting{}, errors.New(fmt.Sprintf("invalid setting value '%s'. must be 'on' or 'off'", settingValue)) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/argo/smart_routing" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PATCH", uri, ArgoFeatureSetting{Value: settingValue}) | ||||
| 	if err != nil { | ||||
| 		return ArgoFeatureSetting{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var argoDetailsResponse ArgoDetailsResponse | ||||
| 	err = json.Unmarshal(res, &argoDetailsResponse) | ||||
| 	if err != nil { | ||||
| 		return ArgoFeatureSetting{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return argoDetailsResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ArgoTieredCaching returns the current settings for tiered caching.
 | ||||
| //
 | ||||
| // API reference: TBA
 | ||||
| func (api *API) ArgoTieredCaching(zoneID string) (ArgoFeatureSetting, error) { | ||||
| 	uri := "/zones/" + zoneID + "/argo/tiered_caching" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return ArgoFeatureSetting{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var argoDetailsResponse ArgoDetailsResponse | ||||
| 	err = json.Unmarshal(res, &argoDetailsResponse) | ||||
| 	if err != nil { | ||||
| 		return ArgoFeatureSetting{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return argoDetailsResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateArgoTieredCaching updates the setting for tiered caching.
 | ||||
| //
 | ||||
| // API reference: TBA
 | ||||
| func (api *API) UpdateArgoTieredCaching(zoneID, settingValue string) (ArgoFeatureSetting, error) { | ||||
| 	if !contains(validSettingValues, settingValue) { | ||||
| 		return ArgoFeatureSetting{}, errors.New(fmt.Sprintf("invalid setting value '%s'. must be 'on' or 'off'", settingValue)) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/argo/tiered_caching" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PATCH", uri, ArgoFeatureSetting{Value: settingValue}) | ||||
| 	if err != nil { | ||||
| 		return ArgoFeatureSetting{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var argoDetailsResponse ArgoDetailsResponse | ||||
| 	err = json.Unmarshal(res, &argoDetailsResponse) | ||||
| 	if err != nil { | ||||
| 		return ArgoFeatureSetting{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return argoDetailsResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| func contains(s []string, e string) bool { | ||||
| 	for _, a := range s { | ||||
| 		if a == e { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										143
									
								
								vendor/github.com/cloudflare/cloudflare-go/auditlogs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/github.com/cloudflare/cloudflare-go/auditlogs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // AuditLogAction is a member of AuditLog, the action that was taken.
 | ||||
| type AuditLogAction struct { | ||||
| 	Result bool   `json:"result"` | ||||
| 	Type   string `json:"type"` | ||||
| } | ||||
| 
 | ||||
| // AuditLogActor is a member of AuditLog, who performed the action.
 | ||||
| type AuditLogActor struct { | ||||
| 	Email string `json:"email"` | ||||
| 	ID    string `json:"id"` | ||||
| 	IP    string `json:"ip"` | ||||
| 	Type  string `json:"type"` | ||||
| } | ||||
| 
 | ||||
| // AuditLogOwner is a member of AuditLog, who owns this audit log.
 | ||||
| type AuditLogOwner struct { | ||||
| 	ID string `json:"id"` | ||||
| } | ||||
| 
 | ||||
| // AuditLogResource is a member of AuditLog, what was the action performed on.
 | ||||
| type AuditLogResource struct { | ||||
| 	ID   string `json:"id"` | ||||
| 	Type string `json:"type"` | ||||
| } | ||||
| 
 | ||||
| // AuditLog is an resource that represents an update in the cloudflare dash
 | ||||
| type AuditLog struct { | ||||
| 	Action   AuditLogAction         `json:"action"` | ||||
| 	Actor    AuditLogActor          `json:"actor"` | ||||
| 	ID       string                 `json:"id"` | ||||
| 	Metadata map[string]interface{} `json:"metadata"` | ||||
| 	NewValue string                 `json:"newValue"` | ||||
| 	OldValue string                 `json:"oldValue"` | ||||
| 	Owner    AuditLogOwner          `json:"owner"` | ||||
| 	Resource AuditLogResource       `json:"resource"` | ||||
| 	When     time.Time              `json:"when"` | ||||
| } | ||||
| 
 | ||||
| // AuditLogResponse is the response returned from the cloudflare v4 api
 | ||||
| type AuditLogResponse struct { | ||||
| 	Response   Response | ||||
| 	Result     []AuditLog `json:"result"` | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // AuditLogFilter is an object for filtering the audit log response from the api.
 | ||||
| type AuditLogFilter struct { | ||||
| 	ID         string | ||||
| 	ActorIP    string | ||||
| 	ActorEmail string | ||||
| 	Direction  string | ||||
| 	ZoneName   string | ||||
| 	Since      string | ||||
| 	Before     string | ||||
| 	PerPage    int | ||||
| 	Page       int | ||||
| } | ||||
| 
 | ||||
| // String turns an audit log filter in to an HTTP Query Param
 | ||||
| // list. It will not inclue empty members of the struct in the
 | ||||
| // query parameters.
 | ||||
| func (a AuditLogFilter) String() string { | ||||
| 	params := "?" | ||||
| 	if a.ID != "" { | ||||
| 		params += "&id=" + a.ID | ||||
| 	} | ||||
| 	if a.ActorIP != "" { | ||||
| 		params += "&actor.ip=" + a.ActorIP | ||||
| 	} | ||||
| 	if a.ActorEmail != "" { | ||||
| 		params += "&actor.email=" + a.ActorEmail | ||||
| 	} | ||||
| 	if a.ZoneName != "" { | ||||
| 		params += "&zone.name=" + a.ZoneName | ||||
| 	} | ||||
| 	if a.Direction != "" { | ||||
| 		params += "&direction=" + a.Direction | ||||
| 	} | ||||
| 	if a.Since != "" { | ||||
| 		params += "&since=" + a.Since | ||||
| 	} | ||||
| 	if a.Before != "" { | ||||
| 		params += "&before=" + a.Before | ||||
| 	} | ||||
| 	if a.PerPage > 0 { | ||||
| 		params += "&per_page=" + fmt.Sprintf("%d", a.PerPage) | ||||
| 	} | ||||
| 	if a.Page > 0 { | ||||
| 		params += "&page=" + fmt.Sprintf("%d", a.Page) | ||||
| 	} | ||||
| 	return params | ||||
| } | ||||
| 
 | ||||
| // GetOrganizationAuditLogs will return the audit logs of a specific
 | ||||
| // organization, based on the ID passed in. The audit logs can be
 | ||||
| // filtered based on any argument in the AuditLogFilter
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#audit-logs-list-organization-audit-logs
 | ||||
| func (api *API) GetOrganizationAuditLogs(organizationID string, a AuditLogFilter) (AuditLogResponse, error) { | ||||
| 	uri := "/organizations/" + organizationID + "/audit_logs" + fmt.Sprintf("%s", a) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return AuditLogResponse{}, err | ||||
| 	} | ||||
| 	buf, err := base64.RawStdEncoding.DecodeString(string(res)) | ||||
| 	if err != nil { | ||||
| 		return AuditLogResponse{}, err | ||||
| 	} | ||||
| 	return unmarshalReturn(buf) | ||||
| } | ||||
| 
 | ||||
| // unmarshalReturn will unmarshal bytes and return an auditlogresponse
 | ||||
| func unmarshalReturn(res []byte) (AuditLogResponse, error) { | ||||
| 	var auditResponse AuditLogResponse | ||||
| 	err := json.Unmarshal(res, &auditResponse) | ||||
| 	if err != nil { | ||||
| 		return auditResponse, err | ||||
| 	} | ||||
| 	return auditResponse, nil | ||||
| } | ||||
| 
 | ||||
| // GetUserAuditLogs will return your user's audit logs. The audit logs can be
 | ||||
| // filtered based on any argument in the AuditLogFilter
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#audit-logs-list-user-audit-logs
 | ||||
| func (api *API) GetUserAuditLogs(a AuditLogFilter) (AuditLogResponse, error) { | ||||
| 	uri := "/user/audit_logs" + fmt.Sprintf("%s", a) | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return AuditLogResponse{}, err | ||||
| 	} | ||||
| 	return unmarshalReturn(res) | ||||
| } | ||||
							
								
								
									
										435
									
								
								vendor/github.com/cloudflare/cloudflare-go/cloudflare.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										435
									
								
								vendor/github.com/cloudflare/cloudflare-go/cloudflare.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,435 @@ | ||||
| // Package cloudflare implements the Cloudflare v4 API.
 | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"math" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| 	"golang.org/x/time/rate" | ||||
| ) | ||||
| 
 | ||||
| const apiURL = "https://api.cloudflare.com/client/v4" | ||||
| 
 | ||||
| const ( | ||||
| 	// AuthKeyEmail specifies that we should authenticate with API key and email address
 | ||||
| 	AuthKeyEmail = 1 << iota | ||||
| 	// AuthUserService specifies that we should authenticate with a User-Service key
 | ||||
| 	AuthUserService | ||||
| 	// AuthToken specifies that we should authenticate with an API Token
 | ||||
| 	AuthToken | ||||
| ) | ||||
| 
 | ||||
| // API holds the configuration for the current API client. A client should not
 | ||||
| // be modified concurrently.
 | ||||
| type API struct { | ||||
| 	APIKey            string | ||||
| 	APIEmail          string | ||||
| 	APIUserServiceKey string | ||||
| 	APIToken          string | ||||
| 	BaseURL           string | ||||
| 	AccountID         string | ||||
| 	UserAgent         string | ||||
| 	headers           http.Header | ||||
| 	httpClient        *http.Client | ||||
| 	authType          int | ||||
| 	rateLimiter       *rate.Limiter | ||||
| 	retryPolicy       RetryPolicy | ||||
| 	logger            Logger | ||||
| } | ||||
| 
 | ||||
| // newClient provides shared logic for New and NewWithUserServiceKey
 | ||||
| func newClient(opts ...Option) (*API, error) { | ||||
| 	silentLogger := log.New(ioutil.Discard, "", log.LstdFlags) | ||||
| 
 | ||||
| 	api := &API{ | ||||
| 		BaseURL:     apiURL, | ||||
| 		headers:     make(http.Header), | ||||
| 		rateLimiter: rate.NewLimiter(rate.Limit(4), 1), // 4rps equates to default api limit (1200 req/5 min)
 | ||||
| 		retryPolicy: RetryPolicy{ | ||||
| 			MaxRetries:    3, | ||||
| 			MinRetryDelay: time.Duration(1) * time.Second, | ||||
| 			MaxRetryDelay: time.Duration(30) * time.Second, | ||||
| 		}, | ||||
| 		logger: silentLogger, | ||||
| 	} | ||||
| 
 | ||||
| 	err := api.parseOptions(opts...) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "options parsing failed") | ||||
| 	} | ||||
| 
 | ||||
| 	// Fall back to http.DefaultClient if the package user does not provide
 | ||||
| 	// their own.
 | ||||
| 	if api.httpClient == nil { | ||||
| 		api.httpClient = http.DefaultClient | ||||
| 	} | ||||
| 
 | ||||
| 	return api, nil | ||||
| } | ||||
| 
 | ||||
| // New creates a new Cloudflare v4 API client.
 | ||||
| func New(key, email string, opts ...Option) (*API, error) { | ||||
| 	if key == "" || email == "" { | ||||
| 		return nil, errors.New(errEmptyCredentials) | ||||
| 	} | ||||
| 
 | ||||
| 	api, err := newClient(opts...) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	api.APIKey = key | ||||
| 	api.APIEmail = email | ||||
| 	api.authType = AuthKeyEmail | ||||
| 
 | ||||
| 	return api, nil | ||||
| } | ||||
| 
 | ||||
| // NewWithAPIToken creates a new Cloudflare v4 API client using API Tokens
 | ||||
| func NewWithAPIToken(token string, opts ...Option) (*API, error) { | ||||
| 	if token == "" { | ||||
| 		return nil, errors.New(errEmptyAPIToken) | ||||
| 	} | ||||
| 
 | ||||
| 	api, err := newClient(opts...) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	api.APIToken = token | ||||
| 	api.authType = AuthToken | ||||
| 
 | ||||
| 	return api, nil | ||||
| } | ||||
| 
 | ||||
| // NewWithUserServiceKey creates a new Cloudflare v4 API client using service key authentication.
 | ||||
| func NewWithUserServiceKey(key string, opts ...Option) (*API, error) { | ||||
| 	if key == "" { | ||||
| 		return nil, errors.New(errEmptyCredentials) | ||||
| 	} | ||||
| 
 | ||||
| 	api, err := newClient(opts...) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	api.APIUserServiceKey = key | ||||
| 	api.authType = AuthUserService | ||||
| 
 | ||||
| 	return api, nil | ||||
| } | ||||
| 
 | ||||
| // SetAuthType sets the authentication method (AuthKeyEmail, AuthToken, or AuthUserService).
 | ||||
| func (api *API) SetAuthType(authType int) { | ||||
| 	api.authType = authType | ||||
| } | ||||
| 
 | ||||
| // ZoneIDByName retrieves a zone's ID from the name.
 | ||||
| func (api *API) ZoneIDByName(zoneName string) (string, error) { | ||||
| 	res, err := api.ListZonesContext(context.TODO(), WithZoneFilter(zoneName)) | ||||
| 	if err != nil { | ||||
| 		return "", errors.Wrap(err, "ListZonesContext command failed") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(res.Result) > 1 && api.AccountID == "" { | ||||
| 		return "", errors.New("ambiguous zone name used without an account ID") | ||||
| 	} | ||||
| 
 | ||||
| 	for _, zone := range res.Result { | ||||
| 		if api.AccountID != "" { | ||||
| 			if zone.Name == zoneName && api.AccountID == zone.Account.ID { | ||||
| 				return zone.ID, nil | ||||
| 			} | ||||
| 		} else { | ||||
| 			if zone.Name == zoneName { | ||||
| 				return zone.ID, nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return "", errors.New("Zone could not be found") | ||||
| } | ||||
| 
 | ||||
| // makeRequest makes a HTTP request and returns the body as a byte slice,
 | ||||
| // closing it before returning. params will be serialized to JSON.
 | ||||
| func (api *API) makeRequest(method, uri string, params interface{}) ([]byte, error) { | ||||
| 	return api.makeRequestWithAuthType(context.TODO(), method, uri, params, api.authType) | ||||
| } | ||||
| 
 | ||||
| func (api *API) makeRequestContext(ctx context.Context, method, uri string, params interface{}) ([]byte, error) { | ||||
| 	return api.makeRequestWithAuthType(ctx, method, uri, params, api.authType) | ||||
| } | ||||
| 
 | ||||
| func (api *API) makeRequestWithHeaders(method, uri string, params interface{}, headers http.Header) ([]byte, error) { | ||||
| 	return api.makeRequestWithAuthTypeAndHeaders(context.TODO(), method, uri, params, api.authType, headers) | ||||
| } | ||||
| 
 | ||||
| func (api *API) makeRequestWithAuthType(ctx context.Context, method, uri string, params interface{}, authType int) ([]byte, error) { | ||||
| 	return api.makeRequestWithAuthTypeAndHeaders(ctx, method, uri, params, authType, nil) | ||||
| } | ||||
| 
 | ||||
| func (api *API) makeRequestWithAuthTypeAndHeaders(ctx context.Context, method, uri string, params interface{}, authType int, headers http.Header) ([]byte, error) { | ||||
| 	// Replace nil with a JSON object if needed
 | ||||
| 	var jsonBody []byte | ||||
| 	var err error | ||||
| 
 | ||||
| 	if params != nil { | ||||
| 		if paramBytes, ok := params.([]byte); ok { | ||||
| 			jsonBody = paramBytes | ||||
| 		} else { | ||||
| 			jsonBody, err = json.Marshal(params) | ||||
| 			if err != nil { | ||||
| 				return nil, errors.Wrap(err, "error marshalling params to JSON") | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		jsonBody = nil | ||||
| 	} | ||||
| 
 | ||||
| 	var resp *http.Response | ||||
| 	var respErr error | ||||
| 	var reqBody io.Reader | ||||
| 	var respBody []byte | ||||
| 	for i := 0; i <= api.retryPolicy.MaxRetries; i++ { | ||||
| 		if jsonBody != nil { | ||||
| 			reqBody = bytes.NewReader(jsonBody) | ||||
| 		} | ||||
| 		if i > 0 { | ||||
| 			// expect the backoff introduced here on errored requests to dominate the effect of rate limiting
 | ||||
| 			// don't need a random component here as the rate limiter should do something similar
 | ||||
| 			// nb time duration could truncate an arbitrary float. Since our inputs are all ints, we should be ok
 | ||||
| 			sleepDuration := time.Duration(math.Pow(2, float64(i-1)) * float64(api.retryPolicy.MinRetryDelay)) | ||||
| 
 | ||||
| 			if sleepDuration > api.retryPolicy.MaxRetryDelay { | ||||
| 				sleepDuration = api.retryPolicy.MaxRetryDelay | ||||
| 			} | ||||
| 			// useful to do some simple logging here, maybe introduce levels later
 | ||||
| 			api.logger.Printf("Sleeping %s before retry attempt number %d for request %s %s", sleepDuration.String(), i, method, uri) | ||||
| 			time.Sleep(sleepDuration) | ||||
| 
 | ||||
| 		} | ||||
| 		err = api.rateLimiter.Wait(context.TODO()) | ||||
| 		if err != nil { | ||||
| 			return nil, errors.Wrap(err, "Error caused by request rate limiting") | ||||
| 		} | ||||
| 		resp, respErr = api.request(ctx, method, uri, reqBody, authType, headers) | ||||
| 
 | ||||
| 		// retry if the server is rate limiting us or if it failed
 | ||||
| 		// assumes server operations are rolled back on failure
 | ||||
| 		if respErr != nil || resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500 { | ||||
| 			// if we got a valid http response, try to read body so we can reuse the connection
 | ||||
| 			// see https://golang.org/pkg/net/http/#Client.Do
 | ||||
| 			if respErr == nil { | ||||
| 				respBody, err = ioutil.ReadAll(resp.Body) | ||||
| 				resp.Body.Close() | ||||
| 
 | ||||
| 				respErr = errors.Wrap(err, "could not read response body") | ||||
| 
 | ||||
| 				api.logger.Printf("Request: %s %s got an error response %d: %s\n", method, uri, resp.StatusCode, | ||||
| 					strings.Replace(strings.Replace(string(respBody), "\n", "", -1), "\t", "", -1)) | ||||
| 			} else { | ||||
| 				api.logger.Printf("Error performing request: %s %s : %s \n", method, uri, respErr.Error()) | ||||
| 			} | ||||
| 			continue | ||||
| 		} else { | ||||
| 			respBody, err = ioutil.ReadAll(resp.Body) | ||||
| 			defer resp.Body.Close() | ||||
| 			if err != nil { | ||||
| 				return nil, errors.Wrap(err, "could not read response body") | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if respErr != nil { | ||||
| 		return nil, respErr | ||||
| 	} | ||||
| 
 | ||||
| 	switch { | ||||
| 	case resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices: | ||||
| 	case resp.StatusCode == http.StatusUnauthorized: | ||||
| 		return nil, errors.Errorf("HTTP status %d: invalid credentials", resp.StatusCode) | ||||
| 	case resp.StatusCode == http.StatusForbidden: | ||||
| 		return nil, errors.Errorf("HTTP status %d: insufficient permissions", resp.StatusCode) | ||||
| 	case resp.StatusCode == http.StatusServiceUnavailable, | ||||
| 		resp.StatusCode == http.StatusBadGateway, | ||||
| 		resp.StatusCode == http.StatusGatewayTimeout, | ||||
| 		resp.StatusCode == 522, | ||||
| 		resp.StatusCode == 523, | ||||
| 		resp.StatusCode == 524: | ||||
| 		return nil, errors.Errorf("HTTP status %d: service failure", resp.StatusCode) | ||||
| 	// This isn't a great solution due to the way the `default` case is
 | ||||
| 	// a catch all and that the `filters/validate-expr` returns a HTTP 400
 | ||||
| 	// yet the clients need to use the HTTP body as a JSON string.
 | ||||
| 	case resp.StatusCode == 400 && strings.HasSuffix(resp.Request.URL.Path, "/filters/validate-expr"): | ||||
| 		return nil, errors.Errorf("%s", respBody) | ||||
| 	default: | ||||
| 		var s string | ||||
| 		if respBody != nil { | ||||
| 			s = string(respBody) | ||||
| 		} | ||||
| 		return nil, errors.Errorf("HTTP status %d: content %q", resp.StatusCode, s) | ||||
| 	} | ||||
| 
 | ||||
| 	return respBody, nil | ||||
| } | ||||
| 
 | ||||
| // request makes a HTTP request to the given API endpoint, returning the raw
 | ||||
| // *http.Response, or an error if one occurred. The caller is responsible for
 | ||||
| // closing the response body.
 | ||||
| func (api *API) request(ctx context.Context, method, uri string, reqBody io.Reader, authType int, headers http.Header) (*http.Response, error) { | ||||
| 	req, err := http.NewRequest(method, api.BaseURL+uri, reqBody) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "HTTP request creation failed") | ||||
| 	} | ||||
| 	req.WithContext(ctx) | ||||
| 
 | ||||
| 	combinedHeaders := make(http.Header) | ||||
| 	copyHeader(combinedHeaders, api.headers) | ||||
| 	copyHeader(combinedHeaders, headers) | ||||
| 	req.Header = combinedHeaders | ||||
| 
 | ||||
| 	if authType&AuthKeyEmail != 0 { | ||||
| 		req.Header.Set("X-Auth-Key", api.APIKey) | ||||
| 		req.Header.Set("X-Auth-Email", api.APIEmail) | ||||
| 	} | ||||
| 	if authType&AuthUserService != 0 { | ||||
| 		req.Header.Set("X-Auth-User-Service-Key", api.APIUserServiceKey) | ||||
| 	} | ||||
| 	if authType&AuthToken != 0 { | ||||
| 		req.Header.Set("Authorization", "Bearer "+api.APIToken) | ||||
| 	} | ||||
| 
 | ||||
| 	if api.UserAgent != "" { | ||||
| 		req.Header.Set("User-Agent", api.UserAgent) | ||||
| 	} | ||||
| 
 | ||||
| 	if req.Header.Get("Content-Type") == "" { | ||||
| 		req.Header.Set("Content-Type", "application/json") | ||||
| 	} | ||||
| 
 | ||||
| 	resp, err := api.httpClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "HTTP request failed") | ||||
| 	} | ||||
| 
 | ||||
| 	return resp, nil | ||||
| } | ||||
| 
 | ||||
| // Returns the base URL to use for API endpoints that exist for accounts.
 | ||||
| // If an account option was used when creating the API instance, returns
 | ||||
| // the account URL.
 | ||||
| //
 | ||||
| // accountBase is the base URL for endpoints referring to the current user.
 | ||||
| // It exists as a parameter because it is not consistent across APIs.
 | ||||
| func (api *API) userBaseURL(accountBase string) string { | ||||
| 	if api.AccountID != "" { | ||||
| 		return "/accounts/" + api.AccountID | ||||
| 	} | ||||
| 	return accountBase | ||||
| } | ||||
| 
 | ||||
| // copyHeader copies all headers for `source` and sets them on `target`.
 | ||||
| // based on https://godoc.org/github.com/golang/gddo/httputil/header#Copy
 | ||||
| func copyHeader(target, source http.Header) { | ||||
| 	for k, vs := range source { | ||||
| 		target[k] = vs | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ResponseInfo contains a code and message returned by the API as errors or
 | ||||
| // informational messages inside the response.
 | ||||
| type ResponseInfo struct { | ||||
| 	Code    int    `json:"code"` | ||||
| 	Message string `json:"message"` | ||||
| } | ||||
| 
 | ||||
| // Response is a template.  There will also be a result struct.  There will be a
 | ||||
| // unique response type for each response, which will include this type.
 | ||||
| type Response struct { | ||||
| 	Success  bool           `json:"success"` | ||||
| 	Errors   []ResponseInfo `json:"errors"` | ||||
| 	Messages []ResponseInfo `json:"messages"` | ||||
| } | ||||
| 
 | ||||
| // ResultInfo contains metadata about the Response.
 | ||||
| type ResultInfo struct { | ||||
| 	Page       int `json:"page"` | ||||
| 	PerPage    int `json:"per_page"` | ||||
| 	TotalPages int `json:"total_pages"` | ||||
| 	Count      int `json:"count"` | ||||
| 	Total      int `json:"total_count"` | ||||
| } | ||||
| 
 | ||||
| // RawResponse keeps the result as JSON form
 | ||||
| type RawResponse struct { | ||||
| 	Response | ||||
| 	Result json.RawMessage `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // Raw makes a HTTP request with user provided params and returns the
 | ||||
| // result as untouched JSON.
 | ||||
| func (api *API) Raw(method, endpoint string, data interface{}) (json.RawMessage, error) { | ||||
| 	res, err := api.makeRequest(method, endpoint, data) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r RawResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // PaginationOptions can be passed to a list request to configure paging
 | ||||
| // These values will be defaulted if omitted, and PerPage has min/max limits set by resource
 | ||||
| type PaginationOptions struct { | ||||
| 	Page    int `json:"page,omitempty"` | ||||
| 	PerPage int `json:"per_page,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // RetryPolicy specifies number of retries and min/max retry delays
 | ||||
| // This config is used when the client exponentially backs off after errored requests
 | ||||
| type RetryPolicy struct { | ||||
| 	MaxRetries    int | ||||
| 	MinRetryDelay time.Duration | ||||
| 	MaxRetryDelay time.Duration | ||||
| } | ||||
| 
 | ||||
| // Logger defines the interface this library needs to use logging
 | ||||
| // This is a subset of the methods implemented in the log package
 | ||||
| type Logger interface { | ||||
| 	Printf(format string, v ...interface{}) | ||||
| } | ||||
| 
 | ||||
| // ReqOption is a functional option for configuring API requests
 | ||||
| type ReqOption func(opt *reqOption) | ||||
| type reqOption struct { | ||||
| 	params url.Values | ||||
| } | ||||
| 
 | ||||
| // WithZoneFilter applies a filter based on zone name.
 | ||||
| func WithZoneFilter(zone string) ReqOption { | ||||
| 	return func(opt *reqOption) { | ||||
| 		opt.params.Set("name", zone) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithPagination configures the pagination for a response.
 | ||||
| func WithPagination(opts PaginationOptions) ReqOption { | ||||
| 	return func(opt *reqOption) { | ||||
| 		opt.params.Set("page", strconv.Itoa(opts.Page)) | ||||
| 		opt.params.Set("per_page", strconv.Itoa(opts.PerPage)) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										161
									
								
								vendor/github.com/cloudflare/cloudflare-go/custom_hostname.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								vendor/github.com/cloudflare/cloudflare-go/custom_hostname.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,161 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // CustomHostnameSSLSettings represents the SSL settings for a custom hostname.
 | ||||
| type CustomHostnameSSLSettings struct { | ||||
| 	HTTP2         string   `json:"http2,omitempty"` | ||||
| 	TLS13         string   `json:"tls_1_3,omitempty"` | ||||
| 	MinTLSVersion string   `json:"min_tls_version,omitempty"` | ||||
| 	Ciphers       []string `json:"ciphers,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // CustomHostnameSSL represents the SSL section in a given custom hostname.
 | ||||
| type CustomHostnameSSL struct { | ||||
| 	Status      string                    `json:"status,omitempty"` | ||||
| 	Method      string                    `json:"method,omitempty"` | ||||
| 	Type        string                    `json:"type,omitempty"` | ||||
| 	CnameTarget string                    `json:"cname_target,omitempty"` | ||||
| 	CnameName   string                    `json:"cname,omitempty"` | ||||
| 	Settings    CustomHostnameSSLSettings `json:"settings,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // CustomMetadata defines custom metadata for the hostname. This requires logic to be implemented by Cloudflare to act on the data provided.
 | ||||
| type CustomMetadata map[string]interface{} | ||||
| 
 | ||||
| // CustomHostname represents a custom hostname in a zone.
 | ||||
| type CustomHostname struct { | ||||
| 	ID                 string            `json:"id,omitempty"` | ||||
| 	Hostname           string            `json:"hostname,omitempty"` | ||||
| 	CustomOriginServer string            `json:"custom_origin_server,omitempty"` | ||||
| 	SSL                CustomHostnameSSL `json:"ssl,omitempty"` | ||||
| 	CustomMetadata     CustomMetadata    `json:"custom_metadata,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // CustomHostnameResponse represents a response from the Custom Hostnames endpoints.
 | ||||
| type CustomHostnameResponse struct { | ||||
| 	Result CustomHostname `json:"result"` | ||||
| 	Response | ||||
| } | ||||
| 
 | ||||
| // CustomHostnameListResponse represents a response from the Custom Hostnames endpoints.
 | ||||
| type CustomHostnameListResponse struct { | ||||
| 	Result []CustomHostname `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // UpdateCustomHostnameSSL modifies SSL configuration for the given custom
 | ||||
| // hostname in the given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-update-custom-hostname-configuration
 | ||||
| func (api *API) UpdateCustomHostnameSSL(zoneID string, customHostnameID string, ssl CustomHostnameSSL) (CustomHostname, error) { | ||||
| 	return CustomHostname{}, errors.New("Not implemented") | ||||
| } | ||||
| 
 | ||||
| // DeleteCustomHostname deletes a custom hostname (and any issued SSL
 | ||||
| // certificates).
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-delete-a-custom-hostname-and-any-issued-ssl-certificates-
 | ||||
| func (api *API) DeleteCustomHostname(zoneID string, customHostnameID string) error { | ||||
| 	uri := "/zones/" + zoneID + "/custom_hostnames/" + customHostnameID | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var response *CustomHostnameResponse | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // CreateCustomHostname creates a new custom hostname and requests that an SSL certificate be issued for it.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-create-custom-hostname
 | ||||
| func (api *API) CreateCustomHostname(zoneID string, ch CustomHostname) (*CustomHostnameResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/custom_hostnames" | ||||
| 	res, err := api.makeRequest("POST", uri, ch) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var response *CustomHostnameResponse | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| // CustomHostnames fetches custom hostnames for the given zone,
 | ||||
| // by applying filter.Hostname if not empty and scoping the result to page'th 50 items.
 | ||||
| //
 | ||||
| // The returned ResultInfo can be used to implement pagination.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-list-custom-hostnames
 | ||||
| func (api *API) CustomHostnames(zoneID string, page int, filter CustomHostname) ([]CustomHostname, ResultInfo, error) { | ||||
| 	v := url.Values{} | ||||
| 	v.Set("per_page", "50") | ||||
| 	v.Set("page", strconv.Itoa(page)) | ||||
| 	if filter.Hostname != "" { | ||||
| 		v.Set("hostname", filter.Hostname) | ||||
| 	} | ||||
| 	query := "?" + v.Encode() | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/custom_hostnames" + query | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []CustomHostname{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var customHostnameListResponse CustomHostnameListResponse | ||||
| 	err = json.Unmarshal(res, &customHostnameListResponse) | ||||
| 	if err != nil { | ||||
| 		return []CustomHostname{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	return customHostnameListResponse.Result, customHostnameListResponse.ResultInfo, nil | ||||
| } | ||||
| 
 | ||||
| // CustomHostname inspects the given custom hostname in the given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-custom-hostname-configuration-details
 | ||||
| func (api *API) CustomHostname(zoneID string, customHostnameID string) (CustomHostname, error) { | ||||
| 	uri := "/zones/" + zoneID + "/custom_hostnames/" + customHostnameID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return CustomHostname{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var response CustomHostnameResponse | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return CustomHostname{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response.Result, nil | ||||
| } | ||||
| 
 | ||||
| // CustomHostnameIDByName retrieves the ID for the given hostname in the given zone.
 | ||||
| func (api *API) CustomHostnameIDByName(zoneID string, hostname string) (string, error) { | ||||
| 	customHostnames, _, err := api.CustomHostnames(zoneID, 1, CustomHostname{Hostname: hostname}) | ||||
| 	if err != nil { | ||||
| 		return "", errors.Wrap(err, "CustomHostnames command failed") | ||||
| 	} | ||||
| 	for _, ch := range customHostnames { | ||||
| 		if ch.Hostname == hostname { | ||||
| 			return ch.ID, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return "", errors.New("CustomHostname could not be found") | ||||
| } | ||||
							
								
								
									
										176
									
								
								vendor/github.com/cloudflare/cloudflare-go/custom_pages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								vendor/github.com/cloudflare/cloudflare-go/custom_pages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // CustomPage represents a custom page configuration.
 | ||||
| type CustomPage struct { | ||||
| 	CreatedOn      time.Time   `json:"created_on"` | ||||
| 	ModifiedOn     time.Time   `json:"modified_on"` | ||||
| 	URL            interface{} `json:"url"` | ||||
| 	State          string      `json:"state"` | ||||
| 	RequiredTokens []string    `json:"required_tokens"` | ||||
| 	PreviewTarget  string      `json:"preview_target"` | ||||
| 	Description    string      `json:"description"` | ||||
| 	ID             string      `json:"id"` | ||||
| } | ||||
| 
 | ||||
| // CustomPageResponse represents the response from the custom pages endpoint.
 | ||||
| type CustomPageResponse struct { | ||||
| 	Response | ||||
| 	Result []CustomPage `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // CustomPageDetailResponse represents the response from the custom page endpoint.
 | ||||
| type CustomPageDetailResponse struct { | ||||
| 	Response | ||||
| 	Result CustomPage `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // CustomPageOptions is used to determine whether or not the operation
 | ||||
| // should take place on an account or zone level based on which is
 | ||||
| // provided to the function.
 | ||||
| //
 | ||||
| // A non-empty value denotes desired use.
 | ||||
| type CustomPageOptions struct { | ||||
| 	AccountID string | ||||
| 	ZoneID    string | ||||
| } | ||||
| 
 | ||||
| // CustomPageParameters is used to update a particular custom page with
 | ||||
| // the values provided.
 | ||||
| type CustomPageParameters struct { | ||||
| 	URL   interface{} `json:"url"` | ||||
| 	State string      `json:"state"` | ||||
| } | ||||
| 
 | ||||
| // CustomPages lists custom pages for a zone or account.
 | ||||
| //
 | ||||
| // Zone API reference: https://api.cloudflare.com/#custom-pages-for-a-zone-list-available-custom-pages
 | ||||
| // Account API reference: https://api.cloudflare.com/#custom-pages-account--list-custom-pages
 | ||||
| func (api *API) CustomPages(options *CustomPageOptions) ([]CustomPage, error) { | ||||
| 	var ( | ||||
| 		pageType, identifier string | ||||
| 	) | ||||
| 
 | ||||
| 	if options.AccountID == "" && options.ZoneID == "" { | ||||
| 		return nil, errors.New("either account ID or zone ID must be provided") | ||||
| 	} | ||||
| 
 | ||||
| 	if options.AccountID != "" && options.ZoneID != "" { | ||||
| 		return nil, errors.New("account ID and zone ID are mutually exclusive") | ||||
| 	} | ||||
| 
 | ||||
| 	// Should the account ID be defined, treat this as an account level operation.
 | ||||
| 	if options.AccountID != "" { | ||||
| 		pageType = "accounts" | ||||
| 		identifier = options.AccountID | ||||
| 	} else { | ||||
| 		pageType = "zones" | ||||
| 		identifier = options.ZoneID | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf("/%s/%s/custom_pages", pageType, identifier) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var customPageResponse CustomPageResponse | ||||
| 	err = json.Unmarshal(res, &customPageResponse) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return customPageResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // CustomPage lists a single custom page based on the ID.
 | ||||
| //
 | ||||
| // Zone API reference: https://api.cloudflare.com/#custom-pages-for-a-zone-custom-page-details
 | ||||
| // Account API reference: https://api.cloudflare.com/#custom-pages-account--custom-page-details
 | ||||
| func (api *API) CustomPage(options *CustomPageOptions, customPageID string) (CustomPage, error) { | ||||
| 	var ( | ||||
| 		pageType, identifier string | ||||
| 	) | ||||
| 
 | ||||
| 	if options.AccountID == "" && options.ZoneID == "" { | ||||
| 		return CustomPage{}, errors.New("either account ID or zone ID must be provided") | ||||
| 	} | ||||
| 
 | ||||
| 	if options.AccountID != "" && options.ZoneID != "" { | ||||
| 		return CustomPage{}, errors.New("account ID and zone ID are mutually exclusive") | ||||
| 	} | ||||
| 
 | ||||
| 	// Should the account ID be defined, treat this as an account level operation.
 | ||||
| 	if options.AccountID != "" { | ||||
| 		pageType = "accounts" | ||||
| 		identifier = options.AccountID | ||||
| 	} else { | ||||
| 		pageType = "zones" | ||||
| 		identifier = options.ZoneID | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf("/%s/%s/custom_pages/%s", pageType, identifier, customPageID) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return CustomPage{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var customPageResponse CustomPageDetailResponse | ||||
| 	err = json.Unmarshal(res, &customPageResponse) | ||||
| 	if err != nil { | ||||
| 		return CustomPage{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return customPageResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateCustomPage updates a single custom page setting.
 | ||||
| //
 | ||||
| // Zone API reference: https://api.cloudflare.com/#custom-pages-for-a-zone-update-custom-page-url
 | ||||
| // Account API reference: https://api.cloudflare.com/#custom-pages-account--update-custom-page
 | ||||
| func (api *API) UpdateCustomPage(options *CustomPageOptions, customPageID string, pageParameters CustomPageParameters) (CustomPage, error) { | ||||
| 	var ( | ||||
| 		pageType, identifier string | ||||
| 	) | ||||
| 
 | ||||
| 	if options.AccountID == "" && options.ZoneID == "" { | ||||
| 		return CustomPage{}, errors.New("either account ID or zone ID must be provided") | ||||
| 	} | ||||
| 
 | ||||
| 	if options.AccountID != "" && options.ZoneID != "" { | ||||
| 		return CustomPage{}, errors.New("account ID and zone ID are mutually exclusive") | ||||
| 	} | ||||
| 
 | ||||
| 	// Should the account ID be defined, treat this as an account level operation.
 | ||||
| 	if options.AccountID != "" { | ||||
| 		pageType = "accounts" | ||||
| 		identifier = options.AccountID | ||||
| 	} else { | ||||
| 		pageType = "zones" | ||||
| 		identifier = options.ZoneID | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf("/%s/%s/custom_pages/%s", pageType, identifier, customPageID) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, pageParameters) | ||||
| 	if err != nil { | ||||
| 		return CustomPage{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var customPageResponse CustomPageDetailResponse | ||||
| 	err = json.Unmarshal(res, &customPageResponse) | ||||
| 	if err != nil { | ||||
| 		return CustomPage{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return customPageResponse.Result, nil | ||||
| } | ||||
							
								
								
									
										174
									
								
								vendor/github.com/cloudflare/cloudflare-go/dns.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								vendor/github.com/cloudflare/cloudflare-go/dns.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,174 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // DNSRecord represents a DNS record in a zone.
 | ||||
| type DNSRecord struct { | ||||
| 	ID         string      `json:"id,omitempty"` | ||||
| 	Type       string      `json:"type,omitempty"` | ||||
| 	Name       string      `json:"name,omitempty"` | ||||
| 	Content    string      `json:"content,omitempty"` | ||||
| 	Proxiable  bool        `json:"proxiable,omitempty"` | ||||
| 	Proxied    bool        `json:"proxied"` | ||||
| 	TTL        int         `json:"ttl,omitempty"` | ||||
| 	Locked     bool        `json:"locked,omitempty"` | ||||
| 	ZoneID     string      `json:"zone_id,omitempty"` | ||||
| 	ZoneName   string      `json:"zone_name,omitempty"` | ||||
| 	CreatedOn  time.Time   `json:"created_on,omitempty"` | ||||
| 	ModifiedOn time.Time   `json:"modified_on,omitempty"` | ||||
| 	Data       interface{} `json:"data,omitempty"` // data returned by: SRV, LOC
 | ||||
| 	Meta       interface{} `json:"meta,omitempty"` | ||||
| 	Priority   int         `json:"priority"` | ||||
| } | ||||
| 
 | ||||
| // DNSRecordResponse represents the response from the DNS endpoint.
 | ||||
| type DNSRecordResponse struct { | ||||
| 	Result DNSRecord `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // DNSListResponse represents the response from the list DNS records endpoint.
 | ||||
| type DNSListResponse struct { | ||||
| 	Result []DNSRecord `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // CreateDNSRecord creates a DNS record for the zone identifier.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record
 | ||||
| func (api *API) CreateDNSRecord(zoneID string, rr DNSRecord) (*DNSRecordResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/dns_records" | ||||
| 	res, err := api.makeRequest("POST", uri, rr) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var recordResp *DNSRecordResponse | ||||
| 	err = json.Unmarshal(res, &recordResp) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return recordResp, nil | ||||
| } | ||||
| 
 | ||||
| // DNSRecords returns a slice of DNS records for the given zone identifier.
 | ||||
| //
 | ||||
| // This takes a DNSRecord to allow filtering of the results returned.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records
 | ||||
| func (api *API) DNSRecords(zoneID string, rr DNSRecord) ([]DNSRecord, error) { | ||||
| 	// Construct a query string
 | ||||
| 	v := url.Values{} | ||||
| 	// Request as many records as possible per page - API max is 50
 | ||||
| 	v.Set("per_page", "50") | ||||
| 	if rr.Name != "" { | ||||
| 		v.Set("name", rr.Name) | ||||
| 	} | ||||
| 	if rr.Type != "" { | ||||
| 		v.Set("type", rr.Type) | ||||
| 	} | ||||
| 	if rr.Content != "" { | ||||
| 		v.Set("content", rr.Content) | ||||
| 	} | ||||
| 
 | ||||
| 	var query string | ||||
| 	var records []DNSRecord | ||||
| 	page := 1 | ||||
| 
 | ||||
| 	// Loop over makeRequest until what we've fetched all records
 | ||||
| 	for { | ||||
| 		v.Set("page", strconv.Itoa(page)) | ||||
| 		query = "?" + v.Encode() | ||||
| 		uri := "/zones/" + zoneID + "/dns_records" + query | ||||
| 		res, err := api.makeRequest("GET", uri, nil) | ||||
| 		if err != nil { | ||||
| 			return []DNSRecord{}, errors.Wrap(err, errMakeRequestError) | ||||
| 		} | ||||
| 		var r DNSListResponse | ||||
| 		err = json.Unmarshal(res, &r) | ||||
| 		if err != nil { | ||||
| 			return []DNSRecord{}, errors.Wrap(err, errUnmarshalError) | ||||
| 		} | ||||
| 		records = append(records, r.Result...) | ||||
| 		if r.ResultInfo.Page >= r.ResultInfo.TotalPages { | ||||
| 			break | ||||
| 		} | ||||
| 		// Loop around and fetch the next page
 | ||||
| 		page++ | ||||
| 	} | ||||
| 	return records, nil | ||||
| } | ||||
| 
 | ||||
| // DNSRecord returns a single DNS record for the given zone & record
 | ||||
| // identifiers.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#dns-records-for-a-zone-dns-record-details
 | ||||
| func (api *API) DNSRecord(zoneID, recordID string) (DNSRecord, error) { | ||||
| 	uri := "/zones/" + zoneID + "/dns_records/" + recordID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return DNSRecord{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r DNSRecordResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return DNSRecord{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateDNSRecord updates a single DNS record for the given zone & record
 | ||||
| // identifiers.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record
 | ||||
| func (api *API) UpdateDNSRecord(zoneID, recordID string, rr DNSRecord) error { | ||||
| 	rec, err := api.DNSRecord(zoneID, recordID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	// Populate the record name from the existing one if the update didn't
 | ||||
| 	// specify it.
 | ||||
| 	if rr.Name == "" { | ||||
| 		rr.Name = rec.Name | ||||
| 	} | ||||
| 	rr.Type = rec.Type | ||||
| 	uri := "/zones/" + zoneID + "/dns_records/" + recordID | ||||
| 	res, err := api.makeRequest("PATCH", uri, rr) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r DNSRecordResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DeleteDNSRecord deletes a single DNS record for the given zone & record
 | ||||
| // identifiers.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#dns-records-for-a-zone-delete-dns-record
 | ||||
| func (api *API) DeleteDNSRecord(zoneID, recordID string) error { | ||||
| 	uri := "/zones/" + zoneID + "/dns_records/" + recordID | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r DNSRecordResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										40
									
								
								vendor/github.com/cloudflare/cloudflare-go/duration.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/cloudflare/cloudflare-go/duration.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Duration implements json.Marshaler and json.Unmarshaler for time.Duration
 | ||||
| // using the fmt.Stringer interface of time.Duration and time.ParseDuration.
 | ||||
| type Duration struct { | ||||
| 	time.Duration | ||||
| } | ||||
| 
 | ||||
| // MarshalJSON encodes a Duration as a JSON string formatted using String.
 | ||||
| func (d Duration) MarshalJSON() ([]byte, error) { | ||||
| 	return json.Marshal(d.Duration.String()) | ||||
| } | ||||
| 
 | ||||
| // UnmarshalJSON decodes a Duration from a JSON string parsed using time.ParseDuration.
 | ||||
| func (d *Duration) UnmarshalJSON(buf []byte) error { | ||||
| 	var str string | ||||
| 
 | ||||
| 	err := json.Unmarshal(buf, &str) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	dur, err := time.ParseDuration(str) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	d.Duration = dur | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	_ = json.Marshaler((*Duration)(nil)) | ||||
| 	_ = json.Unmarshaler((*Duration)(nil)) | ||||
| ) | ||||
							
								
								
									
										50
									
								
								vendor/github.com/cloudflare/cloudflare-go/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/cloudflare/cloudflare-go/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| // Error messages
 | ||||
| const ( | ||||
| 	errEmptyCredentials     = "invalid credentials: key & email must not be empty" | ||||
| 	errEmptyAPIToken        = "invalid credentials: API Token must not be empty" | ||||
| 	errMakeRequestError     = "error from makeRequest" | ||||
| 	errUnmarshalError       = "error unmarshalling the JSON response" | ||||
| 	errRequestNotSuccessful = "error reported by API" | ||||
| 	errMissingAccountID     = "account ID is empty and must be provided" | ||||
| ) | ||||
| 
 | ||||
| var _ Error = &UserError{} | ||||
| 
 | ||||
| // Error represents an error returned from this library.
 | ||||
| type Error interface { | ||||
| 	error | ||||
| 	// Raised when user credentials or configuration is invalid.
 | ||||
| 	User() bool | ||||
| 	// Raised when a parsing error (e.g. JSON) occurs.
 | ||||
| 	Parse() bool | ||||
| 	// Raised when a network error occurs.
 | ||||
| 	Network() bool | ||||
| 	// Contains the most recent error.
 | ||||
| } | ||||
| 
 | ||||
| // UserError represents a user-generated error.
 | ||||
| type UserError struct { | ||||
| 	Err error | ||||
| } | ||||
| 
 | ||||
| // User is a user-caused error.
 | ||||
| func (e *UserError) User() bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Network error.
 | ||||
| func (e *UserError) Network() bool { | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // Parse error.
 | ||||
| func (e *UserError) Parse() bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Error wraps the underlying error.
 | ||||
| func (e *UserError) Error() string { | ||||
| 	return e.Err.Error() | ||||
| } | ||||
							
								
								
									
										241
									
								
								vendor/github.com/cloudflare/cloudflare-go/filter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								vendor/github.com/cloudflare/cloudflare-go/filter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,241 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // Filter holds the structure of the filter type.
 | ||||
| type Filter struct { | ||||
| 	ID          string `json:"id,omitempty"` | ||||
| 	Expression  string `json:"expression"` | ||||
| 	Paused      bool   `json:"paused"` | ||||
| 	Description string `json:"description"` | ||||
| 
 | ||||
| 	// Property is mentioned in documentation however isn't populated in
 | ||||
| 	// any of the API requests. For now, let's just omit it unless it's
 | ||||
| 	// provided.
 | ||||
| 	Ref string `json:"ref,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // FiltersDetailResponse is the API response that is returned
 | ||||
| // for requesting all filters on a zone.
 | ||||
| type FiltersDetailResponse struct { | ||||
| 	Result     []Filter `json:"result"` | ||||
| 	ResultInfo `json:"result_info"` | ||||
| 	Response | ||||
| } | ||||
| 
 | ||||
| // FilterDetailResponse is the API response that is returned
 | ||||
| // for requesting a single filter on a zone.
 | ||||
| type FilterDetailResponse struct { | ||||
| 	Result     Filter `json:"result"` | ||||
| 	ResultInfo `json:"result_info"` | ||||
| 	Response | ||||
| } | ||||
| 
 | ||||
| // FilterValidateExpression represents the JSON payload for checking
 | ||||
| // an expression.
 | ||||
| type FilterValidateExpression struct { | ||||
| 	Expression string `json:"expression"` | ||||
| } | ||||
| 
 | ||||
| // FilterValidateExpressionResponse represents the API response for
 | ||||
| // checking the expression. It conforms to the JSON API approach however
 | ||||
| // we don't need all of the fields exposed.
 | ||||
| type FilterValidateExpressionResponse struct { | ||||
| 	Success bool                                `json:"success"` | ||||
| 	Errors  []FilterValidationExpressionMessage `json:"errors"` | ||||
| } | ||||
| 
 | ||||
| // FilterValidationExpressionMessage represents the API error message.
 | ||||
| type FilterValidationExpressionMessage struct { | ||||
| 	Message string `json:"message"` | ||||
| } | ||||
| 
 | ||||
| // Filter returns a single filter in a zone based on the filter ID.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-filters/get/#get-by-filter-id
 | ||||
| func (api *API) Filter(zoneID, filterID string) (Filter, error) { | ||||
| 	uri := fmt.Sprintf("/zones/%s/filters/%s", zoneID, filterID) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return Filter{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var filterResponse FilterDetailResponse | ||||
| 	err = json.Unmarshal(res, &filterResponse) | ||||
| 	if err != nil { | ||||
| 		return Filter{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return filterResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // Filters returns all filters for a zone.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-filters/get/#get-all-filters
 | ||||
| func (api *API) Filters(zoneID string, pageOpts PaginationOptions) ([]Filter, error) { | ||||
| 	uri := "/zones/" + zoneID + "/filters" | ||||
| 	v := url.Values{} | ||||
| 
 | ||||
| 	if pageOpts.PerPage > 0 { | ||||
| 		v.Set("per_page", strconv.Itoa(pageOpts.PerPage)) | ||||
| 	} | ||||
| 
 | ||||
| 	if pageOpts.Page > 0 { | ||||
| 		v.Set("page", strconv.Itoa(pageOpts.Page)) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(v) > 0 { | ||||
| 		uri = uri + "?" + v.Encode() | ||||
| 	} | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []Filter{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var filtersResponse FiltersDetailResponse | ||||
| 	err = json.Unmarshal(res, &filtersResponse) | ||||
| 	if err != nil { | ||||
| 		return []Filter{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return filtersResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // CreateFilters creates new filters.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-filters/post/
 | ||||
| func (api *API) CreateFilters(zoneID string, filters []Filter) ([]Filter, error) { | ||||
| 	uri := "/zones/" + zoneID + "/filters" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", uri, filters) | ||||
| 	if err != nil { | ||||
| 		return []Filter{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var filtersResponse FiltersDetailResponse | ||||
| 	err = json.Unmarshal(res, &filtersResponse) | ||||
| 	if err != nil { | ||||
| 		return []Filter{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return filtersResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateFilter updates a single filter.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-filters/put/#update-a-single-filter
 | ||||
| func (api *API) UpdateFilter(zoneID string, filter Filter) (Filter, error) { | ||||
| 	if filter.ID == "" { | ||||
| 		return Filter{}, errors.Errorf("filter ID cannot be empty") | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf("/zones/%s/filters/%s", zoneID, filter.ID) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, filter) | ||||
| 	if err != nil { | ||||
| 		return Filter{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var filterResponse FilterDetailResponse | ||||
| 	err = json.Unmarshal(res, &filterResponse) | ||||
| 	if err != nil { | ||||
| 		return Filter{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return filterResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateFilters updates many filters at once.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-filters/put/#update-multiple-filters
 | ||||
| func (api *API) UpdateFilters(zoneID string, filters []Filter) ([]Filter, error) { | ||||
| 	for _, filter := range filters { | ||||
| 		if filter.ID == "" { | ||||
| 			return []Filter{}, errors.Errorf("filter ID cannot be empty") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/filters" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, filters) | ||||
| 	if err != nil { | ||||
| 		return []Filter{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var filtersResponse FiltersDetailResponse | ||||
| 	err = json.Unmarshal(res, &filtersResponse) | ||||
| 	if err != nil { | ||||
| 		return []Filter{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return filtersResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteFilter deletes a single filter.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-filters/delete/#delete-a-single-filter
 | ||||
| func (api *API) DeleteFilter(zoneID, filterID string) error { | ||||
| 	if filterID == "" { | ||||
| 		return errors.Errorf("filter ID cannot be empty") | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf("/zones/%s/filters/%s", zoneID, filterID) | ||||
| 
 | ||||
| 	_, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DeleteFilters deletes multiple filters.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-filters/delete/#delete-multiple-filters
 | ||||
| func (api *API) DeleteFilters(zoneID string, filterIDs []string) error { | ||||
| 	ids := strings.Join(filterIDs, ",") | ||||
| 	uri := fmt.Sprintf("/zones/%s/filters?id=%s", zoneID, ids) | ||||
| 
 | ||||
| 	_, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ValidateFilterExpression checks correctness of a filter expression.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-filters/validation/
 | ||||
| func (api *API) ValidateFilterExpression(expression string) error { | ||||
| 	uri := fmt.Sprintf("/filters/validate-expr") | ||||
| 	expressionPayload := FilterValidateExpression{Expression: expression} | ||||
| 
 | ||||
| 	_, err := api.makeRequest("POST", uri, expressionPayload) | ||||
| 	if err != nil { | ||||
| 		var filterValidationResponse FilterValidateExpressionResponse | ||||
| 
 | ||||
| 		jsonErr := json.Unmarshal([]byte(err.Error()), &filterValidationResponse) | ||||
| 		if jsonErr != nil { | ||||
| 			return errors.Wrap(jsonErr, errUnmarshalError) | ||||
| 		} | ||||
| 
 | ||||
| 		if filterValidationResponse.Success != true { | ||||
| 			// Unsure why but the API returns `errors` as an array but it only
 | ||||
| 			// ever shows the issue with one problem at a time ¯\_(ツ)_/¯
 | ||||
| 			return errors.Errorf(filterValidationResponse.Errors[0].Message) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										280
									
								
								vendor/github.com/cloudflare/cloudflare-go/firewall.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								vendor/github.com/cloudflare/cloudflare-go/firewall.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,280 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // AccessRule represents a firewall access rule.
 | ||||
| type AccessRule struct { | ||||
| 	ID            string                  `json:"id,omitempty"` | ||||
| 	Notes         string                  `json:"notes,omitempty"` | ||||
| 	AllowedModes  []string                `json:"allowed_modes,omitempty"` | ||||
| 	Mode          string                  `json:"mode,omitempty"` | ||||
| 	Configuration AccessRuleConfiguration `json:"configuration,omitempty"` | ||||
| 	Scope         AccessRuleScope         `json:"scope,omitempty"` | ||||
| 	CreatedOn     time.Time               `json:"created_on,omitempty"` | ||||
| 	ModifiedOn    time.Time               `json:"modified_on,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // AccessRuleConfiguration represents the configuration of a firewall
 | ||||
| // access rule.
 | ||||
| type AccessRuleConfiguration struct { | ||||
| 	Target string `json:"target,omitempty"` | ||||
| 	Value  string `json:"value,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // AccessRuleScope represents the scope of a firewall access rule.
 | ||||
| type AccessRuleScope struct { | ||||
| 	ID    string `json:"id,omitempty"` | ||||
| 	Email string `json:"email,omitempty"` | ||||
| 	Name  string `json:"name,omitempty"` | ||||
| 	Type  string `json:"type,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // AccessRuleResponse represents the response from the firewall access
 | ||||
| // rule endpoint.
 | ||||
| type AccessRuleResponse struct { | ||||
| 	Result AccessRule `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // AccessRuleListResponse represents the response from the list access rules
 | ||||
| // endpoint.
 | ||||
| type AccessRuleListResponse struct { | ||||
| 	Result []AccessRule `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // ListUserAccessRules returns a slice of access rules for the logged-in user.
 | ||||
| //
 | ||||
| // This takes an AccessRule to allow filtering of the results returned.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-level-firewall-access-rule-list-access-rules
 | ||||
| func (api *API) ListUserAccessRules(accessRule AccessRule, page int) (*AccessRuleListResponse, error) { | ||||
| 	return api.listAccessRules("/user", accessRule, page) | ||||
| } | ||||
| 
 | ||||
| // CreateUserAccessRule creates a firewall access rule for the logged-in user.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-level-firewall-access-rule-create-access-rule
 | ||||
| func (api *API) CreateUserAccessRule(accessRule AccessRule) (*AccessRuleResponse, error) { | ||||
| 	return api.createAccessRule("/user", accessRule) | ||||
| } | ||||
| 
 | ||||
| // UserAccessRule returns the details of a user's account access rule.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-level-firewall-access-rule-list-access-rules
 | ||||
| func (api *API) UserAccessRule(accessRuleID string) (*AccessRuleResponse, error) { | ||||
| 	return api.retrieveAccessRule("/user", accessRuleID) | ||||
| } | ||||
| 
 | ||||
| // UpdateUserAccessRule updates a single access rule for the logged-in user &
 | ||||
| // given access rule identifier.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-level-firewall-access-rule-update-access-rule
 | ||||
| func (api *API) UpdateUserAccessRule(accessRuleID string, accessRule AccessRule) (*AccessRuleResponse, error) { | ||||
| 	return api.updateAccessRule("/user", accessRuleID, accessRule) | ||||
| } | ||||
| 
 | ||||
| // DeleteUserAccessRule deletes a single access rule for the logged-in user and
 | ||||
| // access rule identifiers.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-level-firewall-access-rule-update-access-rule
 | ||||
| func (api *API) DeleteUserAccessRule(accessRuleID string) (*AccessRuleResponse, error) { | ||||
| 	return api.deleteAccessRule("/user", accessRuleID) | ||||
| } | ||||
| 
 | ||||
| // ListZoneAccessRules returns a slice of access rules for the given zone
 | ||||
| // identifier.
 | ||||
| //
 | ||||
| // This takes an AccessRule to allow filtering of the results returned.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#firewall-access-rule-for-a-zone-list-access-rules
 | ||||
| func (api *API) ListZoneAccessRules(zoneID string, accessRule AccessRule, page int) (*AccessRuleListResponse, error) { | ||||
| 	return api.listAccessRules("/zones/"+zoneID, accessRule, page) | ||||
| } | ||||
| 
 | ||||
| // CreateZoneAccessRule creates a firewall access rule for the given zone
 | ||||
| // identifier.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#firewall-access-rule-for-a-zone-create-access-rule
 | ||||
| func (api *API) CreateZoneAccessRule(zoneID string, accessRule AccessRule) (*AccessRuleResponse, error) { | ||||
| 	return api.createAccessRule("/zones/"+zoneID, accessRule) | ||||
| } | ||||
| 
 | ||||
| // ZoneAccessRule returns the details of a zone's access rule.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#firewall-access-rule-for-a-zone-list-access-rules
 | ||||
| func (api *API) ZoneAccessRule(zoneID string, accessRuleID string) (*AccessRuleResponse, error) { | ||||
| 	return api.retrieveAccessRule("/zones/"+zoneID, accessRuleID) | ||||
| } | ||||
| 
 | ||||
| // UpdateZoneAccessRule updates a single access rule for the given zone &
 | ||||
| // access rule identifiers.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#firewall-access-rule-for-a-zone-update-access-rule
 | ||||
| func (api *API) UpdateZoneAccessRule(zoneID, accessRuleID string, accessRule AccessRule) (*AccessRuleResponse, error) { | ||||
| 	return api.updateAccessRule("/zones/"+zoneID, accessRuleID, accessRule) | ||||
| } | ||||
| 
 | ||||
| // DeleteZoneAccessRule deletes a single access rule for the given zone and
 | ||||
| // access rule identifiers.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#firewall-access-rule-for-a-zone-delete-access-rule
 | ||||
| func (api *API) DeleteZoneAccessRule(zoneID, accessRuleID string) (*AccessRuleResponse, error) { | ||||
| 	return api.deleteAccessRule("/zones/"+zoneID, accessRuleID) | ||||
| } | ||||
| 
 | ||||
| // ListAccountAccessRules returns a slice of access rules for the given
 | ||||
| // account identifier.
 | ||||
| //
 | ||||
| // This takes an AccessRule to allow filtering of the results returned.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#account-level-firewall-access-rule-list-access-rules
 | ||||
| func (api *API) ListAccountAccessRules(accountID string, accessRule AccessRule, page int) (*AccessRuleListResponse, error) { | ||||
| 	return api.listAccessRules("/accounts/"+accountID, accessRule, page) | ||||
| } | ||||
| 
 | ||||
| // CreateAccountAccessRule creates a firewall access rule for the given
 | ||||
| // account identifier.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#account-level-firewall-access-rule-create-access-rule
 | ||||
| func (api *API) CreateAccountAccessRule(accountID string, accessRule AccessRule) (*AccessRuleResponse, error) { | ||||
| 	return api.createAccessRule("/accounts/"+accountID, accessRule) | ||||
| } | ||||
| 
 | ||||
| // AccountAccessRule returns the details of an account's access rule.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#account-level-firewall-access-rule-access-rule-details
 | ||||
| func (api *API) AccountAccessRule(accountID string, accessRuleID string) (*AccessRuleResponse, error) { | ||||
| 	return api.retrieveAccessRule("/accounts/"+accountID, accessRuleID) | ||||
| } | ||||
| 
 | ||||
| // UpdateAccountAccessRule updates a single access rule for the given
 | ||||
| // account & access rule identifiers.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#account-level-firewall-access-rule-update-access-rule
 | ||||
| func (api *API) UpdateAccountAccessRule(accountID, accessRuleID string, accessRule AccessRule) (*AccessRuleResponse, error) { | ||||
| 	return api.updateAccessRule("/accounts/"+accountID, accessRuleID, accessRule) | ||||
| } | ||||
| 
 | ||||
| // DeleteAccountAccessRule deletes a single access rule for the given
 | ||||
| // account and access rule identifiers.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#account-level-firewall-access-rule-delete-access-rule
 | ||||
| func (api *API) DeleteAccountAccessRule(accountID, accessRuleID string) (*AccessRuleResponse, error) { | ||||
| 	return api.deleteAccessRule("/accounts/"+accountID, accessRuleID) | ||||
| } | ||||
| 
 | ||||
| func (api *API) listAccessRules(prefix string, accessRule AccessRule, page int) (*AccessRuleListResponse, error) { | ||||
| 	// Construct a query string
 | ||||
| 	v := url.Values{} | ||||
| 	if page <= 0 { | ||||
| 		page = 1 | ||||
| 	} | ||||
| 	v.Set("page", strconv.Itoa(page)) | ||||
| 	// Request as many rules as possible per page - API max is 100
 | ||||
| 	v.Set("per_page", "100") | ||||
| 	if accessRule.Notes != "" { | ||||
| 		v.Set("notes", accessRule.Notes) | ||||
| 	} | ||||
| 	if accessRule.Mode != "" { | ||||
| 		v.Set("mode", accessRule.Mode) | ||||
| 	} | ||||
| 	if accessRule.Scope.Type != "" { | ||||
| 		v.Set("scope_type", accessRule.Scope.Type) | ||||
| 	} | ||||
| 	if accessRule.Configuration.Value != "" { | ||||
| 		v.Set("configuration_value", accessRule.Configuration.Value) | ||||
| 	} | ||||
| 	if accessRule.Configuration.Target != "" { | ||||
| 		v.Set("configuration_target", accessRule.Configuration.Target) | ||||
| 	} | ||||
| 	v.Set("page", strconv.Itoa(page)) | ||||
| 	query := "?" + v.Encode() | ||||
| 
 | ||||
| 	uri := prefix + "/firewall/access_rules/rules" + query | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &AccessRuleListResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| func (api *API) createAccessRule(prefix string, accessRule AccessRule) (*AccessRuleResponse, error) { | ||||
| 	uri := prefix + "/firewall/access_rules/rules" | ||||
| 	res, err := api.makeRequest("POST", uri, accessRule) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &AccessRuleResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| func (api *API) retrieveAccessRule(prefix, accessRuleID string) (*AccessRuleResponse, error) { | ||||
| 	uri := prefix + "/firewall/access_rules/rules/" + accessRuleID | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &AccessRuleResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| func (api *API) updateAccessRule(prefix, accessRuleID string, accessRule AccessRule) (*AccessRuleResponse, error) { | ||||
| 	uri := prefix + "/firewall/access_rules/rules/" + accessRuleID | ||||
| 	res, err := api.makeRequest("PATCH", uri, accessRule) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &AccessRuleResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| func (api *API) deleteAccessRule(prefix, accessRuleID string) (*AccessRuleResponse, error) { | ||||
| 	uri := prefix + "/firewall/access_rules/rules/" + accessRuleID | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &AccessRuleResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
							
								
								
									
										196
									
								
								vendor/github.com/cloudflare/cloudflare-go/firewall_rules.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								vendor/github.com/cloudflare/cloudflare-go/firewall_rules.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,196 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // FirewallRule is the struct of the firewall rule.
 | ||||
| type FirewallRule struct { | ||||
| 	ID          string      `json:"id,omitempty"` | ||||
| 	Paused      bool        `json:"paused"` | ||||
| 	Description string      `json:"description"` | ||||
| 	Action      string      `json:"action"` | ||||
| 	Priority    interface{} `json:"priority"` | ||||
| 	Filter      Filter      `json:"filter"` | ||||
| 	CreatedOn   time.Time   `json:"created_on,omitempty"` | ||||
| 	ModifiedOn  time.Time   `json:"modified_on,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // FirewallRulesDetailResponse is the API response for the firewall
 | ||||
| // rules.
 | ||||
| type FirewallRulesDetailResponse struct { | ||||
| 	Result     []FirewallRule `json:"result"` | ||||
| 	ResultInfo `json:"result_info"` | ||||
| 	Response | ||||
| } | ||||
| 
 | ||||
| // FirewallRuleResponse is the API response that is returned
 | ||||
| // for requesting a single firewall rule on a zone.
 | ||||
| type FirewallRuleResponse struct { | ||||
| 	Result     FirewallRule `json:"result"` | ||||
| 	ResultInfo `json:"result_info"` | ||||
| 	Response | ||||
| } | ||||
| 
 | ||||
| // FirewallRules returns all firewall rules.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-firewall-rules/get/#get-all-rules
 | ||||
| func (api *API) FirewallRules(zoneID string, pageOpts PaginationOptions) ([]FirewallRule, error) { | ||||
| 	uri := fmt.Sprintf("/zones/%s/firewall/rules", zoneID) | ||||
| 	v := url.Values{} | ||||
| 
 | ||||
| 	if pageOpts.PerPage > 0 { | ||||
| 		v.Set("per_page", strconv.Itoa(pageOpts.PerPage)) | ||||
| 	} | ||||
| 
 | ||||
| 	if pageOpts.Page > 0 { | ||||
| 		v.Set("page", strconv.Itoa(pageOpts.Page)) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(v) > 0 { | ||||
| 		uri = uri + "?" + v.Encode() | ||||
| 	} | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []FirewallRule{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var firewallDetailResponse FirewallRulesDetailResponse | ||||
| 	err = json.Unmarshal(res, &firewallDetailResponse) | ||||
| 	if err != nil { | ||||
| 		return []FirewallRule{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return firewallDetailResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // FirewallRule returns a single firewall rule based on the ID.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-firewall-rules/get/#get-by-rule-id
 | ||||
| func (api *API) FirewallRule(zoneID, firewallRuleID string) (FirewallRule, error) { | ||||
| 	uri := fmt.Sprintf("/zones/%s/firewall/rules/%s", zoneID, firewallRuleID) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return FirewallRule{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var firewallRuleResponse FirewallRuleResponse | ||||
| 	err = json.Unmarshal(res, &firewallRuleResponse) | ||||
| 	if err != nil { | ||||
| 		return FirewallRule{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return firewallRuleResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // CreateFirewallRules creates new firewall rules.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-firewall-rules/post/
 | ||||
| func (api *API) CreateFirewallRules(zoneID string, firewallRules []FirewallRule) ([]FirewallRule, error) { | ||||
| 	uri := fmt.Sprintf("/zones/%s/firewall/rules", zoneID) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", uri, firewallRules) | ||||
| 	if err != nil { | ||||
| 		return []FirewallRule{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var firewallRulesDetailResponse FirewallRulesDetailResponse | ||||
| 	err = json.Unmarshal(res, &firewallRulesDetailResponse) | ||||
| 	if err != nil { | ||||
| 		return []FirewallRule{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return firewallRulesDetailResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateFirewallRule updates a single firewall rule.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-firewall-rules/put/#update-a-single-rule
 | ||||
| func (api *API) UpdateFirewallRule(zoneID string, firewallRule FirewallRule) (FirewallRule, error) { | ||||
| 	if firewallRule.ID == "" { | ||||
| 		return FirewallRule{}, errors.Errorf("firewall rule ID cannot be empty") | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf("/zones/%s/firewall/rules/%s", zoneID, firewallRule.ID) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, firewallRule) | ||||
| 	if err != nil { | ||||
| 		return FirewallRule{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var firewallRuleResponse FirewallRuleResponse | ||||
| 	err = json.Unmarshal(res, &firewallRuleResponse) | ||||
| 	if err != nil { | ||||
| 		return FirewallRule{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return firewallRuleResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateFirewallRules updates a single firewall rule.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-firewall-rules/put/#update-multiple-rules
 | ||||
| func (api *API) UpdateFirewallRules(zoneID string, firewallRules []FirewallRule) ([]FirewallRule, error) { | ||||
| 	for _, firewallRule := range firewallRules { | ||||
| 		if firewallRule.ID == "" { | ||||
| 			return []FirewallRule{}, errors.Errorf("firewall ID cannot be empty") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf("/zones/%s/firewall/rules", zoneID) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, firewallRules) | ||||
| 	if err != nil { | ||||
| 		return []FirewallRule{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var firewallRulesDetailResponse FirewallRulesDetailResponse | ||||
| 	err = json.Unmarshal(res, &firewallRulesDetailResponse) | ||||
| 	if err != nil { | ||||
| 		return []FirewallRule{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return firewallRulesDetailResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteFirewallRule updates a single firewall rule.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-firewall-rules/delete/#delete-a-single-rule
 | ||||
| func (api *API) DeleteFirewallRule(zoneID, firewallRuleID string) error { | ||||
| 	if firewallRuleID == "" { | ||||
| 		return errors.Errorf("firewall rule ID cannot be empty") | ||||
| 	} | ||||
| 
 | ||||
| 	uri := fmt.Sprintf("/zones/%s/firewall/rules/%s", zoneID, firewallRuleID) | ||||
| 
 | ||||
| 	_, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DeleteFirewallRules updates a single firewall rule.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/firewall/api/cf-firewall-rules/delete/#delete-multiple-rules
 | ||||
| func (api *API) DeleteFirewallRules(zoneID string, firewallRuleIDs []string) error { | ||||
| 	ids := strings.Join(firewallRuleIDs, ",") | ||||
| 	uri := fmt.Sprintf("/zones/%s/firewall/rules?id=%s", zoneID, ids) | ||||
| 
 | ||||
| 	_, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										13
									
								
								vendor/github.com/cloudflare/cloudflare-go/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/cloudflare/cloudflare-go/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| module github.com/cloudflare/cloudflare-go | ||||
| 
 | ||||
| go 1.11 | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/davecgh/go-spew v1.1.1 // indirect | ||||
| 	github.com/mattn/go-runewidth v0.0.4 // indirect | ||||
| 	github.com/olekukonko/tablewriter v0.0.1 | ||||
| 	github.com/pkg/errors v0.8.1 | ||||
| 	github.com/stretchr/testify v1.4.0 | ||||
| 	github.com/urfave/cli v1.22.1 | ||||
| 	golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 | ||||
| ) | ||||
							
								
								
									
										26
									
								
								vendor/github.com/cloudflare/cloudflare-go/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/cloudflare/cloudflare-go/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= | ||||
| github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= | ||||
| github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= | ||||
| github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= | ||||
| github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= | ||||
| github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||||
| github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||
| github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE= | ||||
| github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= | ||||
| github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= | ||||
| golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= | ||||
| golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
							
								
								
									
										44
									
								
								vendor/github.com/cloudflare/cloudflare-go/ips.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								vendor/github.com/cloudflare/cloudflare-go/ips.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // IPRanges contains lists of IPv4 and IPv6 CIDRs.
 | ||||
| type IPRanges struct { | ||||
| 	IPv4CIDRs []string `json:"ipv4_cidrs"` | ||||
| 	IPv6CIDRs []string `json:"ipv6_cidrs"` | ||||
| } | ||||
| 
 | ||||
| // IPsResponse is the API response containing a list of IPs.
 | ||||
| type IPsResponse struct { | ||||
| 	Response | ||||
| 	Result IPRanges `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // IPs gets a list of Cloudflare's IP ranges.
 | ||||
| //
 | ||||
| // This does not require logging in to the API.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#cloudflare-ips
 | ||||
| func IPs() (IPRanges, error) { | ||||
| 	resp, err := http.Get(apiURL + "/ips") | ||||
| 	if err != nil { | ||||
| 		return IPRanges{}, errors.Wrap(err, "HTTP request failed") | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 	body, err := ioutil.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return IPRanges{}, errors.Wrap(err, "Response body could not be read") | ||||
| 	} | ||||
| 	var r IPsResponse | ||||
| 	err = json.Unmarshal(body, &r) | ||||
| 	if err != nil { | ||||
| 		return IPRanges{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
							
								
								
									
										52
									
								
								vendor/github.com/cloudflare/cloudflare-go/keyless.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								vendor/github.com/cloudflare/cloudflare-go/keyless.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
| // KeylessSSL represents Keyless SSL configuration.
 | ||||
| type KeylessSSL struct { | ||||
| 	ID          string    `json:"id"` | ||||
| 	Name        string    `json:"name"` | ||||
| 	Host        string    `json:"host"` | ||||
| 	Port        int       `json:"port"` | ||||
| 	Status      string    `json:"success"` | ||||
| 	Enabled     bool      `json:"enabled"` | ||||
| 	Permissions []string  `json:"permissions"` | ||||
| 	CreatedOn   time.Time `json:"created_on"` | ||||
| 	ModifiedOn  time.Time `json:"modifed_on"` | ||||
| } | ||||
| 
 | ||||
| // KeylessSSLResponse represents the response from the Keyless SSL endpoint.
 | ||||
| type KeylessSSLResponse struct { | ||||
| 	Response | ||||
| 	Result []KeylessSSL `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // CreateKeyless creates a new Keyless SSL configuration for the zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#keyless-ssl-for-a-zone-create-a-keyless-ssl-configuration
 | ||||
| func (api *API) CreateKeyless() { | ||||
| } | ||||
| 
 | ||||
| // ListKeyless lists Keyless SSL configurations for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#keyless-ssl-for-a-zone-list-keyless-ssls
 | ||||
| func (api *API) ListKeyless() { | ||||
| } | ||||
| 
 | ||||
| // Keyless provides the configuration for a given Keyless SSL identifier.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#keyless-ssl-for-a-zone-keyless-ssl-details
 | ||||
| func (api *API) Keyless() { | ||||
| } | ||||
| 
 | ||||
| // UpdateKeyless updates an existing Keyless SSL configuration.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#keyless-ssl-for-a-zone-update-keyless-configuration
 | ||||
| func (api *API) UpdateKeyless() { | ||||
| } | ||||
| 
 | ||||
| // DeleteKeyless deletes an existing Keyless SSL configuration.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#keyless-ssl-for-a-zone-delete-keyless-configuration
 | ||||
| func (api *API) DeleteKeyless() { | ||||
| } | ||||
							
								
								
									
										387
									
								
								vendor/github.com/cloudflare/cloudflare-go/load_balancing.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										387
									
								
								vendor/github.com/cloudflare/cloudflare-go/load_balancing.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,387 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // LoadBalancerPool represents a load balancer pool's properties.
 | ||||
| type LoadBalancerPool struct { | ||||
| 	ID                string               `json:"id,omitempty"` | ||||
| 	CreatedOn         *time.Time           `json:"created_on,omitempty"` | ||||
| 	ModifiedOn        *time.Time           `json:"modified_on,omitempty"` | ||||
| 	Description       string               `json:"description"` | ||||
| 	Name              string               `json:"name"` | ||||
| 	Enabled           bool                 `json:"enabled"` | ||||
| 	MinimumOrigins    int                  `json:"minimum_origins,omitempty"` | ||||
| 	Monitor           string               `json:"monitor,omitempty"` | ||||
| 	Origins           []LoadBalancerOrigin `json:"origins"` | ||||
| 	NotificationEmail string               `json:"notification_email,omitempty"` | ||||
| 
 | ||||
| 	// CheckRegions defines the geographic region(s) from where to run health-checks from - e.g. "WNAM", "WEU", "SAF", "SAM".
 | ||||
| 	// Providing a null/empty value means "all regions", which may not be available to all plan types.
 | ||||
| 	CheckRegions []string `json:"check_regions"` | ||||
| } | ||||
| 
 | ||||
| // LoadBalancerOrigin represents a Load Balancer origin's properties.
 | ||||
| type LoadBalancerOrigin struct { | ||||
| 	Name    string  `json:"name"` | ||||
| 	Address string  `json:"address"` | ||||
| 	Enabled bool    `json:"enabled"` | ||||
| 	Weight  float64 `json:"weight"` | ||||
| } | ||||
| 
 | ||||
| // LoadBalancerMonitor represents a load balancer monitor's properties.
 | ||||
| type LoadBalancerMonitor struct { | ||||
| 	ID              string              `json:"id,omitempty"` | ||||
| 	CreatedOn       *time.Time          `json:"created_on,omitempty"` | ||||
| 	ModifiedOn      *time.Time          `json:"modified_on,omitempty"` | ||||
| 	Type            string              `json:"type"` | ||||
| 	Description     string              `json:"description"` | ||||
| 	Method          string              `json:"method"` | ||||
| 	Path            string              `json:"path"` | ||||
| 	Header          map[string][]string `json:"header"` | ||||
| 	Timeout         int                 `json:"timeout"` | ||||
| 	Retries         int                 `json:"retries"` | ||||
| 	Interval        int                 `json:"interval"` | ||||
| 	Port            uint16              `json:"port,omitempty"` | ||||
| 	ExpectedBody    string              `json:"expected_body"` | ||||
| 	ExpectedCodes   string              `json:"expected_codes"` | ||||
| 	FollowRedirects bool                `json:"follow_redirects"` | ||||
| 	AllowInsecure   bool                `json:"allow_insecure"` | ||||
| 	ProbeZone       string              `json:"probe_zone"` | ||||
| } | ||||
| 
 | ||||
| // LoadBalancer represents a load balancer's properties.
 | ||||
| type LoadBalancer struct { | ||||
| 	ID             string              `json:"id,omitempty"` | ||||
| 	CreatedOn      *time.Time          `json:"created_on,omitempty"` | ||||
| 	ModifiedOn     *time.Time          `json:"modified_on,omitempty"` | ||||
| 	Description    string              `json:"description"` | ||||
| 	Name           string              `json:"name"` | ||||
| 	TTL            int                 `json:"ttl,omitempty"` | ||||
| 	FallbackPool   string              `json:"fallback_pool"` | ||||
| 	DefaultPools   []string            `json:"default_pools"` | ||||
| 	RegionPools    map[string][]string `json:"region_pools"` | ||||
| 	PopPools       map[string][]string `json:"pop_pools"` | ||||
| 	Proxied        bool                `json:"proxied"` | ||||
| 	Enabled        *bool               `json:"enabled,omitempty"` | ||||
| 	Persistence    string              `json:"session_affinity,omitempty"` | ||||
| 	PersistenceTTL int                 `json:"session_affinity_ttl,omitempty"` | ||||
| 
 | ||||
| 	// SteeringPolicy controls pool selection logic.
 | ||||
| 	// "off" select pools in DefaultPools order
 | ||||
| 	// "geo" select pools based on RegionPools/PopPools
 | ||||
| 	// "dynamic_latency" select pools based on RTT (requires health checks)
 | ||||
| 	// "random" selects pools in a random order
 | ||||
| 	// "" maps to "geo" if RegionPools or PopPools have entries otherwise "off"
 | ||||
| 	SteeringPolicy string `json:"steering_policy,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // LoadBalancerOriginHealth represents the health of the origin.
 | ||||
| type LoadBalancerOriginHealth struct { | ||||
| 	Healthy       bool     `json:"healthy,omitempty"` | ||||
| 	RTT           Duration `json:"rtt,omitempty"` | ||||
| 	FailureReason string   `json:"failure_reason,omitempty"` | ||||
| 	ResponseCode  int      `json:"response_code,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // LoadBalancerPoolPopHealth represents the health of the pool for given PoP.
 | ||||
| type LoadBalancerPoolPopHealth struct { | ||||
| 	Healthy bool                                  `json:"healthy,omitempty"` | ||||
| 	Origins []map[string]LoadBalancerOriginHealth `json:"origins,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // LoadBalancerPoolHealth represents the healthchecks from different PoPs for a pool.
 | ||||
| type LoadBalancerPoolHealth struct { | ||||
| 	ID        string                               `json:"pool_id,omitempty"` | ||||
| 	PopHealth map[string]LoadBalancerPoolPopHealth `json:"pop_health,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // loadBalancerPoolResponse represents the response from the load balancer pool endpoints.
 | ||||
| type loadBalancerPoolResponse struct { | ||||
| 	Response | ||||
| 	Result LoadBalancerPool `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // loadBalancerPoolListResponse represents the response from the List Pools endpoint.
 | ||||
| type loadBalancerPoolListResponse struct { | ||||
| 	Response | ||||
| 	Result     []LoadBalancerPool `json:"result"` | ||||
| 	ResultInfo ResultInfo         `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // loadBalancerMonitorResponse represents the response from the load balancer monitor endpoints.
 | ||||
| type loadBalancerMonitorResponse struct { | ||||
| 	Response | ||||
| 	Result LoadBalancerMonitor `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // loadBalancerMonitorListResponse represents the response from the List Monitors endpoint.
 | ||||
| type loadBalancerMonitorListResponse struct { | ||||
| 	Response | ||||
| 	Result     []LoadBalancerMonitor `json:"result"` | ||||
| 	ResultInfo ResultInfo            `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // loadBalancerResponse represents the response from the load balancer endpoints.
 | ||||
| type loadBalancerResponse struct { | ||||
| 	Response | ||||
| 	Result LoadBalancer `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // loadBalancerListResponse represents the response from the List Load Balancers endpoint.
 | ||||
| type loadBalancerListResponse struct { | ||||
| 	Response | ||||
| 	Result     []LoadBalancer `json:"result"` | ||||
| 	ResultInfo ResultInfo     `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // loadBalancerPoolHealthResponse represents the response from the Pool Health Details endpoint.
 | ||||
| type loadBalancerPoolHealthResponse struct { | ||||
| 	Response | ||||
| 	Result LoadBalancerPoolHealth `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // CreateLoadBalancerPool creates a new load balancer pool.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancer-pools-create-a-pool
 | ||||
| func (api *API) CreateLoadBalancerPool(pool LoadBalancerPool) (LoadBalancerPool, error) { | ||||
| 	uri := api.userBaseURL("/user") + "/load_balancers/pools" | ||||
| 	res, err := api.makeRequest("POST", uri, pool) | ||||
| 	if err != nil { | ||||
| 		return LoadBalancerPool{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerPoolResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return LoadBalancerPool{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ListLoadBalancerPools lists load balancer pools connected to an account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancer-pools-list-pools
 | ||||
| func (api *API) ListLoadBalancerPools() ([]LoadBalancerPool, error) { | ||||
| 	uri := api.userBaseURL("/user") + "/load_balancers/pools" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerPoolListResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // LoadBalancerPoolDetails returns the details for a load balancer pool.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancer-pools-pool-details
 | ||||
| func (api *API) LoadBalancerPoolDetails(poolID string) (LoadBalancerPool, error) { | ||||
| 	uri := api.userBaseURL("/user") + "/load_balancers/pools/" + poolID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return LoadBalancerPool{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerPoolResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return LoadBalancerPool{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteLoadBalancerPool disables and deletes a load balancer pool.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancer-pools-delete-a-pool
 | ||||
| func (api *API) DeleteLoadBalancerPool(poolID string) error { | ||||
| 	uri := api.userBaseURL("/user") + "/load_balancers/pools/" + poolID | ||||
| 	if _, err := api.makeRequest("DELETE", uri, nil); err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ModifyLoadBalancerPool modifies a configured load balancer pool.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancer-pools-modify-a-pool
 | ||||
| func (api *API) ModifyLoadBalancerPool(pool LoadBalancerPool) (LoadBalancerPool, error) { | ||||
| 	uri := api.userBaseURL("/user") + "/load_balancers/pools/" + pool.ID | ||||
| 	res, err := api.makeRequest("PUT", uri, pool) | ||||
| 	if err != nil { | ||||
| 		return LoadBalancerPool{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerPoolResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return LoadBalancerPool{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // CreateLoadBalancerMonitor creates a new load balancer monitor.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancer-monitors-create-a-monitor
 | ||||
| func (api *API) CreateLoadBalancerMonitor(monitor LoadBalancerMonitor) (LoadBalancerMonitor, error) { | ||||
| 	uri := api.userBaseURL("/user") + "/load_balancers/monitors" | ||||
| 	res, err := api.makeRequest("POST", uri, monitor) | ||||
| 	if err != nil { | ||||
| 		return LoadBalancerMonitor{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerMonitorResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return LoadBalancerMonitor{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ListLoadBalancerMonitors lists load balancer monitors connected to an account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancer-monitors-list-monitors
 | ||||
| func (api *API) ListLoadBalancerMonitors() ([]LoadBalancerMonitor, error) { | ||||
| 	uri := api.userBaseURL("/user") + "/load_balancers/monitors" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerMonitorListResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // LoadBalancerMonitorDetails returns the details for a load balancer monitor.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancer-monitors-monitor-details
 | ||||
| func (api *API) LoadBalancerMonitorDetails(monitorID string) (LoadBalancerMonitor, error) { | ||||
| 	uri := api.userBaseURL("/user") + "/load_balancers/monitors/" + monitorID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return LoadBalancerMonitor{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerMonitorResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return LoadBalancerMonitor{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteLoadBalancerMonitor disables and deletes a load balancer monitor.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancer-monitors-delete-a-monitor
 | ||||
| func (api *API) DeleteLoadBalancerMonitor(monitorID string) error { | ||||
| 	uri := api.userBaseURL("/user") + "/load_balancers/monitors/" + monitorID | ||||
| 	if _, err := api.makeRequest("DELETE", uri, nil); err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ModifyLoadBalancerMonitor modifies a configured load balancer monitor.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancer-monitors-modify-a-monitor
 | ||||
| func (api *API) ModifyLoadBalancerMonitor(monitor LoadBalancerMonitor) (LoadBalancerMonitor, error) { | ||||
| 	uri := api.userBaseURL("/user") + "/load_balancers/monitors/" + monitor.ID | ||||
| 	res, err := api.makeRequest("PUT", uri, monitor) | ||||
| 	if err != nil { | ||||
| 		return LoadBalancerMonitor{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerMonitorResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return LoadBalancerMonitor{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // CreateLoadBalancer creates a new load balancer.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancers-create-a-load-balancer
 | ||||
| func (api *API) CreateLoadBalancer(zoneID string, lb LoadBalancer) (LoadBalancer, error) { | ||||
| 	uri := "/zones/" + zoneID + "/load_balancers" | ||||
| 	res, err := api.makeRequest("POST", uri, lb) | ||||
| 	if err != nil { | ||||
| 		return LoadBalancer{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return LoadBalancer{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ListLoadBalancers lists load balancers configured on a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancers-list-load-balancers
 | ||||
| func (api *API) ListLoadBalancers(zoneID string) ([]LoadBalancer, error) { | ||||
| 	uri := "/zones/" + zoneID + "/load_balancers" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerListResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // LoadBalancerDetails returns the details for a load balancer.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancers-load-balancer-details
 | ||||
| func (api *API) LoadBalancerDetails(zoneID, lbID string) (LoadBalancer, error) { | ||||
| 	uri := "/zones/" + zoneID + "/load_balancers/" + lbID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return LoadBalancer{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return LoadBalancer{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteLoadBalancer disables and deletes a load balancer.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancers-delete-a-load-balancer
 | ||||
| func (api *API) DeleteLoadBalancer(zoneID, lbID string) error { | ||||
| 	uri := "/zones/" + zoneID + "/load_balancers/" + lbID | ||||
| 	if _, err := api.makeRequest("DELETE", uri, nil); err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ModifyLoadBalancer modifies a configured load balancer.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancers-modify-a-load-balancer
 | ||||
| func (api *API) ModifyLoadBalancer(zoneID string, lb LoadBalancer) (LoadBalancer, error) { | ||||
| 	uri := "/zones/" + zoneID + "/load_balancers/" + lb.ID | ||||
| 	res, err := api.makeRequest("PUT", uri, lb) | ||||
| 	if err != nil { | ||||
| 		return LoadBalancer{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return LoadBalancer{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // PoolHealthDetails fetches the latest healtcheck details for a single pool.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#load-balancer-pools-pool-health-details
 | ||||
| func (api *API) PoolHealthDetails(poolID string) (LoadBalancerPoolHealth, error) { | ||||
| 	uri := api.userBaseURL("/user") + "/load_balancers/pools/" + poolID + "/health" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return LoadBalancerPoolHealth{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r loadBalancerPoolHealthResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return LoadBalancerPoolHealth{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
							
								
								
									
										151
									
								
								vendor/github.com/cloudflare/cloudflare-go/lockdown.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								vendor/github.com/cloudflare/cloudflare-go/lockdown.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // ZoneLockdown represents a Zone Lockdown rule. A rule only permits access to
 | ||||
| // the provided URL pattern(s) from the given IP address(es) or subnet(s).
 | ||||
| type ZoneLockdown struct { | ||||
| 	ID             string               `json:"id"` | ||||
| 	Description    string               `json:"description"` | ||||
| 	URLs           []string             `json:"urls"` | ||||
| 	Configurations []ZoneLockdownConfig `json:"configurations"` | ||||
| 	Paused         bool                 `json:"paused"` | ||||
| 	Priority       int                  `json:"priority,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // ZoneLockdownConfig represents a Zone Lockdown config, which comprises
 | ||||
| // a Target ("ip" or "ip_range") and a Value (an IP address or IP+mask,
 | ||||
| // respectively.)
 | ||||
| type ZoneLockdownConfig struct { | ||||
| 	Target string `json:"target"` | ||||
| 	Value  string `json:"value"` | ||||
| } | ||||
| 
 | ||||
| // ZoneLockdownResponse represents a response from the Zone Lockdown endpoint.
 | ||||
| type ZoneLockdownResponse struct { | ||||
| 	Result ZoneLockdown `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // ZoneLockdownListResponse represents a response from the List Zone Lockdown
 | ||||
| // endpoint.
 | ||||
| type ZoneLockdownListResponse struct { | ||||
| 	Result []ZoneLockdown `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // CreateZoneLockdown creates a Zone ZoneLockdown rule for the given zone ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-create-a-ZoneLockdown-rule
 | ||||
| func (api *API) CreateZoneLockdown(zoneID string, ld ZoneLockdown) (*ZoneLockdownResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/firewall/lockdowns" | ||||
| 	res, err := api.makeRequest("POST", uri, ld) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &ZoneLockdownResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateZoneLockdown updates a Zone ZoneLockdown rule (based on the ID) for the
 | ||||
| // given zone ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-update-ZoneLockdown-rule
 | ||||
| func (api *API) UpdateZoneLockdown(zoneID string, id string, ld ZoneLockdown) (*ZoneLockdownResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/firewall/lockdowns/" + id | ||||
| 	res, err := api.makeRequest("PUT", uri, ld) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &ZoneLockdownResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteZoneLockdown deletes a Zone ZoneLockdown rule (based on the ID) for the
 | ||||
| // given zone ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-delete-ZoneLockdown-rule
 | ||||
| func (api *API) DeleteZoneLockdown(zoneID string, id string) (*ZoneLockdownResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/firewall/lockdowns/" + id | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &ZoneLockdownResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| // ZoneLockdown retrieves a Zone ZoneLockdown rule (based on the ID) for the
 | ||||
| // given zone ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-ZoneLockdown-rule-details
 | ||||
| func (api *API) ZoneLockdown(zoneID string, id string) (*ZoneLockdownResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/firewall/lockdowns/" + id | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &ZoneLockdownResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| // ListZoneLockdowns retrieves a list of Zone ZoneLockdown rules for a given
 | ||||
| // zone ID by page number.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-list-ZoneLockdown-rules
 | ||||
| func (api *API) ListZoneLockdowns(zoneID string, page int) (*ZoneLockdownListResponse, error) { | ||||
| 	v := url.Values{} | ||||
| 	if page <= 0 { | ||||
| 		page = 1 | ||||
| 	} | ||||
| 
 | ||||
| 	v.Set("page", strconv.Itoa(page)) | ||||
| 	v.Set("per_page", strconv.Itoa(100)) | ||||
| 	query := "?" + v.Encode() | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/firewall/lockdowns" + query | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &ZoneLockdownListResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
							
								
								
									
										224
									
								
								vendor/github.com/cloudflare/cloudflare-go/logpush.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								vendor/github.com/cloudflare/cloudflare-go/logpush.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,224 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // LogpushJob describes a Logpush job.
 | ||||
| type LogpushJob struct { | ||||
| 	ID                 int        `json:"id,omitempty"` | ||||
| 	Enabled            bool       `json:"enabled"` | ||||
| 	Name               string     `json:"name"` | ||||
| 	LogpullOptions     string     `json:"logpull_options"` | ||||
| 	DestinationConf    string     `json:"destination_conf"` | ||||
| 	OwnershipChallenge string     `json:"ownership_challenge,omitempty"` | ||||
| 	LastComplete       *time.Time `json:"last_complete,omitempty"` | ||||
| 	LastError          *time.Time `json:"last_error,omitempty"` | ||||
| 	ErrorMessage       string     `json:"error_message,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // LogpushJobsResponse is the API response, containing an array of Logpush Jobs.
 | ||||
| type LogpushJobsResponse struct { | ||||
| 	Response | ||||
| 	Result []LogpushJob `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // LogpushJobDetailsResponse is the API response, containing a single Logpush Job.
 | ||||
| type LogpushJobDetailsResponse struct { | ||||
| 	Response | ||||
| 	Result LogpushJob `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // LogpushGetOwnershipChallenge describes a ownership validation.
 | ||||
| type LogpushGetOwnershipChallenge struct { | ||||
| 	Filename string `json:"filename"` | ||||
| 	Valid    bool   `json:"valid"` | ||||
| 	Message  string `json:"message"` | ||||
| } | ||||
| 
 | ||||
| // LogpushGetOwnershipChallengeResponse is the API response, containing a ownership challenge.
 | ||||
| type LogpushGetOwnershipChallengeResponse struct { | ||||
| 	Response | ||||
| 	Result LogpushGetOwnershipChallenge `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // LogpushGetOwnershipChallengeRequest is the API request for get ownership challenge.
 | ||||
| type LogpushGetOwnershipChallengeRequest struct { | ||||
| 	DestinationConf string `json:"destination_conf"` | ||||
| } | ||||
| 
 | ||||
| // LogpushOwnershipChallangeValidationResponse is the API response,
 | ||||
| // containing a ownership challenge validation result.
 | ||||
| type LogpushOwnershipChallangeValidationResponse struct { | ||||
| 	Response | ||||
| 	Result struct { | ||||
| 		Valid bool `json:"valid"` | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // LogpushValidateOwnershipChallengeRequest is the API request for validate ownership challenge.
 | ||||
| type LogpushValidateOwnershipChallengeRequest struct { | ||||
| 	DestinationConf    string `json:"destination_conf"` | ||||
| 	OwnershipChallenge string `json:"ownership_challenge"` | ||||
| } | ||||
| 
 | ||||
| // LogpushDestinationExistsResponse is the API response,
 | ||||
| // containing a destination exists check result.
 | ||||
| type LogpushDestinationExistsResponse struct { | ||||
| 	Response | ||||
| 	Result struct { | ||||
| 		Exists bool `json:"exists"` | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // LogpushDestinationExistsRequest is the API request for check destination exists.
 | ||||
| type LogpushDestinationExistsRequest struct { | ||||
| 	DestinationConf string `json:"destination_conf"` | ||||
| } | ||||
| 
 | ||||
| // CreateLogpushJob creates a new LogpushJob for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#logpush-jobs-create-logpush-job
 | ||||
| func (api *API) CreateLogpushJob(zoneID string, job LogpushJob) (*LogpushJob, error) { | ||||
| 	uri := "/zones/" + zoneID + "/logpush/jobs" | ||||
| 	res, err := api.makeRequest("POST", uri, job) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r LogpushJobDetailsResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return &r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // LogpushJobs returns all Logpush Jobs for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#logpush-jobs-list-logpush-jobs
 | ||||
| func (api *API) LogpushJobs(zoneID string) ([]LogpushJob, error) { | ||||
| 	uri := "/zones/" + zoneID + "/logpush/jobs" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []LogpushJob{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r LogpushJobsResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return []LogpushJob{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // LogpushJob fetches detail about one Logpush Job for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#logpush-jobs-logpush-job-details
 | ||||
| func (api *API) LogpushJob(zoneID string, jobID int) (LogpushJob, error) { | ||||
| 	uri := "/zones/" + zoneID + "/logpush/jobs/" + strconv.Itoa(jobID) | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return LogpushJob{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r LogpushJobDetailsResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return LogpushJob{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateLogpushJob lets you update a Logpush Job.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#logpush-jobs-update-logpush-job
 | ||||
| func (api *API) UpdateLogpushJob(zoneID string, jobID int, job LogpushJob) error { | ||||
| 	uri := "/zones/" + zoneID + "/logpush/jobs/" + strconv.Itoa(jobID) | ||||
| 	res, err := api.makeRequest("PUT", uri, job) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r LogpushJobDetailsResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DeleteLogpushJob deletes a Logpush Job for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#logpush-jobs-delete-logpush-job
 | ||||
| func (api *API) DeleteLogpushJob(zoneID string, jobID int) error { | ||||
| 	uri := "/zones/" + zoneID + "/logpush/jobs/" + strconv.Itoa(jobID) | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r LogpushJobDetailsResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // GetLogpushOwnershipChallenge returns ownership challenge.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#logpush-jobs-get-ownership-challenge
 | ||||
| func (api *API) GetLogpushOwnershipChallenge(zoneID, destinationConf string) (*LogpushGetOwnershipChallenge, error) { | ||||
| 	uri := "/zones/" + zoneID + "/logpush/ownership" | ||||
| 	res, err := api.makeRequest("POST", uri, LogpushGetOwnershipChallengeRequest{ | ||||
| 		DestinationConf: destinationConf, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r LogpushGetOwnershipChallengeResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return &r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ValidateLogpushOwnershipChallenge returns ownership challenge validation result.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#logpush-jobs-validate-ownership-challenge
 | ||||
| func (api *API) ValidateLogpushOwnershipChallenge(zoneID, destinationConf, ownershipChallenge string) (bool, error) { | ||||
| 	uri := "/zones/" + zoneID + "/logpush/ownership/validate" | ||||
| 	res, err := api.makeRequest("POST", uri, LogpushValidateOwnershipChallengeRequest{ | ||||
| 		DestinationConf:    destinationConf, | ||||
| 		OwnershipChallenge: ownershipChallenge, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return false, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r LogpushGetOwnershipChallengeResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return false, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result.Valid, nil | ||||
| } | ||||
| 
 | ||||
| // CheckLogpushDestinationExists returns destination exists check result.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#logpush-jobs-check-destination-exists
 | ||||
| func (api *API) CheckLogpushDestinationExists(zoneID, destinationConf string) (bool, error) { | ||||
| 	uri := "/zones/" + zoneID + "/logpush/validate/destination/exists" | ||||
| 	res, err := api.makeRequest("POST", uri, LogpushDestinationExistsRequest{ | ||||
| 		DestinationConf: destinationConf, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return false, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r LogpushDestinationExistsResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return false, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result.Exists, nil | ||||
| } | ||||
							
								
								
									
										101
									
								
								vendor/github.com/cloudflare/cloudflare-go/options.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								vendor/github.com/cloudflare/cloudflare-go/options.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,101 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/time/rate" | ||||
| ) | ||||
| 
 | ||||
| // Option is a functional option for configuring the API client.
 | ||||
| type Option func(*API) error | ||||
| 
 | ||||
| // HTTPClient accepts a custom *http.Client for making API calls.
 | ||||
| func HTTPClient(client *http.Client) Option { | ||||
| 	return func(api *API) error { | ||||
| 		api.httpClient = client | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Headers allows you to set custom HTTP headers when making API calls (e.g. for
 | ||||
| // satisfying HTTP proxies, or for debugging).
 | ||||
| func Headers(headers http.Header) Option { | ||||
| 	return func(api *API) error { | ||||
| 		api.headers = headers | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // UsingAccount allows you to apply account-level changes (Load Balancing,
 | ||||
| // Railguns) to an account instead.
 | ||||
| func UsingAccount(accountID string) Option { | ||||
| 	return func(api *API) error { | ||||
| 		api.AccountID = accountID | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // UsingRateLimit applies a non-default rate limit to client API requests
 | ||||
| // If not specified the default of 4rps will be applied
 | ||||
| func UsingRateLimit(rps float64) Option { | ||||
| 	return func(api *API) error { | ||||
| 		// because ratelimiter doesnt do any windowing
 | ||||
| 		// setting burst makes it difficult to enforce a fixed rate
 | ||||
| 		// so setting it equal to 1 this effectively disables bursting
 | ||||
| 		// this doesn't check for sensible values, ultimately the api will enforce that the value is ok
 | ||||
| 		api.rateLimiter = rate.NewLimiter(rate.Limit(rps), 1) | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // UsingRetryPolicy applies a non-default number of retries and min/max retry delays
 | ||||
| // This will be used when the client exponentially backs off after errored requests
 | ||||
| func UsingRetryPolicy(maxRetries int, minRetryDelaySecs int, maxRetryDelaySecs int) Option { | ||||
| 	// seconds is very granular for a minimum delay - but this is only in case of failure
 | ||||
| 	return func(api *API) error { | ||||
| 		api.retryPolicy = RetryPolicy{ | ||||
| 			MaxRetries:    maxRetries, | ||||
| 			MinRetryDelay: time.Duration(minRetryDelaySecs) * time.Second, | ||||
| 			MaxRetryDelay: time.Duration(maxRetryDelaySecs) * time.Second, | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // UsingLogger can be set if you want to get log output from this API instance
 | ||||
| // By default no log output is emitted
 | ||||
| func UsingLogger(logger Logger) Option { | ||||
| 	return func(api *API) error { | ||||
| 		api.logger = logger | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // UserAgent can be set if you want to send a software name and version for HTTP access logs.
 | ||||
| // It is recommended to set it in order to help future Customer Support diagnostics
 | ||||
| // and prevent collateral damage by sharing generic User-Agent string with abusive users.
 | ||||
| // E.g. "my-software/1.2.3". By default generic Go User-Agent is used.
 | ||||
| func UserAgent(userAgent string) Option { | ||||
| 	return func(api *API) error { | ||||
| 		api.UserAgent = userAgent | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // parseOptions parses the supplied options functions and returns a configured
 | ||||
| // *API instance.
 | ||||
| func (api *API) parseOptions(opts ...Option) error { | ||||
| 	// Range over each options function and apply it to our API type to
 | ||||
| 	// configure it. Options functions are applied in order, with any
 | ||||
| 	// conflicting options overriding earlier calls.
 | ||||
| 	for _, option := range opts { | ||||
| 		err := option(api) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										169
									
								
								vendor/github.com/cloudflare/cloudflare-go/origin_ca.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								vendor/github.com/cloudflare/cloudflare-go/origin_ca.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,169 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"net/url" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // OriginCACertificate represents a Cloudflare-issued certificate.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#cloudflare-ca
 | ||||
| type OriginCACertificate struct { | ||||
| 	ID              string    `json:"id"` | ||||
| 	Certificate     string    `json:"certificate"` | ||||
| 	Hostnames       []string  `json:"hostnames"` | ||||
| 	ExpiresOn       time.Time `json:"expires_on"` | ||||
| 	RequestType     string    `json:"request_type"` | ||||
| 	RequestValidity int       `json:"requested_validity"` | ||||
| 	CSR             string    `json:"csr"` | ||||
| } | ||||
| 
 | ||||
| // OriginCACertificateListOptions represents the parameters used to list Cloudflare-issued certificates.
 | ||||
| type OriginCACertificateListOptions struct { | ||||
| 	ZoneID string | ||||
| } | ||||
| 
 | ||||
| // OriginCACertificateID represents the ID of the revoked certificate from the Revoke Certificate endpoint.
 | ||||
| type OriginCACertificateID struct { | ||||
| 	ID string `json:"id"` | ||||
| } | ||||
| 
 | ||||
| // originCACertificateResponse represents the response from the Create Certificate and the Certificate Details endpoints.
 | ||||
| type originCACertificateResponse struct { | ||||
| 	Response | ||||
| 	Result OriginCACertificate `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // originCACertificateResponseList represents the response from the List Certificates endpoint.
 | ||||
| type originCACertificateResponseList struct { | ||||
| 	Response | ||||
| 	Result     []OriginCACertificate `json:"result"` | ||||
| 	ResultInfo ResultInfo            `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // originCACertificateResponseRevoke represents the response from the Revoke Certificate endpoint.
 | ||||
| type originCACertificateResponseRevoke struct { | ||||
| 	Response | ||||
| 	Result OriginCACertificateID `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // CreateOriginCertificate creates a Cloudflare-signed certificate.
 | ||||
| //
 | ||||
| // This function requires api.APIUserServiceKey be set to your Certificates API key.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#cloudflare-ca-create-certificate
 | ||||
| func (api *API) CreateOriginCertificate(certificate OriginCACertificate) (*OriginCACertificate, error) { | ||||
| 	uri := "/certificates" | ||||
| 	res, err := api.makeRequestWithAuthType(context.TODO(), "POST", uri, certificate, AuthUserService) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var originResponse *originCACertificateResponse | ||||
| 
 | ||||
| 	err = json.Unmarshal(res, &originResponse) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	if !originResponse.Success { | ||||
| 		return nil, errors.New(errRequestNotSuccessful) | ||||
| 	} | ||||
| 
 | ||||
| 	return &originResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // OriginCertificates lists all Cloudflare-issued certificates.
 | ||||
| //
 | ||||
| // This function requires api.APIUserServiceKey be set to your Certificates API key.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#cloudflare-ca-list-certificates
 | ||||
| func (api *API) OriginCertificates(options OriginCACertificateListOptions) ([]OriginCACertificate, error) { | ||||
| 	v := url.Values{} | ||||
| 	if options.ZoneID != "" { | ||||
| 		v.Set("zone_id", options.ZoneID) | ||||
| 	} | ||||
| 	uri := "/certificates" + "?" + v.Encode() | ||||
| 	res, err := api.makeRequestWithAuthType(context.TODO(), "GET", uri, nil, AuthUserService) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var originResponse *originCACertificateResponseList | ||||
| 
 | ||||
| 	err = json.Unmarshal(res, &originResponse) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	if !originResponse.Success { | ||||
| 		return nil, errors.New(errRequestNotSuccessful) | ||||
| 	} | ||||
| 
 | ||||
| 	return originResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // OriginCertificate returns the details for a Cloudflare-issued certificate.
 | ||||
| //
 | ||||
| // This function requires api.APIUserServiceKey be set to your Certificates API key.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#cloudflare-ca-certificate-details
 | ||||
| func (api *API) OriginCertificate(certificateID string) (*OriginCACertificate, error) { | ||||
| 	uri := "/certificates/" + certificateID | ||||
| 	res, err := api.makeRequestWithAuthType(context.TODO(), "GET", uri, nil, AuthUserService) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var originResponse *originCACertificateResponse | ||||
| 
 | ||||
| 	err = json.Unmarshal(res, &originResponse) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	if !originResponse.Success { | ||||
| 		return nil, errors.New(errRequestNotSuccessful) | ||||
| 	} | ||||
| 
 | ||||
| 	return &originResponse.Result, nil | ||||
| } | ||||
| 
 | ||||
| // RevokeOriginCertificate revokes a created certificate for a zone.
 | ||||
| //
 | ||||
| // This function requires api.APIUserServiceKey be set to your Certificates API key.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#cloudflare-ca-revoke-certificate
 | ||||
| func (api *API) RevokeOriginCertificate(certificateID string) (*OriginCACertificateID, error) { | ||||
| 	uri := "/certificates/" + certificateID | ||||
| 	res, err := api.makeRequestWithAuthType(context.TODO(), "DELETE", uri, nil, AuthUserService) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var originResponse *originCACertificateResponseRevoke | ||||
| 
 | ||||
| 	err = json.Unmarshal(res, &originResponse) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	if !originResponse.Success { | ||||
| 		return nil, errors.New(errRequestNotSuccessful) | ||||
| 	} | ||||
| 
 | ||||
| 	return &originResponse.Result, nil | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										235
									
								
								vendor/github.com/cloudflare/cloudflare-go/page_rules.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								vendor/github.com/cloudflare/cloudflare-go/page_rules.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,235 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // PageRuleTarget is the target to evaluate on a request.
 | ||||
| //
 | ||||
| // Currently Target must always be "url" and Operator must be "matches". Value
 | ||||
| // is the URL pattern to match against.
 | ||||
| type PageRuleTarget struct { | ||||
| 	Target     string `json:"target"` | ||||
| 	Constraint struct { | ||||
| 		Operator string `json:"operator"` | ||||
| 		Value    string `json:"value"` | ||||
| 	} `json:"constraint"` | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| PageRuleAction is the action to take when the target is matched. | ||||
| 
 | ||||
| Valid IDs are: | ||||
|   always_online | ||||
|   always_use_https | ||||
|   automatic_https_rewrites | ||||
|   browser_cache_ttl | ||||
|   browser_check | ||||
|   bypass_cache_on_cookie | ||||
|   cache_by_device_type | ||||
|   cache_deception_armor | ||||
|   cache_level | ||||
|   cache_on_cookie | ||||
|   disable_apps | ||||
|   disable_performance | ||||
|   disable_railgun | ||||
|   disable_security | ||||
|   edge_cache_ttl | ||||
|   email_obfuscation | ||||
|   explicit_cache_control | ||||
|   forwarding_url | ||||
|   host_header_override | ||||
|   ip_geolocation | ||||
|   minify | ||||
|   mirage | ||||
|   opportunistic_encryption | ||||
|   origin_error_page_pass_thru | ||||
|   polish | ||||
|   resolve_override | ||||
|   respect_strong_etag | ||||
|   response_buffering | ||||
|   rocket_loader | ||||
|   security_level | ||||
|   server_side_exclude | ||||
|   sort_query_string_for_cache | ||||
|   ssl | ||||
|   true_client_ip_header | ||||
|   waf | ||||
| */ | ||||
| type PageRuleAction struct { | ||||
| 	ID    string      `json:"id"` | ||||
| 	Value interface{} `json:"value"` | ||||
| } | ||||
| 
 | ||||
| // PageRuleActions maps API action IDs to human-readable strings.
 | ||||
| var PageRuleActions = map[string]string{ | ||||
| 	"always_online":               "Always Online",               // Value of type string
 | ||||
| 	"always_use_https":            "Always Use HTTPS",            // Value of type interface{}
 | ||||
| 	"automatic_https_rewrites":    "Automatic HTTPS Rewrites",    // Value of type string
 | ||||
| 	"browser_cache_ttl":           "Browser Cache TTL",           // Value of type int
 | ||||
| 	"browser_check":               "Browser Integrity Check",     // Value of type string
 | ||||
| 	"bypass_cache_on_cookie":      "Bypass Cache on Cookie",      // Value of type string
 | ||||
| 	"cache_by_device_type":        "Cache By Device Type",        // Value of type string
 | ||||
| 	"cache_deception_armor":       "Cache Deception Armor",       // Value of type string
 | ||||
| 	"cache_level":                 "Cache Level",                 // Value of type string
 | ||||
| 	"cache_on_cookie":             "Cache On Cookie",             // Value of type string
 | ||||
| 	"disable_apps":                "Disable Apps",                // Value of type interface{}
 | ||||
| 	"disable_performance":         "Disable Performance",         // Value of type interface{}
 | ||||
| 	"disable_railgun":             "Disable Railgun",             // Value of type string
 | ||||
| 	"disable_security":            "Disable Security",            // Value of type interface{}
 | ||||
| 	"edge_cache_ttl":              "Edge Cache TTL",              // Value of type int
 | ||||
| 	"email_obfuscation":           "Email Obfuscation",           // Value of type string
 | ||||
| 	"explicit_cache_control":      "Origin Cache Control",        // Value of type string
 | ||||
| 	"forwarding_url":              "Forwarding URL",              // Value of type map[string]interface
 | ||||
| 	"host_header_override":        "Host Header Override",        // Value of type string
 | ||||
| 	"ip_geolocation":              "IP Geolocation Header",       // Value of type string
 | ||||
| 	"minify":                      "Minify",                      // Value of type map[string]interface
 | ||||
| 	"mirage":                      "Mirage",                      // Value of type string
 | ||||
| 	"opportunistic_encryption":    "Opportunistic Encryption",    // Value of type string
 | ||||
| 	"origin_error_page_pass_thru": "Origin Error Page Pass-thru", // Value of type string
 | ||||
| 	"polish":                      "Polish",                      // Value of type string
 | ||||
| 	"resolve_override":            "Resolve Override",            // Value of type string
 | ||||
| 	"respect_strong_etag":         "Respect Strong ETags",        // Value of type string
 | ||||
| 	"response_buffering":          "Response Buffering",          // Value of type string
 | ||||
| 	"rocket_loader":               "Rocker Loader",               // Value of type string
 | ||||
| 	"security_level":              "Security Level",              // Value of type string
 | ||||
| 	"server_side_exclude":         "Server Side Excludes",        // Value of type string
 | ||||
| 	"sort_query_string_for_cache": "Query String Sort",           // Value of type string
 | ||||
| 	"ssl":                         "SSL",                         // Value of type string
 | ||||
| 	"true_client_ip_header":       "True Client IP Header",       // Value of type string
 | ||||
| 	"waf":                         "Web Application Firewall",    // Value of type string
 | ||||
| } | ||||
| 
 | ||||
| // PageRule describes a Page Rule.
 | ||||
| type PageRule struct { | ||||
| 	ID         string           `json:"id,omitempty"` | ||||
| 	Targets    []PageRuleTarget `json:"targets"` | ||||
| 	Actions    []PageRuleAction `json:"actions"` | ||||
| 	Priority   int              `json:"priority"` | ||||
| 	Status     string           `json:"status"` // can be: active, paused
 | ||||
| 	ModifiedOn time.Time        `json:"modified_on,omitempty"` | ||||
| 	CreatedOn  time.Time        `json:"created_on,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // PageRuleDetailResponse is the API response, containing a single PageRule.
 | ||||
| type PageRuleDetailResponse struct { | ||||
| 	Success  bool     `json:"success"` | ||||
| 	Errors   []string `json:"errors"` | ||||
| 	Messages []string `json:"messages"` | ||||
| 	Result   PageRule `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // PageRulesResponse is the API response, containing an array of PageRules.
 | ||||
| type PageRulesResponse struct { | ||||
| 	Success  bool       `json:"success"` | ||||
| 	Errors   []string   `json:"errors"` | ||||
| 	Messages []string   `json:"messages"` | ||||
| 	Result   []PageRule `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // CreatePageRule creates a new Page Rule for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#page-rules-for-a-zone-create-a-page-rule
 | ||||
| func (api *API) CreatePageRule(zoneID string, rule PageRule) (*PageRule, error) { | ||||
| 	uri := "/zones/" + zoneID + "/pagerules" | ||||
| 	res, err := api.makeRequest("POST", uri, rule) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r PageRuleDetailResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return &r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ListPageRules returns all Page Rules for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#page-rules-for-a-zone-list-page-rules
 | ||||
| func (api *API) ListPageRules(zoneID string) ([]PageRule, error) { | ||||
| 	uri := "/zones/" + zoneID + "/pagerules" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []PageRule{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r PageRulesResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return []PageRule{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // PageRule fetches detail about one Page Rule for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#page-rules-for-a-zone-page-rule-details
 | ||||
| func (api *API) PageRule(zoneID, ruleID string) (PageRule, error) { | ||||
| 	uri := "/zones/" + zoneID + "/pagerules/" + ruleID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return PageRule{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r PageRuleDetailResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return PageRule{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ChangePageRule lets you change individual settings for a Page Rule. This is
 | ||||
| // in contrast to UpdatePageRule which replaces the entire Page Rule.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#page-rules-for-a-zone-change-a-page-rule
 | ||||
| func (api *API) ChangePageRule(zoneID, ruleID string, rule PageRule) error { | ||||
| 	uri := "/zones/" + zoneID + "/pagerules/" + ruleID | ||||
| 	res, err := api.makeRequest("PATCH", uri, rule) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r PageRuleDetailResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // UpdatePageRule lets you replace a Page Rule. This is in contrast to
 | ||||
| // ChangePageRule which lets you change individual settings.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#page-rules-for-a-zone-update-a-page-rule
 | ||||
| func (api *API) UpdatePageRule(zoneID, ruleID string, rule PageRule) error { | ||||
| 	uri := "/zones/" + zoneID + "/pagerules/" + ruleID | ||||
| 	res, err := api.makeRequest("PUT", uri, rule) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r PageRuleDetailResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DeletePageRule deletes a Page Rule for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#page-rules-for-a-zone-delete-a-page-rule
 | ||||
| func (api *API) DeletePageRule(zoneID, ruleID string) error { | ||||
| 	uri := "/zones/" + zoneID + "/pagerules/" + ruleID | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r PageRuleDetailResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										297
									
								
								vendor/github.com/cloudflare/cloudflare-go/railgun.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								vendor/github.com/cloudflare/cloudflare-go/railgun.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,297 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/url" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // Railgun represents a Railgun's properties.
 | ||||
| type Railgun struct { | ||||
| 	ID             string    `json:"id"` | ||||
| 	Name           string    `json:"name"` | ||||
| 	Status         string    `json:"status"` | ||||
| 	Enabled        bool      `json:"enabled"` | ||||
| 	ZonesConnected int       `json:"zones_connected"` | ||||
| 	Build          string    `json:"build"` | ||||
| 	Version        string    `json:"version"` | ||||
| 	Revision       string    `json:"revision"` | ||||
| 	ActivationKey  string    `json:"activation_key"` | ||||
| 	ActivatedOn    time.Time `json:"activated_on"` | ||||
| 	CreatedOn      time.Time `json:"created_on"` | ||||
| 	ModifiedOn     time.Time `json:"modified_on"` | ||||
| 	UpgradeInfo    struct { | ||||
| 		LatestVersion string `json:"latest_version"` | ||||
| 		DownloadLink  string `json:"download_link"` | ||||
| 	} `json:"upgrade_info"` | ||||
| } | ||||
| 
 | ||||
| // RailgunListOptions represents the parameters used to list railguns.
 | ||||
| type RailgunListOptions struct { | ||||
| 	Direction string | ||||
| } | ||||
| 
 | ||||
| // railgunResponse represents the response from the Create Railgun and the Railgun Details endpoints.
 | ||||
| type railgunResponse struct { | ||||
| 	Response | ||||
| 	Result Railgun `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // railgunsResponse represents the response from the List Railguns endpoint.
 | ||||
| type railgunsResponse struct { | ||||
| 	Response | ||||
| 	Result []Railgun `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // CreateRailgun creates a new Railgun.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railgun-create-railgun
 | ||||
| func (api *API) CreateRailgun(name string) (Railgun, error) { | ||||
| 	uri := api.userBaseURL("") + "/railguns" | ||||
| 	params := struct { | ||||
| 		Name string `json:"name"` | ||||
| 	}{ | ||||
| 		Name: name, | ||||
| 	} | ||||
| 	res, err := api.makeRequest("POST", uri, params) | ||||
| 	if err != nil { | ||||
| 		return Railgun{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r railgunResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return Railgun{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ListRailguns lists Railguns connected to an account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railgun-list-railguns
 | ||||
| func (api *API) ListRailguns(options RailgunListOptions) ([]Railgun, error) { | ||||
| 	v := url.Values{} | ||||
| 	if options.Direction != "" { | ||||
| 		v.Set("direction", options.Direction) | ||||
| 	} | ||||
| 	uri := api.userBaseURL("") + "/railguns" + "?" + v.Encode() | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r railgunsResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // RailgunDetails returns the details for a Railgun.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railgun-railgun-details
 | ||||
| func (api *API) RailgunDetails(railgunID string) (Railgun, error) { | ||||
| 	uri := api.userBaseURL("") + "/railguns/" + railgunID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return Railgun{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r railgunResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return Railgun{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // RailgunZones returns the zones that are currently using a Railgun.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railgun-get-zones-connected-to-a-railgun
 | ||||
| func (api *API) RailgunZones(railgunID string) ([]Zone, error) { | ||||
| 	uri := api.userBaseURL("") + "/railguns/" + railgunID + "/zones" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r ZonesResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // enableRailgun enables (true) or disables (false) a Railgun for all zones connected to it.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railgun-enable-or-disable-a-railgun
 | ||||
| func (api *API) enableRailgun(railgunID string, enable bool) (Railgun, error) { | ||||
| 	uri := api.userBaseURL("") + "/railguns/" + railgunID | ||||
| 	params := struct { | ||||
| 		Enabled bool `json:"enabled"` | ||||
| 	}{ | ||||
| 		Enabled: enable, | ||||
| 	} | ||||
| 	res, err := api.makeRequest("PATCH", uri, params) | ||||
| 	if err != nil { | ||||
| 		return Railgun{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r railgunResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return Railgun{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // EnableRailgun enables a Railgun for all zones connected to it.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railgun-enable-or-disable-a-railgun
 | ||||
| func (api *API) EnableRailgun(railgunID string) (Railgun, error) { | ||||
| 	return api.enableRailgun(railgunID, true) | ||||
| } | ||||
| 
 | ||||
| // DisableRailgun enables a Railgun for all zones connected to it.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railgun-enable-or-disable-a-railgun
 | ||||
| func (api *API) DisableRailgun(railgunID string) (Railgun, error) { | ||||
| 	return api.enableRailgun(railgunID, false) | ||||
| } | ||||
| 
 | ||||
| // DeleteRailgun disables and deletes a Railgun.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railgun-delete-railgun
 | ||||
| func (api *API) DeleteRailgun(railgunID string) error { | ||||
| 	uri := api.userBaseURL("") + "/railguns/" + railgunID | ||||
| 	if _, err := api.makeRequest("DELETE", uri, nil); err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ZoneRailgun represents the status of a Railgun on a zone.
 | ||||
| type ZoneRailgun struct { | ||||
| 	ID        string `json:"id"` | ||||
| 	Name      string `json:"name"` | ||||
| 	Enabled   bool   `json:"enabled"` | ||||
| 	Connected bool   `json:"connected"` | ||||
| } | ||||
| 
 | ||||
| // zoneRailgunResponse represents the response from the Zone Railgun Details endpoint.
 | ||||
| type zoneRailgunResponse struct { | ||||
| 	Response | ||||
| 	Result ZoneRailgun `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // zoneRailgunsResponse represents the response from the Zone Railgun endpoint.
 | ||||
| type zoneRailgunsResponse struct { | ||||
| 	Response | ||||
| 	Result []ZoneRailgun `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // RailgunDiagnosis represents the test results from testing railgun connections
 | ||||
| // to a zone.
 | ||||
| type RailgunDiagnosis struct { | ||||
| 	Method          string `json:"method"` | ||||
| 	HostName        string `json:"host_name"` | ||||
| 	HTTPStatus      int    `json:"http_status"` | ||||
| 	Railgun         string `json:"railgun"` | ||||
| 	URL             string `json:"url"` | ||||
| 	ResponseStatus  string `json:"response_status"` | ||||
| 	Protocol        string `json:"protocol"` | ||||
| 	ElapsedTime     string `json:"elapsed_time"` | ||||
| 	BodySize        string `json:"body_size"` | ||||
| 	BodyHash        string `json:"body_hash"` | ||||
| 	MissingHeaders  string `json:"missing_headers"` | ||||
| 	ConnectionClose bool   `json:"connection_close"` | ||||
| 	Cloudflare      string `json:"cloudflare"` | ||||
| 	CFRay           string `json:"cf-ray"` | ||||
| 	// NOTE: Cloudflare's online API documentation does not yet have definitions
 | ||||
| 	// for the following fields. See: https://api.cloudflare.com/#railgun-connections-for-a-zone-test-railgun-connection/
 | ||||
| 	CFWANError    string `json:"cf-wan-error"` | ||||
| 	CFCacheStatus string `json:"cf-cache-status"` | ||||
| } | ||||
| 
 | ||||
| // railgunDiagnosisResponse represents the response from the Test Railgun Connection enpoint.
 | ||||
| type railgunDiagnosisResponse struct { | ||||
| 	Response | ||||
| 	Result RailgunDiagnosis `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // ZoneRailguns returns the available Railguns for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railguns-for-a-zone-get-available-railguns
 | ||||
| func (api *API) ZoneRailguns(zoneID string) ([]ZoneRailgun, error) { | ||||
| 	uri := "/zones/" + zoneID + "/railguns" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r zoneRailgunsResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ZoneRailgunDetails returns the configuration for a given Railgun.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railguns-for-a-zone-get-railgun-details
 | ||||
| func (api *API) ZoneRailgunDetails(zoneID, railgunID string) (ZoneRailgun, error) { | ||||
| 	uri := "/zones/" + zoneID + "/railguns/" + railgunID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return ZoneRailgun{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r zoneRailgunResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return ZoneRailgun{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // TestRailgunConnection tests a Railgun connection for a given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railgun-connections-for-a-zone-test-railgun-connection
 | ||||
| func (api *API) TestRailgunConnection(zoneID, railgunID string) (RailgunDiagnosis, error) { | ||||
| 	uri := "/zones/" + zoneID + "/railguns/" + railgunID + "/diagnose" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return RailgunDiagnosis{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r railgunDiagnosisResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return RailgunDiagnosis{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // connectZoneRailgun connects (true) or disconnects (false) a Railgun for a given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railguns-for-a-zone-connect-or-disconnect-a-railgun
 | ||||
| func (api *API) connectZoneRailgun(zoneID, railgunID string, connect bool) (ZoneRailgun, error) { | ||||
| 	uri := "/zones/" + zoneID + "/railguns/" + railgunID | ||||
| 	params := struct { | ||||
| 		Connected bool `json:"connected"` | ||||
| 	}{ | ||||
| 		Connected: connect, | ||||
| 	} | ||||
| 	res, err := api.makeRequest("PATCH", uri, params) | ||||
| 	if err != nil { | ||||
| 		return ZoneRailgun{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r zoneRailgunResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return ZoneRailgun{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ConnectZoneRailgun connects a Railgun for a given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railguns-for-a-zone-connect-or-disconnect-a-railgun
 | ||||
| func (api *API) ConnectZoneRailgun(zoneID, railgunID string) (ZoneRailgun, error) { | ||||
| 	return api.connectZoneRailgun(zoneID, railgunID, true) | ||||
| } | ||||
| 
 | ||||
| // DisconnectZoneRailgun disconnects a Railgun for a given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#railguns-for-a-zone-connect-or-disconnect-a-railgun
 | ||||
| func (api *API) DisconnectZoneRailgun(zoneID, railgunID string) (ZoneRailgun, error) { | ||||
| 	return api.connectZoneRailgun(zoneID, railgunID, false) | ||||
| } | ||||
							
								
								
									
										210
									
								
								vendor/github.com/cloudflare/cloudflare-go/rate_limiting.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								vendor/github.com/cloudflare/cloudflare-go/rate_limiting.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,210 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // RateLimit is a policy than can be applied to limit traffic within a customer domain
 | ||||
| type RateLimit struct { | ||||
| 	ID          string                  `json:"id,omitempty"` | ||||
| 	Disabled    bool                    `json:"disabled,omitempty"` | ||||
| 	Description string                  `json:"description,omitempty"` | ||||
| 	Match       RateLimitTrafficMatcher `json:"match"` | ||||
| 	Bypass      []RateLimitKeyValue     `json:"bypass,omitempty"` | ||||
| 	Threshold   int                     `json:"threshold"` | ||||
| 	Period      int                     `json:"period"` | ||||
| 	Action      RateLimitAction         `json:"action"` | ||||
| 	Correlate   *RateLimitCorrelate     `json:"correlate,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // RateLimitTrafficMatcher contains the rules that will be used to apply a rate limit to traffic
 | ||||
| type RateLimitTrafficMatcher struct { | ||||
| 	Request  RateLimitRequestMatcher  `json:"request"` | ||||
| 	Response RateLimitResponseMatcher `json:"response"` | ||||
| } | ||||
| 
 | ||||
| // RateLimitRequestMatcher contains the matching rules pertaining to requests
 | ||||
| type RateLimitRequestMatcher struct { | ||||
| 	Methods    []string `json:"methods,omitempty"` | ||||
| 	Schemes    []string `json:"schemes,omitempty"` | ||||
| 	URLPattern string   `json:"url,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // RateLimitResponseMatcher contains the matching rules pertaining to responses
 | ||||
| type RateLimitResponseMatcher struct { | ||||
| 	Statuses      []int                            `json:"status,omitempty"` | ||||
| 	OriginTraffic *bool                            `json:"origin_traffic,omitempty"` // api defaults to true so we need an explicit empty value
 | ||||
| 	Headers       []RateLimitResponseMatcherHeader `json:"headers,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // RateLimitResponseMatcherHeader contains the structure of the origin
 | ||||
| // HTTP headers used in request matcher checks.
 | ||||
| type RateLimitResponseMatcherHeader struct { | ||||
| 	Name  string `json:"name"` | ||||
| 	Op    string `json:"op"` | ||||
| 	Value string `json:"value"` | ||||
| } | ||||
| 
 | ||||
| // RateLimitKeyValue is k-v formatted as expected in the rate limit description
 | ||||
| type RateLimitKeyValue struct { | ||||
| 	Name  string `json:"name"` | ||||
| 	Value string `json:"value"` | ||||
| } | ||||
| 
 | ||||
| // RateLimitAction is the action that will be taken when the rate limit threshold is reached
 | ||||
| type RateLimitAction struct { | ||||
| 	Mode     string                   `json:"mode"` | ||||
| 	Timeout  int                      `json:"timeout"` | ||||
| 	Response *RateLimitActionResponse `json:"response"` | ||||
| } | ||||
| 
 | ||||
| // RateLimitActionResponse is the response that will be returned when rate limit action is triggered
 | ||||
| type RateLimitActionResponse struct { | ||||
| 	ContentType string `json:"content_type"` | ||||
| 	Body        string `json:"body"` | ||||
| } | ||||
| 
 | ||||
| // RateLimitCorrelate pertainings to NAT support
 | ||||
| type RateLimitCorrelate struct { | ||||
| 	By string `json:"by"` | ||||
| } | ||||
| 
 | ||||
| type rateLimitResponse struct { | ||||
| 	Response | ||||
| 	Result RateLimit `json:"result"` | ||||
| } | ||||
| 
 | ||||
| type rateLimitListResponse struct { | ||||
| 	Response | ||||
| 	Result     []RateLimit `json:"result"` | ||||
| 	ResultInfo ResultInfo  `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // CreateRateLimit creates a new rate limit for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-create-a-ratelimit
 | ||||
| func (api *API) CreateRateLimit(zoneID string, limit RateLimit) (RateLimit, error) { | ||||
| 	uri := "/zones/" + zoneID + "/rate_limits" | ||||
| 	res, err := api.makeRequest("POST", uri, limit) | ||||
| 	if err != nil { | ||||
| 		return RateLimit{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r rateLimitResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return RateLimit{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ListRateLimits returns Rate Limits for a zone, paginated according to the provided options
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-list-rate-limits
 | ||||
| func (api *API) ListRateLimits(zoneID string, pageOpts PaginationOptions) ([]RateLimit, ResultInfo, error) { | ||||
| 	v := url.Values{} | ||||
| 	if pageOpts.PerPage > 0 { | ||||
| 		v.Set("per_page", strconv.Itoa(pageOpts.PerPage)) | ||||
| 	} | ||||
| 	if pageOpts.Page > 0 { | ||||
| 		v.Set("page", strconv.Itoa(pageOpts.Page)) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/rate_limits" | ||||
| 	if len(v) > 0 { | ||||
| 		uri = uri + "?" + v.Encode() | ||||
| 	} | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []RateLimit{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r rateLimitListResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return []RateLimit{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, r.ResultInfo, nil | ||||
| } | ||||
| 
 | ||||
| // ListAllRateLimits returns all Rate Limits for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-list-rate-limits
 | ||||
| func (api *API) ListAllRateLimits(zoneID string) ([]RateLimit, error) { | ||||
| 	pageOpts := PaginationOptions{ | ||||
| 		PerPage: 100, // this is the max page size allowed
 | ||||
| 		Page:    1, | ||||
| 	} | ||||
| 
 | ||||
| 	allRateLimits := make([]RateLimit, 0) | ||||
| 	for { | ||||
| 		rateLimits, resultInfo, err := api.ListRateLimits(zoneID, pageOpts) | ||||
| 		if err != nil { | ||||
| 			return []RateLimit{}, err | ||||
| 		} | ||||
| 		allRateLimits = append(allRateLimits, rateLimits...) | ||||
| 		// total pages is not returned on this call
 | ||||
| 		// if number of records is less than the max, this must be the last page
 | ||||
| 		// in case TotalCount % PerPage = 0, the last request will return an empty list
 | ||||
| 		if resultInfo.Count < resultInfo.PerPage { | ||||
| 			break | ||||
| 		} | ||||
| 		// continue with the next page
 | ||||
| 		pageOpts.Page = pageOpts.Page + 1 | ||||
| 	} | ||||
| 
 | ||||
| 	return allRateLimits, nil | ||||
| } | ||||
| 
 | ||||
| // RateLimit fetches detail about one Rate Limit for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-rate-limit-details
 | ||||
| func (api *API) RateLimit(zoneID, limitID string) (RateLimit, error) { | ||||
| 	uri := "/zones/" + zoneID + "/rate_limits/" + limitID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return RateLimit{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r rateLimitResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return RateLimit{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateRateLimit lets you replace a Rate Limit for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-update-rate-limit
 | ||||
| func (api *API) UpdateRateLimit(zoneID, limitID string, limit RateLimit) (RateLimit, error) { | ||||
| 	uri := "/zones/" + zoneID + "/rate_limits/" + limitID | ||||
| 	res, err := api.makeRequest("PUT", uri, limit) | ||||
| 	if err != nil { | ||||
| 		return RateLimit{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r rateLimitResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return RateLimit{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteRateLimit deletes a Rate Limit for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-delete-rate-limit
 | ||||
| func (api *API) DeleteRateLimit(zoneID, limitID string) error { | ||||
| 	uri := "/zones/" + zoneID + "/rate_limits/" + limitID | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r rateLimitResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										175
									
								
								vendor/github.com/cloudflare/cloudflare-go/registrar.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								vendor/github.com/cloudflare/cloudflare-go/registrar.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,175 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // RegistrarDomain is the structure of the API response for a new
 | ||||
| // Cloudflare Registrar domain.
 | ||||
| type RegistrarDomain struct { | ||||
| 	ID                string              `json:"id"` | ||||
| 	Available         bool                `json:"available"` | ||||
| 	SupportedTLD      bool                `json:"supported_tld"` | ||||
| 	CanRegister       bool                `json:"can_register"` | ||||
| 	TransferIn        RegistrarTransferIn `json:"transfer_in"` | ||||
| 	CurrentRegistrar  string              `json:"current_registrar"` | ||||
| 	ExpiresAt         time.Time           `json:"expires_at"` | ||||
| 	RegistryStatuses  string              `json:"registry_statuses"` | ||||
| 	Locked            bool                `json:"locked"` | ||||
| 	CreatedAt         time.Time           `json:"created_at"` | ||||
| 	UpdatedAt         time.Time           `json:"updated_at"` | ||||
| 	RegistrantContact RegistrantContact   `json:"registrant_contact"` | ||||
| } | ||||
| 
 | ||||
| // RegistrarTransferIn contains the structure for a domain transfer in
 | ||||
| // request.
 | ||||
| type RegistrarTransferIn struct { | ||||
| 	UnlockDomain      string `json:"unlock_domain"` | ||||
| 	DisablePrivacy    string `json:"disable_privacy"` | ||||
| 	EnterAuthCode     string `json:"enter_auth_code"` | ||||
| 	ApproveTransfer   string `json:"approve_transfer"` | ||||
| 	AcceptFoa         string `json:"accept_foa"` | ||||
| 	CanCancelTransfer bool   `json:"can_cancel_transfer"` | ||||
| } | ||||
| 
 | ||||
| // RegistrantContact is the contact details for the domain registration.
 | ||||
| type RegistrantContact struct { | ||||
| 	ID           string `json:"id"` | ||||
| 	FirstName    string `json:"first_name"` | ||||
| 	LastName     string `json:"last_name"` | ||||
| 	Organization string `json:"organization"` | ||||
| 	Address      string `json:"address"` | ||||
| 	Address2     string `json:"address2"` | ||||
| 	City         string `json:"city"` | ||||
| 	State        string `json:"state"` | ||||
| 	Zip          string `json:"zip"` | ||||
| 	Country      string `json:"country"` | ||||
| 	Phone        string `json:"phone"` | ||||
| 	Email        string `json:"email"` | ||||
| 	Fax          string `json:"fax"` | ||||
| } | ||||
| 
 | ||||
| // RegistrarDomainConfiguration is the structure for making updates to
 | ||||
| // and existing domain.
 | ||||
| type RegistrarDomainConfiguration struct { | ||||
| 	NameServers []string `json:"name_servers"` | ||||
| 	Privacy     bool     `json:"privacy"` | ||||
| 	Locked      bool     `json:"locked"` | ||||
| 	AutoRenew   bool     `json:"auto_renew"` | ||||
| } | ||||
| 
 | ||||
| // RegistrarDomainDetailResponse is the structure of the detailed
 | ||||
| // response from the API for a single domain.
 | ||||
| type RegistrarDomainDetailResponse struct { | ||||
| 	Response | ||||
| 	Result RegistrarDomain `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // RegistrarDomainsDetailResponse is the structure of the detailed
 | ||||
| // response from the API.
 | ||||
| type RegistrarDomainsDetailResponse struct { | ||||
| 	Response | ||||
| 	Result []RegistrarDomain `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // RegistrarDomain returns a single domain based on the account ID and
 | ||||
| // domain name.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#registrar-domains-get-domain
 | ||||
| func (api *API) RegistrarDomain(accountID, domainName string) (RegistrarDomain, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/registrar/domains/%s", accountID, domainName) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return RegistrarDomain{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r RegistrarDomainDetailResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return RegistrarDomain{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // RegistrarDomains returns all registrar domains based on the account
 | ||||
| // ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#registrar-domains-list-domains
 | ||||
| func (api *API) RegistrarDomains(accountID string) ([]RegistrarDomain, error) { | ||||
| 	uri := "/accounts/" + accountID + "/registrar/domains" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []RegistrarDomain{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r RegistrarDomainsDetailResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return []RegistrarDomain{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // TransferRegistrarDomain initiates the transfer from another registrar
 | ||||
| // to Cloudflare Registrar.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#registrar-domains-transfer-domain
 | ||||
| func (api *API) TransferRegistrarDomain(accountID, domainName string) ([]RegistrarDomain, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/registrar/domains/%s/transfer", accountID, domainName) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []RegistrarDomain{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r RegistrarDomainsDetailResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return []RegistrarDomain{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // CancelRegistrarDomainTransfer cancels a pending domain transfer.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#registrar-domains-cancel-transfer
 | ||||
| func (api *API) CancelRegistrarDomainTransfer(accountID, domainName string) ([]RegistrarDomain, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/registrar/domains/%s/cancel_transfer", accountID, domainName) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []RegistrarDomain{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r RegistrarDomainsDetailResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return []RegistrarDomain{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateRegistrarDomain updates an existing Registrar Domain configuration.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#registrar-domains-update-domain
 | ||||
| func (api *API) UpdateRegistrarDomain(accountID, domainName string, domainConfiguration RegistrarDomainConfiguration) (RegistrarDomain, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/registrar/domains/%s", accountID, domainName) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, domainConfiguration) | ||||
| 	if err != nil { | ||||
| 		return RegistrarDomain{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r RegistrarDomainDetailResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return RegistrarDomain{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
							
								
								
									
										5
									
								
								vendor/github.com/cloudflare/cloudflare-go/renovate.json
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/cloudflare/cloudflare-go/renovate.json
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| { | ||||
|   "extends": [ | ||||
|     "config:base" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										158
									
								
								vendor/github.com/cloudflare/cloudflare-go/spectrum.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								vendor/github.com/cloudflare/cloudflare-go/spectrum.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,158 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // SpectrumApplication defines a single Spectrum Application.
 | ||||
| type SpectrumApplication struct { | ||||
| 	ID            string                        `json:"id,omitempty"` | ||||
| 	Protocol      string                        `json:"protocol,omitempty"` | ||||
| 	IPv4          bool                          `json:"ipv4,omitempty"` | ||||
| 	DNS           SpectrumApplicationDNS        `json:"dns,omitempty"` | ||||
| 	OriginDirect  []string                      `json:"origin_direct,omitempty"` | ||||
| 	OriginPort    int                           `json:"origin_port,omitempty"` | ||||
| 	OriginDNS     *SpectrumApplicationOriginDNS `json:"origin_dns,omitempty"` | ||||
| 	IPFirewall    bool                          `json:"ip_firewall,omitempty"` | ||||
| 	ProxyProtocol bool                          `json:"proxy_protocol,omitempty"` | ||||
| 	TLS           string                        `json:"tls,omitempty"` | ||||
| 	CreatedOn     *time.Time                    `json:"created_on,omitempty"` | ||||
| 	ModifiedOn    *time.Time                    `json:"modified_on,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // SpectrumApplicationDNS holds the external DNS configuration for a Spectrum
 | ||||
| // Application.
 | ||||
| type SpectrumApplicationDNS struct { | ||||
| 	Type string `json:"type"` | ||||
| 	Name string `json:"name"` | ||||
| } | ||||
| 
 | ||||
| // SpectrumApplicationOriginDNS holds the origin DNS configuration for a Spectrum
 | ||||
| // Application.
 | ||||
| type SpectrumApplicationOriginDNS struct { | ||||
| 	Name string `json:"name"` | ||||
| } | ||||
| 
 | ||||
| // SpectrumApplicationDetailResponse is the structure of the detailed response
 | ||||
| // from the API.
 | ||||
| type SpectrumApplicationDetailResponse struct { | ||||
| 	Response | ||||
| 	Result SpectrumApplication `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // SpectrumApplicationsDetailResponse is the structure of the detailed response
 | ||||
| // from the API.
 | ||||
| type SpectrumApplicationsDetailResponse struct { | ||||
| 	Response | ||||
| 	Result []SpectrumApplication `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // SpectrumApplications fetches all of the Spectrum applications for a zone.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/spectrum/api-reference/#list-spectrum-applications
 | ||||
| func (api *API) SpectrumApplications(zoneID string) ([]SpectrumApplication, error) { | ||||
| 	uri := "/zones/" + zoneID + "/spectrum/apps" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []SpectrumApplication{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var spectrumApplications SpectrumApplicationsDetailResponse | ||||
| 	err = json.Unmarshal(res, &spectrumApplications) | ||||
| 	if err != nil { | ||||
| 		return []SpectrumApplication{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return spectrumApplications.Result, nil | ||||
| } | ||||
| 
 | ||||
| // SpectrumApplication fetches a single Spectrum application based on the ID.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/spectrum/api-reference/#list-spectrum-applications
 | ||||
| func (api *API) SpectrumApplication(zoneID string, applicationID string) (SpectrumApplication, error) { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/spectrum/apps/%s", | ||||
| 		zoneID, | ||||
| 		applicationID, | ||||
| 	) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return SpectrumApplication{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var spectrumApplication SpectrumApplicationDetailResponse | ||||
| 	err = json.Unmarshal(res, &spectrumApplication) | ||||
| 	if err != nil { | ||||
| 		return SpectrumApplication{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return spectrumApplication.Result, nil | ||||
| } | ||||
| 
 | ||||
| // CreateSpectrumApplication creates a new Spectrum application.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/spectrum/api-reference/#create-a-spectrum-application
 | ||||
| func (api *API) CreateSpectrumApplication(zoneID string, appDetails SpectrumApplication) (SpectrumApplication, error) { | ||||
| 	uri := "/zones/" + zoneID + "/spectrum/apps" | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", uri, appDetails) | ||||
| 	if err != nil { | ||||
| 		return SpectrumApplication{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var spectrumApplication SpectrumApplicationDetailResponse | ||||
| 	err = json.Unmarshal(res, &spectrumApplication) | ||||
| 	if err != nil { | ||||
| 		return SpectrumApplication{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return spectrumApplication.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateSpectrumApplication updates an existing Spectrum application.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/spectrum/api-reference/#update-a-spectrum-application
 | ||||
| func (api *API) UpdateSpectrumApplication(zoneID, appID string, appDetails SpectrumApplication) (SpectrumApplication, error) { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/spectrum/apps/%s", | ||||
| 		zoneID, | ||||
| 		appID, | ||||
| 	) | ||||
| 
 | ||||
| 	res, err := api.makeRequest("PUT", uri, appDetails) | ||||
| 	if err != nil { | ||||
| 		return SpectrumApplication{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var spectrumApplication SpectrumApplicationDetailResponse | ||||
| 	err = json.Unmarshal(res, &spectrumApplication) | ||||
| 	if err != nil { | ||||
| 		return SpectrumApplication{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return spectrumApplication.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteSpectrumApplication removes a Spectrum application based on the ID.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/spectrum/api-reference/#delete-a-spectrum-application
 | ||||
| func (api *API) DeleteSpectrumApplication(zoneID string, applicationID string) error { | ||||
| 	uri := fmt.Sprintf( | ||||
| 		"/zones/%s/spectrum/apps/%s", | ||||
| 		zoneID, | ||||
| 		applicationID, | ||||
| 	) | ||||
| 
 | ||||
| 	_, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										157
									
								
								vendor/github.com/cloudflare/cloudflare-go/ssl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								vendor/github.com/cloudflare/cloudflare-go/ssl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,157 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // ZoneCustomSSL represents custom SSL certificate metadata.
 | ||||
| type ZoneCustomSSL struct { | ||||
| 	ID              string                       `json:"id"` | ||||
| 	Hosts           []string                     `json:"hosts"` | ||||
| 	Issuer          string                       `json:"issuer"` | ||||
| 	Signature       string                       `json:"signature"` | ||||
| 	Status          string                       `json:"status"` | ||||
| 	BundleMethod    string                       `json:"bundle_method"` | ||||
| 	GeoRestrictions ZoneCustomSSLGeoRestrictions `json:"geo_restrictions"` | ||||
| 	ZoneID          string                       `json:"zone_id"` | ||||
| 	UploadedOn      time.Time                    `json:"uploaded_on"` | ||||
| 	ModifiedOn      time.Time                    `json:"modified_on"` | ||||
| 	ExpiresOn       time.Time                    `json:"expires_on"` | ||||
| 	Priority        int                          `json:"priority"` | ||||
| 	KeylessServer   KeylessSSL                   `json:"keyless_server"` | ||||
| } | ||||
| 
 | ||||
| // ZoneCustomSSLGeoRestrictions represents the parameter to create or update
 | ||||
| // geographic restrictions on a custom ssl certificate.
 | ||||
| type ZoneCustomSSLGeoRestrictions struct { | ||||
| 	Label string `json:"label"` | ||||
| } | ||||
| 
 | ||||
| // zoneCustomSSLResponse represents the response from the zone SSL details endpoint.
 | ||||
| type zoneCustomSSLResponse struct { | ||||
| 	Response | ||||
| 	Result ZoneCustomSSL `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // zoneCustomSSLsResponse represents the response from the zone SSL list endpoint.
 | ||||
| type zoneCustomSSLsResponse struct { | ||||
| 	Response | ||||
| 	Result []ZoneCustomSSL `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // ZoneCustomSSLOptions represents the parameters to create or update an existing
 | ||||
| // custom SSL configuration.
 | ||||
| type ZoneCustomSSLOptions struct { | ||||
| 	Certificate     string                       `json:"certificate"` | ||||
| 	PrivateKey      string                       `json:"private_key"` | ||||
| 	BundleMethod    string                       `json:"bundle_method,omitempty"` | ||||
| 	GeoRestrictions ZoneCustomSSLGeoRestrictions `json:"geo_restrictions,omitempty"` | ||||
| 	Type            string                       `json:"type,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // ZoneCustomSSLPriority represents a certificate's ID and priority. It is a
 | ||||
| // subset of ZoneCustomSSL used for patch requests.
 | ||||
| type ZoneCustomSSLPriority struct { | ||||
| 	ID       string `json:"ID"` | ||||
| 	Priority int    `json:"priority"` | ||||
| } | ||||
| 
 | ||||
| // CreateSSL allows you to add a custom SSL certificate to the given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-create-ssl-configuration
 | ||||
| func (api *API) CreateSSL(zoneID string, options ZoneCustomSSLOptions) (ZoneCustomSSL, error) { | ||||
| 	uri := "/zones/" + zoneID + "/custom_certificates" | ||||
| 	res, err := api.makeRequest("POST", uri, options) | ||||
| 	if err != nil { | ||||
| 		return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r zoneCustomSSLResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ListSSL lists the custom certificates for the given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-list-ssl-configurations
 | ||||
| func (api *API) ListSSL(zoneID string) ([]ZoneCustomSSL, error) { | ||||
| 	uri := "/zones/" + zoneID + "/custom_certificates" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r zoneCustomSSLsResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // SSLDetails returns the configuration details for a custom SSL certificate.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-ssl-configuration-details
 | ||||
| func (api *API) SSLDetails(zoneID, certificateID string) (ZoneCustomSSL, error) { | ||||
| 	uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r zoneCustomSSLResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateSSL updates (replaces) a custom SSL certificate.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-update-ssl-configuration
 | ||||
| func (api *API) UpdateSSL(zoneID, certificateID string, options ZoneCustomSSLOptions) (ZoneCustomSSL, error) { | ||||
| 	uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID | ||||
| 	res, err := api.makeRequest("PATCH", uri, options) | ||||
| 	if err != nil { | ||||
| 		return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r zoneCustomSSLResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ReprioritizeSSL allows you to change the priority (which is served for a given
 | ||||
| // request) of custom SSL certificates associated with the given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-re-prioritize-ssl-certificates
 | ||||
| func (api *API) ReprioritizeSSL(zoneID string, p []ZoneCustomSSLPriority) ([]ZoneCustomSSL, error) { | ||||
| 	uri := "/zones/" + zoneID + "/custom_certificates/prioritize" | ||||
| 	params := struct { | ||||
| 		Certificates []ZoneCustomSSLPriority `json:"certificates"` | ||||
| 	}{ | ||||
| 		Certificates: p, | ||||
| 	} | ||||
| 	res, err := api.makeRequest("PUT", uri, params) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r zoneCustomSSLsResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteSSL deletes a custom SSL certificate from the given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-delete-an-ssl-certificate
 | ||||
| func (api *API) DeleteSSL(zoneID, certificateID string) error { | ||||
| 	uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID | ||||
| 	if _, err := api.makeRequest("DELETE", uri, nil); err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										88
									
								
								vendor/github.com/cloudflare/cloudflare-go/universal_ssl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								vendor/github.com/cloudflare/cloudflare-go/universal_ssl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // UniversalSSLSetting represents a universal ssl setting's properties.
 | ||||
| type UniversalSSLSetting struct { | ||||
| 	Enabled bool `json:"enabled"` | ||||
| } | ||||
| 
 | ||||
| type universalSSLSettingResponse struct { | ||||
| 	Response | ||||
| 	Result UniversalSSLSetting `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // UniversalSSLVerificationDetails represents a universal ssl verifcation's properties.
 | ||||
| type UniversalSSLVerificationDetails struct { | ||||
| 	CertificateStatus  string                       `json:"certificate_status"` | ||||
| 	VerificationType   string                       `json:"verification_type"` | ||||
| 	ValidationMethod   string                       `json:"validation_method"` | ||||
| 	CertPackUUID       string                       `json:"cert_pack_uuid"` | ||||
| 	VerificationStatus bool                         `json:"verification_status"` | ||||
| 	BrandCheck         bool                         `json:"brand_check"` | ||||
| 	VerificationInfo   UniversalSSLVerificationInfo `json:"verification_info"` | ||||
| } | ||||
| 
 | ||||
| // UniversalSSLVerificationInfo represents DCV record.
 | ||||
| type UniversalSSLVerificationInfo struct { | ||||
| 	RecordName   string `json:"record_name"` | ||||
| 	RecordTarget string `json:"record_target"` | ||||
| } | ||||
| 
 | ||||
| type universalSSLVerificationResponse struct { | ||||
| 	Response | ||||
| 	Result []UniversalSSLVerificationDetails `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // UniversalSSLSettingDetails returns the details for a universal ssl setting
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#universal-ssl-settings-for-a-zone-universal-ssl-settings-details
 | ||||
| func (api *API) UniversalSSLSettingDetails(zoneID string) (UniversalSSLSetting, error) { | ||||
| 	uri := "/zones/" + zoneID + "/ssl/universal/settings" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return UniversalSSLSetting{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r universalSSLSettingResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return UniversalSSLSetting{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // EditUniversalSSLSetting edits the uniersal ssl setting for a zone
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#universal-ssl-settings-for-a-zone-edit-universal-ssl-settings
 | ||||
| func (api *API) EditUniversalSSLSetting(zoneID string, setting UniversalSSLSetting) (UniversalSSLSetting, error) { | ||||
| 	uri := "/zones/" + zoneID + "/ssl/universal/settings" | ||||
| 	res, err := api.makeRequest("PATCH", uri, setting) | ||||
| 	if err != nil { | ||||
| 		return UniversalSSLSetting{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r universalSSLSettingResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return UniversalSSLSetting{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // UniversalSSLVerificationDetails returns the details for a universal ssl verifcation
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#ssl-verification-ssl-verification-details
 | ||||
| func (api *API) UniversalSSLVerificationDetails(zoneID string) ([]UniversalSSLVerificationDetails, error) { | ||||
| 	uri := "/zones/" + zoneID + "/ssl/verification" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []UniversalSSLVerificationDetails{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r universalSSLVerificationResponse | ||||
| 	if err := json.Unmarshal(res, &r); err != nil { | ||||
| 		return []UniversalSSLVerificationDetails{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
							
								
								
									
										113
									
								
								vendor/github.com/cloudflare/cloudflare-go/user.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								vendor/github.com/cloudflare/cloudflare-go/user.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,113 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // User describes a user account.
 | ||||
| type User struct { | ||||
| 	ID         string     `json:"id,omitempty"` | ||||
| 	Email      string     `json:"email,omitempty"` | ||||
| 	FirstName  string     `json:"first_name,omitempty"` | ||||
| 	LastName   string     `json:"last_name,omitempty"` | ||||
| 	Username   string     `json:"username,omitempty"` | ||||
| 	Telephone  string     `json:"telephone,omitempty"` | ||||
| 	Country    string     `json:"country,omitempty"` | ||||
| 	Zipcode    string     `json:"zipcode,omitempty"` | ||||
| 	CreatedOn  *time.Time `json:"created_on,omitempty"` | ||||
| 	ModifiedOn *time.Time `json:"modified_on,omitempty"` | ||||
| 	APIKey     string     `json:"api_key,omitempty"` | ||||
| 	TwoFA      bool       `json:"two_factor_authentication_enabled,omitempty"` | ||||
| 	Betas      []string   `json:"betas,omitempty"` | ||||
| 	Accounts   []Account  `json:"organizations,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // UserResponse wraps a response containing User accounts.
 | ||||
| type UserResponse struct { | ||||
| 	Response | ||||
| 	Result User `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // userBillingProfileResponse wraps a response containing Billing Profile information.
 | ||||
| type userBillingProfileResponse struct { | ||||
| 	Response | ||||
| 	Result UserBillingProfile | ||||
| } | ||||
| 
 | ||||
| // UserBillingProfile contains Billing Profile information.
 | ||||
| type UserBillingProfile struct { | ||||
| 	ID              string     `json:"id,omitempty"` | ||||
| 	FirstName       string     `json:"first_name,omitempty"` | ||||
| 	LastName        string     `json:"last_name,omitempty"` | ||||
| 	Address         string     `json:"address,omitempty"` | ||||
| 	Address2        string     `json:"address2,omitempty"` | ||||
| 	Company         string     `json:"company,omitempty"` | ||||
| 	City            string     `json:"city,omitempty"` | ||||
| 	State           string     `json:"state,omitempty"` | ||||
| 	ZipCode         string     `json:"zipcode,omitempty"` | ||||
| 	Country         string     `json:"country,omitempty"` | ||||
| 	Telephone       string     `json:"telephone,omitempty"` | ||||
| 	CardNumber      string     `json:"card_number,omitempty"` | ||||
| 	CardExpiryYear  int        `json:"card_expiry_year,omitempty"` | ||||
| 	CardExpiryMonth int        `json:"card_expiry_month,omitempty"` | ||||
| 	VAT             string     `json:"vat,omitempty"` | ||||
| 	CreatedOn       *time.Time `json:"created_on,omitempty"` | ||||
| 	EditedOn        *time.Time `json:"edited_on,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // UserDetails provides information about the logged-in user.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-user-details
 | ||||
| func (api *API) UserDetails() (User, error) { | ||||
| 	var r UserResponse | ||||
| 	res, err := api.makeRequest("GET", "/user", nil) | ||||
| 	if err != nil { | ||||
| 		return User{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return User{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateUser updates the properties of the given user.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-update-user
 | ||||
| func (api *API) UpdateUser(user *User) (User, error) { | ||||
| 	var r UserResponse | ||||
| 	res, err := api.makeRequest("PATCH", "/user", user) | ||||
| 	if err != nil { | ||||
| 		return User{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return User{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UserBillingProfile returns the billing profile of the user.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-billing-profile
 | ||||
| func (api *API) UserBillingProfile() (UserBillingProfile, error) { | ||||
| 	var r userBillingProfileResponse | ||||
| 	res, err := api.makeRequest("GET", "/user/billing/profile", nil) | ||||
| 	if err != nil { | ||||
| 		return UserBillingProfile{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return UserBillingProfile{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return r.Result, nil | ||||
| } | ||||
							
								
								
									
										149
									
								
								vendor/github.com/cloudflare/cloudflare-go/user_agent.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								vendor/github.com/cloudflare/cloudflare-go/user_agent.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,149 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // UserAgentRule represents a User-Agent Block. These rules can be used to
 | ||||
| // challenge, block or whitelist specific User-Agents for a given zone.
 | ||||
| type UserAgentRule struct { | ||||
| 	ID            string              `json:"id"` | ||||
| 	Description   string              `json:"description"` | ||||
| 	Mode          string              `json:"mode"` | ||||
| 	Configuration UserAgentRuleConfig `json:"configuration"` | ||||
| 	Paused        bool                `json:"paused"` | ||||
| } | ||||
| 
 | ||||
| // UserAgentRuleConfig represents a Zone Lockdown config, which comprises
 | ||||
| // a Target ("ip" or "ip_range") and a Value (an IP address or IP+mask,
 | ||||
| // respectively.)
 | ||||
| type UserAgentRuleConfig ZoneLockdownConfig | ||||
| 
 | ||||
| // UserAgentRuleResponse represents a response from the Zone Lockdown endpoint.
 | ||||
| type UserAgentRuleResponse struct { | ||||
| 	Result UserAgentRule `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // UserAgentRuleListResponse represents a response from the List Zone Lockdown endpoint.
 | ||||
| type UserAgentRuleListResponse struct { | ||||
| 	Result []UserAgentRule `json:"result"` | ||||
| 	Response | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // CreateUserAgentRule creates a User-Agent Block rule for the given zone ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-agent-blocking-rules-create-a-useragent-rule
 | ||||
| func (api *API) CreateUserAgentRule(zoneID string, ld UserAgentRule) (*UserAgentRuleResponse, error) { | ||||
| 	switch ld.Mode { | ||||
| 	case "block", "challenge", "js_challenge", "whitelist": | ||||
| 		break | ||||
| 	default: | ||||
| 		return nil, errors.New(`the User-Agent Block rule mode must be one of "block", "challenge", "js_challenge", "whitelist"`) | ||||
| 	} | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/firewall/ua_rules" | ||||
| 	res, err := api.makeRequest("POST", uri, ld) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &UserAgentRuleResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateUserAgentRule updates a User-Agent Block rule (based on the ID) for the given zone ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-agent-blocking-rules-update-useragent-rule
 | ||||
| func (api *API) UpdateUserAgentRule(zoneID string, id string, ld UserAgentRule) (*UserAgentRuleResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/firewall/ua_rules/" + id | ||||
| 	res, err := api.makeRequest("PUT", uri, ld) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &UserAgentRuleResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteUserAgentRule deletes a User-Agent Block rule (based on the ID) for the given zone ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-agent-blocking-rules-delete-useragent-rule
 | ||||
| func (api *API) DeleteUserAgentRule(zoneID string, id string) (*UserAgentRuleResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/firewall/ua_rules/" + id | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &UserAgentRuleResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| // UserAgentRule retrieves a User-Agent Block rule (based on the ID) for the given zone ID.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-agent-blocking-rules-useragent-rule-details
 | ||||
| func (api *API) UserAgentRule(zoneID string, id string) (*UserAgentRuleResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/firewall/ua_rules/" + id | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &UserAgentRuleResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| // ListUserAgentRules retrieves a list of User-Agent Block rules for a given zone ID by page number.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#user-agent-blocking-rules-list-useragent-rules
 | ||||
| func (api *API) ListUserAgentRules(zoneID string, page int) (*UserAgentRuleListResponse, error) { | ||||
| 	v := url.Values{} | ||||
| 	if page <= 0 { | ||||
| 		page = 1 | ||||
| 	} | ||||
| 
 | ||||
| 	v.Set("page", strconv.Itoa(page)) | ||||
| 	v.Set("per_page", strconv.Itoa(100)) | ||||
| 	query := "?" + v.Encode() | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/firewall/ua_rules" + query | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &UserAgentRuleListResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
							
								
								
									
										192
									
								
								vendor/github.com/cloudflare/cloudflare-go/virtualdns.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								vendor/github.com/cloudflare/cloudflare-go/virtualdns.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,192 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // VirtualDNS represents a Virtual DNS configuration.
 | ||||
| type VirtualDNS struct { | ||||
| 	ID                   string   `json:"id"` | ||||
| 	Name                 string   `json:"name"` | ||||
| 	OriginIPs            []string `json:"origin_ips"` | ||||
| 	VirtualDNSIPs        []string `json:"virtual_dns_ips"` | ||||
| 	MinimumCacheTTL      uint     `json:"minimum_cache_ttl"` | ||||
| 	MaximumCacheTTL      uint     `json:"maximum_cache_ttl"` | ||||
| 	DeprecateAnyRequests bool     `json:"deprecate_any_requests"` | ||||
| 	ModifiedOn           string   `json:"modified_on"` | ||||
| } | ||||
| 
 | ||||
| // VirtualDNSAnalyticsMetrics respresents a group of aggregated Virtual DNS metrics.
 | ||||
| type VirtualDNSAnalyticsMetrics struct { | ||||
| 	QueryCount         *int64   `json:"queryCount"` | ||||
| 	UncachedCount      *int64   `json:"uncachedCount"` | ||||
| 	StaleCount         *int64   `json:"staleCount"` | ||||
| 	ResponseTimeAvg    *float64 `json:"responseTimeAvg"` | ||||
| 	ResponseTimeMedian *float64 `json:"responseTimeMedian"` | ||||
| 	ResponseTime90th   *float64 `json:"responseTime90th"` | ||||
| 	ResponseTime99th   *float64 `json:"responseTime99th"` | ||||
| } | ||||
| 
 | ||||
| // VirtualDNSAnalytics represents a set of aggregated Virtual DNS metrics.
 | ||||
| // TODO: Add the queried data and not only the aggregated values.
 | ||||
| type VirtualDNSAnalytics struct { | ||||
| 	Totals VirtualDNSAnalyticsMetrics `json:"totals"` | ||||
| 	Min    VirtualDNSAnalyticsMetrics `json:"min"` | ||||
| 	Max    VirtualDNSAnalyticsMetrics `json:"max"` | ||||
| } | ||||
| 
 | ||||
| // VirtualDNSUserAnalyticsOptions represents range and dimension selection on analytics endpoint
 | ||||
| type VirtualDNSUserAnalyticsOptions struct { | ||||
| 	Metrics []string | ||||
| 	Since   *time.Time | ||||
| 	Until   *time.Time | ||||
| } | ||||
| 
 | ||||
| // VirtualDNSResponse represents a Virtual DNS response.
 | ||||
| type VirtualDNSResponse struct { | ||||
| 	Response | ||||
| 	Result *VirtualDNS `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // VirtualDNSListResponse represents an array of Virtual DNS responses.
 | ||||
| type VirtualDNSListResponse struct { | ||||
| 	Response | ||||
| 	Result []*VirtualDNS `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // VirtualDNSAnalyticsResponse represents a Virtual DNS analytics response.
 | ||||
| type VirtualDNSAnalyticsResponse struct { | ||||
| 	Response | ||||
| 	Result VirtualDNSAnalytics `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // CreateVirtualDNS creates a new Virtual DNS cluster.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#virtual-dns-users--create-a-virtual-dns-cluster
 | ||||
| func (api *API) CreateVirtualDNS(v *VirtualDNS) (*VirtualDNS, error) { | ||||
| 	res, err := api.makeRequest("POST", "/user/virtual_dns", v) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &VirtualDNSResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response.Result, nil | ||||
| } | ||||
| 
 | ||||
| // VirtualDNS fetches a single virtual DNS cluster.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#virtual-dns-users--get-a-virtual-dns-cluster
 | ||||
| func (api *API) VirtualDNS(virtualDNSID string) (*VirtualDNS, error) { | ||||
| 	uri := "/user/virtual_dns/" + virtualDNSID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &VirtualDNSResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ListVirtualDNS lists the virtual DNS clusters associated with an account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#virtual-dns-users--get-virtual-dns-clusters
 | ||||
| func (api *API) ListVirtualDNS() ([]*VirtualDNS, error) { | ||||
| 	res, err := api.makeRequest("GET", "/user/virtual_dns", nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &VirtualDNSListResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateVirtualDNS updates a Virtual DNS cluster.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#virtual-dns-users--modify-a-virtual-dns-cluster
 | ||||
| func (api *API) UpdateVirtualDNS(virtualDNSID string, vv VirtualDNS) error { | ||||
| 	uri := "/user/virtual_dns/" + virtualDNSID | ||||
| 	res, err := api.makeRequest("PUT", uri, vv) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &VirtualDNSResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DeleteVirtualDNS deletes a Virtual DNS cluster. Note that this cannot be
 | ||||
| // undone, and will stop all traffic to that cluster.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#virtual-dns-users--delete-a-virtual-dns-cluster
 | ||||
| func (api *API) DeleteVirtualDNS(virtualDNSID string) error { | ||||
| 	uri := "/user/virtual_dns/" + virtualDNSID | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &VirtualDNSResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // encode encodes non-nil fields into URL encoded form.
 | ||||
| func (o VirtualDNSUserAnalyticsOptions) encode() string { | ||||
| 	v := url.Values{} | ||||
| 	if o.Since != nil { | ||||
| 		v.Set("since", (*o.Since).UTC().Format(time.RFC3339)) | ||||
| 	} | ||||
| 	if o.Until != nil { | ||||
| 		v.Set("until", (*o.Until).UTC().Format(time.RFC3339)) | ||||
| 	} | ||||
| 	if o.Metrics != nil { | ||||
| 		v.Set("metrics", strings.Join(o.Metrics, ",")) | ||||
| 	} | ||||
| 	return v.Encode() | ||||
| } | ||||
| 
 | ||||
| // VirtualDNSUserAnalytics retrieves analytics report for a specified dimension and time range
 | ||||
| func (api *API) VirtualDNSUserAnalytics(virtualDNSID string, o VirtualDNSUserAnalyticsOptions) (VirtualDNSAnalytics, error) { | ||||
| 	uri := "/user/virtual_dns/" + virtualDNSID + "/dns_analytics/report?" + o.encode() | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return VirtualDNSAnalytics{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := VirtualDNSAnalyticsResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return VirtualDNSAnalytics{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response.Result, nil | ||||
| } | ||||
							
								
								
									
										300
									
								
								vendor/github.com/cloudflare/cloudflare-go/waf.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								vendor/github.com/cloudflare/cloudflare-go/waf.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,300 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // WAFPackage represents a WAF package configuration.
 | ||||
| type WAFPackage struct { | ||||
| 	ID            string `json:"id"` | ||||
| 	Name          string `json:"name"` | ||||
| 	Description   string `json:"description"` | ||||
| 	ZoneID        string `json:"zone_id"` | ||||
| 	DetectionMode string `json:"detection_mode"` | ||||
| 	Sensitivity   string `json:"sensitivity"` | ||||
| 	ActionMode    string `json:"action_mode"` | ||||
| } | ||||
| 
 | ||||
| // WAFPackagesResponse represents the response from the WAF packages endpoint.
 | ||||
| type WAFPackagesResponse struct { | ||||
| 	Response | ||||
| 	Result     []WAFPackage `json:"result"` | ||||
| 	ResultInfo ResultInfo   `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // WAFPackageResponse represents the response from the WAF package endpoint.
 | ||||
| type WAFPackageResponse struct { | ||||
| 	Response | ||||
| 	Result     WAFPackage `json:"result"` | ||||
| 	ResultInfo ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // WAFPackageOptions represents options to edit a WAF package.
 | ||||
| type WAFPackageOptions struct { | ||||
| 	Sensitivity string `json:"sensitivity,omitempty"` | ||||
| 	ActionMode  string `json:"action_mode,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // WAFGroup represents a WAF rule group.
 | ||||
| type WAFGroup struct { | ||||
| 	ID                 string   `json:"id"` | ||||
| 	Name               string   `json:"name"` | ||||
| 	Description        string   `json:"description"` | ||||
| 	RulesCount         int      `json:"rules_count"` | ||||
| 	ModifiedRulesCount int      `json:"modified_rules_count"` | ||||
| 	PackageID          string   `json:"package_id"` | ||||
| 	Mode               string   `json:"mode"` | ||||
| 	AllowedModes       []string `json:"allowed_modes"` | ||||
| } | ||||
| 
 | ||||
| // WAFGroupsResponse represents the response from the WAF groups endpoint.
 | ||||
| type WAFGroupsResponse struct { | ||||
| 	Response | ||||
| 	Result     []WAFGroup `json:"result"` | ||||
| 	ResultInfo ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // WAFGroupResponse represents the response from the WAF group endpoint.
 | ||||
| type WAFGroupResponse struct { | ||||
| 	Response | ||||
| 	Result     WAFGroup   `json:"result"` | ||||
| 	ResultInfo ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // WAFRule represents a WAF rule.
 | ||||
| type WAFRule struct { | ||||
| 	ID          string `json:"id"` | ||||
| 	Description string `json:"description"` | ||||
| 	Priority    string `json:"priority"` | ||||
| 	PackageID   string `json:"package_id"` | ||||
| 	Group       struct { | ||||
| 		ID   string `json:"id"` | ||||
| 		Name string `json:"name"` | ||||
| 	} `json:"group"` | ||||
| 	Mode         string   `json:"mode"` | ||||
| 	DefaultMode  string   `json:"default_mode"` | ||||
| 	AllowedModes []string `json:"allowed_modes"` | ||||
| } | ||||
| 
 | ||||
| // WAFRulesResponse represents the response from the WAF rules endpoint.
 | ||||
| type WAFRulesResponse struct { | ||||
| 	Response | ||||
| 	Result     []WAFRule  `json:"result"` | ||||
| 	ResultInfo ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // WAFRuleResponse represents the response from the WAF rule endpoint.
 | ||||
| type WAFRuleResponse struct { | ||||
| 	Response | ||||
| 	Result     WAFRule    `json:"result"` | ||||
| 	ResultInfo ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // WAFRuleOptions is a subset of WAFRule, for editable options.
 | ||||
| type WAFRuleOptions struct { | ||||
| 	Mode string `json:"mode"` | ||||
| } | ||||
| 
 | ||||
| // ListWAFPackages returns a slice of the WAF packages for the given zone.
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#waf-rule-packages-list-firewall-packages
 | ||||
| func (api *API) ListWAFPackages(zoneID string) ([]WAFPackage, error) { | ||||
| 	var p WAFPackagesResponse | ||||
| 	var packages []WAFPackage | ||||
| 	var res []byte | ||||
| 	var err error | ||||
| 	uri := "/zones/" + zoneID + "/firewall/waf/packages" | ||||
| 	res, err = api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []WAFPackage{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	err = json.Unmarshal(res, &p) | ||||
| 	if err != nil { | ||||
| 		return []WAFPackage{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	if !p.Success { | ||||
| 		// TODO: Provide an actual error message instead of always returning nil
 | ||||
| 		return []WAFPackage{}, err | ||||
| 	} | ||||
| 	for pi := range p.Result { | ||||
| 		packages = append(packages, p.Result[pi]) | ||||
| 	} | ||||
| 	return packages, nil | ||||
| } | ||||
| 
 | ||||
| // WAFPackage returns a WAF package for the given zone.
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#waf-rule-packages-firewall-package-details
 | ||||
| func (api *API) WAFPackage(zoneID, packageID string) (WAFPackage, error) { | ||||
| 	uri := "/zones/" + zoneID + "/firewall/waf/packages/" + packageID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return WAFPackage{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r WAFPackageResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return WAFPackage{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateWAFPackage lets you update the a WAF Package.
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#waf-rule-packages-edit-firewall-package
 | ||||
| func (api *API) UpdateWAFPackage(zoneID, packageID string, opts WAFPackageOptions) (WAFPackage, error) { | ||||
| 	uri := "/zones/" + zoneID + "/firewall/waf/packages/" + packageID | ||||
| 	res, err := api.makeRequest("PATCH", uri, opts) | ||||
| 	if err != nil { | ||||
| 		return WAFPackage{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r WAFPackageResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return WAFPackage{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ListWAFGroups returns a slice of the WAF groups for the given WAF package.
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#waf-rule-groups-list-rule-groups
 | ||||
| func (api *API) ListWAFGroups(zoneID, packageID string) ([]WAFGroup, error) { | ||||
| 	var groups []WAFGroup | ||||
| 	var res []byte | ||||
| 	var err error | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/firewall/waf/packages/" + packageID + "/groups" | ||||
| 	res, err = api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []WAFGroup{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r WAFGroupsResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return []WAFGroup{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	if !r.Success { | ||||
| 		// TODO: Provide an actual error message instead of always returning nil
 | ||||
| 		return []WAFGroup{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	for gi := range r.Result { | ||||
| 		groups = append(groups, r.Result[gi]) | ||||
| 	} | ||||
| 	return groups, nil | ||||
| } | ||||
| 
 | ||||
| // WAFGroup returns a WAF rule group from the given WAF package.
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#waf-rule-groups-rule-group-details
 | ||||
| func (api *API) WAFGroup(zoneID, packageID, groupID string) (WAFGroup, error) { | ||||
| 	uri := "/zones/" + zoneID + "/firewall/waf/packages/" + packageID + "/groups/" + groupID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return WAFGroup{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r WAFGroupResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return WAFGroup{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateWAFGroup lets you update the mode of a WAF Group.
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#waf-rule-groups-edit-rule-group
 | ||||
| func (api *API) UpdateWAFGroup(zoneID, packageID, groupID, mode string) (WAFGroup, error) { | ||||
| 	opts := WAFRuleOptions{Mode: mode} | ||||
| 	uri := "/zones/" + zoneID + "/firewall/waf/packages/" + packageID + "/groups/" + groupID | ||||
| 	res, err := api.makeRequest("PATCH", uri, opts) | ||||
| 	if err != nil { | ||||
| 		return WAFGroup{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r WAFGroupResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return WAFGroup{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ListWAFRules returns a slice of the WAF rules for the given WAF package.
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#waf-rules-list-rules
 | ||||
| func (api *API) ListWAFRules(zoneID, packageID string) ([]WAFRule, error) { | ||||
| 	var rules []WAFRule | ||||
| 	var res []byte | ||||
| 	var err error | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/firewall/waf/packages/" + packageID + "/rules" | ||||
| 	res, err = api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []WAFRule{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r WAFRulesResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return []WAFRule{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	if !r.Success { | ||||
| 		// TODO: Provide an actual error message instead of always returning nil
 | ||||
| 		return []WAFRule{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	for ri := range r.Result { | ||||
| 		rules = append(rules, r.Result[ri]) | ||||
| 	} | ||||
| 	return rules, nil | ||||
| } | ||||
| 
 | ||||
| // WAFRule returns a WAF rule from the given WAF package.
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#waf-rules-rule-details
 | ||||
| func (api *API) WAFRule(zoneID, packageID, ruleID string) (WAFRule, error) { | ||||
| 	uri := "/zones/" + zoneID + "/firewall/waf/packages/" + packageID + "/rules/" + ruleID | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return WAFRule{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r WAFRuleResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return WAFRule{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateWAFRule lets you update the mode of a WAF Rule.
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#waf-rules-edit-rule
 | ||||
| func (api *API) UpdateWAFRule(zoneID, packageID, ruleID, mode string) (WAFRule, error) { | ||||
| 	opts := WAFRuleOptions{Mode: mode} | ||||
| 	uri := "/zones/" + zoneID + "/firewall/waf/packages/" + packageID + "/rules/" + ruleID | ||||
| 	res, err := api.makeRequest("PATCH", uri, opts) | ||||
| 	if err != nil { | ||||
| 		return WAFRule{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r WAFRuleResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return WAFRule{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
							
								
								
									
										314
									
								
								vendor/github.com/cloudflare/cloudflare-go/workers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								vendor/github.com/cloudflare/cloudflare-go/workers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,314 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // WorkerRequestParams provides parameters for worker requests for both enterprise and standard requests
 | ||||
| type WorkerRequestParams struct { | ||||
| 	ZoneID     string | ||||
| 	ScriptName string | ||||
| } | ||||
| 
 | ||||
| // WorkerRoute aka filters are patterns used to enable or disable workers that match requests.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#worker-filters-properties
 | ||||
| type WorkerRoute struct { | ||||
| 	ID      string `json:"id,omitempty"` | ||||
| 	Pattern string `json:"pattern"` | ||||
| 	Enabled bool   `json:"enabled"` | ||||
| 	Script  string `json:"script,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // WorkerRoutesResponse embeds Response struct and slice of WorkerRoutes
 | ||||
| type WorkerRoutesResponse struct { | ||||
| 	Response | ||||
| 	Routes []WorkerRoute `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // WorkerRouteResponse embeds Response struct and a single WorkerRoute
 | ||||
| type WorkerRouteResponse struct { | ||||
| 	Response | ||||
| 	WorkerRoute `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // WorkerScript Cloudflare Worker struct with metadata
 | ||||
| type WorkerScript struct { | ||||
| 	WorkerMetaData | ||||
| 	Script string `json:"script"` | ||||
| } | ||||
| 
 | ||||
| // WorkerMetaData contains worker script information such as size, creation & modification dates
 | ||||
| type WorkerMetaData struct { | ||||
| 	ID         string    `json:"id,omitempty"` | ||||
| 	ETAG       string    `json:"etag,omitempty"` | ||||
| 	Size       int       `json:"size,omitempty"` | ||||
| 	CreatedOn  time.Time `json:"created_on,omitempty"` | ||||
| 	ModifiedOn time.Time `json:"modified_on,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // WorkerListResponse wrapper struct for API response to worker script list API call
 | ||||
| type WorkerListResponse struct { | ||||
| 	Response | ||||
| 	WorkerList []WorkerMetaData `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // WorkerScriptResponse wrapper struct for API response to worker script calls
 | ||||
| type WorkerScriptResponse struct { | ||||
| 	Response | ||||
| 	WorkerScript `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // DeleteWorker deletes worker for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#worker-script-delete-worker
 | ||||
| func (api *API) DeleteWorker(requestParams *WorkerRequestParams) (WorkerScriptResponse, error) { | ||||
| 	// if ScriptName is provided we will treat as org request
 | ||||
| 	if requestParams.ScriptName != "" { | ||||
| 		return api.deleteWorkerWithName(requestParams.ScriptName) | ||||
| 	} | ||||
| 	uri := "/zones/" + requestParams.ZoneID + "/workers/script" | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	var r WorkerScriptResponse | ||||
| 	if err != nil { | ||||
| 		return r, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return r, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteWorkerWithName deletes worker for a zone.
 | ||||
| // This is an enterprise only feature https://developers.cloudflare.com/workers/api/config-api-for-enterprise
 | ||||
| // account must be specified as api option https://godoc.org/github.com/cloudflare/cloudflare-go#UsingAccount
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#worker-script-delete-worker
 | ||||
| func (api *API) deleteWorkerWithName(scriptName string) (WorkerScriptResponse, error) { | ||||
| 	if api.AccountID == "" { | ||||
| 		return WorkerScriptResponse{}, errors.New("account ID required for enterprise only request") | ||||
| 	} | ||||
| 	uri := "/accounts/" + api.AccountID + "/workers/scripts/" + scriptName | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	var r WorkerScriptResponse | ||||
| 	if err != nil { | ||||
| 		return r, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return r, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // DownloadWorker fetch raw script content for your worker returns []byte containing worker code js
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#worker-script-download-worker
 | ||||
| func (api *API) DownloadWorker(requestParams *WorkerRequestParams) (WorkerScriptResponse, error) { | ||||
| 	if requestParams.ScriptName != "" { | ||||
| 		return api.downloadWorkerWithName(requestParams.ScriptName) | ||||
| 	} | ||||
| 	uri := "/zones/" + requestParams.ZoneID + "/workers/script" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	var r WorkerScriptResponse | ||||
| 	if err != nil { | ||||
| 		return r, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	r.Script = string(res) | ||||
| 	r.Success = true | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // DownloadWorkerWithName fetch raw script content for your worker returns string containing worker code js
 | ||||
| // This is an enterprise only feature https://developers.cloudflare.com/workers/api/config-api-for-enterprise/
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#worker-script-download-worker
 | ||||
| func (api *API) downloadWorkerWithName(scriptName string) (WorkerScriptResponse, error) { | ||||
| 	if api.AccountID == "" { | ||||
| 		return WorkerScriptResponse{}, errors.New("account ID required for enterprise only request") | ||||
| 	} | ||||
| 	uri := "/accounts/" + api.AccountID + "/workers/scripts/" + scriptName | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	var r WorkerScriptResponse | ||||
| 	if err != nil { | ||||
| 		return r, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	r.Script = string(res) | ||||
| 	r.Success = true | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // ListWorkerScripts returns list of worker scripts for given account.
 | ||||
| //
 | ||||
| // This is an enterprise only feature https://developers.cloudflare.com/workers/api/config-api-for-enterprise
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/workers/api/config-api-for-enterprise/
 | ||||
| func (api *API) ListWorkerScripts() (WorkerListResponse, error) { | ||||
| 	if api.AccountID == "" { | ||||
| 		return WorkerListResponse{}, errors.New("account ID required for enterprise only request") | ||||
| 	} | ||||
| 	uri := "/accounts/" + api.AccountID + "/workers/scripts" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return WorkerListResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r WorkerListResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return WorkerListResponse{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // UploadWorker push raw script content for your worker.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#worker-script-upload-worker
 | ||||
| func (api *API) UploadWorker(requestParams *WorkerRequestParams, data string) (WorkerScriptResponse, error) { | ||||
| 	if requestParams.ScriptName != "" { | ||||
| 		return api.uploadWorkerWithName(requestParams.ScriptName, data) | ||||
| 	} | ||||
| 	uri := "/zones/" + requestParams.ZoneID + "/workers/script" | ||||
| 	headers := make(http.Header) | ||||
| 	headers.Set("Content-Type", "application/javascript") | ||||
| 	res, err := api.makeRequestWithHeaders("PUT", uri, []byte(data), headers) | ||||
| 	var r WorkerScriptResponse | ||||
| 	if err != nil { | ||||
| 		return r, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return r, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // UploadWorkerWithName push raw script content for your worker.
 | ||||
| //
 | ||||
| // This is an enterprise only feature https://developers.cloudflare.com/workers/api/config-api-for-enterprise/
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#worker-script-upload-worker
 | ||||
| func (api *API) uploadWorkerWithName(scriptName string, data string) (WorkerScriptResponse, error) { | ||||
| 	if api.AccountID == "" { | ||||
| 		return WorkerScriptResponse{}, errors.New("account ID required for enterprise only request") | ||||
| 	} | ||||
| 	uri := "/accounts/" + api.AccountID + "/workers/scripts/" + scriptName | ||||
| 	headers := make(http.Header) | ||||
| 	headers.Set("Content-Type", "application/javascript") | ||||
| 	res, err := api.makeRequestWithHeaders("PUT", uri, []byte(data), headers) | ||||
| 	var r WorkerScriptResponse | ||||
| 	if err != nil { | ||||
| 		return r, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return r, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // CreateWorkerRoute creates worker route for a zone
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#worker-filters-create-filter
 | ||||
| func (api *API) CreateWorkerRoute(zoneID string, route WorkerRoute) (WorkerRouteResponse, error) { | ||||
| 	// Check whether a script name is defined in order to determine whether
 | ||||
| 	// to use the single-script or multi-script endpoint.
 | ||||
| 	pathComponent := "filters" | ||||
| 	if route.Script != "" { | ||||
| 		if api.AccountID == "" { | ||||
| 			return WorkerRouteResponse{}, errors.New("account ID required for enterprise only request") | ||||
| 		} | ||||
| 		pathComponent = "routes" | ||||
| 	} | ||||
| 
 | ||||
| 	uri := "/zones/" + zoneID + "/workers/" + pathComponent | ||||
| 	res, err := api.makeRequest("POST", uri, route) | ||||
| 	if err != nil { | ||||
| 		return WorkerRouteResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r WorkerRouteResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return WorkerRouteResponse{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteWorkerRoute deletes worker route for a zone
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#worker-filters-delete-filter
 | ||||
| func (api *API) DeleteWorkerRoute(zoneID string, routeID string) (WorkerRouteResponse, error) { | ||||
| 	// For deleting a route, it doesn't matter whether we use the
 | ||||
| 	// single-script or multi-script endpoint
 | ||||
| 	uri := "/zones/" + zoneID + "/workers/filters/" + routeID | ||||
| 	res, err := api.makeRequest("DELETE", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return WorkerRouteResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r WorkerRouteResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return WorkerRouteResponse{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // ListWorkerRoutes returns list of worker routes
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#worker-filters-list-filters
 | ||||
| func (api *API) ListWorkerRoutes(zoneID string) (WorkerRoutesResponse, error) { | ||||
| 	pathComponent := "filters" | ||||
| 	if api.AccountID != "" { | ||||
| 		pathComponent = "routes" | ||||
| 	} | ||||
| 	uri := "/zones/" + zoneID + "/workers/" + pathComponent | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return WorkerRoutesResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r WorkerRoutesResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return WorkerRoutesResponse{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	for i := range r.Routes { | ||||
| 		route := &r.Routes[i] | ||||
| 		// The Enabled flag will not be set in the multi-script API response
 | ||||
| 		// so we manually set it to true if the script name is not empty
 | ||||
| 		// in case any multi-script customers rely on the Enabled field
 | ||||
| 		if route.Script != "" { | ||||
| 			route.Enabled = true | ||||
| 		} | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateWorkerRoute updates worker route for a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#worker-filters-update-filter
 | ||||
| func (api *API) UpdateWorkerRoute(zoneID string, routeID string, route WorkerRoute) (WorkerRouteResponse, error) { | ||||
| 	// Check whether a script name is defined in order to determine whether
 | ||||
| 	// to use the single-script or multi-script endpoint.
 | ||||
| 	pathComponent := "filters" | ||||
| 	if route.Script != "" { | ||||
| 		if api.AccountID == "" { | ||||
| 			return WorkerRouteResponse{}, errors.New("account ID required for enterprise only request") | ||||
| 		} | ||||
| 		pathComponent = "routes" | ||||
| 	} | ||||
| 	uri := "/zones/" + zoneID + "/workers/" + pathComponent + "/" + routeID | ||||
| 	res, err := api.makeRequest("PUT", uri, route) | ||||
| 	if err != nil { | ||||
| 		return WorkerRouteResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r WorkerRouteResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return WorkerRouteResponse{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
							
								
								
									
										192
									
								
								vendor/github.com/cloudflare/cloudflare-go/workers_kv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								vendor/github.com/cloudflare/cloudflare-go/workers_kv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,192 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // WorkersKVNamespaceRequest provides parameters for creating and updating storage namespaces
 | ||||
| type WorkersKVNamespaceRequest struct { | ||||
| 	Title string `json:"title"` | ||||
| } | ||||
| 
 | ||||
| // WorkersKVNamespaceResponse is the response received when creating storage namespaces
 | ||||
| type WorkersKVNamespaceResponse struct { | ||||
| 	Response | ||||
| 	Result WorkersKVNamespace `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // WorkersKVNamespace contains the unique identifier and title of a storage namespace
 | ||||
| type WorkersKVNamespace struct { | ||||
| 	ID    string `json:"id"` | ||||
| 	Title string `json:"title"` | ||||
| } | ||||
| 
 | ||||
| // ListWorkersKVNamespacesResponse contains a slice of storage namespaces associated with an
 | ||||
| // account, pagination information, and an embedded response struct
 | ||||
| type ListWorkersKVNamespacesResponse struct { | ||||
| 	Response | ||||
| 	Result     []WorkersKVNamespace `json:"result"` | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // StorageKey is a key name used to identify a storage value
 | ||||
| type StorageKey struct { | ||||
| 	Name string `json:"name"` | ||||
| } | ||||
| 
 | ||||
| // ListStorageKeysResponse contains a slice of keys belonging to a storage namespace,
 | ||||
| // pagination information, and an embedded response struct
 | ||||
| type ListStorageKeysResponse struct { | ||||
| 	Response | ||||
| 	Result     []StorageKey `json:"result"` | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // CreateWorkersKVNamespace creates a namespace under the given title.
 | ||||
| // A 400 is returned if the account already owns a namespace with this title.
 | ||||
| // A namespace must be explicitly deleted to be replaced.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#workers-kv-namespace-create-a-namespace
 | ||||
| func (api *API) CreateWorkersKVNamespace(ctx context.Context, req *WorkersKVNamespaceRequest) (WorkersKVNamespaceResponse, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces", api.AccountID) | ||||
| 	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, req) | ||||
| 	if err != nil { | ||||
| 		return WorkersKVNamespaceResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	result := WorkersKVNamespaceResponse{} | ||||
| 	if err := json.Unmarshal(res, &result); err != nil { | ||||
| 		return result, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return result, err | ||||
| } | ||||
| 
 | ||||
| // ListWorkersKVNamespaces lists storage namespaces
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#workers-kv-namespace-list-namespaces
 | ||||
| func (api *API) ListWorkersKVNamespaces(ctx context.Context) (ListWorkersKVNamespacesResponse, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces", api.AccountID) | ||||
| 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) | ||||
| 	if err != nil { | ||||
| 		return ListWorkersKVNamespacesResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	result := ListWorkersKVNamespacesResponse{} | ||||
| 	if err := json.Unmarshal(res, &result); err != nil { | ||||
| 		return result, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return result, err | ||||
| } | ||||
| 
 | ||||
| // DeleteWorkersKVNamespace deletes the namespace corresponding to the given ID
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#workers-kv-namespace-remove-a-namespace
 | ||||
| func (api *API) DeleteWorkersKVNamespace(ctx context.Context, namespaceID string) (Response, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s", api.AccountID, namespaceID) | ||||
| 	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil) | ||||
| 	if err != nil { | ||||
| 		return Response{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	result := Response{} | ||||
| 	if err := json.Unmarshal(res, &result); err != nil { | ||||
| 		return result, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return result, err | ||||
| } | ||||
| 
 | ||||
| // UpdateWorkersKVNamespace modifies a namespace's title
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#workers-kv-namespace-rename-a-namespace
 | ||||
| func (api *API) UpdateWorkersKVNamespace(ctx context.Context, namespaceID string, req *WorkersKVNamespaceRequest) (Response, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s", api.AccountID, namespaceID) | ||||
| 	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, req) | ||||
| 	if err != nil { | ||||
| 		return Response{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	result := Response{} | ||||
| 	if err := json.Unmarshal(res, &result); err != nil { | ||||
| 		return result, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return result, err | ||||
| } | ||||
| 
 | ||||
| // WriteWorkersKV writes a value identified by a key.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#workers-kv-namespace-write-key-value-pair
 | ||||
| func (api *API) WriteWorkersKV(ctx context.Context, namespaceID, key string, value []byte) (Response, error) { | ||||
| 	key = url.PathEscape(key) | ||||
| 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", api.AccountID, namespaceID, key) | ||||
| 	res, err := api.makeRequestWithAuthTypeAndHeaders( | ||||
| 		ctx, http.MethodPut, uri, value, api.authType, http.Header{"Content-Type": []string{"application/octet-stream"}}, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return Response{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	result := Response{} | ||||
| 	if err := json.Unmarshal(res, &result); err != nil { | ||||
| 		return result, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return result, err | ||||
| } | ||||
| 
 | ||||
| // ReadWorkersKV returns the value associated with the given key in the given namespace
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#workers-kv-namespace-read-key-value-pair
 | ||||
| func (api API) ReadWorkersKV(ctx context.Context, namespaceID, key string) ([]byte, error) { | ||||
| 	key = url.PathEscape(key) | ||||
| 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", api.AccountID, namespaceID, key) | ||||
| 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteWorkersKV deletes a key and value for a provided storage namespace
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#workers-kv-namespace-delete-key-value-pair
 | ||||
| func (api API) DeleteWorkersKV(ctx context.Context, namespaceID, key string) (Response, error) { | ||||
| 	key = url.PathEscape(key) | ||||
| 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", api.AccountID, namespaceID, key) | ||||
| 	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil) | ||||
| 	if err != nil { | ||||
| 		return Response{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	result := Response{} | ||||
| 	if err := json.Unmarshal(res, &result); err != nil { | ||||
| 		return result, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return result, err | ||||
| } | ||||
| 
 | ||||
| // ListWorkersKVs lists a namespace's keys
 | ||||
| //
 | ||||
| // API Reference: https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys
 | ||||
| func (api API) ListWorkersKVs(ctx context.Context, namespaceID string) (ListStorageKeysResponse, error) { | ||||
| 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/keys", api.AccountID, namespaceID) | ||||
| 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) | ||||
| 	if err != nil { | ||||
| 		return ListStorageKeysResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	result := ListStorageKeysResponse{} | ||||
| 	if err := json.Unmarshal(res, &result); err != nil { | ||||
| 		return result, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return result, err | ||||
| } | ||||
							
								
								
									
										740
									
								
								vendor/github.com/cloudflare/cloudflare-go/zone.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										740
									
								
								vendor/github.com/cloudflare/cloudflare-go/zone.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,740 @@ | ||||
| package cloudflare | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // Owner describes the resource owner.
 | ||||
| type Owner struct { | ||||
| 	ID        string `json:"id"` | ||||
| 	Email     string `json:"email"` | ||||
| 	Name      string `json:"name"` | ||||
| 	OwnerType string `json:"type"` | ||||
| } | ||||
| 
 | ||||
| // Zone describes a Cloudflare zone.
 | ||||
| type Zone struct { | ||||
| 	ID   string `json:"id"` | ||||
| 	Name string `json:"name"` | ||||
| 	// DevMode contains the time in seconds until development expires (if
 | ||||
| 	// positive) or since it expired (if negative). It will be 0 if never used.
 | ||||
| 	DevMode           int       `json:"development_mode"` | ||||
| 	OriginalNS        []string  `json:"original_name_servers"` | ||||
| 	OriginalRegistrar string    `json:"original_registrar"` | ||||
| 	OriginalDNSHost   string    `json:"original_dnshost"` | ||||
| 	CreatedOn         time.Time `json:"created_on"` | ||||
| 	ModifiedOn        time.Time `json:"modified_on"` | ||||
| 	NameServers       []string  `json:"name_servers"` | ||||
| 	Owner             Owner     `json:"owner"` | ||||
| 	Permissions       []string  `json:"permissions"` | ||||
| 	Plan              ZonePlan  `json:"plan"` | ||||
| 	PlanPending       ZonePlan  `json:"plan_pending,omitempty"` | ||||
| 	Status            string    `json:"status"` | ||||
| 	Paused            bool      `json:"paused"` | ||||
| 	Type              string    `json:"type"` | ||||
| 	Host              struct { | ||||
| 		Name    string | ||||
| 		Website string | ||||
| 	} `json:"host"` | ||||
| 	VanityNS    []string `json:"vanity_name_servers"` | ||||
| 	Betas       []string `json:"betas"` | ||||
| 	DeactReason string   `json:"deactivation_reason"` | ||||
| 	Meta        ZoneMeta `json:"meta"` | ||||
| 	Account     Account  `json:"account"` | ||||
| } | ||||
| 
 | ||||
| // ZoneMeta describes metadata about a zone.
 | ||||
| type ZoneMeta struct { | ||||
| 	// custom_certificate_quota is broken - sometimes it's a string, sometimes a number!
 | ||||
| 	// CustCertQuota     int    `json:"custom_certificate_quota"`
 | ||||
| 	PageRuleQuota     int  `json:"page_rule_quota"` | ||||
| 	WildcardProxiable bool `json:"wildcard_proxiable"` | ||||
| 	PhishingDetected  bool `json:"phishing_detected"` | ||||
| } | ||||
| 
 | ||||
| // ZonePlan contains the plan information for a zone.
 | ||||
| type ZonePlan struct { | ||||
| 	ZonePlanCommon | ||||
| 	IsSubscribed      bool   `json:"is_subscribed"` | ||||
| 	CanSubscribe      bool   `json:"can_subscribe"` | ||||
| 	LegacyID          string `json:"legacy_id"` | ||||
| 	LegacyDiscount    bool   `json:"legacy_discount"` | ||||
| 	ExternallyManaged bool   `json:"externally_managed"` | ||||
| } | ||||
| 
 | ||||
| // ZoneRatePlan contains the plan information for a zone.
 | ||||
| type ZoneRatePlan struct { | ||||
| 	ZonePlanCommon | ||||
| 	Components []zoneRatePlanComponents `json:"components,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // ZonePlanCommon contains fields used by various Plan endpoints
 | ||||
| type ZonePlanCommon struct { | ||||
| 	ID        string `json:"id"` | ||||
| 	Name      string `json:"name,omitempty"` | ||||
| 	Price     int    `json:"price,omitempty"` | ||||
| 	Currency  string `json:"currency,omitempty"` | ||||
| 	Frequency string `json:"frequency,omitempty"` | ||||
| } | ||||
| 
 | ||||
| type zoneRatePlanComponents struct { | ||||
| 	Name      string `json:"name"` | ||||
| 	Default   int    `json:"Default"` | ||||
| 	UnitPrice int    `json:"unit_price"` | ||||
| } | ||||
| 
 | ||||
| // ZoneID contains only the zone ID.
 | ||||
| type ZoneID struct { | ||||
| 	ID string `json:"id"` | ||||
| } | ||||
| 
 | ||||
| // ZoneResponse represents the response from the Zone endpoint containing a single zone.
 | ||||
| type ZoneResponse struct { | ||||
| 	Response | ||||
| 	Result Zone `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // ZonesResponse represents the response from the Zone endpoint containing an array of zones.
 | ||||
| type ZonesResponse struct { | ||||
| 	Response | ||||
| 	Result     []Zone `json:"result"` | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // ZoneIDResponse represents the response from the Zone endpoint, containing only a zone ID.
 | ||||
| type ZoneIDResponse struct { | ||||
| 	Response | ||||
| 	Result ZoneID `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // AvailableZoneRatePlansResponse represents the response from the Available Rate Plans endpoint.
 | ||||
| type AvailableZoneRatePlansResponse struct { | ||||
| 	Response | ||||
| 	Result     []ZoneRatePlan `json:"result"` | ||||
| 	ResultInfo `json:"result_info"` | ||||
| } | ||||
| 
 | ||||
| // AvailableZonePlansResponse represents the response from the Available Plans endpoint.
 | ||||
| type AvailableZonePlansResponse struct { | ||||
| 	Response | ||||
| 	Result []ZonePlan `json:"result"` | ||||
| 	ResultInfo | ||||
| } | ||||
| 
 | ||||
| // ZoneRatePlanResponse represents the response from the Plan Details endpoint.
 | ||||
| type ZoneRatePlanResponse struct { | ||||
| 	Response | ||||
| 	Result ZoneRatePlan `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // ZoneSetting contains settings for a zone.
 | ||||
| type ZoneSetting struct { | ||||
| 	ID            string      `json:"id"` | ||||
| 	Editable      bool        `json:"editable"` | ||||
| 	ModifiedOn    string      `json:"modified_on"` | ||||
| 	Value         interface{} `json:"value"` | ||||
| 	TimeRemaining int         `json:"time_remaining"` | ||||
| } | ||||
| 
 | ||||
| // ZoneSettingResponse represents the response from the Zone Setting endpoint.
 | ||||
| type ZoneSettingResponse struct { | ||||
| 	Response | ||||
| 	Result []ZoneSetting `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // ZoneSSLSetting contains ssl setting for a zone.
 | ||||
| type ZoneSSLSetting struct { | ||||
| 	ID                string `json:"id"` | ||||
| 	Editable          bool   `json:"editable"` | ||||
| 	ModifiedOn        string `json:"modified_on"` | ||||
| 	Value             string `json:"value"` | ||||
| 	CertificateStatus string `json:"certificate_status"` | ||||
| } | ||||
| 
 | ||||
| // ZoneSSLSettingResponse represents the response from the Zone SSL Setting
 | ||||
| // endpoint.
 | ||||
| type ZoneSSLSettingResponse struct { | ||||
| 	Response | ||||
| 	Result ZoneSSLSetting `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // ZoneAnalyticsData contains totals and timeseries analytics data for a zone.
 | ||||
| type ZoneAnalyticsData struct { | ||||
| 	Totals     ZoneAnalytics   `json:"totals"` | ||||
| 	Timeseries []ZoneAnalytics `json:"timeseries"` | ||||
| } | ||||
| 
 | ||||
| // zoneAnalyticsDataResponse represents the response from the Zone Analytics Dashboard endpoint.
 | ||||
| type zoneAnalyticsDataResponse struct { | ||||
| 	Response | ||||
| 	Result ZoneAnalyticsData `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // ZoneAnalyticsColocation contains analytics data by datacenter.
 | ||||
| type ZoneAnalyticsColocation struct { | ||||
| 	ColocationID string          `json:"colo_id"` | ||||
| 	Timeseries   []ZoneAnalytics `json:"timeseries"` | ||||
| } | ||||
| 
 | ||||
| // zoneAnalyticsColocationResponse represents the response from the Zone Analytics By Co-location endpoint.
 | ||||
| type zoneAnalyticsColocationResponse struct { | ||||
| 	Response | ||||
| 	Result []ZoneAnalyticsColocation `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // ZoneAnalytics contains analytics data for a zone.
 | ||||
| type ZoneAnalytics struct { | ||||
| 	Since    time.Time `json:"since"` | ||||
| 	Until    time.Time `json:"until"` | ||||
| 	Requests struct { | ||||
| 		All         int            `json:"all"` | ||||
| 		Cached      int            `json:"cached"` | ||||
| 		Uncached    int            `json:"uncached"` | ||||
| 		ContentType map[string]int `json:"content_type"` | ||||
| 		Country     map[string]int `json:"country"` | ||||
| 		SSL         struct { | ||||
| 			Encrypted   int `json:"encrypted"` | ||||
| 			Unencrypted int `json:"unencrypted"` | ||||
| 		} `json:"ssl"` | ||||
| 		HTTPStatus map[string]int `json:"http_status"` | ||||
| 	} `json:"requests"` | ||||
| 	Bandwidth struct { | ||||
| 		All         int            `json:"all"` | ||||
| 		Cached      int            `json:"cached"` | ||||
| 		Uncached    int            `json:"uncached"` | ||||
| 		ContentType map[string]int `json:"content_type"` | ||||
| 		Country     map[string]int `json:"country"` | ||||
| 		SSL         struct { | ||||
| 			Encrypted   int `json:"encrypted"` | ||||
| 			Unencrypted int `json:"unencrypted"` | ||||
| 		} `json:"ssl"` | ||||
| 	} `json:"bandwidth"` | ||||
| 	Threats struct { | ||||
| 		All     int            `json:"all"` | ||||
| 		Country map[string]int `json:"country"` | ||||
| 		Type    map[string]int `json:"type"` | ||||
| 	} `json:"threats"` | ||||
| 	Pageviews struct { | ||||
| 		All           int            `json:"all"` | ||||
| 		SearchEngines map[string]int `json:"search_engines"` | ||||
| 	} `json:"pageviews"` | ||||
| 	Uniques struct { | ||||
| 		All int `json:"all"` | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ZoneAnalyticsOptions represents the optional parameters in Zone Analytics
 | ||||
| // endpoint requests.
 | ||||
| type ZoneAnalyticsOptions struct { | ||||
| 	Since      *time.Time | ||||
| 	Until      *time.Time | ||||
| 	Continuous *bool | ||||
| } | ||||
| 
 | ||||
| // PurgeCacheRequest represents the request format made to the purge endpoint.
 | ||||
| type PurgeCacheRequest struct { | ||||
| 	Everything bool `json:"purge_everything,omitempty"` | ||||
| 	// Purge by filepath (exact match). Limit of 30
 | ||||
| 	Files []string `json:"files,omitempty"` | ||||
| 	// Purge by Tag (Enterprise only):
 | ||||
| 	// https://support.cloudflare.com/hc/en-us/articles/206596608-How-to-Purge-Cache-Using-Cache-Tags-Enterprise-only-
 | ||||
| 	Tags []string `json:"tags,omitempty"` | ||||
| 	// Purge by hostname - e.g. "assets.example.com"
 | ||||
| 	Hosts []string `json:"hosts,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // PurgeCacheResponse represents the response from the purge endpoint.
 | ||||
| type PurgeCacheResponse struct { | ||||
| 	Response | ||||
| 	Result struct { | ||||
| 		ID string `json:"id"` | ||||
| 	} `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // newZone describes a new zone.
 | ||||
| type newZone struct { | ||||
| 	Name      string `json:"name"` | ||||
| 	JumpStart bool   `json:"jump_start"` | ||||
| 	Type      string `json:"type"` | ||||
| 	// We use a pointer to get a nil type when the field is empty.
 | ||||
| 	// This allows us to completely omit this with json.Marshal().
 | ||||
| 	Account *Account `json:"organization,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // FallbackOrigin describes a fallback origin
 | ||||
| type FallbackOrigin struct { | ||||
| 	Value string `json:"value"` | ||||
| 	ID    string `json:"id,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // FallbackOriginResponse represents the response from the fallback_origin endpoint
 | ||||
| type FallbackOriginResponse struct { | ||||
| 	Response | ||||
| 	Result FallbackOrigin `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // CreateZone creates a zone on an account.
 | ||||
| //
 | ||||
| // Setting jumpstart to true will attempt to automatically scan for existing
 | ||||
| // DNS records. Setting this to false will create the zone with no DNS records.
 | ||||
| //
 | ||||
| // If account is non-empty, it must have at least the ID field populated.
 | ||||
| // This will add the new zone to the specified multi-user account.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-create-a-zone
 | ||||
| func (api *API) CreateZone(name string, jumpstart bool, account Account, zoneType string) (Zone, error) { | ||||
| 	var newzone newZone | ||||
| 	newzone.Name = name | ||||
| 	newzone.JumpStart = jumpstart | ||||
| 	if account.ID != "" { | ||||
| 		newzone.Account = &account | ||||
| 	} | ||||
| 
 | ||||
| 	if zoneType == "partial" { | ||||
| 		newzone.Type = "partial" | ||||
| 	} else { | ||||
| 		newzone.Type = "full" | ||||
| 	} | ||||
| 
 | ||||
| 	res, err := api.makeRequest("POST", "/zones", newzone) | ||||
| 	if err != nil { | ||||
| 		return Zone{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r ZoneResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return Zone{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ZoneActivationCheck initiates another zone activation check for newly-created zones.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-initiate-another-zone-activation-check
 | ||||
| func (api *API) ZoneActivationCheck(zoneID string) (Response, error) { | ||||
| 	res, err := api.makeRequest("PUT", "/zones/"+zoneID+"/activation_check", nil) | ||||
| 	if err != nil { | ||||
| 		return Response{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r Response | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return Response{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // ListZones lists zones on an account. Optionally takes a list of zone names
 | ||||
| // to filter against.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-list-zones
 | ||||
| func (api *API) ListZones(z ...string) ([]Zone, error) { | ||||
| 	v := url.Values{} | ||||
| 	var res []byte | ||||
| 	var r ZonesResponse | ||||
| 	var zones []Zone | ||||
| 	var err error | ||||
| 	if len(z) > 0 { | ||||
| 		for _, zone := range z { | ||||
| 			v.Set("name", zone) | ||||
| 			res, err = api.makeRequest("GET", "/zones?"+v.Encode(), nil) | ||||
| 			if err != nil { | ||||
| 				return []Zone{}, errors.Wrap(err, errMakeRequestError) | ||||
| 			} | ||||
| 			err = json.Unmarshal(res, &r) | ||||
| 			if err != nil { | ||||
| 				return []Zone{}, errors.Wrap(err, errUnmarshalError) | ||||
| 			} | ||||
| 			if !r.Success { | ||||
| 				// TODO: Provide an actual error message instead of always returning nil
 | ||||
| 				return []Zone{}, err | ||||
| 			} | ||||
| 			for zi := range r.Result { | ||||
| 				zones = append(zones, r.Result[zi]) | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		res, err = api.makeRequest("GET", "/zones?per_page=50", nil) | ||||
| 		if err != nil { | ||||
| 			return []Zone{}, errors.Wrap(err, errMakeRequestError) | ||||
| 		} | ||||
| 		err = json.Unmarshal(res, &r) | ||||
| 		if err != nil { | ||||
| 			return []Zone{}, errors.Wrap(err, errUnmarshalError) | ||||
| 		} | ||||
| 
 | ||||
| 		totalPageCount := r.TotalPages | ||||
| 		var wg sync.WaitGroup | ||||
| 		wg.Add(totalPageCount) | ||||
| 		errc := make(chan error) | ||||
| 
 | ||||
| 		for i := 1; i <= totalPageCount; i++ { | ||||
| 			go func(pageNumber int) error { | ||||
| 				res, err = api.makeRequest("GET", fmt.Sprintf("/zones?per_page=50&page=%d", pageNumber), nil) | ||||
| 				if err != nil { | ||||
| 					errc <- err | ||||
| 				} | ||||
| 
 | ||||
| 				err = json.Unmarshal(res, &r) | ||||
| 				if err != nil { | ||||
| 					errc <- err | ||||
| 				} | ||||
| 
 | ||||
| 				for _, zone := range r.Result { | ||||
| 					zones = append(zones, zone) | ||||
| 				} | ||||
| 
 | ||||
| 				select { | ||||
| 				case err := <-errc: | ||||
| 					return err | ||||
| 				default: | ||||
| 					wg.Done() | ||||
| 				} | ||||
| 
 | ||||
| 				return nil | ||||
| 			}(i) | ||||
| 		} | ||||
| 
 | ||||
| 		wg.Wait() | ||||
| 	} | ||||
| 
 | ||||
| 	return zones, nil | ||||
| } | ||||
| 
 | ||||
| // ListZonesContext lists zones on an account. Optionally takes a list of ReqOptions.
 | ||||
| func (api *API) ListZonesContext(ctx context.Context, opts ...ReqOption) (r ZonesResponse, err error) { | ||||
| 	var res []byte | ||||
| 	opt := reqOption{ | ||||
| 		params: url.Values{}, | ||||
| 	} | ||||
| 	for _, of := range opts { | ||||
| 		of(&opt) | ||||
| 	} | ||||
| 
 | ||||
| 	res, err = api.makeRequestContext(ctx, "GET", "/zones?"+opt.params.Encode(), nil) | ||||
| 	if err != nil { | ||||
| 		return ZonesResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return ZonesResponse{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // ZoneDetails fetches information about a zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-zone-details
 | ||||
| func (api *API) ZoneDetails(zoneID string) (Zone, error) { | ||||
| 	res, err := api.makeRequest("GET", "/zones/"+zoneID, nil) | ||||
| 	if err != nil { | ||||
| 		return Zone{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r ZoneResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return Zone{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ZoneOptions is a subset of Zone, for editable options.
 | ||||
| type ZoneOptions struct { | ||||
| 	Paused   *bool     `json:"paused,omitempty"` | ||||
| 	VanityNS []string  `json:"vanity_name_servers,omitempty"` | ||||
| 	Plan     *ZonePlan `json:"plan,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // ZoneSetPaused pauses Cloudflare service for the entire zone, sending all
 | ||||
| // traffic direct to the origin.
 | ||||
| func (api *API) ZoneSetPaused(zoneID string, paused bool) (Zone, error) { | ||||
| 	zoneopts := ZoneOptions{Paused: &paused} | ||||
| 	zone, err := api.EditZone(zoneID, zoneopts) | ||||
| 	if err != nil { | ||||
| 		return Zone{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return zone, nil | ||||
| } | ||||
| 
 | ||||
| // ZoneSetVanityNS sets custom nameservers for the zone.
 | ||||
| // These names must be within the same zone.
 | ||||
| func (api *API) ZoneSetVanityNS(zoneID string, ns []string) (Zone, error) { | ||||
| 	zoneopts := ZoneOptions{VanityNS: ns} | ||||
| 	zone, err := api.EditZone(zoneID, zoneopts) | ||||
| 	if err != nil { | ||||
| 		return Zone{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return zone, nil | ||||
| } | ||||
| 
 | ||||
| // ZoneSetPlan changes the zone plan.
 | ||||
| func (api *API) ZoneSetPlan(zoneID string, plan ZonePlan) (Zone, error) { | ||||
| 	zoneopts := ZoneOptions{Plan: &plan} | ||||
| 	zone, err := api.EditZone(zoneID, zoneopts) | ||||
| 	if err != nil { | ||||
| 		return Zone{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return zone, nil | ||||
| } | ||||
| 
 | ||||
| // EditZone edits the given zone.
 | ||||
| //
 | ||||
| // This is usually called by ZoneSetPaused, ZoneSetVanityNS or ZoneSetPlan.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-edit-zone-properties
 | ||||
| func (api *API) EditZone(zoneID string, zoneOpts ZoneOptions) (Zone, error) { | ||||
| 	res, err := api.makeRequest("PATCH", "/zones/"+zoneID, zoneOpts) | ||||
| 	if err != nil { | ||||
| 		return Zone{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r ZoneResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return Zone{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // PurgeEverything purges the cache for the given zone.
 | ||||
| //
 | ||||
| // Note: this will substantially increase load on the origin server for that
 | ||||
| // zone if there is a high cached vs. uncached request ratio.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-purge-all-files
 | ||||
| func (api *API) PurgeEverything(zoneID string) (PurgeCacheResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/purge_cache" | ||||
| 	res, err := api.makeRequest("POST", uri, PurgeCacheRequest{true, nil, nil, nil}) | ||||
| 	if err != nil { | ||||
| 		return PurgeCacheResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r PurgeCacheResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return PurgeCacheResponse{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // PurgeCache purges the cache using the given PurgeCacheRequest (zone/url/tag).
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-purge-individual-files-by-url-and-cache-tags
 | ||||
| func (api *API) PurgeCache(zoneID string, pcr PurgeCacheRequest) (PurgeCacheResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/purge_cache" | ||||
| 	res, err := api.makeRequest("POST", uri, pcr) | ||||
| 	if err != nil { | ||||
| 		return PurgeCacheResponse{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r PurgeCacheResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return PurgeCacheResponse{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteZone deletes the given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-delete-a-zone
 | ||||
| func (api *API) DeleteZone(zoneID string) (ZoneID, error) { | ||||
| 	res, err := api.makeRequest("DELETE", "/zones/"+zoneID, nil) | ||||
| 	if err != nil { | ||||
| 		return ZoneID{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r ZoneIDResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return ZoneID{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // AvailableZoneRatePlans returns information about all plans available to the specified zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-plan-available-plans
 | ||||
| func (api *API) AvailableZoneRatePlans(zoneID string) ([]ZoneRatePlan, error) { | ||||
| 	uri := "/zones/" + zoneID + "/available_rate_plans" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []ZoneRatePlan{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r AvailableZoneRatePlansResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return []ZoneRatePlan{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // AvailableZonePlans returns information about all plans available to the specified zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-rate-plan-list-available-plans
 | ||||
| func (api *API) AvailableZonePlans(zoneID string) ([]ZonePlan, error) { | ||||
| 	uri := "/zones/" + zoneID + "/available_plans" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return []ZonePlan{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r AvailableZonePlansResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return []ZonePlan{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // encode encodes non-nil fields into URL encoded form.
 | ||||
| func (o ZoneAnalyticsOptions) encode() string { | ||||
| 	v := url.Values{} | ||||
| 	if o.Since != nil { | ||||
| 		v.Set("since", (*o.Since).Format(time.RFC3339)) | ||||
| 	} | ||||
| 	if o.Until != nil { | ||||
| 		v.Set("until", (*o.Until).Format(time.RFC3339)) | ||||
| 	} | ||||
| 	if o.Continuous != nil { | ||||
| 		v.Set("continuous", fmt.Sprintf("%t", *o.Continuous)) | ||||
| 	} | ||||
| 	return v.Encode() | ||||
| } | ||||
| 
 | ||||
| // ZoneAnalyticsDashboard returns zone analytics information.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-analytics-dashboard
 | ||||
| func (api *API) ZoneAnalyticsDashboard(zoneID string, options ZoneAnalyticsOptions) (ZoneAnalyticsData, error) { | ||||
| 	uri := "/zones/" + zoneID + "/analytics/dashboard" + "?" + options.encode() | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return ZoneAnalyticsData{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r zoneAnalyticsDataResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return ZoneAnalyticsData{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ZoneAnalyticsByColocation returns zone analytics information by datacenter.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-analytics-analytics-by-co-locations
 | ||||
| func (api *API) ZoneAnalyticsByColocation(zoneID string, options ZoneAnalyticsOptions) ([]ZoneAnalyticsColocation, error) { | ||||
| 	uri := "/zones/" + zoneID + "/analytics/colos" + "?" + options.encode() | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r zoneAnalyticsColocationResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // ZoneSettings returns all of the settings for a given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-settings-get-all-zone-settings
 | ||||
| func (api *API) ZoneSettings(zoneID string) (*ZoneSettingResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/settings" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &ZoneSettingResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateZoneSettings updates the settings for a given zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-settings-edit-zone-settings-info
 | ||||
| func (api *API) UpdateZoneSettings(zoneID string, settings []ZoneSetting) (*ZoneSettingResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/settings" | ||||
| 	res, err := api.makeRequest("PATCH", uri, struct { | ||||
| 		Items []ZoneSetting `json:"items"` | ||||
| 	}{settings}) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &ZoneSettingResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
| 
 | ||||
| // ZoneSSLSettings returns information about SSL setting to the specified zone.
 | ||||
| //
 | ||||
| // API reference: https://api.cloudflare.com/#zone-settings-get-ssl-setting
 | ||||
| func (api *API) ZoneSSLSettings(zoneID string) (ZoneSSLSetting, error) { | ||||
| 	uri := "/zones/" + zoneID + "/settings/ssl" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return ZoneSSLSetting{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 	var r ZoneSSLSettingResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return ZoneSSLSetting{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // FallbackOrigin returns information about the fallback origin for the specified zone.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/ssl/ssl-for-saas/api-calls/#fallback-origin-configuration
 | ||||
| func (api *API) FallbackOrigin(zoneID string) (FallbackOrigin, error) { | ||||
| 	uri := "/zones/" + zoneID + "/fallback_origin" | ||||
| 	res, err := api.makeRequest("GET", uri, nil) | ||||
| 	if err != nil { | ||||
| 		return FallbackOrigin{}, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	var r FallbackOriginResponse | ||||
| 	err = json.Unmarshal(res, &r) | ||||
| 	if err != nil { | ||||
| 		return FallbackOrigin{}, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return r.Result, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateFallbackOrigin updates the fallback origin for a given zone.
 | ||||
| //
 | ||||
| // API reference: https://developers.cloudflare.com/ssl/ssl-for-saas/api-calls/#4-example-patch-to-change-fallback-origin
 | ||||
| func (api *API) UpdateFallbackOrigin(zoneID string, fbo FallbackOrigin) (*FallbackOriginResponse, error) { | ||||
| 	uri := "/zones/" + zoneID + "/fallback_origin" | ||||
| 	res, err := api.makeRequest("PATCH", uri, fbo) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errMakeRequestError) | ||||
| 	} | ||||
| 
 | ||||
| 	response := &FallbackOriginResponse{} | ||||
| 	err = json.Unmarshal(res, &response) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, errUnmarshalError) | ||||
| 	} | ||||
| 
 | ||||
| 	return response, nil | ||||
| } | ||||
							
								
								
									
										27
									
								
								vendor/golang.org/x/time/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/time/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| Copyright (c) 2009 The Go Authors. All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
| 
 | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										22
									
								
								vendor/golang.org/x/time/PATENTS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/golang.org/x/time/PATENTS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| Additional IP Rights Grant (Patents) | ||||
| 
 | ||||
| "This implementation" means the copyrightable works distributed by | ||||
| Google as part of the Go project. | ||||
| 
 | ||||
| Google hereby grants to You a perpetual, worldwide, non-exclusive, | ||||
| no-charge, royalty-free, irrevocable (except as stated in this section) | ||||
| patent license to make, have made, use, offer to sell, sell, import, | ||||
| transfer and otherwise run, modify and propagate the contents of this | ||||
| implementation of Go, where such license applies only to those patent | ||||
| claims, both currently owned or controlled by Google and acquired in | ||||
| the future, licensable by Google that are necessarily infringed by this | ||||
| implementation of Go.  This grant does not include claims that would be | ||||
| infringed only as a consequence of further modification of this | ||||
| implementation.  If you or your agent or exclusive licensee institute or | ||||
| order or agree to the institution of patent litigation against any | ||||
| entity (including a cross-claim or counterclaim in a lawsuit) alleging | ||||
| that this implementation of Go or any code incorporated within this | ||||
| implementation of Go constitutes direct or contributory patent | ||||
| infringement, or inducement of patent infringement, then any patent | ||||
| rights granted to you under this License for this implementation of Go | ||||
| shall terminate as of the date such litigation is filed. | ||||
							
								
								
									
										374
									
								
								vendor/golang.org/x/time/rate/rate.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								vendor/golang.org/x/time/rate/rate.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,374 @@ | ||||
| // Copyright 2015 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| // Package rate provides a rate limiter.
 | ||||
| package rate | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Limit defines the maximum frequency of some events.
 | ||||
| // Limit is represented as number of events per second.
 | ||||
| // A zero Limit allows no events.
 | ||||
| type Limit float64 | ||||
| 
 | ||||
| // Inf is the infinite rate limit; it allows all events (even if burst is zero).
 | ||||
| const Inf = Limit(math.MaxFloat64) | ||||
| 
 | ||||
| // Every converts a minimum time interval between events to a Limit.
 | ||||
| func Every(interval time.Duration) Limit { | ||||
| 	if interval <= 0 { | ||||
| 		return Inf | ||||
| 	} | ||||
| 	return 1 / Limit(interval.Seconds()) | ||||
| } | ||||
| 
 | ||||
| // A Limiter controls how frequently events are allowed to happen.
 | ||||
| // It implements a "token bucket" of size b, initially full and refilled
 | ||||
| // at rate r tokens per second.
 | ||||
| // Informally, in any large enough time interval, the Limiter limits the
 | ||||
| // rate to r tokens per second, with a maximum burst size of b events.
 | ||||
| // As a special case, if r == Inf (the infinite rate), b is ignored.
 | ||||
| // See https://en.wikipedia.org/wiki/Token_bucket for more about token buckets.
 | ||||
| //
 | ||||
| // The zero value is a valid Limiter, but it will reject all events.
 | ||||
| // Use NewLimiter to create non-zero Limiters.
 | ||||
| //
 | ||||
| // Limiter has three main methods, Allow, Reserve, and Wait.
 | ||||
| // Most callers should use Wait.
 | ||||
| //
 | ||||
| // Each of the three methods consumes a single token.
 | ||||
| // They differ in their behavior when no token is available.
 | ||||
| // If no token is available, Allow returns false.
 | ||||
| // If no token is available, Reserve returns a reservation for a future token
 | ||||
| // and the amount of time the caller must wait before using it.
 | ||||
| // If no token is available, Wait blocks until one can be obtained
 | ||||
| // or its associated context.Context is canceled.
 | ||||
| //
 | ||||
| // The methods AllowN, ReserveN, and WaitN consume n tokens.
 | ||||
| type Limiter struct { | ||||
| 	limit Limit | ||||
| 	burst int | ||||
| 
 | ||||
| 	mu     sync.Mutex | ||||
| 	tokens float64 | ||||
| 	// last is the last time the limiter's tokens field was updated
 | ||||
| 	last time.Time | ||||
| 	// lastEvent is the latest time of a rate-limited event (past or future)
 | ||||
| 	lastEvent time.Time | ||||
| } | ||||
| 
 | ||||
| // Limit returns the maximum overall event rate.
 | ||||
| func (lim *Limiter) Limit() Limit { | ||||
| 	lim.mu.Lock() | ||||
| 	defer lim.mu.Unlock() | ||||
| 	return lim.limit | ||||
| } | ||||
| 
 | ||||
| // Burst returns the maximum burst size. Burst is the maximum number of tokens
 | ||||
| // that can be consumed in a single call to Allow, Reserve, or Wait, so higher
 | ||||
| // Burst values allow more events to happen at once.
 | ||||
| // A zero Burst allows no events, unless limit == Inf.
 | ||||
| func (lim *Limiter) Burst() int { | ||||
| 	return lim.burst | ||||
| } | ||||
| 
 | ||||
| // NewLimiter returns a new Limiter that allows events up to rate r and permits
 | ||||
| // bursts of at most b tokens.
 | ||||
| func NewLimiter(r Limit, b int) *Limiter { | ||||
| 	return &Limiter{ | ||||
| 		limit: r, | ||||
| 		burst: b, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Allow is shorthand for AllowN(time.Now(), 1).
 | ||||
| func (lim *Limiter) Allow() bool { | ||||
| 	return lim.AllowN(time.Now(), 1) | ||||
| } | ||||
| 
 | ||||
| // AllowN reports whether n events may happen at time now.
 | ||||
| // Use this method if you intend to drop / skip events that exceed the rate limit.
 | ||||
| // Otherwise use Reserve or Wait.
 | ||||
| func (lim *Limiter) AllowN(now time.Time, n int) bool { | ||||
| 	return lim.reserveN(now, n, 0).ok | ||||
| } | ||||
| 
 | ||||
| // A Reservation holds information about events that are permitted by a Limiter to happen after a delay.
 | ||||
| // A Reservation may be canceled, which may enable the Limiter to permit additional events.
 | ||||
| type Reservation struct { | ||||
| 	ok        bool | ||||
| 	lim       *Limiter | ||||
| 	tokens    int | ||||
| 	timeToAct time.Time | ||||
| 	// This is the Limit at reservation time, it can change later.
 | ||||
| 	limit Limit | ||||
| } | ||||
| 
 | ||||
| // OK returns whether the limiter can provide the requested number of tokens
 | ||||
| // within the maximum wait time.  If OK is false, Delay returns InfDuration, and
 | ||||
| // Cancel does nothing.
 | ||||
| func (r *Reservation) OK() bool { | ||||
| 	return r.ok | ||||
| } | ||||
| 
 | ||||
| // Delay is shorthand for DelayFrom(time.Now()).
 | ||||
| func (r *Reservation) Delay() time.Duration { | ||||
| 	return r.DelayFrom(time.Now()) | ||||
| } | ||||
| 
 | ||||
| // InfDuration is the duration returned by Delay when a Reservation is not OK.
 | ||||
| const InfDuration = time.Duration(1<<63 - 1) | ||||
| 
 | ||||
| // DelayFrom returns the duration for which the reservation holder must wait
 | ||||
| // before taking the reserved action.  Zero duration means act immediately.
 | ||||
| // InfDuration means the limiter cannot grant the tokens requested in this
 | ||||
| // Reservation within the maximum wait time.
 | ||||
| func (r *Reservation) DelayFrom(now time.Time) time.Duration { | ||||
| 	if !r.ok { | ||||
| 		return InfDuration | ||||
| 	} | ||||
| 	delay := r.timeToAct.Sub(now) | ||||
| 	if delay < 0 { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	return delay | ||||
| } | ||||
| 
 | ||||
| // Cancel is shorthand for CancelAt(time.Now()).
 | ||||
| func (r *Reservation) Cancel() { | ||||
| 	r.CancelAt(time.Now()) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // CancelAt indicates that the reservation holder will not perform the reserved action
 | ||||
| // and reverses the effects of this Reservation on the rate limit as much as possible,
 | ||||
| // considering that other reservations may have already been made.
 | ||||
| func (r *Reservation) CancelAt(now time.Time) { | ||||
| 	if !r.ok { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	r.lim.mu.Lock() | ||||
| 	defer r.lim.mu.Unlock() | ||||
| 
 | ||||
| 	if r.lim.limit == Inf || r.tokens == 0 || r.timeToAct.Before(now) { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// calculate tokens to restore
 | ||||
| 	// The duration between lim.lastEvent and r.timeToAct tells us how many tokens were reserved
 | ||||
| 	// after r was obtained. These tokens should not be restored.
 | ||||
| 	restoreTokens := float64(r.tokens) - r.limit.tokensFromDuration(r.lim.lastEvent.Sub(r.timeToAct)) | ||||
| 	if restoreTokens <= 0 { | ||||
| 		return | ||||
| 	} | ||||
| 	// advance time to now
 | ||||
| 	now, _, tokens := r.lim.advance(now) | ||||
| 	// calculate new number of tokens
 | ||||
| 	tokens += restoreTokens | ||||
| 	if burst := float64(r.lim.burst); tokens > burst { | ||||
| 		tokens = burst | ||||
| 	} | ||||
| 	// update state
 | ||||
| 	r.lim.last = now | ||||
| 	r.lim.tokens = tokens | ||||
| 	if r.timeToAct == r.lim.lastEvent { | ||||
| 		prevEvent := r.timeToAct.Add(r.limit.durationFromTokens(float64(-r.tokens))) | ||||
| 		if !prevEvent.Before(now) { | ||||
| 			r.lim.lastEvent = prevEvent | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Reserve is shorthand for ReserveN(time.Now(), 1).
 | ||||
| func (lim *Limiter) Reserve() *Reservation { | ||||
| 	return lim.ReserveN(time.Now(), 1) | ||||
| } | ||||
| 
 | ||||
| // ReserveN returns a Reservation that indicates how long the caller must wait before n events happen.
 | ||||
| // The Limiter takes this Reservation into account when allowing future events.
 | ||||
| // ReserveN returns false if n exceeds the Limiter's burst size.
 | ||||
| // Usage example:
 | ||||
| //   r := lim.ReserveN(time.Now(), 1)
 | ||||
| //   if !r.OK() {
 | ||||
| //     // Not allowed to act! Did you remember to set lim.burst to be > 0 ?
 | ||||
| //     return
 | ||||
| //   }
 | ||||
| //   time.Sleep(r.Delay())
 | ||||
| //   Act()
 | ||||
| // Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events.
 | ||||
| // If you need to respect a deadline or cancel the delay, use Wait instead.
 | ||||
| // To drop or skip events exceeding rate limit, use Allow instead.
 | ||||
| func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation { | ||||
| 	r := lim.reserveN(now, n, InfDuration) | ||||
| 	return &r | ||||
| } | ||||
| 
 | ||||
| // Wait is shorthand for WaitN(ctx, 1).
 | ||||
| func (lim *Limiter) Wait(ctx context.Context) (err error) { | ||||
| 	return lim.WaitN(ctx, 1) | ||||
| } | ||||
| 
 | ||||
| // WaitN blocks until lim permits n events to happen.
 | ||||
| // It returns an error if n exceeds the Limiter's burst size, the Context is
 | ||||
| // canceled, or the expected wait time exceeds the Context's Deadline.
 | ||||
| // The burst limit is ignored if the rate limit is Inf.
 | ||||
| func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) { | ||||
| 	if n > lim.burst && lim.limit != Inf { | ||||
| 		return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, lim.burst) | ||||
| 	} | ||||
| 	// Check if ctx is already cancelled
 | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		return ctx.Err() | ||||
| 	default: | ||||
| 	} | ||||
| 	// Determine wait limit
 | ||||
| 	now := time.Now() | ||||
| 	waitLimit := InfDuration | ||||
| 	if deadline, ok := ctx.Deadline(); ok { | ||||
| 		waitLimit = deadline.Sub(now) | ||||
| 	} | ||||
| 	// Reserve
 | ||||
| 	r := lim.reserveN(now, n, waitLimit) | ||||
| 	if !r.ok { | ||||
| 		return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", n) | ||||
| 	} | ||||
| 	// Wait if necessary
 | ||||
| 	delay := r.DelayFrom(now) | ||||
| 	if delay == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	t := time.NewTimer(delay) | ||||
| 	defer t.Stop() | ||||
| 	select { | ||||
| 	case <-t.C: | ||||
| 		// We can proceed.
 | ||||
| 		return nil | ||||
| 	case <-ctx.Done(): | ||||
| 		// Context was canceled before we could proceed.  Cancel the
 | ||||
| 		// reservation, which may permit other events to proceed sooner.
 | ||||
| 		r.Cancel() | ||||
| 		return ctx.Err() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetLimit is shorthand for SetLimitAt(time.Now(), newLimit).
 | ||||
| func (lim *Limiter) SetLimit(newLimit Limit) { | ||||
| 	lim.SetLimitAt(time.Now(), newLimit) | ||||
| } | ||||
| 
 | ||||
| // SetLimitAt sets a new Limit for the limiter. The new Limit, and Burst, may be violated
 | ||||
| // or underutilized by those which reserved (using Reserve or Wait) but did not yet act
 | ||||
| // before SetLimitAt was called.
 | ||||
| func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit) { | ||||
| 	lim.mu.Lock() | ||||
| 	defer lim.mu.Unlock() | ||||
| 
 | ||||
| 	now, _, tokens := lim.advance(now) | ||||
| 
 | ||||
| 	lim.last = now | ||||
| 	lim.tokens = tokens | ||||
| 	lim.limit = newLimit | ||||
| } | ||||
| 
 | ||||
| // reserveN is a helper method for AllowN, ReserveN, and WaitN.
 | ||||
| // maxFutureReserve specifies the maximum reservation wait duration allowed.
 | ||||
| // reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN.
 | ||||
| func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation { | ||||
| 	lim.mu.Lock() | ||||
| 
 | ||||
| 	if lim.limit == Inf { | ||||
| 		lim.mu.Unlock() | ||||
| 		return Reservation{ | ||||
| 			ok:        true, | ||||
| 			lim:       lim, | ||||
| 			tokens:    n, | ||||
| 			timeToAct: now, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	now, last, tokens := lim.advance(now) | ||||
| 
 | ||||
| 	// Calculate the remaining number of tokens resulting from the request.
 | ||||
| 	tokens -= float64(n) | ||||
| 
 | ||||
| 	// Calculate the wait duration
 | ||||
| 	var waitDuration time.Duration | ||||
| 	if tokens < 0 { | ||||
| 		waitDuration = lim.limit.durationFromTokens(-tokens) | ||||
| 	} | ||||
| 
 | ||||
| 	// Decide result
 | ||||
| 	ok := n <= lim.burst && waitDuration <= maxFutureReserve | ||||
| 
 | ||||
| 	// Prepare reservation
 | ||||
| 	r := Reservation{ | ||||
| 		ok:    ok, | ||||
| 		lim:   lim, | ||||
| 		limit: lim.limit, | ||||
| 	} | ||||
| 	if ok { | ||||
| 		r.tokens = n | ||||
| 		r.timeToAct = now.Add(waitDuration) | ||||
| 	} | ||||
| 
 | ||||
| 	// Update state
 | ||||
| 	if ok { | ||||
| 		lim.last = now | ||||
| 		lim.tokens = tokens | ||||
| 		lim.lastEvent = r.timeToAct | ||||
| 	} else { | ||||
| 		lim.last = last | ||||
| 	} | ||||
| 
 | ||||
| 	lim.mu.Unlock() | ||||
| 	return r | ||||
| } | ||||
| 
 | ||||
| // advance calculates and returns an updated state for lim resulting from the passage of time.
 | ||||
| // lim is not changed.
 | ||||
| func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) { | ||||
| 	last := lim.last | ||||
| 	if now.Before(last) { | ||||
| 		last = now | ||||
| 	} | ||||
| 
 | ||||
| 	// Avoid making delta overflow below when last is very old.
 | ||||
| 	maxElapsed := lim.limit.durationFromTokens(float64(lim.burst) - lim.tokens) | ||||
| 	elapsed := now.Sub(last) | ||||
| 	if elapsed > maxElapsed { | ||||
| 		elapsed = maxElapsed | ||||
| 	} | ||||
| 
 | ||||
| 	// Calculate the new number of tokens, due to time that passed.
 | ||||
| 	delta := lim.limit.tokensFromDuration(elapsed) | ||||
| 	tokens := lim.tokens + delta | ||||
| 	if burst := float64(lim.burst); tokens > burst { | ||||
| 		tokens = burst | ||||
| 	} | ||||
| 
 | ||||
| 	return now, last, tokens | ||||
| } | ||||
| 
 | ||||
| // durationFromTokens is a unit conversion function from the number of tokens to the duration
 | ||||
| // of time it takes to accumulate them at a rate of limit tokens per second.
 | ||||
| func (limit Limit) durationFromTokens(tokens float64) time.Duration { | ||||
| 	seconds := tokens / float64(limit) | ||||
| 	return time.Nanosecond * time.Duration(1e9*seconds) | ||||
| } | ||||
| 
 | ||||
| // tokensFromDuration is a unit conversion function from a time duration to the number of tokens
 | ||||
| // which could be accumulated during that duration at a rate of limit tokens per second.
 | ||||
| func (limit Limit) tokensFromDuration(d time.Duration) float64 { | ||||
| 	return d.Seconds() * float64(limit) | ||||
| } | ||||
							
								
								
									
										12
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							| @ -56,6 +56,12 @@ | ||||
| 			"revision": "165db2f241fd235aec29ba6d9b1ccd5f1c14637c", | ||||
| 			"revisionTime": "2015-01-22T07:26:53Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "WILMZlCPSNbyMzYRNo/RkDcUH2M=", | ||||
| 			"path": "github.com/cloudflare/cloudflare-go", | ||||
| 			"revision": "a80f83b9add9d67ca4098ccbf42cd865ebb36ffb", | ||||
| 			"revisionTime": "2019-09-16T15:18:08Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "dvabztWVQX8f6oMLRyv4dLH+TGY=", | ||||
| 			"path": "github.com/davecgh/go-spew/spew", | ||||
| @ -844,6 +850,12 @@ | ||||
| 			"revision": "31e7599a6c37728c25ca34167be099d072ad335d", | ||||
| 			"revisionTime": "2019-04-05T05:38:27Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "7Ev/X4Xe8P3961myez/hBKO05ig=", | ||||
| 			"path": "golang.org/x/time/rate", | ||||
| 			"revision": "9d24e82272b4f38b78bc8cff74fa936d31ccd8ef", | ||||
| 			"revisionTime": "2019-02-15T22:48:40Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "CEFTYXtWmgSh+3Ik1NmDaJcz4E0=", | ||||
| 			"path": "gopkg.in/check.v1", | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user