Files
waf-platform/EdgeDNS/internal/nodes/manager_domain.go
2026-02-04 20:27:13 +08:00

320 lines
7.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package nodes
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeDNS/internal/dbs"
"github.com/TeaOSLab/EdgeDNS/internal/models"
"github.com/TeaOSLab/EdgeDNS/internal/remotelogs"
"github.com/TeaOSLab/EdgeDNS/internal/rpc"
"github.com/iwind/TeaGo/types"
"strings"
"sync"
"time"
)
// DomainManager 域名管理器
type DomainManager struct {
domainMap map[int64]*models.NSDomain // domainId => domain
namesMap map[string]map[int64]*models.NSDomain // domain name => { domainId => domain }
clusterId int64
db *dbs.DB
version int64
locker *sync.RWMutex
notifier chan bool
}
// NewDomainManager 获取域名管理器对象
func NewDomainManager(db *dbs.DB, clusterId int64) *DomainManager {
return &DomainManager{
db: db,
domainMap: map[int64]*models.NSDomain{},
namesMap: map[string]map[int64]*models.NSDomain{},
clusterId: clusterId,
notifier: make(chan bool, 8),
locker: &sync.RWMutex{},
}
}
// Start 启动自动任务
func (this *DomainManager) Start() {
remotelogs.Println("DOMAIN_MANAGER", "starting ...")
// 从本地数据库中加载数据
err := this.Load()
if err != nil {
if rpc.IsConnError(err) {
remotelogs.Debug("DOMAIN_MANAGER", "load failed: "+err.Error())
} else {
remotelogs.Error("DOMAIN_MANAGER", "load failed: "+err.Error())
}
}
// 初始化运行
err = this.LoopAll()
if err != nil {
if rpc.IsConnError(err) {
remotelogs.Debug("DOMAIN_MANAGER", "loop failed: "+err.Error())
} else {
remotelogs.Error("DOMAIN_MANAGER", "loop failed: "+err.Error())
}
}
// 更新
var ticker = time.NewTicker(20 * time.Second)
for {
select {
case <-ticker.C:
case <-this.notifier:
}
err = this.LoopAll()
if err != nil {
if rpc.IsConnError(err) {
remotelogs.Debug("DOMAIN_MANAGER", "loop failed: "+err.Error())
} else {
remotelogs.Error("DOMAIN_MANAGER", "loop failed: "+err.Error())
}
}
}
}
func (this *DomainManager) LoopAll() error {
for {
hasNext, err := this.Loop()
if err != nil {
return err
}
if !hasNext {
break
}
}
return nil
}
// Load 从数据库中加载数据
func (this *DomainManager) Load() error {
var offset = 0
var size = 10000
for {
domains, err := this.db.ListDomains(this.clusterId, offset, size)
if err != nil {
return err
}
if len(domains) == 0 {
break
}
this.locker.Lock()
for _, domain := range domains {
this.domainMap[domain.Id] = domain
nameMap, ok := this.namesMap[domain.Name]
if ok {
nameMap[domain.Id] = domain
} else {
this.namesMap[domain.Name] = map[int64]*models.NSDomain{
domain.Id: domain,
}
}
if domain.Version > this.version {
this.version = domain.Version
}
}
this.locker.Unlock()
offset += size
}
if this.version > 0 {
this.version++
}
return nil
}
// Loop 单次循环任务
func (this *DomainManager) Loop() (hasNext bool, err error) {
client, err := rpc.SharedRPC()
if err != nil {
return false, err
}
resp, err := client.NSDomainRPC.ListNSDomainsAfterVersion(client.Context(), &pb.ListNSDomainsAfterVersionRequest{
Version: this.version,
Size: 20000,
})
if err != nil {
return false, err
}
var domains = resp.NsDomains
if len(domains) == 0 {
return false, nil
}
for _, domain := range domains {
this.processDomain(domain)
if domain.Version > this.version {
this.version = domain.Version
}
}
this.version++
return true, nil
}
// FindDomain 根据名称查找域名
func (this *DomainManager) FindDomain(name string) (domain *models.NSDomain, ok bool) {
this.locker.RLock()
defer this.locker.RUnlock()
nameMap, ok := this.namesMap[name]
if !ok {
return nil, false
}
for _, domain2 := range nameMap {
return domain2, true
}
return
}
// FindDomainWithId 根据域名ID查询域名
func (this *DomainManager) FindDomainWithId(domainId int64) (domain *models.NSDomain) {
this.locker.RLock()
defer this.locker.RUnlock()
return this.domainMap[domainId]
}
// NotifyUpdate 通知更新
func (this *DomainManager) NotifyUpdate() {
select {
case this.notifier <- true:
default:
}
}
// SplitDomain 分解域名
func (this *DomainManager) SplitDomain(fullDomainName string) (rootDomain *models.NSDomain, recordName string) {
if len(fullDomainName) == 0 {
return
}
fullDomainName = strings.TrimSuffix(fullDomainName, ".") // 去除尾部的点(.
fullDomainName = strings.ToLower(fullDomainName) // 转换为小写
var domainName = fullDomainName
var domain, ok = this.FindDomain(domainName)
if !ok {
for {
var index = strings.Index(domainName, ".")
if index < 0 {
break
}
domainName = domainName[index+1:]
domain, ok = this.FindDomain(domainName)
if ok {
recordName = fullDomainName[:len(fullDomainName)-len(domainName)-1]
break
}
}
}
return domain, recordName
}
// 处理域名
func (this *DomainManager) processDomain(domain *pb.NSDomain) {
if !domain.IsOn || domain.IsDeleted || domain.Status != dnsconfigs.NSDomainStatusVerified {
this.locker.Lock()
delete(this.domainMap, domain.Id)
nameMap, ok := this.namesMap[domain.Name]
if ok {
delete(nameMap, domain.Id)
if len(nameMap) == 0 {
delete(this.namesMap, domain.Name)
}
}
this.locker.Unlock()
// 从数据库中删除
if this.db != nil {
err := this.db.DeleteDomain(domain.Id)
if err != nil {
remotelogs.Error("DOMAIN_MANAGER", "delete domain from db failed: "+err.Error())
}
}
return
}
// 存入数据库
if this.db != nil {
exists, err := this.db.ExistsDomain(domain.Id)
if err != nil {
remotelogs.Error("DOMAIN_MANAGER", "query failed: "+err.Error())
} else {
if exists {
err = this.db.UpdateDomain(domain.Id, domain.NsCluster.Id, domain.UserId, domain.Name, domain.TsigJSON, domain.Version)
if err != nil {
remotelogs.Error("DOMAIN_MANAGER", "update failed: "+err.Error())
}
} else {
err = this.db.InsertDomain(domain.Id, domain.NsCluster.Id, domain.UserId, domain.Name, domain.TsigJSON, domain.Version)
if err != nil {
remotelogs.Error("DOMAIN_MANAGER", "insert failed: "+err.Error())
}
}
}
}
// 同集群的才需要加载
if this.clusterId == domain.NsCluster.Id {
this.locker.Lock()
var tsigConfig = &dnsconfigs.NSTSIGConfig{}
if len(domain.TsigJSON) > 0 {
err := json.Unmarshal(domain.TsigJSON, tsigConfig)
if err != nil {
remotelogs.Error("DOMAIN_MANAGER", "decode TSIG json failed: "+err.Error()+", domain: "+domain.Name+", domainId: "+types.String(domain.Id)+", JSON: "+string(domain.TsigJSON))
}
}
var nsDomain = &models.NSDomain{
Id: domain.Id,
ClusterId: domain.NsCluster.Id,
UserId: domain.UserId,
Name: domain.Name,
TSIG: tsigConfig,
Version: domain.Version,
}
this.domainMap[domain.Id] = nsDomain
nameMap, ok := this.namesMap[domain.Name]
if ok {
nameMap[nsDomain.Id] = nsDomain
} else {
this.namesMap[domain.Name] = map[int64]*models.NSDomain{
nsDomain.Id: nsDomain,
}
}
this.locker.Unlock()
} else {
// 不同集群的删除域名
this.locker.Lock()
delete(this.domainMap, domain.Id)
nameMap, ok := this.namesMap[domain.Name]
if ok {
delete(nameMap, domain.Id)
if len(nameMap) == 0 {
delete(this.namesMap, domain.Name)
}
}
this.locker.Unlock()
}
}