style: rename controllers to login, cancel internal/pkg
This commit is contained in:
179
internal/dns/aliyun/aliyun.go
Normal file
179
internal/dns/aliyun/aliyun.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package aliyun
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Mmx233/BitSrunLoginGo/internal/dns/util"
|
||||
"github.com/Mmx233/tool"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Aliyun struct {
|
||||
AccessKeyId string `json:"access_key_id,omitempty" yaml:"access_key_id,omitempty"`
|
||||
AccessKeySecret string `json:"access_key_secret,omitempty" yaml:"access_key_secret,omitempty"`
|
||||
}
|
||||
|
||||
type DnsProvider struct {
|
||||
TTL uint
|
||||
Http *tool.Http
|
||||
Aliyun
|
||||
}
|
||||
|
||||
func New(ttl uint, conf Aliyun, Http *http.Client) (*DnsProvider, error) {
|
||||
if conf.AccessKeyId == "" || conf.AccessKeySecret == "" {
|
||||
return nil, errors.New("aliyun AccessKey 不能为空")
|
||||
}
|
||||
return &DnsProvider{
|
||||
TTL: ttl,
|
||||
Http: tool.NewHttpTool(Http),
|
||||
Aliyun: conf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a DnsProvider) SendRequest(Type, Action string, data map[string]interface{}) (*http.Response, error) {
|
||||
var reqOpt = tool.DoHttpReq{
|
||||
Url: "https://alidns.aliyuncs.com",
|
||||
}
|
||||
data["Format"] = "json"
|
||||
data["Version"] = "2015-01-09"
|
||||
data["SignatureMethod"] = "HMAC-SHA1"
|
||||
data["SignatureVersion"] = "1.0"
|
||||
data["SignatureNonce"] = fmt.Sprint(tool.RandMath(rand.NewSource(time.Now().UnixNano())).Num(10000000, 90000000))
|
||||
data["Timestamp"] = time.Now().UTC().Format("2006-01-02T15:04:05Z")
|
||||
data["Action"] = Action
|
||||
data["AccessKeyId"] = a.AccessKeyId
|
||||
|
||||
signStr := Type + "&" + url.QueryEscape("/") + "&"
|
||||
var keys = make([]string, len(data))
|
||||
var i int
|
||||
for k := range data {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for i, k := range keys {
|
||||
str := k + "=" + url.QueryEscape(fmt.Sprint(data[k]))
|
||||
if i == 0 {
|
||||
str = url.QueryEscape(str)
|
||||
} else {
|
||||
str = url.QueryEscape("&" + str)
|
||||
}
|
||||
signStr += str
|
||||
}
|
||||
|
||||
mac := hmac.New(sha1.New, []byte(a.AccessKeySecret+"&"))
|
||||
_, err := mac.Write([]byte(signStr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data["Signature"] = base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
||||
|
||||
if Type == "GET" || Type == "DELETE" {
|
||||
reqOpt.Query = data
|
||||
} else {
|
||||
reqOpt.Body = data
|
||||
}
|
||||
|
||||
resp, err := a.Http.Request(Type, &reqOpt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode > 299 {
|
||||
defer resp.Body.Close()
|
||||
var res Response
|
||||
if err = json.NewDecoder(resp.Body).Decode(&res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, errors.New(res.Message)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (a DnsProvider) DomainRecordStatus(subDomain, rootDomain string) (*DomainStatus, bool, error) {
|
||||
resp, err := a.SendRequest("GET", "DescribeDomainRecords", map[string]interface{}{
|
||||
"DomainName": rootDomain,
|
||||
"SearchMode": "EXACT",
|
||||
"KeyWord": subDomain,
|
||||
"PageSize": 1,
|
||||
"Type": "A",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var res DomainStatusRes
|
||||
if err = json.NewDecoder(resp.Body).Decode(&res); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if res.TotalCount == 0 || len(res.DomainRecords.Record) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return &res.DomainRecords.Record[0], true, nil
|
||||
}
|
||||
|
||||
func (a DnsProvider) UpdateRecord(RecordId, subDomain, ip string) error {
|
||||
resp, err := a.SendRequest("POST", "UpdateDomainRecord", map[string]interface{}{
|
||||
"RecordId": RecordId,
|
||||
"RR": subDomain,
|
||||
"Type": "A",
|
||||
"Value": ip,
|
||||
"TTL": a.TTL,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a DnsProvider) NewRecord(subDomain, rootDomain, ip string) error {
|
||||
resp, err := a.SendRequest("POST", "AddDomainRecord", map[string]interface{}{
|
||||
"DomainName": rootDomain,
|
||||
"RR": subDomain,
|
||||
"Type": "A",
|
||||
"Value": ip,
|
||||
"TTL": a.TTL,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a DnsProvider) SetDomainRecord(domain, ip string) error {
|
||||
subDomain, rootDomain, err := dnsUtil.DecodeDomain(domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record, exist, err := a.DomainRecordStatus(subDomain, rootDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exist {
|
||||
if record.Value == ip {
|
||||
return nil
|
||||
}
|
||||
return a.UpdateRecord(record.RecordId, subDomain, ip)
|
||||
} else {
|
||||
return a.NewRecord(subDomain, rootDomain, ip)
|
||||
}
|
||||
}
|
||||
19
internal/dns/aliyun/models.go
Normal file
19
internal/dns/aliyun/models.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package aliyun
|
||||
|
||||
type Response struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type DomainStatus struct {
|
||||
DomainName string `json:"DomainName"`
|
||||
RecordId string `json:"RecordId"`
|
||||
Value string `json:"Value"`
|
||||
}
|
||||
|
||||
type DomainStatusRes struct {
|
||||
TotalCount uint `json:"TotalCount"`
|
||||
DomainRecords struct {
|
||||
Record []DomainStatus `json:"Record"`
|
||||
} `json:"DomainRecords"`
|
||||
}
|
||||
67
internal/dns/cloudflare/cloudflare.go
Normal file
67
internal/dns/cloudflare/cloudflare.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/cloudflare/cloudflare-go"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Cloudflare struct {
|
||||
Zone string `json:"zone" yaml:"zone"`
|
||||
Token string `json:"token" yaml:"token"`
|
||||
}
|
||||
|
||||
type DnsProvider struct {
|
||||
Api *cloudflare.API
|
||||
TTL int
|
||||
ZoneResource *cloudflare.ResourceContainer
|
||||
Cloudflare
|
||||
}
|
||||
|
||||
func New(ttl int, conf Cloudflare, Http *http.Client) (*DnsProvider, error) {
|
||||
var p = DnsProvider{
|
||||
TTL: ttl,
|
||||
Cloudflare: conf,
|
||||
}
|
||||
if p.Zone == "" {
|
||||
return nil, errors.New("cloudflare zone 不能为空")
|
||||
}
|
||||
if p.Token == "" {
|
||||
return nil, errors.New("cloudflare token 不能为空")
|
||||
}
|
||||
p.ZoneResource = cloudflare.ZoneIdentifier(p.Zone)
|
||||
var err error
|
||||
p.Api, err = cloudflare.NewWithAPIToken(p.Token, cloudflare.HTTPClient(Http))
|
||||
return &p, err
|
||||
}
|
||||
|
||||
func (a DnsProvider) SetDomainRecord(domain, ip string) error {
|
||||
records, _, err := a.Api.ListDNSRecords(context.Background(), a.ZoneResource, cloudflare.ListDNSRecordsParams{
|
||||
Type: "A",
|
||||
Name: domain,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(records) == 0 {
|
||||
_, err = a.Api.CreateDNSRecord(context.Background(), a.ZoneResource, cloudflare.CreateDNSRecordParams{
|
||||
Type: "A",
|
||||
Name: domain,
|
||||
Content: ip,
|
||||
TTL: a.TTL,
|
||||
})
|
||||
return err
|
||||
} else {
|
||||
record := records[0]
|
||||
if record.Content == ip {
|
||||
return nil
|
||||
}
|
||||
_, err = a.Api.UpdateDNSRecord(context.Background(), a.ZoneResource, cloudflare.UpdateDNSRecordParams{
|
||||
ID: record.ID,
|
||||
Content: ip,
|
||||
})
|
||||
return err
|
||||
}
|
||||
}
|
||||
56
internal/dns/dns.go
Normal file
56
internal/dns/dns.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Mmx233/BitSrunLoginGo/internal/dns/aliyun"
|
||||
"github.com/Mmx233/BitSrunLoginGo/internal/dns/cloudflare"
|
||||
"github.com/Mmx233/BitSrunLoginGo/internal/dns/dnspod"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Run(c *Config) error {
|
||||
if c.Logger == nil {
|
||||
c.Logger = log.New()
|
||||
}
|
||||
if c.TTL == 0 {
|
||||
c.TTL = 600
|
||||
}
|
||||
|
||||
c.Logger.Infof("开始 %s DDNS 流程", c.Provider)
|
||||
|
||||
var dns Provider
|
||||
var err error
|
||||
switch c.Provider {
|
||||
case "aliyun":
|
||||
dns, err = aliyun.New(c.TTL, c.Conf.Aliyun, c.Http)
|
||||
case "cloudflare":
|
||||
dns, err = cloudflare.New(int(c.TTL), c.Conf.Cloudflare, c.Http)
|
||||
case "dnspod":
|
||||
dns, err = dnspod.New(uint64(c.TTL), c.Conf.DnsPod, c.Http.Transport)
|
||||
default:
|
||||
var msg string
|
||||
if c.Provider == "" {
|
||||
msg = "DDNS 模块 dns 运营商不能为空"
|
||||
} else {
|
||||
msg = fmt.Sprintf("DDNS 模块 dns 运营商 %s 不支持", c.Provider)
|
||||
}
|
||||
c.Logger.Warnln(msg)
|
||||
return errors.New(msg)
|
||||
}
|
||||
if err != nil {
|
||||
c.Logger.Warnf("解析 DDNS config 失败:%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 修改 dns 记录
|
||||
|
||||
if err = dns.SetDomainRecord(c.Domain, c.IP); err != nil {
|
||||
c.Logger.Warnf("设置 dns 解析记录失败:%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
c.Logger.Infof("DDNS 配置应用成功: %s | %s", c.Domain, c.IP)
|
||||
|
||||
return nil
|
||||
}
|
||||
72
internal/dns/dnspod/dnspod.go
Normal file
72
internal/dns/dnspod/dnspod.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package dnspod
|
||||
|
||||
import (
|
||||
"github.com/Mmx233/BitSrunLoginGo/internal/dns/util"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/regions"
|
||||
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DnsPod struct {
|
||||
SecretId string `json:"secret_id,omitempty" yaml:"secret_id,omitempty"`
|
||||
SecretKey string `json:"secret_key,omitempty" yaml:"secret_key,omitempty"`
|
||||
}
|
||||
|
||||
type DnsProvider struct {
|
||||
Client *dnspod.Client
|
||||
TTL uint64
|
||||
DnsPod
|
||||
}
|
||||
|
||||
func New(ttl uint64, conf DnsPod, Http http.RoundTripper) (*DnsProvider, error) {
|
||||
var p = DnsProvider{TTL: ttl, DnsPod: conf}
|
||||
var err error
|
||||
p.Client, err = dnspod.NewClient(common.NewCredential(p.SecretId, p.SecretKey), regions.Guangzhou, profile.NewClientProfile())
|
||||
p.Client.WithHttpTransport(Http)
|
||||
return &p, err
|
||||
}
|
||||
|
||||
func (a DnsProvider) SetDomainRecord(domain, ip string) error {
|
||||
subDomain, rootDomain, err := dnsUtil.DecodeDomain(domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
recordType = "A"
|
||||
recordLine = "默认"
|
||||
limit uint64 = 1
|
||||
)
|
||||
|
||||
reqRecordList := dnspod.NewDescribeRecordListRequest()
|
||||
reqRecordList.Domain = &rootDomain
|
||||
reqRecordList.Subdomain = &subDomain
|
||||
reqRecordList.Limit = &limit
|
||||
res, err := a.Client.DescribeRecordList(reqRecordList)
|
||||
if (err != nil && strings.Contains(err.Error(), dnspod.RESOURCENOTFOUND_NODATAOFRECORD)) || (err == nil && len(res.Response.RecordList) == 0) {
|
||||
reqNewRecord := dnspod.NewCreateRecordRequest()
|
||||
reqNewRecord.TTL = &a.TTL
|
||||
reqNewRecord.Domain = &rootDomain
|
||||
reqNewRecord.RecordType = &recordType
|
||||
reqNewRecord.RecordLine = &recordLine
|
||||
reqNewRecord.Value = &ip
|
||||
reqNewRecord.SubDomain = &subDomain
|
||||
_, err = a.Client.CreateRecord(reqNewRecord)
|
||||
return err
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reqModifyRecord := dnspod.NewModifyRecordRequest()
|
||||
reqModifyRecord.Domain = &rootDomain
|
||||
reqModifyRecord.SubDomain = &subDomain
|
||||
reqModifyRecord.Value = &ip
|
||||
reqModifyRecord.RecordId = res.Response.RecordList[0].RecordId
|
||||
reqModifyRecord.RecordLine = &recordLine
|
||||
reqModifyRecord.RecordType = &recordType
|
||||
_, err = a.Client.ModifyRecord(reqModifyRecord)
|
||||
return err
|
||||
}
|
||||
21
internal/dns/models.go
Normal file
21
internal/dns/models.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"github.com/Mmx233/BitSrunLoginGo/internal/config"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Provider interface {
|
||||
SetDomainRecord(domain, ip string) error
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Provider string
|
||||
IP string
|
||||
Domain string
|
||||
TTL uint
|
||||
Conf config.DdnsProviderConfigSum
|
||||
Http *http.Client
|
||||
Logger log.FieldLogger
|
||||
}
|
||||
18
internal/dns/util/domain.go
Normal file
18
internal/dns/util/domain.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package dnsUtil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func DecodeDomain(domain string) (subStr string, rootDomain string, e error) {
|
||||
t := strings.Split(domain, ".")
|
||||
if len(t) == 1 {
|
||||
return "", "", errors.New("域名不合法")
|
||||
} else if len(t) == 2 {
|
||||
return "@", domain, nil
|
||||
}
|
||||
|
||||
l := len(t)
|
||||
return strings.Join(t[:l-2], "."), strings.Join(t[l-2:l], "."), nil
|
||||
}
|
||||
Reference in New Issue
Block a user