chore: 初始提交 Fonu 1.0.2
- 公网反代:Nginx 状态卡片(版本 / 运行状态 / 配置校验 / 端口监听) - 部署设置第一、二梯队(HTTPS 开关、安全头、限流、连接数、仅中国大陆 IP) - 隧道检测兼容 frps v0.68 / v0.71(V2 与旧版端点) - 恢复被清空的 internal/frp/store.go、internal/api/ddns_handler.go、web/src/views/DdnsView.vue
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
package ddns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
errorsdk "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
|
||||
)
|
||||
|
||||
type TencentCloud struct{}
|
||||
|
||||
func NewTencentCloud() *TencentCloud { return &TencentCloud{} }
|
||||
|
||||
func (t *TencentCloud) Name() string { return "tencentcloud" }
|
||||
|
||||
func (t *TencentCloud) Verify(ctx context.Context, cred Credentials) error {
|
||||
client, err := t.client(cred)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req := dnspod.NewDescribeDomainListRequest()
|
||||
req.Limit = common.Int64Ptr(1)
|
||||
_, err = client.DescribeDomainList(req)
|
||||
return wrapTencentErr(err)
|
||||
}
|
||||
|
||||
func (t *TencentCloud) HasZone(ctx context.Context, cred Credentials, zone string) (bool, error) {
|
||||
client, err := t.client(cred)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, err = t.domainInfo(client, zone)
|
||||
return zoneLookupOK(wrapTencentErr(err))
|
||||
}
|
||||
|
||||
func (t *TencentCloud) GetRecordIP(ctx context.Context, cred Credentials, rootDomain, recordName, recordType string) (string, error) {
|
||||
client, err := t.client(cred)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
zone, err := t.domainInfo(client, rootDomain)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
records, err := t.listRecords(client, zone, tencentSubdomain(recordName), recordType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, rec := range records {
|
||||
if rec.Value != nil {
|
||||
return *rec.Value, nil
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (t *TencentCloud) UpdateRecord(ctx context.Context, cred Credentials, rootDomain, recordName, recordType, ip string) error {
|
||||
client, err := t.client(cred)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
zone, err := t.domainInfo(client, rootDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subDomain := tencentSubdomain(recordName)
|
||||
records, err := t.listRecords(client, zone, subDomain, recordType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(records) > 0 && records[0].RecordId != nil {
|
||||
req := dnspod.NewModifyRecordRequest()
|
||||
req.Domain = zone.Name
|
||||
req.DomainId = zone.DomainId
|
||||
req.RecordId = records[0].RecordId
|
||||
req.SubDomain = common.StringPtr(subDomain)
|
||||
req.RecordType = common.StringPtr(recordType)
|
||||
req.RecordLine = common.StringPtr(tencentRecordLine(records[0]))
|
||||
req.Value = common.StringPtr(ip)
|
||||
req.TTL = common.Uint64Ptr(600)
|
||||
_, err = client.ModifyRecord(req)
|
||||
return wrapTencentErr(err)
|
||||
}
|
||||
|
||||
req := dnspod.NewCreateRecordRequest()
|
||||
req.Domain = zone.Name
|
||||
req.DomainId = zone.DomainId
|
||||
req.SubDomain = common.StringPtr(subDomain)
|
||||
req.RecordType = common.StringPtr(recordType)
|
||||
req.RecordLine = common.StringPtr("默认")
|
||||
req.Value = common.StringPtr(ip)
|
||||
req.TTL = common.Uint64Ptr(600)
|
||||
_, err = client.CreateRecord(req)
|
||||
return wrapTencentErr(err)
|
||||
}
|
||||
|
||||
func (t *TencentCloud) client(cred Credentials) (*dnspod.Client, error) {
|
||||
if cred.Token == "" || cred.Secret == "" {
|
||||
return nil, fmt.Errorf("腾讯云 DNS 凭证未配置")
|
||||
}
|
||||
credential := common.NewCredential(cred.Token, cred.Secret)
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.Endpoint = "dnspod.tencentcloudapi.com"
|
||||
return dnspod.NewClient(credential, "", cpf)
|
||||
}
|
||||
|
||||
func (t *TencentCloud) domainInfo(client *dnspod.Client, rootDomain string) (*dnspod.DomainListItem, error) {
|
||||
rootDomain = strings.ToLower(strings.TrimSpace(rootDomain))
|
||||
req := dnspod.NewDescribeDomainListRequest()
|
||||
var offset int64
|
||||
for {
|
||||
req.Offset = common.Int64Ptr(offset)
|
||||
resp, err := client.DescribeDomainList(req)
|
||||
if err != nil {
|
||||
return nil, wrapTencentErr(err)
|
||||
}
|
||||
for _, zone := range resp.Response.DomainList {
|
||||
if zone.Name != nil && strings.EqualFold(*zone.Name, rootDomain) {
|
||||
return zone, nil
|
||||
}
|
||||
if zone.Punycode != nil && strings.EqualFold(*zone.Punycode, rootDomain) {
|
||||
return zone, nil
|
||||
}
|
||||
}
|
||||
total := int64(0)
|
||||
if resp.Response.DomainCountInfo != nil && resp.Response.DomainCountInfo.AllTotal != nil {
|
||||
total = int64(*resp.Response.DomainCountInfo.AllTotal)
|
||||
}
|
||||
offset += int64(len(resp.Response.DomainList))
|
||||
if offset >= total || len(resp.Response.DomainList) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("未找到域名 %s 对应的腾讯云 DNS 域名", rootDomain)
|
||||
}
|
||||
|
||||
func (t *TencentCloud) listRecords(client *dnspod.Client, zone *dnspod.DomainListItem, subDomain, recordType string) ([]*dnspod.RecordListItem, error) {
|
||||
req := dnspod.NewDescribeRecordListRequest()
|
||||
req.Domain = zone.Name
|
||||
req.DomainId = zone.DomainId
|
||||
req.Subdomain = common.StringPtr(subDomain)
|
||||
req.RecordType = common.StringPtr(recordType)
|
||||
resp, err := client.DescribeRecordList(req)
|
||||
if err != nil {
|
||||
var sdkErr *errorsdk.TencentCloudSDKError
|
||||
if errors.As(err, &sdkErr) && sdkErr.Code == dnspod.RESOURCENOTFOUND_NODATAOFRECORD {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, wrapTencentErr(err)
|
||||
}
|
||||
if resp.Response == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return resp.Response.RecordList, nil
|
||||
}
|
||||
|
||||
func tencentSubdomain(recordName string) string {
|
||||
sub := subDomainForRecord(recordName)
|
||||
if sub == "@" {
|
||||
return "@"
|
||||
}
|
||||
return sub
|
||||
}
|
||||
|
||||
func tencentRecordLine(rec *dnspod.RecordListItem) string {
|
||||
if rec != nil && rec.Line != nil && *rec.Line != "" {
|
||||
return *rec.Line
|
||||
}
|
||||
return "默认"
|
||||
}
|
||||
|
||||
func wrapTencentErr(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
var sdkErr *errorsdk.TencentCloudSDKError
|
||||
if errors.As(err, &sdkErr) {
|
||||
return fmt.Errorf("腾讯云 DNS API 错误:%s", sdkErr.Message)
|
||||
}
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user