Cloudflare Cache Management:
- GET /api/v1/admin/cloudflare/zones
- POST /api/v1/admin/cloudflare/cache/purge-all
- POST /api/v1/admin/cloudflare/cache/purge-urls
- POST /api/v1/admin/cloudflare/cache/purge-tags
- POST /api/v1/admin/cloudflare/cache/purge-hosts
cPanel Email Management:
- GET /api/v1/admin/cpanel/emails
- POST /api/v1/admin/cpanel/emails
- DELETE /api/v1/admin/cpanel/emails/{email}
- PUT /api/v1/admin/cpanel/emails/{email}/password
- PUT /api/v1/admin/cpanel/emails/{email}/quota
All routes protected by JWT auth middleware.
Added CLOUDFLARE_* and CPANEL_* env vars to .env.example
206 lines
5 KiB
Go
206 lines
5 KiB
Go
package cloudflare
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
baseURL = "https://api.cloudflare.com/client/v4"
|
|
)
|
|
|
|
// Client handles Cloudflare API interactions
|
|
type Client struct {
|
|
apiToken string
|
|
zoneID string
|
|
http *http.Client
|
|
}
|
|
|
|
// NewClient creates a new Cloudflare API client
|
|
func NewClient() *Client {
|
|
return &Client{
|
|
apiToken: os.Getenv("CLOUDFLARE_API_TOKEN"),
|
|
zoneID: os.Getenv("CLOUDFLARE_ZONE_ID"),
|
|
http: &http.Client{
|
|
Timeout: 30 * time.Second,
|
|
},
|
|
}
|
|
}
|
|
|
|
// NewClientWithConfig creates a client with custom config
|
|
func NewClientWithConfig(apiToken, zoneID string) *Client {
|
|
return &Client{
|
|
apiToken: apiToken,
|
|
zoneID: zoneID,
|
|
http: &http.Client{
|
|
Timeout: 30 * time.Second,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Zone represents a Cloudflare zone
|
|
type Zone struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
// ZonesResponse is the response from /zones endpoint
|
|
type ZonesResponse struct {
|
|
Success bool `json:"success"`
|
|
Errors []Error `json:"errors"`
|
|
Messages []string `json:"messages"`
|
|
Result []Zone `json:"result"`
|
|
}
|
|
|
|
// PurgeResponse is the response from cache purge endpoints
|
|
type PurgeResponse struct {
|
|
Success bool `json:"success"`
|
|
Errors []Error `json:"errors"`
|
|
Messages []string `json:"messages"`
|
|
Result struct {
|
|
ID string `json:"id"`
|
|
} `json:"result"`
|
|
}
|
|
|
|
// Error represents a Cloudflare API error
|
|
type Error struct {
|
|
Code int `json:"code"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// doRequest executes an HTTP request to Cloudflare API
|
|
func (c *Client) doRequest(method, endpoint string, body interface{}) ([]byte, error) {
|
|
var reqBody io.Reader
|
|
if body != nil {
|
|
jsonBody, err := json.Marshal(body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal request body: %w", err)
|
|
}
|
|
reqBody = bytes.NewBuffer(jsonBody)
|
|
}
|
|
|
|
req, err := http.NewRequest(method, baseURL+endpoint, reqBody)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
req.Header.Set("Authorization", "Bearer "+c.apiToken)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := c.http.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("request failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read response: %w", err)
|
|
}
|
|
|
|
if resp.StatusCode >= 400 {
|
|
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(respBody))
|
|
}
|
|
|
|
return respBody, nil
|
|
}
|
|
|
|
// GetZones returns all zones for the account
|
|
func (c *Client) GetZones() ([]Zone, error) {
|
|
respBody, err := c.doRequest("GET", "/zones", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var response ZonesResponse
|
|
if err := json.Unmarshal(respBody, &response); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
if !response.Success {
|
|
if len(response.Errors) > 0 {
|
|
return nil, fmt.Errorf("API error: %s", response.Errors[0].Message)
|
|
}
|
|
return nil, fmt.Errorf("unknown API error")
|
|
}
|
|
|
|
return response.Result, nil
|
|
}
|
|
|
|
// PurgeAll purges all cached content for the zone
|
|
func (c *Client) PurgeAll() (*PurgeResponse, error) {
|
|
endpoint := fmt.Sprintf("/zones/%s/purge_cache", c.zoneID)
|
|
body := map[string]bool{"purge_everything": true}
|
|
|
|
respBody, err := c.doRequest("POST", endpoint, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var response PurgeResponse
|
|
if err := json.Unmarshal(respBody, &response); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
// PurgeByURLs purges specific URLs from cache
|
|
func (c *Client) PurgeByURLs(urls []string) (*PurgeResponse, error) {
|
|
endpoint := fmt.Sprintf("/zones/%s/purge_cache", c.zoneID)
|
|
body := map[string][]string{"files": urls}
|
|
|
|
respBody, err := c.doRequest("POST", endpoint, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var response PurgeResponse
|
|
if err := json.Unmarshal(respBody, &response); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
// PurgeByTags purges content by cache tags (Enterprise only)
|
|
func (c *Client) PurgeByTags(tags []string) (*PurgeResponse, error) {
|
|
endpoint := fmt.Sprintf("/zones/%s/purge_cache", c.zoneID)
|
|
body := map[string][]string{"tags": tags}
|
|
|
|
respBody, err := c.doRequest("POST", endpoint, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var response PurgeResponse
|
|
if err := json.Unmarshal(respBody, &response); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
// PurgeByHosts purges content by hostnames
|
|
func (c *Client) PurgeByHosts(hosts []string) (*PurgeResponse, error) {
|
|
endpoint := fmt.Sprintf("/zones/%s/purge_cache", c.zoneID)
|
|
body := map[string][]string{"hosts": hosts}
|
|
|
|
respBody, err := c.doRequest("POST", endpoint, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var response PurgeResponse
|
|
if err := json.Unmarshal(respBody, &response); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
return &response, nil
|
|
}
|