porkbacon/internal/porkbun/client.go
2026-01-26 21:45:35 +00:00

173 lines
3.9 KiB
Go

package porkbun
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
)
const BaseURL = "https://api.porkbun.com/api/json/v3"
type Client struct {
APIKey string
SecretAPIKey string
HTTPClient *http.Client
}
func New(apiKey, secretKey string) *Client {
return &Client{
APIKey: apiKey,
SecretAPIKey: secretKey,
HTTPClient: &http.Client{
Timeout: 10 * time.Second,
},
}
}
// Get all domain names in account. Domains are returned in chunks of 1000.
func (c *Client) DomainListAll(start int, includeLabels bool) (*DomainListAllResponse, error) {
reqBody := DomainListAllRequest{
BaseRequest: BaseRequest{
APIKey: c.APIKey,
SecretAPIKey: c.SecretAPIKey,
},
Start: start,
IncludeLabels: "no",
}
if includeLabels {
reqBody.IncludeLabels = "yes"
}
var resp DomainListAllResponse
err := c.post("/domain/listAll", reqBody, &resp)
if err != nil {
return nil, err
}
return &resp, nil
}
func (c *Client) RetrieveDNSRecords(domain string) (*DNSRecordsResponse, error) {
reqBody := DNSRecordsRequest{
BaseRequest: BaseRequest{
APIKey: c.APIKey,
SecretAPIKey: c.SecretAPIKey,
},
}
var resp DNSRecordsResponse
err := c.post("/dns/retrieve/"+domain, reqBody, &resp)
if err != nil {
return nil, err
}
return &resp, nil
}
func (c *Client) CreateDNSRecord(domain string, record DNSRecord) error {
reqBody := struct {
BaseRequest
Name string `json:"name"`
Type string `json:"type"`
Content string `json:"content"`
TTL string `json:"ttl,omitempty"`
Priority string `json:"prio,omitempty"`
}{
BaseRequest: BaseRequest{
APIKey: c.APIKey,
SecretAPIKey: c.SecretAPIKey,
},
Name: record.Name,
Type: record.Type,
Content: record.Content,
TTL: fmt.Sprintf("%v", record.TTL),
Priority: fmt.Sprintf("%v", record.Priority),
}
var resp BaseResponse
return c.post("/dns/create/"+domain, reqBody, &resp)
}
func (c *Client) EditDNSRecord(domain string, id string, record DNSRecord) error {
reqBody := struct {
BaseRequest
Name string `json:"name"`
Type string `json:"type"`
Content string `json:"content"`
TTL string `json:"ttl,omitempty"`
Priority string `json:"prio,omitempty"`
}{
BaseRequest: BaseRequest{
APIKey: c.APIKey,
SecretAPIKey: c.SecretAPIKey,
},
Name: record.Name,
Type: record.Type,
Content: record.Content,
TTL: fmt.Sprintf("%v", record.TTL),
Priority: fmt.Sprintf("%v", record.Priority),
}
var resp BaseResponse
return c.post("/dns/edit/"+domain+"/"+id, reqBody, &resp)
}
func (c *Client) DeleteDNSRecord(domain, id string) error {
reqBody := struct {
BaseRequest
}{
BaseRequest: BaseRequest{
APIKey: c.APIKey,
SecretAPIKey: c.SecretAPIKey,
},
}
var resp BaseResponse
return c.post("/dns/delete/"+domain+"/"+id, reqBody, &resp)
}
func (c *Client) Ping() (*PingResponse, error) {
reqBody := PingRequest{
BaseRequest: BaseRequest{
APIKey: c.APIKey,
SecretAPIKey: c.SecretAPIKey,
},
}
var resp PingResponse
err := c.post("/ping", reqBody, &resp)
if err != nil {
return nil, err
}
return &resp, nil
}
func (c *Client) post(endpoint string, body any, target any) error {
jsonBody, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("failed to marshal request body: %w", err)
}
req, err := http.NewRequest("POST", BaseURL+endpoint, bytes.NewBuffer(jsonBody))
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
res, err := c.HTTPClient.Do(req)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer res.Body.Close()
if res.StatusCode >= 400 {
return fmt.Errorf("API returned error status: %d", res.StatusCode)
}
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return fmt.Errorf("failed to decode response: %w", err)
}
return nil
}