Initial commit
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type cloudflareProvider struct {
|
||||
token string
|
||||
client *http.Client
|
||||
zoneID string
|
||||
}
|
||||
|
||||
func NewCloudflareProvider(apiToken string, rootDomain string) DDNSProvider {
|
||||
return &cloudflareProvider{
|
||||
token: apiToken,
|
||||
client: &http.Client{Timeout: 10 * time.Second},
|
||||
// 注意:实际场景需通过 rootDomain 获取 zoneID。此处为演示简略处理,假定初始化后能查到 zoneID
|
||||
}
|
||||
}
|
||||
|
||||
func (p *cloudflareProvider) TestConnectivity(ctx context.Context) error {
|
||||
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.cloudflare.com/client/v4/user/tokens/verify", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+p.token)
|
||||
resp, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("Cloudflare 认证失败,状态码: %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *cloudflareProvider) SyncRecords(ctx context.Context, baseDomain string, records []DDNSRecord) error {
|
||||
// 实战中首先需要查询 Zones 获取 zone_id
|
||||
// 为了简化演示修复,此处假设获取 zone 的逻辑 (伪代码实现,可进一步真实拉取)
|
||||
zoneID, err := p.getZoneID(ctx, baseDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rec := range records {
|
||||
fullDomain := rec.Name + "." + baseDomain
|
||||
if rec.Name == "@" {
|
||||
fullDomain = baseDomain
|
||||
}
|
||||
|
||||
// 1. 查询现有的 Record
|
||||
recordID, _ := p.getRecordID(ctx, zoneID, fullDomain, rec.Type)
|
||||
|
||||
// 2. 构造 payload
|
||||
payload := map[string]interface{}{
|
||||
"type": rec.Type,
|
||||
"name": fullDomain,
|
||||
"content": rec.Value,
|
||||
"ttl": 1, // 自动
|
||||
"proxied": false,
|
||||
}
|
||||
data, _ := json.Marshal(payload)
|
||||
|
||||
var req *http.Request
|
||||
if recordID != "" {
|
||||
// 更新
|
||||
req, _ = http.NewRequestWithContext(ctx, "PUT", fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records/%s", zoneID, recordID), bytes.NewReader(data))
|
||||
} else {
|
||||
// 创建
|
||||
req, _ = http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records", zoneID), bytes.NewReader(data))
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+p.token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 辅助方法:获取 Zone ID
|
||||
func (p *cloudflareProvider) getZoneID(ctx context.Context, domain string) (string, error) {
|
||||
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.cloudflare.com/client/v4/zones?name="+domain, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+p.token)
|
||||
resp, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result struct {
|
||||
Result []struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"result"`
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
json.Unmarshal(body, &result)
|
||||
if len(result.Result) > 0 {
|
||||
return result.Result[0].ID, nil
|
||||
}
|
||||
return "", fmt.Errorf("找不到域名 %s 的 Zone", domain)
|
||||
}
|
||||
|
||||
// 辅助方法:获取 Record ID
|
||||
func (p *cloudflareProvider) getRecordID(ctx context.Context, zoneID, name, recType string) (string, error) {
|
||||
req, _ := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records?name=%s&type=%s", zoneID, name, recType), nil)
|
||||
req.Header.Set("Authorization", "Bearer "+p.token)
|
||||
resp, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result struct {
|
||||
Result []struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"result"`
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
json.Unmarshal(body, &result)
|
||||
if len(result.Result) > 0 {
|
||||
return result.Result[0].ID, nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
Reference in New Issue
Block a user