- Add comprehensive database migrations (001-024) for schema evolution - Enhance API schema with expanded type definitions and resolvers - Add new middleware: audit logging, rate limiting, MFA enforcement, security, tenant auth - Implement new services: AI optimization, billing, blockchain, compliance, marketplace - Add adapter layer for cloud integrations (Cloudflare, Kubernetes, Proxmox, storage) - Update Crossplane provider with enhanced VM management capabilities - Add comprehensive test suite for API endpoints and services - Update frontend components with improved GraphQL subscriptions and real-time updates - Enhance security configurations and headers (CSP, CORS, etc.) - Update documentation and configuration files - Add new CI/CD workflows and validation scripts - Implement design system improvements and UI enhancements
272 lines
6.4 KiB
Go
272 lines
6.4 KiB
Go
package cloudflare
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// Client represents a Cloudflare API client
|
|
type Client struct {
|
|
apiToken string
|
|
accountID string
|
|
httpClient *http.Client
|
|
baseURL string
|
|
}
|
|
|
|
// NewClient creates a new Cloudflare API client
|
|
func NewClient(apiToken, accountID string) *Client {
|
|
return &Client{
|
|
apiToken: apiToken,
|
|
accountID: accountID,
|
|
httpClient: &http.Client{
|
|
Timeout: 30 * time.Second,
|
|
},
|
|
baseURL: "https://api.cloudflare.com/client/v4",
|
|
}
|
|
}
|
|
|
|
// makeRequest makes an HTTP request to the Cloudflare API
|
|
func (c *Client) makeRequest(ctx context.Context, method, path string, body interface{}) (*http.Response, error) {
|
|
var reqBody io.Reader
|
|
if body != nil {
|
|
jsonData, err := json.Marshal(body)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to marshal request body")
|
|
}
|
|
reqBody = bytes.NewBuffer(jsonData)
|
|
}
|
|
|
|
url := c.baseURL + path
|
|
req, err := http.NewRequestWithContext(ctx, method, url, reqBody)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to create request")
|
|
}
|
|
|
|
req.Header.Set("Authorization", "Bearer "+c.apiToken)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := c.httpClient.Do(req)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "request failed")
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// parseResponse parses the Cloudflare API response
|
|
func (c *Client) parseResponse(resp *http.Response, result interface{}) error {
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("API error: %d %s - %s", resp.StatusCode, resp.Status, string(bodyBytes))
|
|
}
|
|
|
|
if result != nil {
|
|
if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
|
|
return errors.Wrap(err, "failed to decode response")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Tunnel represents a Cloudflare Tunnel
|
|
type Tunnel struct {
|
|
ID string
|
|
Name string
|
|
Status string
|
|
Connections int
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
// DNSRecord represents a DNS record
|
|
type DNSRecord struct {
|
|
ID string
|
|
Type string
|
|
Name string
|
|
Content string
|
|
TTL int
|
|
ZoneID string
|
|
ZoneName string
|
|
}
|
|
|
|
// ZeroTrustPolicy represents a Zero Trust access policy
|
|
type ZeroTrustPolicy struct {
|
|
ID string
|
|
Name string
|
|
Decision string
|
|
Include []map[string]interface{}
|
|
Exclude []map[string]interface{}
|
|
Require []map[string]interface{}
|
|
}
|
|
|
|
// ListTunnels lists all Cloudflare Tunnels
|
|
func (c *Client) ListTunnels(ctx context.Context) ([]Tunnel, error) {
|
|
resp, err := c.makeRequest(ctx, "GET", fmt.Sprintf("/accounts/%s/cfd_tunnel", c.accountID), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var apiResponse struct {
|
|
Success bool `json:"success"`
|
|
Result []struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Status string `json:"status"`
|
|
Connections int `json:"connections"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
} `json:"result"`
|
|
Errors []interface{} `json:"errors"`
|
|
}
|
|
|
|
if err := c.parseResponse(resp, &apiResponse); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !apiResponse.Success {
|
|
return nil, fmt.Errorf("API returned success=false")
|
|
}
|
|
|
|
tunnels := make([]Tunnel, len(apiResponse.Result))
|
|
for i, t := range apiResponse.Result {
|
|
tunnels[i] = Tunnel{
|
|
ID: t.ID,
|
|
Name: t.Name,
|
|
Status: t.Status,
|
|
Connections: t.Connections,
|
|
CreatedAt: t.CreatedAt,
|
|
}
|
|
}
|
|
|
|
return tunnels, nil
|
|
}
|
|
|
|
// ListDNSRecords lists DNS records for a zone
|
|
func (c *Client) ListDNSRecords(ctx context.Context, zoneID string) ([]DNSRecord, error) {
|
|
resp, err := c.makeRequest(ctx, "GET", fmt.Sprintf("/zones/%s/dns_records", zoneID), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var apiResponse struct {
|
|
Success bool `json:"success"`
|
|
Result []struct {
|
|
ID string `json:"id"`
|
|
Type string `json:"type"`
|
|
Name string `json:"name"`
|
|
Content string `json:"content"`
|
|
TTL int `json:"ttl"`
|
|
ZoneID string `json:"zone_id"`
|
|
ZoneName string `json:"zone_name"`
|
|
} `json:"result"`
|
|
Errors []interface{} `json:"errors"`
|
|
}
|
|
|
|
if err := c.parseResponse(resp, &apiResponse); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !apiResponse.Success {
|
|
return nil, fmt.Errorf("API returned success=false")
|
|
}
|
|
|
|
records := make([]DNSRecord, len(apiResponse.Result))
|
|
for i, r := range apiResponse.Result {
|
|
records[i] = DNSRecord{
|
|
ID: r.ID,
|
|
Type: r.Type,
|
|
Name: r.Name,
|
|
Content: r.Content,
|
|
TTL: r.TTL,
|
|
ZoneID: r.ZoneID,
|
|
ZoneName: r.ZoneName,
|
|
}
|
|
}
|
|
|
|
return records, nil
|
|
}
|
|
|
|
// ListZones lists all DNS zones
|
|
func (c *Client) ListZones(ctx context.Context) ([]string, error) {
|
|
resp, err := c.makeRequest(ctx, "GET", "/zones", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var apiResponse struct {
|
|
Success bool `json:"success"`
|
|
Result []struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
} `json:"result"`
|
|
Errors []interface{} `json:"errors"`
|
|
}
|
|
|
|
if err := c.parseResponse(resp, &apiResponse); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !apiResponse.Success {
|
|
return nil, fmt.Errorf("API returned success=false")
|
|
}
|
|
|
|
zoneIDs := make([]string, len(apiResponse.Result))
|
|
for i, z := range apiResponse.Result {
|
|
zoneIDs[i] = z.ID
|
|
}
|
|
|
|
return zoneIDs, nil
|
|
}
|
|
|
|
// ListZeroTrustPolicies lists Zero Trust access policies
|
|
func (c *Client) ListZeroTrustPolicies(ctx context.Context) ([]ZeroTrustPolicy, error) {
|
|
resp, err := c.makeRequest(ctx, "GET", fmt.Sprintf("/accounts/%s/access/policies", c.accountID), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var apiResponse struct {
|
|
Success bool `json:"success"`
|
|
Result []struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Decision string `json:"decision"`
|
|
Include []map[string]interface{} `json:"include"`
|
|
Exclude []map[string]interface{} `json:"exclude"`
|
|
Require []map[string]interface{} `json:"require"`
|
|
} `json:"result"`
|
|
Errors []interface{} `json:"errors"`
|
|
}
|
|
|
|
if err := c.parseResponse(resp, &apiResponse); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !apiResponse.Success {
|
|
return nil, fmt.Errorf("API returned success=false")
|
|
}
|
|
|
|
policies := make([]ZeroTrustPolicy, len(apiResponse.Result))
|
|
for i, p := range apiResponse.Result {
|
|
policies[i] = ZeroTrustPolicy{
|
|
ID: p.ID,
|
|
Name: p.Name,
|
|
Decision: p.Decision,
|
|
Include: p.Include,
|
|
Exclude: p.Exclude,
|
|
Require: p.Require,
|
|
}
|
|
}
|
|
|
|
return policies, nil
|
|
}
|
|
|