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
253 lines
6 KiB
Go
253 lines
6 KiB
Go
package cpanel
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// Client handles cPanel UAPI interactions
|
|
type Client struct {
|
|
host string
|
|
username string
|
|
apiToken string
|
|
http *http.Client
|
|
}
|
|
|
|
// NewClient creates a new cPanel API client
|
|
func NewClient() *Client {
|
|
return &Client{
|
|
host: os.Getenv("CPANEL_HOST"),
|
|
username: os.Getenv("CPANEL_USERNAME"),
|
|
apiToken: os.Getenv("CPANEL_API_TOKEN"),
|
|
http: &http.Client{
|
|
Timeout: 30 * time.Second,
|
|
},
|
|
}
|
|
}
|
|
|
|
// NewClientWithConfig creates a client with custom config
|
|
func NewClientWithConfig(host, username, apiToken string) *Client {
|
|
return &Client{
|
|
host: host,
|
|
username: username,
|
|
apiToken: apiToken,
|
|
http: &http.Client{
|
|
Timeout: 30 * time.Second,
|
|
},
|
|
}
|
|
}
|
|
|
|
// EmailAccount represents a cPanel email account
|
|
type EmailAccount struct {
|
|
Email string `json:"email"`
|
|
Login string `json:"login"`
|
|
Domain string `json:"domain"`
|
|
DiskUsed string `json:"diskused"`
|
|
DiskQuota string `json:"diskquota"`
|
|
HumandiskUsed string `json:"humandiskused"`
|
|
HumandiskQuota string `json:"humandiskquota"`
|
|
}
|
|
|
|
// UAPIResponse is the generic UAPI response structure
|
|
type UAPIResponse struct {
|
|
APIVersion int `json:"apiversion"`
|
|
Func string `json:"func"`
|
|
Module string `json:"module"`
|
|
Result *json.RawMessage `json:"result"`
|
|
Status int `json:"status"`
|
|
Errors []string `json:"errors"`
|
|
Messages []string `json:"messages"`
|
|
}
|
|
|
|
// ListEmailsResult is the result from list_pops
|
|
type ListEmailsResult struct {
|
|
Data []EmailAccount `json:"data"`
|
|
}
|
|
|
|
// doRequest executes an HTTP request to cPanel UAPI
|
|
func (c *Client) doRequest(module, function string, params map[string]string) ([]byte, error) {
|
|
// Build UAPI URL
|
|
apiURL := fmt.Sprintf("%s/execute/%s/%s", c.host, module, function)
|
|
|
|
// Add query parameters
|
|
if len(params) > 0 {
|
|
values := url.Values{}
|
|
for k, v := range params {
|
|
values.Set(k, v)
|
|
}
|
|
apiURL += "?" + values.Encode()
|
|
}
|
|
|
|
req, err := http.NewRequest("GET", apiURL, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
// cPanel API Token authentication
|
|
req.Header.Set("Authorization", fmt.Sprintf("cpanel %s:%s", c.username, c.apiToken))
|
|
|
|
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
|
|
}
|
|
|
|
// ListEmails returns all email accounts for a domain
|
|
func (c *Client) ListEmails(domain string) ([]EmailAccount, error) {
|
|
params := map[string]string{}
|
|
if domain != "" {
|
|
params["domain"] = domain
|
|
}
|
|
|
|
respBody, err := c.doRequest("Email", "list_pops", params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var response UAPIResponse
|
|
if err := json.Unmarshal(respBody, &response); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
if response.Status != 1 {
|
|
if len(response.Errors) > 0 {
|
|
return nil, fmt.Errorf("API error: %s", response.Errors[0])
|
|
}
|
|
return nil, fmt.Errorf("unknown API error")
|
|
}
|
|
|
|
var result []EmailAccount
|
|
if response.Result != nil {
|
|
if err := json.Unmarshal(*response.Result, &result); err != nil {
|
|
return nil, fmt.Errorf("failed to parse result: %w", err)
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// CreateEmail creates a new email account
|
|
func (c *Client) CreateEmail(email, password string, quota int) error {
|
|
// Parse email to get user and domain
|
|
params := map[string]string{
|
|
"email": email,
|
|
"password": password,
|
|
"quota": fmt.Sprintf("%d", quota), // MB, 0 = unlimited
|
|
}
|
|
|
|
respBody, err := c.doRequest("Email", "add_pop", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var response UAPIResponse
|
|
if err := json.Unmarshal(respBody, &response); err != nil {
|
|
return fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
if response.Status != 1 {
|
|
if len(response.Errors) > 0 {
|
|
return fmt.Errorf("API error: %s", response.Errors[0])
|
|
}
|
|
return fmt.Errorf("failed to create email")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteEmail removes an email account
|
|
func (c *Client) DeleteEmail(email string) error {
|
|
params := map[string]string{
|
|
"email": email,
|
|
}
|
|
|
|
respBody, err := c.doRequest("Email", "delete_pop", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var response UAPIResponse
|
|
if err := json.Unmarshal(respBody, &response); err != nil {
|
|
return fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
if response.Status != 1 {
|
|
if len(response.Errors) > 0 {
|
|
return fmt.Errorf("API error: %s", response.Errors[0])
|
|
}
|
|
return fmt.Errorf("failed to delete email")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ChangePassword changes an email account's password
|
|
func (c *Client) ChangePassword(email, newPassword string) error {
|
|
params := map[string]string{
|
|
"email": email,
|
|
"password": newPassword,
|
|
}
|
|
|
|
respBody, err := c.doRequest("Email", "passwd_pop", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var response UAPIResponse
|
|
if err := json.Unmarshal(respBody, &response); err != nil {
|
|
return fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
if response.Status != 1 {
|
|
if len(response.Errors) > 0 {
|
|
return fmt.Errorf("API error: %s", response.Errors[0])
|
|
}
|
|
return fmt.Errorf("failed to change password")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateQuota updates an email account's disk quota
|
|
func (c *Client) UpdateQuota(email string, quotaMB int) error {
|
|
params := map[string]string{
|
|
"email": email,
|
|
"quota": fmt.Sprintf("%d", quotaMB),
|
|
}
|
|
|
|
respBody, err := c.doRequest("Email", "edit_pop_quota", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var response UAPIResponse
|
|
if err := json.Unmarshal(respBody, &response); err != nil {
|
|
return fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
if response.Status != 1 {
|
|
if len(response.Errors) > 0 {
|
|
return fmt.Errorf("API error: %s", response.Errors[0])
|
|
}
|
|
return fmt.Errorf("failed to update quota")
|
|
}
|
|
|
|
return nil
|
|
}
|