This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

View File

@@ -0,0 +1,263 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package nameservers
import (
"context"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
// NSService 域名服务
type NSService struct {
services.BaseService
}
// ComposeNSBoard 组合看板数据
func (this *NSService) ComposeNSBoard(ctx context.Context, req *pb.ComposeNSBoardRequest) (*pb.ComposeNSBoardResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
var result = &pb.ComposeNSBoardResponse{}
// 域名
countDomains, err := nameservers.SharedNSDomainDAO.CountAllEnabledDomains(tx, 0, 0, 0, dnsconfigs.NSDomainStatusVerified, "")
if err != nil {
return nil, err
}
result.CountNSDomains = countDomains
// 记录
countRecords, err := nameservers.SharedNSRecordDAO.CountAllEnabledRecords(tx)
if err != nil {
return nil, err
}
result.CountNSRecords = countRecords
// 集群数
countClusters, err := models.SharedNSClusterDAO.CountAllEnabledClusters(tx)
if err != nil {
return nil, err
}
result.CountNSClusters = countClusters
// 节点数
countNodes, err := models.SharedNSNodeDAO.CountAllEnabledNodes(tx)
if err != nil {
return nil, err
}
result.CountNSNodes = countNodes
// 离线节点数
countOfflineNodes, err := models.SharedNSNodeDAO.CountAllOfflineNodes(tx)
if err != nil {
return nil, err
}
result.CountOfflineNSNodes = countOfflineNodes
// 按小时统计
var hourFrom = timeutil.Format("YmdH", time.Now().Add(-23*time.Hour))
var hourTo = timeutil.Format("YmdH")
hourlyStats, err := nameservers.SharedNSRecordHourlyStatDAO.FindHourlyStats(tx, hourFrom, hourTo)
if err != nil {
return nil, err
}
for _, stat := range hourlyStats {
result.HourlyTrafficStats = append(result.HourlyTrafficStats, &pb.ComposeNSBoardResponse_HourlyTrafficStat{
Hour: stat.Hour,
Bytes: int64(stat.Bytes),
CountRequests: int64(stat.CountRequests),
})
}
// 按天统计
var dayFrom = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -14))
var dayTo = timeutil.Format("Ymd")
dailyStats, err := nameservers.SharedNSRecordHourlyStatDAO.FindDailyStats(tx, dayFrom, dayTo)
if err != nil {
return nil, err
}
for _, stat := range dailyStats {
result.DailyTrafficStats = append(result.DailyTrafficStats, &pb.ComposeNSBoardResponse_DailyTrafficStat{
Day: stat.Day,
Bytes: int64(stat.Bytes),
CountRequests: int64(stat.CountRequests),
})
}
// 域名排行
topDomainStats, err := nameservers.SharedNSRecordHourlyStatDAO.ListTopDomains(tx, 0, hourFrom, hourTo, 10)
if err != nil {
return nil, err
}
for _, stat := range topDomainStats {
domainName, err := nameservers.SharedNSDomainDAO.FindNSDomainName(tx, int64(stat.DomainId))
if err != nil {
return nil, err
}
if len(domainName) == 0 {
continue
}
result.TopNSDomainStats = append(result.TopNSDomainStats, &pb.ComposeNSBoardResponse_DomainStat{
NsDomainId: int64(stat.DomainId),
NsDomainName: domainName,
CountRequests: int64(stat.CountRequests),
Bytes: int64(stat.Bytes),
})
}
// 节点排行
topNodeStats, err := nameservers.SharedNSRecordHourlyStatDAO.ListTopNodes(tx, hourFrom, hourTo, 10)
if err != nil {
return nil, err
}
for _, stat := range topNodeStats {
nodeName, err := models.SharedNSNodeDAO.FindEnabledNSNodeName(tx, int64(stat.NodeId))
if err != nil {
return nil, err
}
if len(nodeName) == 0 {
continue
}
result.TopNSNodeStats = append(result.TopNSNodeStats, &pb.ComposeNSBoardResponse_NodeStat{
NsClusterId: int64(stat.ClusterId),
NsNodeId: int64(stat.NodeId),
NsNodeName: nodeName,
CountRequests: int64(stat.CountRequests),
Bytes: int64(stat.Bytes),
})
}
// CPU、内存、负载
cpuValues, err := models.SharedNodeValueDAO.ListValuesForNSNodes(tx, nodeconfigs.NodeValueItemCPU, "usage", nodeconfigs.NodeValueRangeMinute)
if err != nil {
return nil, err
}
for _, v := range cpuValues {
result.CpuNodeValues = append(result.CpuNodeValues, &pb.NodeValue{
ValueJSON: v.Value,
CreatedAt: int64(v.CreatedAt),
})
}
memoryValues, err := models.SharedNodeValueDAO.ListValuesForNSNodes(tx, nodeconfigs.NodeValueItemMemory, "usage", nodeconfigs.NodeValueRangeMinute)
if err != nil {
return nil, err
}
for _, v := range memoryValues {
result.MemoryNodeValues = append(result.MemoryNodeValues, &pb.NodeValue{
ValueJSON: v.Value,
CreatedAt: int64(v.CreatedAt),
})
}
loadValues, err := models.SharedNodeValueDAO.ListValuesForNSNodes(tx, nodeconfigs.NodeValueItemLoad, "load1m", nodeconfigs.NodeValueRangeMinute)
if err != nil {
return nil, err
}
for _, v := range loadValues {
result.LoadNodeValues = append(result.LoadNodeValues, &pb.NodeValue{
ValueJSON: v.Value,
CreatedAt: int64(v.CreatedAt),
})
}
return result, nil
}
// ComposeNSUserBoard 组合用户看板数据
func (this *NSService) ComposeNSUserBoard(ctx context.Context, req *pb.ComposeNSUserBoardRequest) (*pb.ComposeNSUserBoardResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
if userId > 0 {
req.UserId = userId
}
var result = &pb.ComposeNSUserBoardResponse{}
var tx = this.NullTx()
countDomains, err := nameservers.SharedNSDomainDAO.CountAllEnabledDomains(tx, 0, req.UserId, 0, "", "")
if err != nil {
return nil, err
}
result.CountNSDomains = countDomains
countRecords, err := nameservers.SharedNSRecordDAO.CountAllUserRecords(tx, req.UserId)
if err != nil {
return nil, err
}
result.CountNSRecords = countRecords
countRoutes, err := nameservers.SharedNSRouteDAO.CountAllEnabledRoutes(tx, 0, 0, req.UserId)
if err != nil {
return nil, err
}
result.CountNSRoutes = countRoutes
// 用户套餐
userPlan, err := nameservers.SharedNSUserPlanDAO.FindUserPlan(tx, req.UserId)
if err != nil {
return nil, err
}
if userPlan != nil {
if userPlan.PlanId > 0 && userPlan.DayTo >= timeutil.Format("Ymd") {
plan, err := nameservers.SharedNSPlanDAO.FindEnabledNSPlan(tx, int64(userPlan.PlanId))
if err != nil {
return nil, err
}
if plan != nil && plan.IsOn {
result.NsUserPlan = &pb.NSUserPlan{
Id: int64(userPlan.Id),
NsPlanId: int64(userPlan.PlanId),
DayFrom: userPlan.DayFrom,
DayTo: userPlan.DayTo,
PeriodUnit: userPlan.PeriodUnit,
NsPlan: &pb.NSPlan{
Id: int64(plan.Id),
Name: plan.Name,
IsOn: plan.IsOn,
},
User: nil,
}
}
}
}
// 域名排行
var hourFrom = timeutil.Format("YmdH", time.Now().Add(-23*time.Hour))
var hourTo = timeutil.Format("YmdH")
topDomainStats, err := nameservers.SharedNSRecordHourlyStatDAO.ListTopDomains(tx, userId, hourFrom, hourTo, 10)
if err != nil {
return nil, err
}
for _, stat := range topDomainStats {
domainName, err := nameservers.SharedNSDomainDAO.FindNSDomainName(tx, int64(stat.DomainId))
if err != nil {
return nil, err
}
if len(domainName) == 0 {
continue
}
result.TopNSDomainStats = append(result.TopNSDomainStats, &pb.ComposeNSUserBoardResponse_DomainStat{
NsDomainId: int64(stat.DomainId),
NsDomainName: domainName,
CountRequests: int64(stat.CountRequests),
Bytes: int64(stat.Bytes),
})
}
return result, nil
}

View File

@@ -0,0 +1,127 @@
//go:build plus
package nameservers
import (
"context"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/types"
)
// NSAccessLogService 访问日志相关服务
type NSAccessLogService struct {
services.BaseService
}
// CreateNSAccessLogs 创建访问日志
func (this *NSAccessLogService) CreateNSAccessLogs(ctx context.Context, req *pb.CreateNSAccessLogsRequest) (*pb.CreateNSAccessLogsResponse, error) {
// 校验请求
_, _, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeDNS)
if err != nil {
return nil, err
}
if len(req.NsAccessLogs) == 0 {
return &pb.CreateNSAccessLogsResponse{}, nil
}
var tx = this.NullTx()
err = models.SharedNSAccessLogDAO.CreateNSAccessLogs(tx, req.NsAccessLogs)
if err != nil {
return nil, err
}
return &pb.CreateNSAccessLogsResponse{}, nil
}
// ListNSAccessLogs 列出单页访问日志
func (this *NSAccessLogService) ListNSAccessLogs(ctx context.Context, req *pb.ListNSAccessLogsRequest) (*pb.ListNSAccessLogsResponse, error) {
// 校验请求
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查服务ID
if userId > 0 {
// TODO 检查权限
}
accessLogs, requestId, hasMore, err := models.SharedNSAccessLogDAO.ListAccessLogs(tx, req.RequestId, req.Size, req.Day, req.NsClusterId, req.NsNodeId, req.NsDomainId, req.NsRecordId, req.RecordType, req.Keyword, req.Reverse)
if err != nil {
return nil, err
}
var result = []*pb.NSAccessLog{}
for _, accessLog := range accessLogs {
a, err := accessLog.ToPB()
if err != nil {
return nil, err
}
// 线路
if len(a.NsRouteCodes) > 0 {
for _, routeCode := range a.NsRouteCodes {
route, err := nameservers.SharedNSRouteDAO.FindEnabledRouteWithCode(nil, routeCode)
if err != nil {
return nil, err
}
if route != nil {
a.NsRoutes = append(a.NsRoutes, &pb.NSRoute{
Id: types.Int64(route.Id),
IsOn: route.IsOn,
Name: route.Name,
Code: routeCode,
NsCluster: nil,
NsDomain: nil,
})
}
}
}
result = append(result, a)
}
return &pb.ListNSAccessLogsResponse{
NsAccessLogs: result,
HasMore: hasMore,
RequestId: requestId,
}, nil
}
// FindNSAccessLog 查找单个日志
func (this *NSAccessLogService) FindNSAccessLog(ctx context.Context, req *pb.FindNSAccessLogRequest) (*pb.FindNSAccessLogResponse, error) {
// 校验请求
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
accessLog, err := models.SharedNSAccessLogDAO.FindAccessLogWithRequestId(tx, req.RequestId)
if err != nil {
return nil, err
}
if accessLog == nil {
return &pb.FindNSAccessLogResponse{NsAccessLog: nil}, nil
}
// 检查权限
if userId > 0 {
// TODO
}
a, err := accessLog.ToPB()
if err != nil {
return nil, err
}
return &pb.FindNSAccessLogResponse{NsAccessLog: a}, nil
}

View File

@@ -0,0 +1,679 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package nameservers
import (
"context"
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
"github.com/iwind/TeaGo/dbs"
)
// NSClusterService 域名服务集群相关服务
type NSClusterService struct {
services.BaseService
}
// CreateNSCluster 创建集群
func (this *NSClusterService) CreateNSCluster(ctx context.Context, req *pb.CreateNSClusterRequest) (*pb.CreateNSClusterResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// SOA
var soaConfig = dnsconfigs.DefaultNSSOAConfig()
if len(req.SoaJSON) > 0 {
err = json.Unmarshal(req.SoaJSON, soaConfig)
if err != nil {
return nil, err
}
err = soaConfig.Init()
if err != nil {
return nil, errors.New("validate SOA config failed: " + err.Error())
}
}
// 校验管理员邮箱
if len(req.Email) == 0 {
return nil, errors.New("required 'email'")
}
if !utils.ValidateEmail(req.Email) {
return nil, errors.New("invalid email format '" + req.Email + "'")
}
// 校验访问日志配置
var accessLogRef = &dnsconfigs.NSAccessLogRef{}
if len(req.AccessLogJSON) > 0 {
err = json.Unmarshal(req.AccessLogJSON, accessLogRef)
if err != nil {
return nil, errors.New("invalid accessLogJSON: " + err.Error())
}
err = accessLogRef.Init()
if err != nil {
return nil, errors.New("validate accessLogJSON failed: " + err.Error())
}
}
clusterId, err := models.SharedNSClusterDAO.CreateCluster(tx, req.Name, req.Email, req.AccessLogJSON, req.Hosts, soaConfig)
if err != nil {
return nil, err
}
return &pb.CreateNSClusterResponse{NsClusterId: clusterId}, nil
}
// UpdateNSCluster 修改集群
func (this *NSClusterService) UpdateNSCluster(ctx context.Context, req *pb.UpdateNSClusterRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 校验管理员邮箱
if len(req.Email) == 0 {
return nil, errors.New("required 'email'")
}
if !utils.ValidateEmail(req.Email) {
return nil, errors.New("invalid email format '" + req.Email + "'")
}
err = models.SharedNSClusterDAO.UpdateCluster(tx, req.NsClusterId, req.Name, req.Email, req.Hosts, req.IsOn, req.TimeZone, req.AutoRemoteStart, req.DetectAgents, req.CheckingPorts)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSClusterAccessLog 查找集群访问日志配置
func (this *NSClusterService) FindNSClusterAccessLog(ctx context.Context, req *pb.FindNSClusterAccessLogRequest) (*pb.FindNSClusterAccessLogResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
accessLogJSON, err := models.SharedNSClusterDAO.FindClusterAccessLog(tx, req.NsClusterId)
if err != nil {
return nil, err
}
return &pb.FindNSClusterAccessLogResponse{AccessLogJSON: accessLogJSON}, nil
}
// UpdateNSClusterAccessLog 修改集群访问日志配置
func (this *NSClusterService) UpdateNSClusterAccessLog(ctx context.Context, req *pb.UpdateNSClusterAccessLogRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 校验访问日志配置
var accessLogRef = &dnsconfigs.NSAccessLogRef{}
if len(req.AccessLogJSON) > 0 {
err = json.Unmarshal(req.AccessLogJSON, accessLogRef)
if err != nil {
return nil, errors.New("invalid accessLogJSON: " + err.Error())
}
err = accessLogRef.Init()
if err != nil {
return nil, errors.New("validate accessLogJSON failed: " + err.Error())
}
}
err = models.SharedNSClusterDAO.UpdateClusterAccessLog(tx, req.NsClusterId, req.AccessLogJSON)
if err != nil {
return nil, err
}
return this.Success()
}
// DeleteNSCluster 删除集群
func (this *NSClusterService) DeleteNSCluster(ctx context.Context, req *pb.DeleteNSCluster) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = models.SharedNSClusterDAO.DisableNSCluster(tx, req.NsClusterId)
if err != nil {
return nil, err
}
// 删除任务
err = models.SharedNodeTaskDAO.DeleteAllClusterTasks(tx, nodeconfigs.NodeRoleDNS, req.NsClusterId)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSCluster 查找单个可用集群信息
func (this *NSClusterService) FindNSCluster(ctx context.Context, req *pb.FindNSClusterRequest) (*pb.FindNSClusterResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
cluster, err := models.SharedNSClusterDAO.FindEnabledNSCluster(tx, req.NsClusterId)
if err != nil {
return nil, err
}
if cluster == nil {
return &pb.FindNSClusterResponse{NsCluster: nil}, nil
}
return &pb.FindNSClusterResponse{
NsCluster: &pb.NSCluster{
Id: int64(cluster.Id),
IsOn: cluster.IsOn,
Name: cluster.Name,
Email: cluster.Email,
Hosts: cluster.DecodeHosts(),
InstallDir: cluster.InstallDir,
TcpJSON: cluster.Tcp,
TlsJSON: cluster.Tls,
UdpJSON: cluster.Udp,
DohJSON: cluster.Doh,
TimeZone: cluster.TimeZone,
AutoRemoteStart: cluster.AutoRemoteStart,
AnswerJSON: cluster.Answer,
SoaJSON: cluster.Soa,
DetectAgents: cluster.DetectAgents,
CheckingPorts: cluster.CheckingPorts,
},
}, nil
}
// CountAllNSClusters 计算所有可用集群的数量
func (this *NSClusterService) CountAllNSClusters(ctx context.Context, req *pb.CountAllNSClustersRequest) (*pb.RPCCountResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
count, err := models.SharedNSClusterDAO.CountAllEnabledClusters(tx)
if err != nil {
return nil, err
}
return this.SuccessCount(count)
}
// ListNSClusters 列出单页可用集群
func (this *NSClusterService) ListNSClusters(ctx context.Context, req *pb.ListNSClustersRequest) (*pb.ListNSClustersResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
clusters, err := models.SharedNSClusterDAO.ListEnabledClusters(tx, req.Offset, req.Size)
if err != nil {
return nil, err
}
var pbClusters = []*pb.NSCluster{}
for _, cluster := range clusters {
pbClusters = append(pbClusters, &pb.NSCluster{
Id: int64(cluster.Id),
IsOn: cluster.IsOn,
Name: cluster.Name,
Hosts: cluster.DecodeHosts(),
InstallDir: cluster.InstallDir,
})
}
return &pb.ListNSClustersResponse{NsClusters: pbClusters}, nil
}
// FindAllNSClusters 查找所有可用集群
func (this *NSClusterService) FindAllNSClusters(ctx context.Context, req *pb.FindAllNSClustersRequest) (*pb.FindAllNSClustersResponse, error) {
_, _, err := this.ValidateAdminAndUser(ctx, false)
if err != nil {
return nil, err
}
var tx = this.NullTx()
clusters, err := models.SharedNSClusterDAO.FindAllEnabledClusters(tx)
if err != nil {
return nil, err
}
var pbClusters = []*pb.NSCluster{}
for _, cluster := range clusters {
pbClusters = append(pbClusters, &pb.NSCluster{
Id: int64(cluster.Id),
IsOn: cluster.IsOn,
Name: cluster.Name,
InstallDir: cluster.InstallDir,
})
}
return &pb.FindAllNSClustersResponse{NsClusters: pbClusters}, nil
}
// UpdateNSClusterRecursionConfig 设置递归DNS配置
func (this *NSClusterService) UpdateNSClusterRecursionConfig(ctx context.Context, req *pb.UpdateNSClusterRecursionConfigRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
// 校验配置
var config = &dnsconfigs.NSRecursionConfig{}
err = json.Unmarshal(req.RecursionJSON, config)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = models.SharedNSClusterDAO.UpdateRecursion(tx, req.NsClusterId, req.RecursionJSON)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSClusterRecursionConfig 读取递归DNS配置
func (this *NSClusterService) FindNSClusterRecursionConfig(ctx context.Context, req *pb.FindNSClusterRecursionConfigRequest) (*pb.FindNSClusterRecursionConfigResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
recursion, err := models.SharedNSClusterDAO.FindClusterRecursion(tx, req.NsClusterId)
if err != nil {
return nil, err
}
return &pb.FindNSClusterRecursionConfigResponse{
RecursionJSON: recursion,
}, nil
}
// FindNSClusterTCPConfig 查找集群的TCP设置
func (this *NSClusterService) FindNSClusterTCPConfig(ctx context.Context, req *pb.FindNSClusterTCPConfigRequest) (*pb.FindNSClusterTCPConfigResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
tcpJSON, err := models.SharedNSClusterDAO.FindClusterTCP(tx, req.NsClusterId)
if err != nil {
return nil, err
}
return &pb.FindNSClusterTCPConfigResponse{
TcpJSON: tcpJSON,
}, nil
}
// UpdateNSClusterTCP 修改集群的TCP设置
func (this *NSClusterService) UpdateNSClusterTCP(ctx context.Context, req *pb.UpdateNSClusterTCPRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var config = &serverconfigs.TCPProtocolConfig{}
err = json.Unmarshal(req.TcpJSON, config)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = models.SharedNSClusterDAO.UpdateClusterTCP(tx, req.NsClusterId, config)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSClusterTLSConfig 查找集群的TLS设置
func (this *NSClusterService) FindNSClusterTLSConfig(ctx context.Context, req *pb.FindNSClusterTLSConfigRequest) (*pb.FindNSClusterTLSConfigResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
tlsJSON, err := models.SharedNSClusterDAO.FindClusterTLS(tx, req.NsClusterId)
if err != nil {
return nil, err
}
return &pb.FindNSClusterTLSConfigResponse{
TlsJSON: tlsJSON,
}, nil
}
// UpdateNSClusterTLS 修改集群的TLS设置
func (this *NSClusterService) UpdateNSClusterTLS(ctx context.Context, req *pb.UpdateNSClusterTLSRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var config = &serverconfigs.TLSProtocolConfig{}
err = json.Unmarshal(req.TlsJSON, config)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = models.SharedNSClusterDAO.UpdateClusterTLS(tx, req.NsClusterId, config)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSClusterUDPConfig 查找集群的UDP设置
func (this *NSClusterService) FindNSClusterUDPConfig(ctx context.Context, req *pb.FindNSClusterUDPConfigRequest) (*pb.FindNSClusterUDPConfigResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
udpJSON, err := models.SharedNSClusterDAO.FindClusterUDP(tx, req.NsClusterId)
if err != nil {
return nil, err
}
return &pb.FindNSClusterUDPConfigResponse{
UdpJSON: udpJSON,
}, nil
}
// UpdateNSClusterUDP 修改集群的UDP设置
func (this *NSClusterService) UpdateNSClusterUDP(ctx context.Context, req *pb.UpdateNSClusterUDPRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var config = &serverconfigs.UDPProtocolConfig{}
err = json.Unmarshal(req.UdpJSON, config)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = models.SharedNSClusterDAO.UpdateClusterUDP(tx, req.NsClusterId, config)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSClusterDoHConfig 查找集群的DoH设置
func (this *NSClusterService) FindNSClusterDoHConfig(ctx context.Context, req *pb.FindNSClusterDoHConfigRequest) (*pb.FindNSClusterDoHConfigResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
dohJSON, err := models.SharedNSClusterDAO.FindClusterDoH(tx, req.NsClusterId)
if err != nil {
return nil, err
}
return &pb.FindNSClusterDoHConfigResponse{
DohJSON: dohJSON,
}, nil
}
// UpdateNSClusterDoH 修改集群的DoH设置
func (this *NSClusterService) UpdateNSClusterDoH(ctx context.Context, req *pb.UpdateNSClusterDoHRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var config = dnsconfigs.NewNSDoHConfig()
err = json.Unmarshal(req.DohJSON, config)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = models.SharedNSClusterDAO.UpdateClusterDoH(tx, req.NsClusterId, config)
if err != nil {
return nil, err
}
return this.Success()
}
// CountAllNSClustersWithSSLCertId 计算使用某个SSL证书的集群数量
func (this *NSClusterService) CountAllNSClustersWithSSLCertId(ctx context.Context, req *pb.CountAllNSClustersWithSSLCertIdRequest) (*pb.RPCCountResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
policyIds, err := models.SharedSSLPolicyDAO.FindAllEnabledPolicyIdsWithCertId(tx, req.SslCertId)
if err != nil {
return nil, err
}
if len(policyIds) == 0 {
return this.SuccessCount(0)
}
count, err := models.SharedNSClusterDAO.CountAllClustersWithSSLPolicyIds(tx, policyIds)
if err != nil {
return nil, err
}
return this.SuccessCount(count)
}
// FindNSClusterDDoSProtection 获取集群的DDoS设置
func (this *NSClusterService) FindNSClusterDDoSProtection(ctx context.Context, req *pb.FindNSClusterDDoSProtectionRequest) (*pb.FindNSClusterDDoSProtectionResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx *dbs.Tx
ddosProtection, err := models.SharedNSClusterDAO.FindClusterDDoSProtection(tx, req.NsClusterId)
if err != nil {
return nil, err
}
if ddosProtection == nil {
ddosProtection = ddosconfigs.DefaultProtectionConfig()
}
ddosProtectionJSON, err := json.Marshal(ddosProtection)
if err != nil {
return nil, err
}
var result = &pb.FindNSClusterDDoSProtectionResponse{
DdosProtectionJSON: ddosProtectionJSON,
}
return result, nil
}
// UpdateNSClusterDDoSProtection 修改集群的DDoS设置
func (this *NSClusterService) UpdateNSClusterDDoSProtection(ctx context.Context, req *pb.UpdateNSClusterDDoSProtectionRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var ddosProtection = &ddosconfigs.ProtectionConfig{}
err = json.Unmarshal(req.DdosProtectionJSON, ddosProtection)
if err != nil {
return nil, err
}
var tx *dbs.Tx
err = models.SharedNSClusterDAO.UpdateClusterDDoSProtection(tx, req.NsClusterId, ddosProtection)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSClusterHosts 查找NS集群的主机地址
func (this *NSClusterService) FindNSClusterHosts(ctx context.Context, req *pb.FindNSClusterHostsRequest) (*pb.FindNSClusterHostsResponse, error) {
_, _, err := this.ValidateAdminAndUser(ctx, false)
if err != nil {
return nil, err
}
var tx = this.NullTx()
hosts, err := models.SharedNSClusterDAO.FindClusterHosts(tx, req.NsClusterId)
if err != nil {
return nil, err
}
return &pb.FindNSClusterHostsResponse{
Hosts: hosts,
}, nil
}
// FindAvailableNSHostsForUser 查找用户可以使用的主机地址
func (this *NSClusterService) FindAvailableNSHostsForUser(ctx context.Context, req *pb.FindAvailableNSHostsForUserRequest) (*pb.FindAvailableNSHostsForUserResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, false)
if err != nil {
return nil, err
}
if userId > 0 {
req.UserId = userId
}
if req.UserId <= 0 {
return &pb.FindAvailableNSHostsForUserResponse{
Hosts: nil,
}, nil
}
// 所属集群
var tx = this.NullTx()
userConfig, err := models.SharedSysSettingDAO.ReadNSUserConfig(tx)
if err != nil {
return nil, err
}
if userConfig == nil || userConfig.DefaultClusterId <= 0 {
return &pb.FindAvailableNSHostsForUserResponse{
Hosts: nil,
}, nil
}
hosts, err := models.SharedNSClusterDAO.FindClusterHosts(tx, userConfig.DefaultClusterId)
if err != nil {
return nil, err
}
return &pb.FindAvailableNSHostsForUserResponse{
Hosts: hosts,
}, nil
}
// FindNSClusterAnswerConfig 查找应答模式
func (this *NSClusterService) FindNSClusterAnswerConfig(ctx context.Context, req *pb.FindNSClusterAnswerConfigRequest) (*pb.FindNSClusterAnswerConfigResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
config, err := models.SharedNSClusterDAO.FindClusterAnswer(tx, req.NsClusterId)
if err != nil {
return nil, err
}
configJSON, err := json.Marshal(config)
if err != nil {
return nil, err
}
return &pb.FindNSClusterAnswerConfigResponse{
AnswerJSON: configJSON,
}, nil
}
// UpdateNSClusterAnswerConfig 设置应答模式
func (this *NSClusterService) UpdateNSClusterAnswerConfig(ctx context.Context, req *pb.UpdateNSClusterAnswerConfigRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var config = dnsconfigs.DefaultNSAnswerConfig()
if len(req.AnswerJSON) > 0 {
err = json.Unmarshal(req.AnswerJSON, config)
if err != nil {
return nil, err
}
}
var tx = this.NullTx()
err = models.SharedNSClusterDAO.UpdateClusterAnswer(tx, req.NsClusterId, config)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSClusterSOAConfig 查询SOA配置
func (this *NSClusterService) FindNSClusterSOAConfig(ctx context.Context, req *pb.FindNSClusterSOAConfigRequest) (*pb.FindNSClusterSOAConfigResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
config, err := models.SharedNSClusterDAO.FindClusterSOA(tx, req.NsClusterId)
if err != nil {
return nil, err
}
configJSON, err := json.Marshal(config)
if err != nil {
return nil, err
}
return &pb.FindNSClusterSOAConfigResponse{
SoaJSON: configJSON,
}, nil
}
// UpdateNSClusterSOAConfig 修改SOA配置
func (this *NSClusterService) UpdateNSClusterSOAConfig(ctx context.Context, req *pb.UpdateNSClusterSOAConfigRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var config = dnsconfigs.DefaultNSSOAConfig()
if len(req.SoaJSON) > 0 {
err = json.Unmarshal(req.SoaJSON, config)
if err != nil {
return nil, err
}
}
var tx = this.NullTx()
err = models.SharedNSClusterDAO.UpdateClusterSOA(tx, req.NsClusterId, config)
if err != nil {
return nil, err
}
return this.Success()
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,194 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package nameservers
import (
"context"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
// NSDomainGroupService 域名分组服务
type NSDomainGroupService struct {
services.BaseService
}
// CreateNSDomainGroup 创建分组
func (this *NSDomainGroupService) CreateNSDomainGroup(ctx context.Context, req *pb.CreateNSDomainGroupRequest) (*pb.CreateNSDomainGroupResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
groupId, err := nameservers.SharedNSDomainGroupDAO.CreateGroup(tx, userId, req.Name)
if err != nil {
return nil, err
}
return &pb.CreateNSDomainGroupResponse{
NsDomainGroupId: groupId,
}, nil
}
// UpdateNSDomainGroup 修改分组
func (this *NSDomainGroupService) UpdateNSDomainGroup(ctx context.Context, req *pb.UpdateNSDomainGroupRequest) (*pb.RPCSuccess, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSDomainGroupDAO.CheckUserGroup(tx, userId, req.NsDomainGroupId)
if err != nil {
return nil, err
}
}
err = nameservers.SharedNSDomainGroupDAO.UpdateGroup(tx, req.NsDomainGroupId, req.Name, req.IsOn)
if err != nil {
return nil, err
}
return this.Success()
}
// DeleteNSDomainGroup 删除分组
func (this *NSDomainGroupService) DeleteNSDomainGroup(ctx context.Context, req *pb.DeleteNSDomainGroupRequest) (*pb.RPCSuccess, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSDomainGroupDAO.CheckUserGroup(tx, userId, req.NsDomainGroupId)
if err != nil {
return nil, err
}
}
err = nameservers.SharedNSDomainGroupDAO.DisableNSDomainGroup(tx, req.NsDomainGroupId)
if err != nil {
return nil, err
}
return this.Success()
}
// FindAllNSDomainGroups 查询所有分组
func (this *NSDomainGroupService) FindAllNSDomainGroups(ctx context.Context, req *pb.FindAllNSDomainGroupsRequest) (*pb.FindAllNSDomainGroupsResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if userId > 0 {
req.UserId = userId
}
groups, err := nameservers.SharedNSDomainGroupDAO.FindAllGroups(tx, req.UserId)
if err != nil {
return nil, err
}
var pbGroups = []*pb.NSDomainGroup{}
for _, group := range groups {
pbGroups = append(pbGroups, &pb.NSDomainGroup{
Id: int64(group.Id),
Name: group.Name,
IsOn: group.IsOn,
UserId: int64(group.UserId),
})
}
return &pb.FindAllNSDomainGroupsResponse{
NsDomainGroups: pbGroups,
}, nil
}
// CountAllAvailableNSDomainGroups 查询可用分组数量
func (this *NSDomainGroupService) CountAllAvailableNSDomainGroups(ctx context.Context, req *pb.CountAllAvailableNSDomainGroupsRequest) (*pb.RPCCountResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
if userId > 0 {
req.UserId = userId
}
var tx = this.NullTx()
count, err := nameservers.SharedNSDomainGroupDAO.CountAllAvailableGroups(tx, req.UserId)
if err != nil {
return nil, err
}
return this.SuccessCount(count)
}
// FindAllAvailableNSDomainGroups 查询所有分组
func (this *NSDomainGroupService) FindAllAvailableNSDomainGroups(ctx context.Context, req *pb.FindAllAvailableNSDomainGroupsRequest) (*pb.FindAllAvailableNSDomainGroupsResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
if userId > 0 {
req.UserId = userId
}
var tx = this.NullTx()
groups, err := nameservers.SharedNSDomainGroupDAO.FindAllAvailableGroups(tx, req.UserId)
if err != nil {
return nil, err
}
var pbGroups = []*pb.NSDomainGroup{}
for _, group := range groups {
pbGroups = append(pbGroups, &pb.NSDomainGroup{
Id: int64(group.Id),
Name: group.Name,
IsOn: group.IsOn,
UserId: int64(group.UserId),
})
}
return &pb.FindAllAvailableNSDomainGroupsResponse{
NsDomainGroups: pbGroups,
}, nil
}
// FindNSDomainGroup 查找单个分组
func (this *NSDomainGroupService) FindNSDomainGroup(ctx context.Context, req *pb.FindNSDomainGroupRequest) (*pb.FindNSDomainGroupResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
group, err := nameservers.SharedNSDomainGroupDAO.FindEnabledNSDomainGroup(tx, req.NsDomainGroupId)
if err != nil {
return nil, err
}
if group == nil {
return &pb.FindNSDomainGroupResponse{
NsDomainGroup: nil,
}, nil
}
if int64(group.UserId) != userId {
return &pb.FindNSDomainGroupResponse{
NsDomainGroup: nil,
}, nil
}
return &pb.FindNSDomainGroupResponse{
NsDomainGroup: &pb.NSDomainGroup{
Id: int64(group.Id),
Name: group.Name,
IsOn: group.IsOn,
UserId: int64(group.UserId),
},
}, nil
}

View File

@@ -0,0 +1,225 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package nameservers
import (
"context"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
// NSKeyService NS密钥相关服务
type NSKeyService struct {
services.BaseService
}
// CreateNSKey 创建密钥
func (this *NSKeyService) CreateNSKey(ctx context.Context, req *pb.CreateNSKeyRequest) (*pb.CreateNSKeyResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查用户权限
if userId > 0 {
err = nameservers.SharedNSDomainDAO.CheckUserDomain(tx, userId, req.NsDomainId)
if err != nil {
return nil, err
}
}
keyId, err := nameservers.SharedNSKeyDAO.CreateKey(tx, req.NsDomainId, req.NsZoneId, req.Name, req.Algo, req.Secret, req.SecretType)
if err != nil {
return nil, err
}
return &pb.CreateNSKeyResponse{NsKeyId: keyId}, nil
}
// UpdateNSKey 修改密钥
func (this *NSKeyService) UpdateNSKey(ctx context.Context, req *pb.UpdateNSKeyRequest) (*pb.RPCSuccess, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查用户权限
if userId > 0 {
err = nameservers.SharedNSKeyDAO.CheckUserKey(tx, userId, req.NsKeyId)
if err != nil {
return nil, err
}
}
err = nameservers.SharedNSKeyDAO.UpdateKey(tx, req.NsKeyId, req.Name, req.Algo, req.Secret, req.SecretType, req.IsOn)
if err != nil {
return nil, err
}
return this.Success()
}
// DeleteNSKey 删除密钥
func (this *NSKeyService) DeleteNSKey(ctx context.Context, req *pb.DeleteNSKeyRequest) (*pb.RPCSuccess, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查用户权限
if userId > 0 {
err = nameservers.SharedNSKeyDAO.CheckUserKey(tx, userId, req.NsKeyId)
if err != nil {
return nil, err
}
}
err = nameservers.SharedNSKeyDAO.DisableNSKey(tx, req.NsKeyId)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSKey 查找单个密钥
func (this *NSKeyService) FindNSKey(ctx context.Context, req *pb.FindNSKeyRequest) (*pb.FindNSKeyResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查用户权限
if userId > 0 {
err = nameservers.SharedNSKeyDAO.CheckUserKey(tx, userId, req.NsKeyId)
if err != nil {
return nil, err
}
}
key, err := nameservers.SharedNSKeyDAO.FindEnabledNSKey(tx, req.NsKeyId)
if err != nil {
return nil, err
}
if key == nil {
return &pb.FindNSKeyResponse{NsKey: nil}, nil
}
return &pb.FindNSKeyResponse{
NsKey: &pb.NSKey{
Id: int64(key.Id),
IsOn: key.IsOn,
Name: key.Name,
Algo: key.Algo,
Secret: key.Secret,
SecretType: key.SecretType,
},
}, nil
}
// CountAllNSKeys 计算密钥数量
func (this *NSKeyService) CountAllNSKeys(ctx context.Context, req *pb.CountAllNSKeysRequest) (*pb.RPCCountResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查用户权限
if userId > 0 {
err = nameservers.SharedNSDomainDAO.CheckUserDomain(tx, userId, req.NsDomainId)
if err != nil {
return nil, err
}
}
count, err := nameservers.SharedNSKeyDAO.CountEnabledKeys(tx, req.NsDomainId, req.NsZoneId)
if err != nil {
return nil, err
}
return this.SuccessCount(count)
}
// ListNSKeys 列出单页密钥
func (this *NSKeyService) ListNSKeys(ctx context.Context, req *pb.ListNSKeysRequest) (*pb.ListNSKeysResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查用户权限
if userId > 0 {
err = nameservers.SharedNSDomainDAO.CheckUserDomain(tx, userId, req.NsDomainId)
if err != nil {
return nil, err
}
}
keys, err := nameservers.SharedNSKeyDAO.ListEnabledKeys(tx, req.NsDomainId, req.NsZoneId, req.Offset, req.Size)
if err != nil {
return nil, err
}
var pbKeys = []*pb.NSKey{}
for _, key := range keys {
pbKeys = append(pbKeys, &pb.NSKey{
Id: int64(key.Id),
IsOn: key.IsOn,
Name: key.Name,
Algo: key.Algo,
Secret: key.Secret,
SecretType: key.SecretType,
})
}
return &pb.ListNSKeysResponse{NsKeys: pbKeys}, nil
}
// ListNSKeysAfterVersion 根据版本列出一组密钥
func (this *NSKeyService) ListNSKeysAfterVersion(ctx context.Context, req *pb.ListNSKeysAfterVersionRequest) (*pb.ListNSKeysAfterVersionResponse, error) {
_, err := this.ValidateNSNode(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if req.Size <= 0 {
req.Size = 2000
}
keys, err := nameservers.SharedNSKeyDAO.ListKeysAfterVersion(tx, req.Version, req.Size)
if err != nil {
return nil, err
}
var pbKeys = []*pb.NSKey{}
for _, key := range keys {
var pbDomain *pb.NSDomain
var pbZone *pb.NSZone
if key.DomainId > 0 {
pbDomain = &pb.NSDomain{Id: int64(key.DomainId)}
}
if key.ZoneId > 0 {
pbZone = &pb.NSZone{Id: int64(key.ZoneId)}
}
pbKeys = append(pbKeys, &pb.NSKey{
Id: int64(key.Id),
IsOn: key.IsOn,
Name: "",
Algo: key.Algo,
Secret: key.Secret,
SecretType: key.SecretType,
IsDeleted: key.State == nameservers.NSKeyStateDisabled,
Version: int64(key.Version),
NsDomain: pbDomain,
NsZone: pbZone,
})
}
return &pb.ListNSKeysAfterVersionResponse{NsKeys: pbKeys}, nil
}

View File

@@ -0,0 +1,740 @@
// Copyright 2021-2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package nameservers
import (
"context"
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/installers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/logs"
stringutil "github.com/iwind/TeaGo/utils/string"
"io"
"path/filepath"
"time"
)
// NSNodeService 域名服务器节点服务
type NSNodeService struct {
services.BaseService
}
// FindAllNSNodesWithNSClusterId 根据集群查找所有节点
func (this *NSNodeService) FindAllNSNodesWithNSClusterId(ctx context.Context, req *pb.FindAllNSNodesWithNSClusterIdRequest) (*pb.FindAllNSNodesWithNSClusterIdResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
nodes, err := models.SharedNSNodeDAO.FindAllEnabledNodesWithClusterId(tx, req.NsClusterId)
if err != nil {
return nil, err
}
pbNodes := []*pb.NSNode{}
for _, node := range nodes {
pbNodes = append(pbNodes, &pb.NSNode{
Id: int64(node.Id),
Name: node.Name,
IsOn: node.IsOn,
UniqueId: node.UniqueId,
Secret: node.Secret,
IsInstalled: node.IsInstalled,
InstallDir: node.InstallDir,
IsUp: node.IsUp,
ConnectedAPINodeIds: node.DecodeConnectedAPINodes(),
NsCluster: nil,
})
}
return &pb.FindAllNSNodesWithNSClusterIdResponse{NsNodes: pbNodes}, nil
}
// CountAllNSNodes 所有可用的节点数量
func (this *NSNodeService) CountAllNSNodes(ctx context.Context, req *pb.CountAllNSNodesRequest) (*pb.RPCCountResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
count, err := models.SharedNSNodeDAO.CountAllEnabledNodes(tx)
if err != nil {
return nil, err
}
return this.SuccessCount(count)
}
// CountAllNSNodesMatch 计算匹配的节点数量
func (this *NSNodeService) CountAllNSNodesMatch(ctx context.Context, req *pb.CountAllNSNodesMatchRequest) (*pb.RPCCountResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
count, err := models.SharedNSNodeDAO.CountAllEnabledNodesMatch(tx, req.NsClusterId, configutils.ToBoolState(req.InstallState), configutils.ToBoolState(req.ActiveState), req.Keyword)
if err != nil {
return nil, err
}
return this.SuccessCount(count)
}
// ListNSNodesMatch 列出单页节点
func (this *NSNodeService) ListNSNodesMatch(ctx context.Context, req *pb.ListNSNodesMatchRequest) (*pb.ListNSNodesMatchResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
nodes, err := models.SharedNSNodeDAO.ListAllEnabledNodesMatch(tx, req.NsClusterId, configutils.ToBoolState(req.InstallState), configutils.ToBoolState(req.ActiveState), req.Keyword, req.Offset, req.Size)
if err != nil {
return nil, err
}
pbNodes := []*pb.NSNode{}
for _, node := range nodes {
// 安装信息
installStatus, err := node.DecodeInstallStatus()
if err != nil {
return nil, err
}
installStatusResult := &pb.NodeInstallStatus{}
if installStatus != nil {
installStatusResult = &pb.NodeInstallStatus{
IsRunning: installStatus.IsRunning,
IsFinished: installStatus.IsFinished,
IsOk: installStatus.IsOk,
Error: installStatus.Error,
ErrorCode: installStatus.ErrorCode,
UpdatedAt: installStatus.UpdatedAt,
}
}
pbNodes = append(pbNodes, &pb.NSNode{
Id: int64(node.Id),
Name: node.Name,
IsOn: node.IsOn,
UniqueId: node.UniqueId,
Secret: node.Secret,
IsActive: node.IsActive,
IsInstalled: node.IsInstalled,
InstallDir: node.InstallDir,
IsUp: node.IsUp,
StatusJSON: node.Status,
InstallStatus: installStatusResult,
NsCluster: nil,
})
}
return &pb.ListNSNodesMatchResponse{NsNodes: pbNodes}, nil
}
// CountAllUpgradeNSNodesWithNSClusterId 计算需要升级的节点数量
func (this *NSNodeService) CountAllUpgradeNSNodesWithNSClusterId(ctx context.Context, req *pb.CountAllUpgradeNSNodesWithNSClusterIdRequest) (*pb.RPCCountResponse, error) {
// 校验请求
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
deployFiles := installers.SharedDeployManager.LoadNSNodeFiles()
total := int64(0)
for _, deployFile := range deployFiles {
count, err := models.SharedNSNodeDAO.CountAllLowerVersionNodesWithClusterId(tx, req.NsClusterId, deployFile.OS, deployFile.Arch, deployFile.Version)
if err != nil {
return nil, err
}
total += count
}
return this.SuccessCount(total)
}
// CreateNSNode 创建节点
func (this *NSNodeService) CreateNSNode(ctx context.Context, req *pb.CreateNSNodeRequest) (*pb.CreateNSNodeResponse, error) {
adminId, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
nodeId, err := models.SharedNSNodeDAO.CreateNode(tx, adminId, req.Name, req.NodeClusterId)
if err != nil {
return nil, err
}
// 增加认证相关
if req.NodeLogin != nil {
_, err = models.SharedNodeLoginDAO.CreateNodeLogin(tx, nodeconfigs.NodeRoleDNS, nodeId, req.NodeLogin.Name, req.NodeLogin.Type, req.NodeLogin.Params)
if err != nil {
return nil, err
}
}
return &pb.CreateNSNodeResponse{
NsNodeId: nodeId,
}, nil
}
// DeleteNSNode 删除节点
func (this *NSNodeService) DeleteNSNode(ctx context.Context, req *pb.DeleteNSNodeRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = models.SharedNSNodeDAO.DisableNSNode(tx, req.NsNodeId)
if err != nil {
return nil, err
}
// 删除任务
err = models.SharedNodeTaskDAO.DeleteNodeTasks(tx, nodeconfigs.NodeRoleDNS, req.NsNodeId)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSNode 查询单个节点信息
func (this *NSNodeService) FindNSNode(ctx context.Context, req *pb.FindNSNodeRequest) (*pb.FindNSNodeResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
node, err := models.SharedNSNodeDAO.FindEnabledNSNode(tx, req.NsNodeId)
if err != nil {
return nil, err
}
if node == nil {
return &pb.FindNSNodeResponse{NsNode: nil}, nil
}
// 集群信息
clusterName, err := models.SharedNSClusterDAO.FindEnabledNSClusterName(tx, int64(node.ClusterId))
if err != nil {
return nil, err
}
// 认证信息
login, err := models.SharedNodeLoginDAO.FindEnabledNodeLoginWithNodeId(tx, nodeconfigs.NodeRoleDNS, req.NsNodeId)
if err != nil {
return nil, err
}
var respLogin *pb.NodeLogin = nil
if login != nil {
respLogin = &pb.NodeLogin{
Id: int64(login.Id),
Name: login.Name,
Type: login.Type,
Params: login.Params,
}
}
// 安装信息
installStatus, err := node.DecodeInstallStatus()
if err != nil {
return nil, err
}
var installStatusResult = &pb.NodeInstallStatus{}
if installStatus != nil {
installStatusResult = &pb.NodeInstallStatus{
IsRunning: installStatus.IsRunning,
IsFinished: installStatus.IsFinished,
IsOk: installStatus.IsOk,
Error: installStatus.Error,
ErrorCode: installStatus.ErrorCode,
UpdatedAt: installStatus.UpdatedAt,
}
}
return &pb.FindNSNodeResponse{NsNode: &pb.NSNode{
Id: int64(node.Id),
Name: node.Name,
StatusJSON: node.Status,
UniqueId: node.UniqueId,
Secret: node.Secret,
IsInstalled: node.IsInstalled,
InstallDir: node.InstallDir,
ApiNodeAddrsJSON: node.ApiNodeAddrs,
NsCluster: &pb.NSCluster{
Id: int64(node.ClusterId),
Name: clusterName,
},
InstallStatus: installStatusResult,
IsOn: node.IsOn,
IsActive: node.IsActive,
NodeLogin: respLogin,
}}, nil
}
// UpdateNSNode 修改节点
func (this *NSNodeService) UpdateNSNode(ctx context.Context, req *pb.UpdateNSNodeRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = models.SharedNSNodeDAO.UpdateNode(tx, req.NsNodeId, req.Name, req.NsClusterId, req.IsOn)
if err != nil {
return nil, err
}
// 登录信息
if req.NodeLogin == nil {
err = models.SharedNodeLoginDAO.DisableNodeLogins(tx, nodeconfigs.NodeRoleDNS, req.NsNodeId)
if err != nil {
return nil, err
}
} else {
if req.NodeLogin.Id > 0 {
err = models.SharedNodeLoginDAO.UpdateNodeLogin(tx, req.NodeLogin.Id, req.NodeLogin.Name, req.NodeLogin.Type, req.NodeLogin.Params)
if err != nil {
return nil, err
}
} else {
_, err = models.SharedNodeLoginDAO.CreateNodeLogin(tx, nodeconfigs.NodeRoleDNS, req.NsNodeId, req.NodeLogin.Name, req.NodeLogin.Type, req.NodeLogin.Params)
if err != nil {
return nil, err
}
}
}
return this.Success()
}
// InstallNSNode 安装节点
func (this *NSNodeService) InstallNSNode(ctx context.Context, req *pb.InstallNSNodeRequest) (*pb.InstallNSNodeResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
goman.New(func() {
err = installers.SharedNSNodeQueue().InstallNodeProcess(req.NsNodeId, false)
if err != nil {
logs.Println("[RPC]install dns node:" + err.Error())
}
})
return &pb.InstallNSNodeResponse{}, nil
}
// FindNSNodeInstallStatus 读取节点安装状态
func (this *NSNodeService) FindNSNodeInstallStatus(ctx context.Context, req *pb.FindNSNodeInstallStatusRequest) (*pb.FindNSNodeInstallStatusResponse, error) {
// 校验请求
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
installStatus, err := models.SharedNSNodeDAO.FindNodeInstallStatus(tx, req.NsNodeId)
if err != nil {
return nil, err
}
if installStatus == nil {
return &pb.FindNSNodeInstallStatusResponse{InstallStatus: nil}, nil
}
pbInstallStatus := &pb.NodeInstallStatus{
IsRunning: installStatus.IsRunning,
IsFinished: installStatus.IsFinished,
IsOk: installStatus.IsOk,
Error: installStatus.Error,
ErrorCode: installStatus.ErrorCode,
UpdatedAt: installStatus.UpdatedAt,
}
return &pb.FindNSNodeInstallStatusResponse{InstallStatus: pbInstallStatus}, nil
}
// UpdateNSNodeIsInstalled 修改节点安装状态
func (this *NSNodeService) UpdateNSNodeIsInstalled(ctx context.Context, req *pb.UpdateNSNodeIsInstalledRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = models.SharedNSNodeDAO.UpdateNodeIsInstalled(tx, req.NsNodeId, req.IsInstalled)
if err != nil {
return nil, err
}
return this.Success()
}
// UpdateNSNodeStatus 更新节点状态
func (this *NSNodeService) UpdateNSNodeStatus(ctx context.Context, req *pb.UpdateNSNodeStatusRequest) (*pb.RPCSuccess, error) {
// 校验节点
_, nodeId, err := this.ValidateNodeId(ctx, rpcutils.UserTypeDNS)
if err != nil {
return nil, err
}
if req.NodeId > 0 {
nodeId = req.NodeId
}
if nodeId <= 0 {
return nil, errors.New("'nodeId' should be greater than 0")
}
var tx = this.NullTx()
// 修改时间戳
var nodeStatus = &nodeconfigs.NodeStatus{}
err = json.Unmarshal(req.StatusJSON, nodeStatus)
if err != nil {
return nil, errors.New("decode node status json failed: " + err.Error())
}
nodeStatus.UpdatedAt = time.Now().Unix()
// 保存
err = models.SharedNSNodeDAO.UpdateNodeStatus(tx, nodeId, nodeStatus)
if err != nil {
return nil, err
}
return this.Success()
}
// FindCurrentNSNodeConfig 获取当前节点信息
func (this *NSNodeService) FindCurrentNSNodeConfig(ctx context.Context, req *pb.FindCurrentNSNodeConfigRequest) (*pb.FindCurrentNSNodeConfigResponse, error) {
// 校验节点
_, nodeId, err := this.ValidateNodeId(ctx, rpcutils.UserTypeDNS)
if err != nil {
return nil, err
}
var tx = this.NullTx()
config, err := models.SharedNSNodeDAO.ComposeNodeConfig(tx, nodeId)
if err != nil {
return nil, err
}
if config == nil {
return &pb.FindCurrentNSNodeConfigResponse{NsNodeJSON: nil}, nil
}
configJSON, err := json.Marshal(config)
if err != nil {
return nil, err
}
return &pb.FindCurrentNSNodeConfigResponse{NsNodeJSON: configJSON}, nil
}
// CheckNSNodeLatestVersion 检查新版本
func (this *NSNodeService) CheckNSNodeLatestVersion(ctx context.Context, req *pb.CheckNSNodeLatestVersionRequest) (*pb.CheckNSNodeLatestVersionResponse, error) {
_, _, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeAdmin, rpcutils.UserTypeDNS)
if err != nil {
return nil, err
}
deployFiles := installers.SharedDeployManager.LoadNSNodeFiles()
for _, file := range deployFiles {
if file.OS == req.Os && file.Arch == req.Arch && stringutil.VersionCompare(file.Version, req.CurrentVersion) > 0 {
return &pb.CheckNSNodeLatestVersionResponse{
HasNewVersion: true,
NewVersion: file.Version,
}, nil
}
}
return &pb.CheckNSNodeLatestVersionResponse{HasNewVersion: false}, nil
}
// FindLatestNSNodeVersion 获取NS节点最新版本
func (this *NSNodeService) FindLatestNSNodeVersion(ctx context.Context, req *pb.FindLatestNSNodeVersionRequest) (*pb.FindLatestNSNodeVersionResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
return &pb.FindLatestNSNodeVersionResponse{Version: teaconst.DNSNodeVersion}, nil
}
// DownloadNSNodeInstallationFile 下载最新DNS节点安装文件
func (this *NSNodeService) DownloadNSNodeInstallationFile(ctx context.Context, req *pb.DownloadNSNodeInstallationFileRequest) (*pb.DownloadNSNodeInstallationFileResponse, error) {
nodeId, err := this.ValidateNSNode(ctx)
if err != nil {
return nil, err
}
var file = installers.SharedDeployManager.FindNSNodeFile(req.Os, req.Arch)
if file == nil {
return &pb.DownloadNSNodeInstallationFileResponse{}, nil
}
sum, err := file.Sum()
if err != nil {
return nil, err
}
data, offset, err := file.Read(req.ChunkOffset)
if err != nil && err != io.EOF {
return nil, err
}
// 增加下载速度监控
installers.SharedUpgradeLimiter.UpdateNodeBytes(nodeconfigs.NodeRoleDNS, nodeId, int64(len(data)))
return &pb.DownloadNSNodeInstallationFileResponse{
Sum: sum,
Offset: offset,
ChunkData: data,
Version: file.Version,
Filename: filepath.Base(file.Path),
}, nil
}
// UpdateNSNodeConnectedAPINodes 更改节点连接的API节点信息
func (this *NSNodeService) UpdateNSNodeConnectedAPINodes(ctx context.Context, req *pb.UpdateNSNodeConnectedAPINodesRequest) (*pb.RPCSuccess, error) {
// 校验节点
_, _, nodeId, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeDNS)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = models.SharedNSNodeDAO.UpdateNodeConnectedAPINodes(tx, nodeId, req.ApiNodeIds)
if err != nil {
return nil, errors.Wrap(err)
}
return this.Success()
}
// UpdateNSNodeLogin 修改节点登录信息
func (this *NSNodeService) UpdateNSNodeLogin(ctx context.Context, req *pb.UpdateNSNodeLoginRequest) (*pb.RPCSuccess, error) {
// 校验请求
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if req.NodeLogin.Id <= 0 {
loginId, err := models.SharedNodeLoginDAO.CreateNodeLogin(tx, nodeconfigs.NodeRoleDNS, req.NsNodeId, req.NodeLogin.Name, req.NodeLogin.Type, req.NodeLogin.Params)
if err != nil {
return nil, err
}
req.NodeLogin.Id = loginId
}
err = models.SharedNodeLoginDAO.UpdateNodeLogin(tx, req.NodeLogin.Id, req.NodeLogin.Name, req.NodeLogin.Type, req.NodeLogin.Params)
if err != nil {
return nil, err
}
return this.Success()
}
// StartNSNode 启动节点
func (this *NSNodeService) StartNSNode(ctx context.Context, req *pb.StartNSNodeRequest) (*pb.StartNSNodeResponse, error) {
// 校验节点
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
err = installers.SharedNSNodeQueue().StartNode(req.NsNodeId)
if err != nil {
return &pb.StartNSNodeResponse{
IsOk: false,
Error: err.Error(),
}, nil
}
// 修改状态
var tx = this.NullTx()
err = models.SharedNSNodeDAO.UpdateNodeActive(tx, req.NsNodeId, true)
if err != nil {
return nil, err
}
return &pb.StartNSNodeResponse{IsOk: true}, nil
}
// StopNSNode 停止节点
func (this *NSNodeService) StopNSNode(ctx context.Context, req *pb.StopNSNodeRequest) (*pb.StopNSNodeResponse, error) {
// 校验节点
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
err = installers.SharedNSNodeQueue().StopNode(req.NsNodeId)
if err != nil {
return &pb.StopNSNodeResponse{
IsOk: false,
Error: err.Error(),
}, nil
}
// 修改状态
var tx = this.NullTx()
err = models.SharedNSNodeDAO.UpdateNodeActive(tx, req.NsNodeId, false)
if err != nil {
return nil, err
}
return &pb.StopNSNodeResponse{IsOk: true}, nil
}
// FindNSNodeDDoSProtection 获取集群的DDoS设置
func (this *NSNodeService) FindNSNodeDDoSProtection(ctx context.Context, req *pb.FindNSNodeDDoSProtectionRequest) (*pb.FindNSNodeDDoSProtectionResponse, error) {
var nodeId = req.NsNodeId
var isFromNode = false
_, err := this.ValidateAdmin(ctx)
if err != nil {
// 检查是否来自节点
currentNodeId, err2 := this.ValidateNSNode(ctx)
if err2 != nil {
return nil, err
}
if nodeId > 0 && currentNodeId != nodeId {
return nil, errors.New("invalid 'nsNodeId'")
}
nodeId = currentNodeId
isFromNode = true
}
var tx *dbs.Tx
ddosProtection, err := models.SharedNSNodeDAO.FindNodeDDoSProtection(tx, nodeId)
if err != nil {
return nil, err
}
if ddosProtection == nil {
ddosProtection = ddosconfigs.DefaultProtectionConfig()
}
// 组合父级节点配置
// 只有从节点读取配置时才需要组合
if isFromNode {
clusterId, err := models.SharedNSNodeDAO.FindNodeClusterId(tx, nodeId)
if err != nil {
return nil, err
}
if clusterId > 0 {
clusterDDoSProtection, err := models.SharedNSClusterDAO.FindClusterDDoSProtection(tx, clusterId)
if err != nil {
return nil, err
}
if clusterDDoSProtection == nil {
clusterDDoSProtection = ddosconfigs.DefaultProtectionConfig()
}
clusterDDoSProtection.Merge(ddosProtection)
ddosProtection = clusterDDoSProtection
}
}
ddosProtectionJSON, err := json.Marshal(ddosProtection)
if err != nil {
return nil, err
}
var result = &pb.FindNSNodeDDoSProtectionResponse{
DdosProtectionJSON: ddosProtectionJSON,
}
return result, nil
}
// UpdateNSNodeDDoSProtection 修改集群的DDoS设置
func (this *NSNodeService) UpdateNSNodeDDoSProtection(ctx context.Context, req *pb.UpdateNSNodeDDoSProtectionRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var ddosProtection = &ddosconfigs.ProtectionConfig{}
err = json.Unmarshal(req.DdosProtectionJSON, ddosProtection)
if err != nil {
return nil, err
}
var tx *dbs.Tx
err = models.SharedNSNodeDAO.UpdateNodeDDoSProtection(tx, req.NsNodeId, ddosProtection)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSNodeAPIConfig 查找单个节点的API相关配置
func (this *NSNodeService) FindNSNodeAPIConfig(ctx context.Context, req *pb.FindNSNodeAPIConfigRequest) (*pb.FindNSNodeAPIConfigResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
node, err := models.SharedNSNodeDAO.FindNodeAPIConfig(tx, req.NsNodeId)
if err != nil {
return nil, err
}
if node == nil {
return &pb.FindNSNodeAPIConfigResponse{
ApiNodeAddrsJSON: nil,
}, nil
}
return &pb.FindNSNodeAPIConfigResponse{
ApiNodeAddrsJSON: node.ApiNodeAddrs,
}, nil
}
// UpdateNSNodeAPIConfig 修改某个节点的API相关配置
func (this *NSNodeService) UpdateNSNodeAPIConfig(ctx context.Context, req *pb.UpdateNSNodeAPIConfigRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
if len(req.ApiNodeAddrsJSON) > 0 {
err = json.Unmarshal(req.ApiNodeAddrsJSON, &apiNodeAddrs)
if err != nil {
return nil, err
}
}
err = models.SharedNSNodeDAO.UpdateNodeAPIConfig(tx, req.NsNodeId, apiNodeAddrs)
if err != nil {
return nil, err
}
return this.Success()
}

View File

@@ -0,0 +1,331 @@
//go:build plus
package nameservers
import (
"context"
"fmt"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/types"
"strconv"
"sync"
"sync/atomic"
"time"
)
// CommandRequest 命令请求相关
type CommandRequest struct {
Id int64
Code string
CommandJSON []byte
}
type CommandRequestWaiting struct {
Timestamp int64
Chan chan *pb.NSNodeStreamMessage
}
func (this *CommandRequestWaiting) Close() {
defer func() {
_ = recover()
}()
close(this.Chan)
}
var responseChanMap = map[int64]*CommandRequestWaiting{} // request id => response
var commandRequestId = int64(0)
var nodeLocker = &sync.Mutex{}
var requestChanMap = map[int64]chan *CommandRequest{} // node id => chan
func NextCommandRequestId() int64 {
return atomic.AddInt64(&commandRequestId, 1)
}
func init() {
dbs.OnReadyDone(func() {
// 清理WaitingChannelMap
goman.New(func() {
ticker := time.NewTicker(30 * time.Second)
for range ticker.C {
nodeLocker.Lock()
for requestId, request := range responseChanMap {
if time.Now().Unix()-request.Timestamp > 3600 {
responseChanMap[requestId].Close()
delete(responseChanMap, requestId)
}
}
nodeLocker.Unlock()
}
})
// 自动同步连接到本API节点的NS节点任务
goman.New(func() {
defer func() {
_ = recover()
}()
// TODO 未来支持同步边缘节点
var ticker = time.NewTicker(3 * time.Second)
for range ticker.C {
nodeIds, err := models.SharedNodeTaskDAO.FindAllDoingNodeIds(nil, nodeconfigs.NodeRoleDNS)
if err != nil {
remotelogs.Error("NSNodeService_SYNC", err.Error())
continue
}
nodeLocker.Lock()
for _, nodeId := range nodeIds {
c, ok := requestChanMap[nodeId]
if ok {
select {
case c <- &CommandRequest{
Id: NextCommandRequestId(),
Code: messageconfigs.NSMessageCodeNewNodeTask,
CommandJSON: nil,
}:
default:
}
}
}
nodeLocker.Unlock()
}
})
})
}
// NsNodeStream 节点stream
func (this *NSNodeService) NsNodeStream(server pb.NSNodeService_NsNodeStreamServer) error {
// TODO 使用此stream快速通知NS节点更新
// 校验节点
_, _, nodeId, err := rpcutils.ValidateRequest(server.Context(), rpcutils.UserTypeDNS)
if err != nil {
return err
}
// 返回连接成功
err = models.SharedNSNodeDAO.UpdateNodeConnectedAPINodes(nil, nodeId, []int64{teaconst.NodeId})
if err != nil {
return err
}
if Tea.IsTesting() {
remotelogs.Println("NSNodeService", "accepted ns node '"+types.String(nodeId)+"' connection")
}
var tx = this.NullTx()
// 是否发送恢复通知
oldIsActive, err := models.SharedNSNodeDAO.FindNodeActive(tx, nodeId)
if err != nil {
return err
}
if !oldIsActive {
inactiveNotifiedAt, err := models.SharedNSNodeDAO.FindNodeInactiveNotifiedAt(tx, nodeId)
if err != nil {
return err
}
// 设置为活跃
err = models.SharedNSNodeDAO.UpdateNodeActive(tx, nodeId, true)
if err != nil {
return err
}
if inactiveNotifiedAt > 0 {
// 发送恢复消息
clusterId, err := models.SharedNSNodeDAO.FindNodeClusterId(tx, nodeId)
if err != nil {
return err
}
nodeName, err := models.SharedNSNodeDAO.FindEnabledNSNodeName(tx, nodeId)
if err != nil {
return err
}
subject := "NS节点\"" + nodeName + "\"已经恢复在线"
msg := "NS节点\"" + nodeName + "\"已经恢复在线"
err = models.SharedMessageDAO.CreateNodeMessage(tx, nodeconfigs.NodeRoleDNS, clusterId, nodeId, models.MessageTypeNSNodeActive, models.MessageLevelSuccess, subject, msg, nil, false)
if err != nil {
return err
}
}
}
nodeLocker.Lock()
requestChan, ok := requestChanMap[nodeId]
if !ok {
requestChan = make(chan *CommandRequest, 1024)
requestChanMap[nodeId] = requestChan
}
nodeLocker.Unlock()
defer func() {
nodeLocker.Lock()
delete(requestChanMap, nodeId)
nodeLocker.Unlock()
}()
// 发送请求
goman.New(func() {
for {
select {
case <-server.Context().Done():
return
case commandRequest := <-requestChan:
// logs.Println("[RPC]sending command '" + commandRequest.Code + "' to node '" + strconv.FormatInt(nodeId, 10) + "'")
retries := 3 // 错误重试次数
for i := 0; i < retries; i++ {
err := server.Send(&pb.NSNodeStreamMessage{
RequestId: commandRequest.Id,
Code: commandRequest.Code,
DataJSON: commandRequest.CommandJSON,
})
if err != nil {
if i == retries-1 {
logs.Println("[RPC]send command '" + commandRequest.Code + "' failed: " + err.Error())
} else {
time.Sleep(1 * time.Second)
}
} else {
break
}
}
}
}
})
// 接受请求
for {
req, err := server.Recv()
if err != nil {
// 修改节点状态
err1 := models.SharedNSNodeDAO.UpdateNodeActive(tx, nodeId, false)
if err1 != nil {
logs.Println(err1.Error())
}
return err
}
func(req *pb.NSNodeStreamMessage) {
// 因为 responseChan.Chan 有被关闭的风险所以我们使用recover防止panic
defer func() {
_ = recover()
}()
nodeLocker.Lock()
responseChan, ok := responseChanMap[req.RequestId]
if ok {
select {
case responseChan.Chan <- req:
default:
}
}
nodeLocker.Unlock()
}(req)
}
}
// SendCommandToNSNode 向节点发送命令
func (this *NSNodeService) SendCommandToNSNode(ctx context.Context, req *pb.NSNodeStreamMessage) (*pb.NSNodeStreamMessage, error) {
// 校验请求
_, _, err := this.ValidateAdminAndUser(ctx, false)
if err != nil {
return nil, err
}
nodeId := req.NsNodeId
if nodeId <= 0 {
return nil, errors.New("node id should not be less than 0")
}
nodeLocker.Lock()
requestChan, ok := requestChanMap[nodeId]
nodeLocker.Unlock()
if !ok {
return &pb.NSNodeStreamMessage{
RequestId: req.RequestId,
IsOk: false,
Message: "node '" + strconv.FormatInt(nodeId, 10) + "' not connected yet",
}, nil
}
req.RequestId = NextCommandRequestId()
select {
case requestChan <- &CommandRequest{
Id: req.RequestId,
Code: req.Code,
CommandJSON: req.DataJSON,
}:
// 加入到等待队列中
respChan := make(chan *pb.NSNodeStreamMessage, 1)
waiting := &CommandRequestWaiting{
Timestamp: time.Now().Unix(),
Chan: respChan,
}
nodeLocker.Lock()
responseChanMap[req.RequestId] = waiting
nodeLocker.Unlock()
// 等待响应
timeoutSeconds := req.TimeoutSeconds
if timeoutSeconds <= 0 {
timeoutSeconds = 10
}
timeout := time.NewTimer(time.Duration(timeoutSeconds) * time.Second)
select {
case resp := <-respChan:
// 从队列中删除
nodeLocker.Lock()
delete(responseChanMap, req.RequestId)
waiting.Close()
nodeLocker.Unlock()
if resp == nil {
return &pb.NSNodeStreamMessage{
RequestId: req.RequestId,
Code: req.Code,
Message: "response timeout",
IsOk: false,
}, nil
}
return resp, nil
case <-timeout.C:
// 从队列中删除
nodeLocker.Lock()
delete(responseChanMap, req.RequestId)
waiting.Close()
nodeLocker.Unlock()
return &pb.NSNodeStreamMessage{
RequestId: req.RequestId,
Code: req.Code,
Message: "response timeout over " + fmt.Sprintf("%d", timeoutSeconds) + " seconds",
IsOk: false,
}, nil
}
default:
return &pb.NSNodeStreamMessage{
RequestId: req.RequestId,
Code: req.Code,
Message: "command queue is full over " + strconv.Itoa(len(requestChan)),
IsOk: false,
}, nil
}
}

View File

@@ -0,0 +1,191 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package nameservers
import (
"context"
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
// NSPlanService DNS套餐服务
type NSPlanService struct {
services.BaseService
}
// CreateNSPlan 创建DNS套餐
func (this *NSPlanService) CreateNSPlan(ctx context.Context, req *pb.CreateNSPlanRequest) (*pb.CreateNSPlanResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if len(req.ConfigJSON) == 0 {
return nil, errors.New("invalid 'configJSON'")
}
var config = dnsconfigs.DefaultNSPlanConfig()
err = json.Unmarshal(req.ConfigJSON, config)
if err != nil {
return nil, errors.New("decode 'configJSON' failed: " + err.Error())
}
planId, err := nameservers.SharedNSPlanDAO.CreatePlan(tx, req.Name, req.MonthlyPrice, req.YearlyPrice, config)
if err != nil {
return nil, err
}
return &pb.CreateNSPlanResponse{NsPlanId: planId}, nil
}
// UpdateNSPlan 修改DNS套餐
func (this *NSPlanService) UpdateNSPlan(ctx context.Context, req *pb.UpdateNSPlanRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if len(req.ConfigJSON) == 0 {
return nil, errors.New("invalid 'configJSON'")
}
var config = dnsconfigs.DefaultNSPlanConfig()
err = json.Unmarshal(req.ConfigJSON, config)
if err != nil {
return nil, errors.New("decode 'configJSON' failed: " + err.Error())
}
err = nameservers.SharedNSPlanDAO.UpdatePlan(tx, req.NsPlanId, req.Name, req.IsOn, req.MonthlyPrice, req.YearlyPrice, config)
if err != nil {
return nil, err
}
return this.Success()
}
// SortNSPlanOrders 修改DNS套餐顺序
func (this *NSPlanService) SortNSPlanOrders(ctx context.Context, req *pb.SortNSPlansRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = nameservers.SharedNSPlanDAO.UpdatePlanOrders(tx, req.NsPlanIds)
if err != nil {
return nil, err
}
return this.Success()
}
// FindAllNSPlans 查找所有DNS套餐
func (this *NSPlanService) FindAllNSPlans(ctx context.Context, req *pb.FindAllNSPlansRequest) (*pb.FindAllNSPlansResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
var pbPlans = []*pb.NSPlan{}
plans, err := nameservers.SharedNSPlanDAO.FindAllPlans(tx)
if err != nil {
return nil, err
}
for _, plan := range plans {
pbPlans = append(pbPlans, &pb.NSPlan{
Id: int64(plan.Id),
Name: plan.Name,
IsOn: plan.IsOn,
MonthlyPrice: float32(plan.MonthlyPrice),
YearlyPrice: float32(plan.YearlyPrice),
ConfigJSON: plan.Config,
})
}
return &pb.FindAllNSPlansResponse{NsPlans: pbPlans}, nil
}
// FindAllEnabledNSPlans 查找所有可用DNS套餐
func (this *NSPlanService) FindAllEnabledNSPlans(ctx context.Context, req *pb.FindAllEnabledNSPlansRequest) (*pb.FindAllEnabledNSPlansResponse, error) {
_, _, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
var pbPlans = []*pb.NSPlan{}
plans, err := nameservers.SharedNSPlanDAO.FindAllEnabledPlans(tx)
if err != nil {
return nil, err
}
for _, plan := range plans {
pbPlans = append(pbPlans, &pb.NSPlan{
Id: int64(plan.Id),
Name: plan.Name,
IsOn: plan.IsOn,
MonthlyPrice: float32(plan.MonthlyPrice),
YearlyPrice: float32(plan.YearlyPrice),
ConfigJSON: plan.Config,
})
}
return &pb.FindAllEnabledNSPlansResponse{NsPlans: pbPlans}, nil
}
// FindNSPlan 查找DNS套餐
func (this *NSPlanService) FindNSPlan(ctx context.Context, req *pb.FindNSPlanRequest) (*pb.FindNSPlanResponse, error) {
_, _, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
plan, err := nameservers.SharedNSPlanDAO.FindEnabledNSPlan(tx, req.NsPlanId)
if err != nil {
return nil, err
}
if plan == nil {
return &pb.FindNSPlanResponse{}, nil
}
return &pb.FindNSPlanResponse{
NsPlan: &pb.NSPlan{
Id: int64(plan.Id),
Name: plan.Name,
IsOn: plan.IsOn,
MonthlyPrice: float32(plan.MonthlyPrice),
YearlyPrice: float32(plan.YearlyPrice),
ConfigJSON: plan.Config,
},
}, nil
}
// DeleteNSPlan 删除DNS套餐
func (this *NSPlanService) DeleteNSPlan(ctx context.Context, req *pb.DeleteNSPlanRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = nameservers.SharedNSPlanDAO.DisableNSPlan(tx, req.NsPlanId)
if err != nil {
return nil, err
}
return this.Success()
}

View File

@@ -0,0 +1,78 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package nameservers
import (
"context"
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
// NSQuestionOptionService DNS查询选项
type NSQuestionOptionService struct {
services.BaseService
}
// CreateNSQuestionOption 创建选项
func (this *NSQuestionOptionService) CreateNSQuestionOption(ctx context.Context, req *pb.CreateNSQuestionOptionRequest) (*pb.CreateNSQuestionOptionResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
var values = maps.Map{}
if len(req.ValuesJSON) > 0 {
err = json.Unmarshal(req.ValuesJSON, &values)
if err != nil {
return nil, err
}
}
optionId, err := nameservers.SharedNSQuestionOptionDAO.CreateOption(tx, req.Name, values)
if err != nil {
return nil, err
}
return &pb.CreateNSQuestionOptionResponse{NsQuestionOptionId: optionId}, nil
}
// FindNSQuestionOption 读取选项
func (this *NSQuestionOptionService) FindNSQuestionOption(ctx context.Context, req *pb.FindNSQuestionOptionRequest) (*pb.FindNSQuestionOptionResponse, error) {
_, err := this.ValidateNSNode(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
option, err := nameservers.SharedNSQuestionOptionDAO.FindOption(tx, req.NsQuestionOptionId)
if err != nil {
return nil, err
}
if option == nil {
return &pb.FindNSQuestionOptionResponse{NsQuestionOption: nil}, nil
}
return &pb.FindNSQuestionOptionResponse{NsQuestionOption: &pb.NSQuestionOption{
Id: int64(option.Id),
Name: option.Name,
ValuesJSON: option.Values,
}}, nil
}
// DeleteNSQuestionOption 删除选项
func (this *NSQuestionOptionService) DeleteNSQuestionOption(ctx context.Context, req *pb.DeleteNSQuestionOptionRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = nameservers.SharedNSQuestionOptionDAO.DeleteOption(tx, req.NsQuestionOptionId)
if err != nil {
return nil, err
}
return this.Success()
}

View File

@@ -0,0 +1,993 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package nameservers
import (
"context"
"encoding/json"
"errors"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/types"
"regexp"
"strings"
)
// NSRecordService 域名记录相关服务
type NSRecordService struct {
services.BaseService
}
// CreateNSRecord 创建记录
func (this *NSRecordService) CreateNSRecord(ctx context.Context, req *pb.CreateNSRecordRequest) (*pb.CreateNSRecordResponse, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查权限
if userId > 0 {
err = nameservers.SharedNSDomainDAO.CheckUserDomain(tx, userId, req.NsDomainId)
if err != nil {
return nil, err
}
}
// 检查线路代号
if len(req.NsRouteCodes) > 0 {
err = nameservers.SharedNSRouteDAO.CheckRouteCodes(tx, req.NsRouteCodes, userId)
if err != nil {
return nil, err
}
}
recordId, err := nameservers.SharedNSRecordDAO.CreateRecord(tx, req.NsDomainId, req.Description, req.Name, req.Type, req.Value, req.MxPriority, req.SrvPriority, req.SrvWeight, req.SrvPort, req.CaaFlag, req.CaaTag, req.Ttl, req.NsRouteCodes, req.Weight)
if err != nil {
return nil, err
}
return &pb.CreateNSRecordResponse{NsRecordId: recordId}, nil
}
// CreateNSRecords 创建记录
func (this *NSRecordService) CreateNSRecords(ctx context.Context, req *pb.CreateNSRecordsRequest) (*pb.CreateNSRecordsResponse, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
// 检查权限
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSDomainDAO.CheckUserDomain(tx, userId, req.NsDomainId)
if err != nil {
return nil, err
}
}
var recordIds = []int64{}
err = this.RunTx(func(tx *dbs.Tx) error {
for _, name := range req.Names {
// 检查线路代号
if len(req.NsRouteCodes) > 0 {
err = nameservers.SharedNSRouteDAO.CheckRouteCodes(tx, req.NsRouteCodes, userId)
if err != nil {
return err
}
}
recordId, err := nameservers.SharedNSRecordDAO.CreateRecord(tx, req.NsDomainId, req.Description, name, req.Type, req.Value, req.MxPriority, req.SrvPriority, req.SrvWeight, req.SrvPort, req.CaaFlag, req.CaaTag, req.Ttl, req.NsRouteCodes, req.Weight)
if err != nil {
return err
}
recordIds = append(recordIds, recordId)
}
return nil
})
if err != nil {
return nil, err
}
return &pb.CreateNSRecordsResponse{NsRecordIds: recordIds}, nil
}
// CreateNSRecordsWithDomainNames 为一组域名批量创建记录
func (this *NSRecordService) CreateNSRecordsWithDomainNames(ctx context.Context, req *pb.CreateNSRecordsWithDomainNamesRequest) (*pb.RPCSuccess, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
if userId > 0 {
req.UserId = userId
}
if len(req.RecordsJSON) == 0 {
return this.Success()
}
type recordItem struct {
Name string `json:"name"`
Type string `json:"type"`
Value string `json:"value"`
MxPriority int32 `json:"mxPriority"`
SRVPriority int32 `json:"srvPriority"`
SRVWeight int32 `json:"srvWeight"`
SRVPort int32 `json:"srvPort"`
CAAFlag int32 `json:"caaFlag"`
CAATag string `json:"caaTag"`
RouteCodes []string `json:"routeCodes"`
TTL int32 `json:"ttl"`
Weight int32 `json:"weight"`
}
var records = []*recordItem{}
err = json.Unmarshal(req.RecordsJSON, &records)
if err != nil {
return nil, err
}
if len(records) == 0 {
return this.Success()
}
err = this.RunTx(func(tx *dbs.Tx) error {
for _, domainName := range req.NsDomainNames {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
domainId, err := nameservers.SharedNSDomainDAO.FindDomainIdWithName(tx, 0, req.UserId, domainName, false)
if err != nil {
return err
}
if domainId <= 0 {
continue
}
// 是否删除所有以往记录
if req.RemoveAll {
err = nameservers.SharedNSRecordDAO.DisableRecordsInDomain(tx, domainId)
if err != nil {
return err
}
}
for _, record := range records {
record.Type = strings.ToLower(record.Type)
if !req.RemoveAll && req.RemoveOld {
err = nameservers.SharedNSRecordDAO.DisableRecordsInDomainWithNameAndType(tx, domainId, record.Name, record.Type)
if err != nil {
return err
}
}
// 检查线路代号
if len(record.RouteCodes) > 0 {
err = nameservers.SharedNSRouteDAO.CheckRouteCodes(tx, record.RouteCodes, userId)
if err != nil {
return err
}
}
_, err = nameservers.SharedNSRecordDAO.CreateRecord(tx, domainId, "批量创建", record.Name, strings.ToUpper(record.Type), record.Value, record.MxPriority, record.SRVPriority, record.SRVWeight, record.SRVPort, record.CAAFlag, record.CAATag, record.TTL, record.RouteCodes, record.Weight)
if err != nil {
return err
}
}
}
return nil
})
if err != nil {
return nil, err
}
return this.Success()
}
// UpdateNSRecordsWithDomainNames 批量修改一组域名的一组记录
func (this *NSRecordService) UpdateNSRecordsWithDomainNames(ctx context.Context, req *pb.UpdateNSRecordsWithDomainNamesRequest) (*pb.RPCSuccess, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
if userId > 0 {
req.UserId = userId
}
err = this.RunTx(func(tx *dbs.Tx) error {
for _, domainName := range req.NsDomainNames {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
domainId, err := nameservers.SharedNSDomainDAO.FindDomainIdWithName(tx, 0, req.UserId, domainName, false)
if err != nil {
return err
}
if domainId <= 0 {
continue
}
err = nameservers.SharedNSRecordDAO.UpdateRecordsWithDomainId(tx, domainId, req.SearchName, req.SearchType, req.SearchValue, req.SearchNSRouteCodes, req.NewName, req.NewType, req.NewValue, req.NewNSRouteCodes)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
return this.Success()
}
// DeleteNSRecordsWithDomainNames 批量删除一组域名的一组记录
func (this *NSRecordService) DeleteNSRecordsWithDomainNames(ctx context.Context, req *pb.DeleteNSRecordsWithDomainNamesRequest) (*pb.RPCSuccess, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
if userId > 0 {
req.UserId = userId
}
err = this.RunTx(func(tx *dbs.Tx) error {
for _, domainName := range req.NsDomainNames {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
domainId, err := nameservers.SharedNSDomainDAO.FindDomainIdWithName(tx, 0, req.UserId, domainName, false)
if err != nil {
return err
}
if domainId <= 0 {
continue
}
err = nameservers.SharedNSRecordDAO.DisableRecordsWithDomainId(tx, domainId, req.SearchName, req.SearchType, req.SearchValue, req.SearchNSRouteCodes)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
return this.Success()
}
// UpdateNSRecordsIsOnWithDomainNames 批量一组域名的一组记录启用状态
func (this *NSRecordService) UpdateNSRecordsIsOnWithDomainNames(ctx context.Context, req *pb.UpdateNSRecordsIsOnWithDomainNamesRequest) (*pb.RPCSuccess, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
if userId > 0 {
req.UserId = userId
}
err = this.RunTx(func(tx *dbs.Tx) error {
for _, domainName := range req.NsDomainNames {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
domainId, err := nameservers.SharedNSDomainDAO.FindDomainIdWithName(tx, 0, req.UserId, domainName, false)
if err != nil {
return err
}
if domainId <= 0 {
continue
}
err = nameservers.SharedNSRecordDAO.UpdateRecordsIsOnWithDomainId(tx, domainId, req.SearchName, req.SearchType, req.SearchValue, req.SearchNSRouteCodes, req.IsOn)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
return this.Success()
}
// ImportNSRecords 导入域名解析
func (this *NSRecordService) ImportNSRecords(ctx context.Context, req *pb.ImportNSRecordsRequest) (*pb.RPCSuccess, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
if userId > 0 {
req.UserId = userId
}
err = this.RunTx(func(tx *dbs.Tx) error {
for _, record := range req.NsRecords {
var domainName = strings.ToLower(strings.TrimSpace(record.NsDomainName))
if len(domainName) == 0 {
continue
}
domainId, err := nameservers.SharedNSDomainDAO.FindDomainIdWithName(tx, 0, req.UserId, domainName, false)
if err != nil {
return err
}
if domainId <= 0 {
continue
}
if record.Ttl <= 0 {
record.Ttl = 600
}
_, err = nameservers.SharedNSRecordDAO.CreateRecord(tx, domainId, "批量导入", record.Name, record.Type, record.Value, record.MxPriority, record.SrvPriority, record.SrvWeight, record.SrvPort, record.CaaFlag, record.CaaTag, record.Ttl, nil, record.Weight)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
return this.Success()
}
// UpdateNSRecord 修改记录
func (this *NSRecordService) UpdateNSRecord(ctx context.Context, req *pb.UpdateNSRecordRequest) (*pb.RPCSuccess, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查权限
if userId > 0 {
err = nameservers.SharedNSRecordDAO.CheckUserRecord(tx, userId, req.NsRecordId)
if err != nil {
return nil, err
}
}
err = nameservers.SharedNSRecordDAO.UpdateRecord(tx, req.NsRecordId, req.Description, req.Name, req.Type, req.Value, req.MxPriority, req.SrvPriority, req.SrvWeight, req.SrvPort, req.CaaFlag, req.CaaTag, req.Ttl, req.NsRouteCodes, req.Weight, req.IsOn)
if err != nil {
return nil, err
}
return this.Success()
}
// DeleteNSRecord 删除记录
func (this *NSRecordService) DeleteNSRecord(ctx context.Context, req *pb.DeleteNSRecordRequest) (*pb.RPCSuccess, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查权限
if userId > 0 {
err = nameservers.SharedNSRecordDAO.CheckUserRecord(tx, userId, req.NsRecordId)
if err != nil {
return nil, err
}
}
err = nameservers.SharedNSRecordDAO.DisableNSRecord(tx, req.NsRecordId)
if err != nil {
return nil, err
}
return this.Success()
}
// CountAllNSRecords 计算记录数量
func (this *NSRecordService) CountAllNSRecords(ctx context.Context, req *pb.CountAllNSRecordsRequest) (*pb.RPCCountResponse, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查权限
if userId > 0 {
err = nameservers.SharedNSDomainDAO.CheckUserDomain(tx, userId, req.NsDomainId)
if err != nil {
return nil, err
}
}
count, err := nameservers.SharedNSRecordDAO.CountAllEnabledDomainRecords(tx, req.NsDomainId, req.Type, req.Keyword, req.NsRouteCode)
if err != nil {
return nil, err
}
return this.SuccessCount(count)
}
// CountAllNSRecordsWithName 查询相同记录名的记录数
func (this *NSRecordService) CountAllNSRecordsWithName(ctx context.Context, req *pb.CountAllNSRecordsWithNameRequest) (*pb.RPCCountResponse, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查权限
if userId > 0 {
err = nameservers.SharedNSDomainDAO.CheckUserDomain(tx, userId, req.NsDomainId)
if err != nil {
return nil, err
}
}
count, err := nameservers.SharedNSRecordDAO.CountAllRecordsWithName(tx, req.NsDomainId, req.Type, req.Name)
if err != nil {
return nil, err
}
return this.SuccessCount(count)
}
// ListNSRecords 读取单页记录
func (this *NSRecordService) ListNSRecords(ctx context.Context, req *pb.ListNSRecordsRequest) (*pb.ListNSRecordsResponse, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查权限
if userId > 0 {
err = nameservers.SharedNSDomainDAO.CheckUserDomain(tx, userId, req.NsDomainId)
if err != nil {
return nil, err
}
}
records, err := nameservers.SharedNSRecordDAO.ListEnabledRecords(tx, req.NsDomainId, req.Type, req.Keyword, req.NsRouteCode, req.NameAsc, req.NameDesc, req.TypeAsc, req.TypeDesc, req.TtlAsc, req.TtlDesc, req.UpAsc, req.UpDesc, req.Offset, req.Size)
if err != nil {
return nil, err
}
var pbRecords = []*pb.NSRecord{}
for _, record := range records {
// 线路
var pbRoutes = []*pb.NSRoute{}
for _, routeCode := range record.DecodeRouteIds() {
route, err := nameservers.SharedNSRouteDAO.FindEnabledRouteWithCode(tx, routeCode)
if err != nil {
return nil, err
}
if route == nil {
continue
}
pbRoutes = append(pbRoutes, &pb.NSRoute{
Id: int64(route.Id),
Name: route.Name,
Code: route.Code,
})
// TODO 读取其他线路
}
pbRecords = append(pbRecords, &pb.NSRecord{
Id: int64(record.Id),
Description: record.Description,
Name: record.Name,
Type: record.Type,
Value: record.Value,
MxPriority: int32(record.MxPriority),
Ttl: types.Int32(record.Ttl),
Weight: types.Int32(record.Weight),
CreatedAt: int64(record.CreatedAt),
IsOn: record.IsOn,
NsDomain: nil,
NsRoutes: pbRoutes,
HealthCheckJSON: record.HealthCheck,
IsUp: record.IsUp,
})
}
return &pb.ListNSRecordsResponse{NsRecords: pbRecords}, nil
}
// FindNSRecord 查询单个记录信息
func (this *NSRecordService) FindNSRecord(ctx context.Context, req *pb.FindNSRecordRequest) (*pb.FindNSRecordResponse, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查权限
if userId > 0 {
err = nameservers.SharedNSRecordDAO.CheckUserRecord(tx, userId, req.NsRecordId)
if err != nil {
return nil, err
}
}
record, err := nameservers.SharedNSRecordDAO.FindEnabledNSRecord(tx, req.NsRecordId)
if err != nil {
return nil, err
}
if record == nil {
return &pb.FindNSRecordResponse{NsRecord: nil}, nil
}
// 域名
domain, err := nameservers.SharedNSDomainDAO.FindEnabledNSDomain(tx, int64(record.DomainId))
if err != nil {
return nil, err
}
if domain == nil {
return &pb.FindNSRecordResponse{NsRecord: nil}, nil
}
var pbDomain = &pb.NSDomain{
Id: int64(domain.Id),
Name: domain.Name,
IsOn: domain.IsOn,
}
// 线路
var pbRoutes = []*pb.NSRoute{}
for _, routeCode := range record.DecodeRouteIds() {
route, err := nameservers.SharedNSRouteDAO.FindEnabledRouteWithCode(tx, routeCode)
if err != nil {
return nil, err
}
if route == nil {
continue
}
pbRoutes = append(pbRoutes, &pb.NSRoute{
Id: int64(route.Id),
Name: route.Name,
Code: route.Code,
})
}
// TODO 读取其他线路
return &pb.FindNSRecordResponse{NsRecord: &pb.NSRecord{
Id: int64(record.Id),
Description: record.Description,
Name: record.Name,
Type: record.Type,
Value: record.Value,
MxPriority: types.Int32(record.MxPriority),
SrvPort: types.Int32(record.SrvPort),
SrvPriority: types.Int32(record.SrvPriority),
SrvWeight: types.Int32(record.SrvWeight),
CaaFlag: types.Int32(record.CaaFlag),
CaaTag: record.CaaTag,
Ttl: types.Int32(record.Ttl),
Weight: types.Int32(record.Weight),
CreatedAt: int64(record.CreatedAt),
IsOn: record.IsOn,
NsDomain: pbDomain,
NsRoutes: pbRoutes,
HealthCheckJSON: record.HealthCheck,
IsUp: record.IsUp,
}}, nil
}
// FindNSRecordWithNameAndType 使用名称和类型查询单个记录信息
func (this *NSRecordService) FindNSRecordWithNameAndType(ctx context.Context, req *pb.FindNSRecordWithNameAndTypeRequest) (*pb.FindNSRecordWithNameAndTypeResponse, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
if req.NsDomainId <= 0 {
return &pb.FindNSRecordWithNameAndTypeResponse{
NsRecord: nil,
}, nil
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
// 检查权限
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSDomainDAO.CheckUserDomain(tx, userId, req.NsDomainId)
if err != nil {
return nil, err
}
}
record, err := nameservers.SharedNSRecordDAO.FindEnabledRecordWithName(tx, req.NsDomainId, req.Name, req.Type)
if err != nil {
return nil, err
}
if record == nil {
return &pb.FindNSRecordWithNameAndTypeResponse{
NsRecord: nil,
}, nil
}
// 线路
var pbRoutes = []*pb.NSRoute{}
for _, routeCode := range record.DecodeRouteIds() {
route, err := nameservers.SharedNSRouteDAO.FindEnabledRouteWithCode(tx, routeCode)
if err != nil {
return nil, err
}
if route == nil {
continue
}
pbRoutes = append(pbRoutes, &pb.NSRoute{
Id: int64(route.Id),
Name: route.Name,
Code: route.Code,
})
}
return &pb.FindNSRecordWithNameAndTypeResponse{
NsRecord: &pb.NSRecord{
Id: int64(record.Id),
Description: record.Description,
Name: record.Name,
Type: record.Type,
Value: record.Value,
MxPriority: types.Int32(record.MxPriority),
SrvPriority: types.Int32(record.SrvPriority),
SrvWeight: types.Int32(record.SrvWeight),
SrvPort: types.Int32(record.SrvPort),
CaaFlag: types.Int32(record.CaaFlag),
CaaTag: record.CaaTag,
Ttl: types.Int32(record.Ttl),
Weight: types.Int32(record.Weight),
CreatedAt: int64(record.CreatedAt),
IsOn: record.IsOn,
NsRoutes: pbRoutes,
HealthCheckJSON: record.HealthCheck,
IsUp: record.IsUp,
},
}, nil
}
// FindNSRecordsWithNameAndType 使用名称和类型查询多个记录信息
func (this *NSRecordService) FindNSRecordsWithNameAndType(ctx context.Context, req *pb.FindNSRecordsWithNameAndTypeRequest) (*pb.FindNSRecordsWithNameAndTypeResponse, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
if req.NsDomainId <= 0 {
return &pb.FindNSRecordsWithNameAndTypeResponse{
NsRecords: nil,
}, nil
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
// 检查权限
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSDomainDAO.CheckUserDomain(tx, userId, req.NsDomainId)
if err != nil {
return nil, err
}
}
records, err := nameservers.SharedNSRecordDAO.FindEnabledRecordsWithName(tx, req.NsDomainId, req.Name, req.Type)
if err != nil {
return nil, err
}
var pbRecords = []*pb.NSRecord{}
for _, record := range records {
// 线路
var pbRoutes = []*pb.NSRoute{}
for _, routeCode := range record.DecodeRouteIds() {
route, err := nameservers.SharedNSRouteDAO.FindEnabledRouteWithCode(tx, routeCode)
if err != nil {
return nil, err
}
if route == nil {
continue
}
pbRoutes = append(pbRoutes, &pb.NSRoute{
Id: int64(route.Id),
Name: route.Name,
Code: route.Code,
})
}
pbRecords = append(pbRecords, &pb.NSRecord{
Id: int64(record.Id),
Description: record.Description,
Name: record.Name,
Type: record.Type,
Value: record.Value,
MxPriority: int32(record.MxPriority),
SrvPriority: types.Int32(record.SrvPriority),
SrvWeight: types.Int32(record.SrvWeight),
SrvPort: types.Int32(record.SrvPort),
CaaFlag: types.Int32(record.CaaFlag),
CaaTag: record.CaaTag,
Ttl: types.Int32(record.Ttl),
Weight: types.Int32(record.Weight),
CreatedAt: int64(record.CreatedAt),
IsOn: record.IsOn,
NsRoutes: pbRoutes,
HealthCheckJSON: record.HealthCheck,
IsUp: record.IsUp,
})
}
return &pb.FindNSRecordsWithNameAndTypeResponse{
NsRecords: pbRecords,
}, nil
}
// ListNSRecordsAfterVersion 根据版本列出一组记录
func (this *NSRecordService) ListNSRecordsAfterVersion(ctx context.Context, req *pb.ListNSRecordsAfterVersionRequest) (*pb.ListNSRecordsAfterVersionResponse, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, _, err := this.ValidateNodeId(ctx, rpcutils.UserTypeDNS)
if err != nil {
return nil, err
}
// 检查是否为商业用户
if !teaconst.IsPlus {
return &pb.ListNSRecordsAfterVersionResponse{
NsRecords: nil,
}, nil
}
// 集群ID
var tx = this.NullTx()
if req.Size <= 0 {
req.Size = 2000
}
records, err := nameservers.SharedNSRecordDAO.ListRecordsAfterVersion(tx, req.Version, req.Size)
if err != nil {
return nil, err
}
var pbRecords []*pb.NSRecord
for _, record := range records {
// 线路
pbRoutes := []*pb.NSRoute{}
routeIds := record.DecodeRouteIds()
for _, routeId := range routeIds {
var routeIdInt int64 = 0
if regexp.MustCompile(`^id:\d+$`).MatchString(routeId) {
routeIdInt = types.Int64(routeId[strings.Index(routeId, ":")+1:])
}
pbRoutes = append(pbRoutes, &pb.NSRoute{
Id: routeIdInt,
Code: routeId,
})
}
// TODO 读取其他线路
pbRecords = append(pbRecords, &pb.NSRecord{
Id: int64(record.Id),
Description: "",
Name: record.Name,
Type: record.Type,
Value: record.Value,
MxPriority: int32(record.MxPriority),
SrvPriority: types.Int32(record.SrvPriority),
SrvWeight: types.Int32(record.SrvWeight),
SrvPort: types.Int32(record.SrvPort),
CaaFlag: types.Int32(record.CaaFlag),
CaaTag: record.CaaTag,
Ttl: types.Int32(record.Ttl),
Weight: types.Int32(record.Weight),
IsDeleted: record.State == nameservers.NSRecordStateDisabled,
IsOn: record.IsOn && record.IsUp,
Version: int64(record.Version),
NsDomain: &pb.NSDomain{Id: int64(record.DomainId)},
NsRoutes: pbRoutes,
HealthCheckJSON: record.HealthCheck,
IsUp: record.IsUp,
})
}
return &pb.ListNSRecordsAfterVersionResponse{NsRecords: pbRecords}, nil
}
// FindNSRecordHealthCheck 查询记录健康检查设置
func (this *NSRecordService) FindNSRecordHealthCheck(ctx context.Context, req *pb.FindNSRecordHealthCheckRequest) (*pb.FindNSRecordHealthCheckResponse, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
// 检查权限
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSRecordDAO.CheckUserRecord(tx, userId, req.NsRecordId)
if err != nil {
return nil, err
}
// TODO 检查套餐
}
config, err := nameservers.SharedNSRecordDAO.FindRecordHealthCheckConfig(tx, req.NsRecordId)
if err != nil {
return nil, err
}
configJSON, err := json.Marshal(config)
if err != nil {
return nil, err
}
return &pb.FindNSRecordHealthCheckResponse{NsRecordHealthCheckJSON: configJSON}, nil
}
// UpdateNSRecordHealthCheck 修改记录健康检查设置
func (this *NSRecordService) UpdateNSRecordHealthCheck(ctx context.Context, req *pb.UpdateNSRecordHealthCheckRequest) (*pb.RPCSuccess, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
// 检查权限
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSRecordDAO.CheckUserRecord(tx, userId, req.NsRecordId)
if err != nil {
return nil, err
}
// TODO 检查套餐
}
if len(req.NsRecordHealthCheckJSON) == 0 {
return nil, errors.New("invalid 'nsRecordHealthCheckJSON'")
}
var healthCheckConfig = dnsconfigs.NewNSRecordHealthCheckConfig()
err = json.Unmarshal(req.NsRecordHealthCheckJSON, healthCheckConfig)
if err != nil {
return nil, errors.New("decode 'nsRecordHealthCheckJSON' failed: " + err.Error())
}
err = nameservers.SharedNSRecordDAO.UpdateRecordHealthCheckConfig(tx, req.NsRecordId, healthCheckConfig)
if err != nil {
return nil, err
}
return this.Success()
}
// UpdateNSRecordIsUp 手动修改记录在线状态
func (this *NSRecordService) UpdateNSRecordIsUp(ctx context.Context, req *pb.UpdateNSRecordIsUpRequest) (*pb.RPCSuccess, error) {
// 检查是否为商业用户
if !teaconst.IsPlus {
return nil, errors.New("non commercial user")
}
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
// 检查权限
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSRecordDAO.CheckUserRecord(tx, userId, req.NsRecordId)
if err != nil {
return nil, err
}
// TODO 检查套餐
}
err = nameservers.SharedNSRecordDAO.UpdateRecordIsUp(tx, req.NsRecordId, req.IsUp, 0, 0)
if err != nil {
return nil, err
}
return this.Success()
}

View File

@@ -0,0 +1,170 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package nameservers
import (
"context"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
timeutil "github.com/iwind/TeaGo/utils/time"
"regexp"
"time"
)
// NSRecordHourlyStatService NS记录小时统计
type NSRecordHourlyStatService struct {
services.BaseService
}
// UploadNSRecordHourlyStats 上传统计
func (this *NSRecordHourlyStatService) UploadNSRecordHourlyStats(ctx context.Context, req *pb.UploadNSRecordHourlyStatsRequest) (*pb.RPCSuccess, error) {
_, nodeId, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeDNS)
if err != nil {
return nil, err
}
if nodeId <= 0 {
return nil, errors.New("invalid nodeId")
}
if len(req.Stats) == 0 {
return this.Success()
}
var tx = this.NullTx()
clusterId, err := models.SharedNSNodeDAO.FindNodeClusterId(tx, nodeId)
if err != nil {
return nil, err
}
// 增加小时统计
for _, stat := range req.Stats {
err := nameservers.SharedNSRecordHourlyStatDAO.IncreaseHourlyStat(tx, clusterId, nodeId, timeutil.FormatTime("YmdH", stat.CreatedAt), stat.NsDomainId, stat.NsRecordId, stat.CountRequests, stat.Bytes)
if err != nil {
return nil, err
}
}
return this.Success()
}
// FindNSRecordHourlyStat 获取单个记录单个小时的统计
func (this *NSRecordHourlyStatService) FindNSRecordHourlyStat(ctx context.Context, req *pb.FindNSRecordHourlyStatRequest) (*pb.FindNSRecordHourlyStatResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
if len(req.Hour) == 0 {
req.Hour = timeutil.Format("YmdH")
} else if !regexp.MustCompile(`^\d{10}$`).MatchString(req.Hour) {
return nil, errors.New("invalid hour '" + req.Hour + "'")
}
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSRecordDAO.CheckUserRecord(tx, userId, req.NsRecordId)
if err != nil {
return nil, err
}
}
stat, err := nameservers.SharedNSRecordHourlyStatDAO.FindHourlyStatWithRecordId(tx, req.NsRecordId, req.Hour)
if err != nil {
return nil, err
}
if stat == nil {
return &pb.FindNSRecordHourlyStatResponse{NsRecordHourlyStat: nil}, nil
}
return &pb.FindNSRecordHourlyStatResponse{
NsRecordHourlyStat: &pb.NSRecordHourlyStat{
NsRecordId: req.NsRecordId,
Bytes: int64(stat.Bytes),
CountRequests: int64(stat.CountRequests),
Hour: req.Hour,
},
}, nil
}
// FindLatestNSRecordsHourlyStats 获取单个记录24小时内的统计
func (this *NSRecordHourlyStatService) FindLatestNSRecordsHourlyStats(ctx context.Context, req *pb.FindLatestNSRecordsHourlyStatsRequest) (*pb.FindLatestNSRecordsHourlyStatsResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSRecordDAO.CheckUserRecord(tx, userId, req.NsRecordId)
if err != nil {
return nil, err
}
}
stats, err := nameservers.SharedNSRecordHourlyStatDAO.FindHourlyStatsWithRecordId(tx, req.NsRecordId, timeutil.Format("YmdH", time.Now().Add(-23*time.Hour)), timeutil.Format("YmdH"))
if err != nil {
return nil, err
}
var pbStats = []*pb.NSRecordHourlyStat{}
for _, stat := range stats {
pbStats = append(pbStats, &pb.NSRecordHourlyStat{
NsRecordId: req.NsRecordId,
Bytes: int64(stat.Bytes),
CountRequests: int64(stat.CountRequests),
Hour: stat.Hour,
})
}
return &pb.FindLatestNSRecordsHourlyStatsResponse{
NsRecordHourlyStats: pbStats,
}, nil
}
// FindNSRecordHourlyStatWithRecordIds 批量获取一组记录的统计
func (this *NSRecordHourlyStatService) FindNSRecordHourlyStatWithRecordIds(ctx context.Context, req *pb.FindNSRecordHourlyStatWithRecordIdsRequest) (*pb.FindNSRecordHourlyStatWithRecordIdsResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
if len(req.Hour) == 0 {
req.Hour = timeutil.Format("YmdH")
} else if !regexp.MustCompile(`^\d{10}$`).MatchString(req.Hour) {
return nil, errors.New("invalid hour '" + req.Hour + "'")
}
var tx = this.NullTx()
if userId > 0 {
for _, recordId := range req.NsRecordIds {
err = nameservers.SharedNSRecordDAO.CheckUserRecord(tx, userId, recordId)
if err != nil {
return nil, err
}
}
}
var pbStats = []*pb.NSRecordHourlyStat{}
for _, recordId := range req.NsRecordIds {
stat, err := nameservers.SharedNSRecordHourlyStatDAO.FindHourlyStatWithRecordId(tx, recordId, req.Hour)
if err != nil {
return nil, err
}
if stat == nil {
continue
}
pbStats = append(pbStats, &pb.NSRecordHourlyStat{
NsRecordId: recordId,
Bytes: int64(stat.Bytes),
CountRequests: int64(stat.CountRequests),
Hour: stat.Hour,
})
}
return &pb.FindNSRecordHourlyStatWithRecordIdsResponse{
NsRecordHourlyStats: pbStats,
}, nil
}

View File

@@ -0,0 +1,614 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package nameservers
import (
"context"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/clients"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/types"
"sort"
)
// NSRouteService 线路相关服务
type NSRouteService struct {
services.BaseService
}
// CreateNSRoute 创建自定义线路
func (this *NSRouteService) CreateNSRoute(ctx context.Context, req *pb.CreateNSRouteRequest) (*pb.CreateNSRouteResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if userId > 0 {
req.UserId = userId
// 暂时不允许在集群和域名下创建线路
req.NsClusterId = 0
req.NsDomainId = 0
}
// TODO 检查线路数限制
// 检查分类是否存在
if req.NsRouteCategoryId > 0 {
if userId > 0 {
err = nameservers.SharedNSRouteCategoryDAO.CheckUserCategory(tx, userId, req.NsRouteCategoryId)
if err != nil {
return nil, err
}
} else {
exists, err := nameservers.SharedNSRouteCategoryDAO.Exist(tx, req.NsRouteCategoryId)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.New("route category id '" + types.String(req.NsRouteCategoryId) + "' not found")
}
}
} else {
req.NsRouteCategoryId = 0
}
routeId, err := nameservers.SharedNSRouteDAO.CreateRoute(tx, req.NsClusterId, req.NsDomainId, req.UserId, req.Name, req.RangesJSON, req.NsRouteCategoryId, req.Priority, req.IsPublic)
if err != nil {
return nil, err
}
return &pb.CreateNSRouteResponse{NsRouteId: routeId}, nil
}
// UpdateNSRoute 修改自定义线路
func (this *NSRouteService) UpdateNSRoute(ctx context.Context, req *pb.UpdateNSRouteRequest) (*pb.RPCSuccess, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查权限
if userId > 0 {
err = nameservers.SharedNSRouteDAO.CheckUserRoute(tx, userId, req.NsRouteId)
if err != nil {
return nil, err
}
}
// 检查分类是否存在
if req.NsRouteCategoryId > 0 {
if userId > 0 {
err = nameservers.SharedNSRouteCategoryDAO.CheckUserCategory(tx, userId, req.NsRouteCategoryId)
if err != nil {
return nil, err
}
} else {
exists, err := nameservers.SharedNSRouteCategoryDAO.Exist(tx, req.NsRouteCategoryId)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.New("route category id '" + types.String(req.NsRouteCategoryId) + "' not found")
}
}
} else {
req.NsRouteCategoryId = 0
}
err = nameservers.SharedNSRouteDAO.UpdateRoute(tx, req.NsRouteId, req.Name, req.RangesJSON, req.NsRouteCategoryId, req.Priority, req.IsPublic, req.IsOn)
if err != nil {
return nil, err
}
return this.Success()
}
// DeleteNSRoute 删除自定义线路
func (this *NSRouteService) DeleteNSRoute(ctx context.Context, req *pb.DeleteNSRouteRequest) (*pb.RPCSuccess, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查权限
if userId > 0 {
err = nameservers.SharedNSRouteDAO.CheckUserRoute(tx, userId, req.NsRouteId)
if err != nil {
return nil, err
}
}
err = nameservers.SharedNSRouteDAO.DisableNSRoute(tx, req.NsRouteId)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSRoute 获取单个自定义路线信息
func (this *NSRouteService) FindNSRoute(ctx context.Context, req *pb.FindNSRouteRequest) (*pb.FindNSRouteResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查权限
if userId > 0 {
err = nameservers.SharedNSRouteDAO.CheckUserRoute(tx, userId, req.NsRouteId)
if err != nil {
return nil, err
}
}
route, err := nameservers.SharedNSRouteDAO.FindEnabledNSRoute(tx, req.NsRouteId)
if err != nil {
return nil, err
}
if route == nil {
return &pb.FindNSRouteResponse{NsRoute: nil}, nil
}
// 集群
var pbCluster *pb.NSCluster
if route.ClusterId > 0 {
cluster, err := models.SharedNSClusterDAO.FindEnabledNSCluster(tx, int64(route.ClusterId))
if err != nil {
return nil, err
}
if cluster != nil {
pbCluster = &pb.NSCluster{
Id: int64(cluster.Id),
IsOn: cluster.IsOn,
Name: cluster.Name,
}
}
}
// 域名
var pbDomain *pb.NSDomain
if route.DomainId > 0 {
domain, err := nameservers.SharedNSDomainDAO.FindEnabledNSDomain(tx, int64(route.DomainId))
if err != nil {
return nil, err
}
if domain != nil {
pbDomain = &pb.NSDomain{
Id: int64(domain.Id),
Name: domain.Name,
IsOn: domain.IsOn,
}
}
}
// 分类
var pbCategory *pb.NSRouteCategory
if route.CategoryId > 0 {
category, err := nameservers.SharedNSRouteCategoryDAO.FindCategory(tx, int64(route.CategoryId))
if err != nil {
return nil, err
}
if category != nil {
pbCategory = &pb.NSRouteCategory{
Id: int64(category.Id),
Name: category.Name,
IsOn: category.IsOn,
}
}
}
return &pb.FindNSRouteResponse{NsRoute: &pb.NSRoute{
Id: int64(route.Id),
IsOn: route.IsOn,
Name: route.Name,
RangesJSON: route.Ranges,
IsPublic: route.IsPublic,
Priority: types.Int32(route.Priority),
NsCluster: pbCluster,
NsDomain: pbDomain,
NsRouteCategory: pbCategory,
}}, nil
}
// CountAllNSRoutes 查询自定义线路数量
func (this *NSRouteService) CountAllNSRoutes(ctx context.Context, req *pb.CountAllNSRoutesRequest) (*pb.RPCCountResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
if userId > 0 {
req.UserId = userId
}
var tx = this.NullTx()
countRoutes, err := nameservers.SharedNSRouteDAO.CountAllEnabledRoutes(tx, req.NsClusterId, req.NsClusterId, req.UserId)
if err != nil {
return nil, err
}
return this.SuccessCount(countRoutes)
}
// FindAllNSRoutes 读取所有自定义线路
func (this *NSRouteService) FindAllNSRoutes(ctx context.Context, req *pb.FindAllNSRoutesRequest) (*pb.FindAllNSRoutesResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if userId > 0 {
req.UserId = userId
}
routes, err := nameservers.SharedNSRouteDAO.FindAllEnabledRoutes(tx, req.NsClusterId, req.NsDomainId, req.UserId, req.NsRouteCategoryId)
if err != nil {
return nil, err
}
var pbRoutes = []*pb.NSRoute{}
for _, route := range routes {
// 集群
var pbCluster *pb.NSCluster
if route.ClusterId > 0 {
cluster, err := models.SharedNSClusterDAO.FindEnabledNSCluster(tx, int64(route.ClusterId))
if err != nil {
return nil, err
}
if cluster != nil {
pbCluster = &pb.NSCluster{
Id: int64(cluster.Id),
IsOn: cluster.IsOn,
Name: cluster.Name,
}
}
}
// 域名
var pbDomain *pb.NSDomain
if route.DomainId > 0 {
domain, err := nameservers.SharedNSDomainDAO.FindEnabledNSDomain(tx, int64(route.DomainId))
if err != nil {
return nil, err
}
if domain != nil {
pbDomain = &pb.NSDomain{
Id: int64(domain.Id),
Name: domain.Name,
IsOn: domain.IsOn,
}
}
}
// 分类
var pbCategory *pb.NSRouteCategory
if route.CategoryId > 0 {
category, err := nameservers.SharedNSRouteCategoryDAO.FindCategory(tx, int64(route.CategoryId))
if err != nil {
return nil, err
}
if category != nil {
pbCategory = &pb.NSRouteCategory{
Id: int64(category.Id),
Name: category.Name,
IsOn: category.IsOn,
Order: types.Int32(category.Order),
}
}
}
pbRoutes = append(pbRoutes, &pb.NSRoute{
Id: int64(route.Id),
IsOn: route.IsOn,
Code: "id:" + types.String(route.Id),
Name: route.Name,
IsPublic: route.IsPublic,
RangesJSON: route.Ranges,
Order: types.Int32(route.Order),
Priority: types.Int32(route.Priority),
NsCluster: pbCluster,
NsDomain: pbDomain,
NsRouteCategory: pbCategory,
})
}
// 按照分类排序
if len(pbRoutes) > 0 {
sort.Slice(pbRoutes, func(i, j int) bool {
var route1 = pbRoutes[i]
var route2 = pbRoutes[j]
// route1.category = nil
if route1.NsRouteCategory == nil {
if route2.NsRouteCategory == nil {
if route1.Order == route2.Order {
return route1.Id < route2.Id
}
return route1.Order > route2.Order
}
return true
}
// route1.category != nil && route2.category = nil
if route2.NsRouteCategory == nil {
return false
}
// 同一个分类
if route1.NsRouteCategory.Id == route2.NsRouteCategory.Id {
if route1.Order == route2.Order {
return route1.Id < route2.Id
}
return route1.Order > route2.Order
}
if route1.NsRouteCategory.Order == route2.NsRouteCategory.Order {
return route1.NsRouteCategory.Id < route2.NsRouteCategory.Id
}
return route1.NsRouteCategory.Order > route2.NsRouteCategory.Order
})
}
return &pb.FindAllNSRoutesResponse{NsRoutes: pbRoutes}, nil
}
// FindAllPublicNSRoutes 读取所有公用的自定义线路
// 目前只允许读取系统管理员设置的公用自定义线路
func (this *NSRouteService) FindAllPublicNSRoutes(ctx context.Context, req *pb.FindAllPublicRoutesRequest) (*pb.FindAllPublicRoutesResponse, error) {
_, err := this.ValidateUserNode(ctx, false)
if err != nil {
return nil, err
}
var tx = this.NullTx()
routes, err := nameservers.SharedNSRouteDAO.FindAllPublicRoutes(tx)
if err != nil {
return nil, err
}
var pbRoutes = []*pb.NSRoute{}
for _, route := range routes {
// 分类
var pbCategory *pb.NSRouteCategory
if route.CategoryId > 0 {
category, err := nameservers.SharedNSRouteCategoryDAO.FindCategory(tx, int64(route.CategoryId))
if err != nil {
return nil, err
}
if category != nil {
// 如果分类未启用,则当前分类下面的线路也不显示
if !category.IsOn {
continue
}
pbCategory = &pb.NSRouteCategory{
Id: int64(category.Id),
Name: category.Name,
IsOn: category.IsOn,
Order: types.Int32(category.Order),
}
}
}
pbRoutes = append(pbRoutes, &pb.NSRoute{
Id: int64(route.Id),
IsOn: route.IsOn,
Code: "id:" + types.String(route.Id),
Name: route.Name,
IsPublic: route.IsPublic,
RangesJSON: route.Ranges,
Order: types.Int32(route.Order),
Priority: types.Int32(route.Priority),
NsCluster: nil,
NsDomain: nil,
NsRouteCategory: pbCategory,
})
}
// 按照分类排序
if len(pbRoutes) > 0 {
sort.Slice(pbRoutes, func(i, j int) bool {
var route1 = pbRoutes[i]
var route2 = pbRoutes[j]
// route1.category = nil
if route1.NsRouteCategory == nil {
if route2.NsRouteCategory == nil {
if route1.Order == route2.Order {
return route1.Id < route2.Id
}
return route1.Order > route2.Order
}
return true
}
// route1.category != nil && route2.category = nil
if route2.NsRouteCategory == nil {
return false
}
// 同一个分类
if route1.NsRouteCategory.Id == route2.NsRouteCategory.Id {
if route1.Order == route2.Order {
return route1.Id < route2.Id
}
return route1.Order > route2.Order
}
if route1.NsRouteCategory.Order == route2.NsRouteCategory.Order {
return route1.NsRouteCategory.Id < route2.NsRouteCategory.Id
}
return route1.NsRouteCategory.Order > route2.NsRouteCategory.Order
})
}
return &pb.FindAllPublicRoutesResponse{NsRoutes: pbRoutes}, nil
}
// UpdateNSRouteOrders 设置自定义线路排序
func (this *NSRouteService) UpdateNSRouteOrders(ctx context.Context, req *pb.UpdateNSRouteOrdersRequest) (*pb.RPCSuccess, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
// 检查权限
if userId > 0 {
for _, routeId := range req.NsRouteIds {
err = nameservers.SharedNSRouteDAO.CheckUserRoute(tx, userId, routeId)
if err != nil {
return nil, err
}
}
}
err = nameservers.SharedNSRouteDAO.UpdateRouteOrders(tx, req.NsRouteIds)
if err != nil {
return nil, err
}
return this.Success()
}
// ListNSRoutesAfterVersion 根据版本列出一组自定义线路
func (this *NSRouteService) ListNSRoutesAfterVersion(ctx context.Context, req *pb.ListNSRoutesAfterVersionRequest) (*pb.ListNSRoutesAfterVersionResponse, error) {
_, _, err := this.ValidateNodeId(ctx, rpcutils.UserTypeDNS)
if err != nil {
return nil, err
}
// 集群ID
var tx = this.NullTx()
routes, err := nameservers.SharedNSRouteDAO.ListRoutesAfterVersion(tx, req.Version, 2000)
if err != nil {
return nil, err
}
var pbRoutes []*pb.NSRoute
for _, route := range routes {
// 集群
var pbCluster *pb.NSCluster
if route.ClusterId > 0 {
pbCluster = &pb.NSCluster{Id: int64(route.ClusterId)}
}
// 域名
var pbDomain *pb.NSDomain
if route.DomainId > 0 {
pbDomain = &pb.NSDomain{Id: int64(route.DomainId)}
}
pbRoutes = append(pbRoutes, &pb.NSRoute{
Id: int64(route.Id),
IsOn: route.IsOn,
Name: "",
RangesJSON: route.Ranges,
IsDeleted: route.State == nameservers.NSRouteStateDisabled,
Order: types.Int32(route.Order),
Priority: types.Int32(route.Priority),
Version: int64(route.Version),
UserId: types.Int64(route.UserId),
NsCluster: pbCluster,
NsDomain: pbDomain,
})
}
return &pb.ListNSRoutesAfterVersionResponse{NsRoutes: pbRoutes}, nil
}
// FindAllDefaultWorldRegionRoutes 查找默认的世界区域线路
func (this *NSRouteService) FindAllDefaultWorldRegionRoutes(ctx context.Context, req *pb.FindAllDefaultWorldRegionRoutesRequest) (*pb.FindAllDefaultWorldRegionRoutesResponse, error) {
_, _, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var pbRoutes = []*pb.NSRoute{}
for _, route := range dnsconfigs.AllDefaultWorldRegionRoutes {
pbRoutes = append(pbRoutes, &pb.NSRoute{
Code: route.Code,
Name: route.Name,
})
}
return &pb.FindAllDefaultWorldRegionRoutesResponse{
NsRoutes: pbRoutes,
}, nil
}
// FindAllDefaultChinaProvinceRoutes 查找默认的中国省份线路
func (this *NSRouteService) FindAllDefaultChinaProvinceRoutes(ctx context.Context, req *pb.FindAllDefaultChinaProvinceRoutesRequest) (*pb.FindAllDefaultChinaProvinceRoutesResponse, error) {
_, _, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var pbRoutes = []*pb.NSRoute{}
for _, route := range dnsconfigs.AllDefaultChinaProvinceRoutes {
pbRoutes = append(pbRoutes, &pb.NSRoute{
Code: route.Code,
Name: route.Name,
})
}
return &pb.FindAllDefaultChinaProvinceRoutesResponse{
NsRoutes: pbRoutes,
}, nil
}
// FindAllDefaultISPRoutes 查找默认的ISP线路
func (this *NSRouteService) FindAllDefaultISPRoutes(ctx context.Context, req *pb.FindAllDefaultISPRoutesRequest) (*pb.FindAllDefaultISPRoutesResponse, error) {
_, _, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var pbRoutes = []*pb.NSRoute{}
for _, route := range dnsconfigs.AllDefaultISPRoutes {
pbRoutes = append(pbRoutes, &pb.NSRoute{
Code: route.Code,
Name: route.Name,
})
}
return &pb.FindAllDefaultISPRoutesResponse{
NsRoutes: pbRoutes,
}, nil
}
// FindAllAgentNSRoutes 查找默认的搜索引擎线路
func (this *NSRouteService) FindAllAgentNSRoutes(ctx context.Context, req *pb.FindAllAgentNSRoutesRequest) (*pb.FindAllAgentNSRoutesResponse, error) {
_, _, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
agents, err := clients.SharedClientAgentDAO.FindAllNSAgents(tx)
if err != nil {
return nil, err
}
var pbRoutes = []*pb.NSRoute{}
for _, agent := range agents {
pbRoutes = append(pbRoutes, &pb.NSRoute{
Code: agent.NSRouteCode(),
Name: agent.Name,
})
}
pbRoutes = append(pbRoutes, &pb.NSRoute{
Code: "agent",
Name: "搜索引擎",
})
return &pb.FindAllAgentNSRoutesResponse{NsRoutes: pbRoutes}, nil
}

View File

@@ -0,0 +1,171 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package nameservers
import (
"context"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
// NSRouteCategoryService 线路分类服务
type NSRouteCategoryService struct {
services.BaseService
}
// CreateNSRouteCategory 创建线路分类
func (this *NSRouteCategoryService) CreateNSRouteCategory(ctx context.Context, req *pb.CreateNSRouteCategoryRequest) (*pb.CreateNSRouteCategoryResponse, error) {
// TODO 需要防止用户恶意创建非常多的分类
adminId, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
categoryId, err := nameservers.SharedNSRouteCategoryDAO.CreateCategory(tx, adminId, userId, req.Name)
if err != nil {
return nil, err
}
return &pb.CreateNSRouteCategoryResponse{NsRouteCategoryId: categoryId}, nil
}
// UpdateNSRouteCategory 修改线路分类
func (this *NSRouteCategoryService) UpdateNSRouteCategory(ctx context.Context, req *pb.UpdateNSRouteCategoryRequest) (*pb.RPCSuccess, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSRouteCategoryDAO.CheckUserCategory(tx, userId, req.NsRouteCategoryId)
if err != nil {
return nil, err
}
}
err = nameservers.SharedNSRouteCategoryDAO.UpdateCategory(tx, req.NsRouteCategoryId, req.Name, req.IsOn)
if err != nil {
return nil, err
}
return this.Success()
}
// DeleteNSRouteCategory 删除线路分类
func (this *NSRouteCategoryService) DeleteNSRouteCategory(ctx context.Context, req *pb.DeleteNSRouteCategoryRequest) (*pb.RPCSuccess, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSRouteCategoryDAO.CheckUserCategory(tx, userId, req.NsRouteCategoryId)
if err != nil {
return nil, err
}
}
err = nameservers.SharedNSRouteCategoryDAO.DisableNSRouteCategory(tx, req.NsRouteCategoryId)
if err != nil {
return nil, err
}
// 重置线路
err = nameservers.SharedNSRouteDAO.ResetRoutesCategory(tx, req.NsRouteCategoryId)
if err != nil {
return nil, err
}
return this.Success()
}
// FindAllNSRouteCategories 列出所有线路分类
func (this *NSRouteCategoryService) FindAllNSRouteCategories(ctx context.Context, req *pb.FindAllNSRouteCategoriesRequest) (*pb.FindAllNSRouteCategoriesResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
categories, err := nameservers.SharedNSRouteCategoryDAO.FindAllCategories(tx, userId)
if err != nil {
return nil, err
}
var pbCategories = []*pb.NSRouteCategory{}
for _, category := range categories {
pbCategories = append(pbCategories, &pb.NSRouteCategory{
Id: int64(category.Id),
Name: category.Name,
IsOn: category.IsOn,
})
}
return &pb.FindAllNSRouteCategoriesResponse{
NsRouteCategories: pbCategories,
}, nil
}
// UpdateNSRouteCategoryOrders 对线路分类进行排序
func (this *NSRouteCategoryService) UpdateNSRouteCategoryOrders(ctx context.Context, req *pb.UpdateNSRouteCategoryOrders) (*pb.RPCSuccess, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if userId > 0 {
for _, categoryId := range req.NsRouteCategoryIds {
err = nameservers.SharedNSRouteCategoryDAO.CheckUserCategory(tx, userId, categoryId)
if err != nil {
return nil, err
}
}
}
err = nameservers.SharedNSRouteCategoryDAO.UpdateCategoryOrders(tx, userId, req.NsRouteCategoryIds)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSRouteCategory 查找单个线路分类
func (this *NSRouteCategoryService) FindNSRouteCategory(ctx context.Context, req *pb.FindNSRouteCategoryRequest) (*pb.FindNSRouteCategoryResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if userId > 0 {
err = nameservers.SharedNSRouteCategoryDAO.CheckUserCategory(tx, userId, req.NsRouteCategoryId)
if err != nil {
return nil, err
}
}
category, err := nameservers.SharedNSRouteCategoryDAO.FindCategory(tx, req.NsRouteCategoryId)
if err != nil {
return nil, err
}
if category == nil {
return &pb.FindNSRouteCategoryResponse{
NsRouteCategory: nil,
}, nil
}
return &pb.FindNSRouteCategoryResponse{
NsRouteCategory: &pb.NSRouteCategory{
Id: int64(category.Id),
Name: category.Name,
IsOn: category.IsOn,
},
}, nil
}

View File

@@ -0,0 +1,370 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package nameservers
import (
"context"
"errors"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/accounts"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
// NSUserPlanService 用户DNS套餐服务
type NSUserPlanService struct {
services.BaseService
}
// CreateNSUserPlan 创建用户套餐
func (this *NSUserPlanService) CreateNSUserPlan(ctx context.Context, req *pb.CreateNSUserPlanRequest) (*pb.CreateNSUserPlanResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var dayFrom = req.DayFrom
var dayTo = req.DayTo
if !regexputils.YYYYMMDD.MatchString(dayFrom) {
return nil, errors.New("invalid dayFrom: " + dayFrom)
}
if !regexputils.YYYYMMDD.MatchString(dayTo) {
return nil, errors.New("invalid dayTo: " + dayTo)
}
if !lists.ContainsString([]string{nameservers.NSUserPlanPeriodUnitMonthly, nameservers.NSUserPlanPeriodUnitYearly}, req.PeriodUnit) {
return nil, errors.New("invalid periodUnit: " + req.PeriodUnit)
}
// 检查plan是否存在
var tx = this.NullTx()
existPlan, err := nameservers.SharedNSPlanDAO.ExistPlan(tx, req.NsPlanId)
if err != nil {
return nil, err
}
if !existPlan {
return nil, errors.New("plan '" + types.String(req.NsPlanId) + "' not found")
}
// 用户Plan是否存在
var resultUserPlanId int64
err = this.RunTx(func(tx *dbs.Tx) error {
userPlan, err := nameservers.SharedNSUserPlanDAO.FindUserPlan(tx, req.UserId)
if err != nil {
return err
}
if userPlan == nil {
userPlanId, err := nameservers.SharedNSUserPlanDAO.CreateUserPlan(tx, req.UserId, req.NsPlanId, dayFrom, dayTo, req.PeriodUnit)
if err != nil {
return err
}
resultUserPlanId = userPlanId
} else {
err = nameservers.SharedNSUserPlanDAO.UpdateUserPlan(tx, int64(userPlan.Id), req.NsPlanId, dayFrom, dayTo, req.PeriodUnit)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
return &pb.CreateNSUserPlanResponse{NsUserPlanId: resultUserPlanId}, nil
}
// UpdateNSUserPlan 修改用户套餐
func (this *NSUserPlanService) UpdateNSUserPlan(ctx context.Context, req *pb.UpdateNSUserPlanRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var dayFrom = req.DayFrom
var dayTo = req.DayTo
if !regexputils.YYYYMMDD.MatchString(dayFrom) {
return nil, errors.New("invalid dayFrom: " + dayFrom)
}
if !regexputils.YYYYMMDD.MatchString(dayTo) {
return nil, errors.New("invalid dayTo: " + dayTo)
}
if !lists.ContainsString([]string{nameservers.NSUserPlanPeriodUnitMonthly, nameservers.NSUserPlanPeriodUnitYearly}, req.PeriodUnit) {
return nil, errors.New("invalid periodUnit: " + req.PeriodUnit)
}
var tx = this.NullTx()
err = nameservers.SharedNSUserPlanDAO.UpdateUserPlan(tx, req.NsUserPlanId, req.NsPlanId, dayFrom, dayTo, req.PeriodUnit)
if err != nil {
return nil, err
}
return this.Success()
}
// DeleteNSUserPlan 删除用户套餐
func (this *NSUserPlanService) DeleteNSUserPlan(ctx context.Context, req *pb.DeleteNSUserPlanRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = nameservers.SharedNSUserPlanDAO.DisableNSUserPlan(tx, req.NsUserPlanId)
if err != nil {
return nil, err
}
return this.Success()
}
// FindNSUserPlan 读取用户套餐
func (this *NSUserPlanService) FindNSUserPlan(ctx context.Context, req *pb.FindNSUserPlanRequest) (*pb.FindNSUserPlanResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, true)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if userId > 0 {
req.UserId = userId
}
var userPlan *nameservers.NSUserPlan
if req.NsUserPlanId > 0 {
userPlan, err = nameservers.SharedNSUserPlanDAO.FindEnabledNSUserPlan(tx, req.NsUserPlanId)
} else if req.UserId > 0 {
userPlan, err = nameservers.SharedNSUserPlanDAO.FindUserPlan(tx, req.UserId)
} else {
return &pb.FindNSUserPlanResponse{NsUserPlan: nil}, nil
}
if err != nil {
return nil, err
}
if userPlan == nil {
return &pb.FindNSUserPlanResponse{NsUserPlan: nil}, nil
}
plan, err := nameservers.SharedNSPlanDAO.FindEnabledNSPlan(tx, int64(userPlan.PlanId))
if err != nil {
return nil, err
}
if plan == nil {
return &pb.FindNSUserPlanResponse{NsUserPlan: nil}, nil
}
// user
user, err := models.SharedUserDAO.FindEnabledBasicUser(tx, int64(userPlan.UserId))
if err != nil {
return nil, err
}
var pbUser *pb.User
if user != nil {
pbUser = &pb.User{
Id: int64(user.Id),
Username: user.Username,
Fullname: user.Fullname,
}
}
return &pb.FindNSUserPlanResponse{
NsUserPlan: &pb.NSUserPlan{
Id: int64(userPlan.Id),
NsPlanId: int64(userPlan.PlanId),
DayFrom: userPlan.DayFrom,
DayTo: userPlan.DayTo,
PeriodUnit: userPlan.PeriodUnit,
NsPlan: &pb.NSPlan{
Id: int64(plan.Id),
Name: plan.Name,
IsOn: plan.IsOn,
MonthlyPrice: float32(plan.MonthlyPrice),
YearlyPrice: float32(plan.YearlyPrice),
ConfigJSON: plan.Config,
},
User: pbUser,
},
}, nil
}
// CountNSUserPlans 计算用户套餐数量
func (this *NSUserPlanService) CountNSUserPlans(ctx context.Context, req *pb.CountNSUserPlansRequest) (*pb.RPCCountResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
count, err := nameservers.SharedNSUserPlanDAO.CountUserPlans(tx, req.UserId, req.NsPlanId, req.PeriodUnit, req.IsExpired, req.ExpireDays)
if err != nil {
return nil, err
}
return this.SuccessCount(count)
}
// ListNSUserPlans 列出单页套餐
func (this *NSUserPlanService) ListNSUserPlans(ctx context.Context, req *pb.ListNSUserPlansRequest) (*pb.ListNSUserPlansResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
userPlans, err := nameservers.SharedNSUserPlanDAO.ListUserPlans(tx, req.UserId, req.NsPlanId, req.PeriodUnit, req.IsExpired, req.ExpireDays, req.Offset, req.Size)
if err != nil {
return nil, err
}
var pbUserPlans = []*pb.NSUserPlan{}
for _, userPlan := range userPlans {
// plan
plan, err := nameservers.SharedNSPlanDAO.FindEnabledNSPlan(tx, int64(userPlan.PlanId))
if err != nil {
return nil, err
}
if plan == nil {
continue
}
// user
user, err := models.SharedUserDAO.FindEnabledBasicUser(tx, int64(userPlan.UserId))
if err != nil {
return nil, err
}
var pbUser *pb.User
if user != nil {
pbUser = &pb.User{
Id: int64(user.Id),
Username: user.Username,
Fullname: user.Fullname,
}
}
pbUserPlans = append(pbUserPlans, &pb.NSUserPlan{
Id: int64(userPlan.Id),
NsPlanId: int64(userPlan.PlanId),
UserId: int64(userPlan.UserId),
DayFrom: userPlan.DayFrom,
DayTo: userPlan.DayTo,
PeriodUnit: userPlan.PeriodUnit,
NsPlan: &pb.NSPlan{
Id: int64(plan.Id),
Name: plan.Name,
IsOn: plan.IsOn,
MonthlyPrice: float32(plan.MonthlyPrice),
YearlyPrice: float32(plan.YearlyPrice),
ConfigJSON: plan.Config,
},
User: pbUser,
})
}
return &pb.ListNSUserPlansResponse{
NsUserPlans: pbUserPlans,
}, nil
}
// BuyNSUserPlan 使用余额购买用户套餐
func (this *NSUserPlanService) BuyNSUserPlan(ctx context.Context, req *pb.BuyNSUserPlanRequest) (*pb.BuyNSUserPlanResponse, error) {
_, userId, err := this.ValidateAdminAndUser(ctx, false)
if err != nil {
return nil, err
}
if userId > 0 {
req.UserId = userId
}
userId = req.UserId
// 查询套餐
var tx *dbs.Tx
plan, err := nameservers.SharedNSPlanDAO.FindEnabledNSPlan(tx, req.PlanId)
if err != nil {
return nil, err
}
if plan == nil || !plan.IsOn {
return nil, errors.New("could not find plan with id '" + types.String(req.PlanId) + "'")
}
var dayFrom = timeutil.Format("Ymd")
var dayTo = ""
var price float64
switch req.Period {
case "yearly":
price = plan.YearlyPrice
dayTo = timeutil.Format("Ymd", time.Now().AddDate(1, 0, 0))
case "monthly":
price = plan.MonthlyPrice
dayTo = timeutil.Format("Ymd", time.Now().AddDate(0, 1, 0))
default:
return nil, errors.New("invalid period '" + req.Period + "'")
}
var userPlanId int64
err = this.RunTx(func(tx *dbs.Tx) error {
// 当前是否有套餐在有效期
userPlan, err := nameservers.SharedNSUserPlanDAO.FindUserPlan(tx, userId)
if err != nil {
return err
}
if userPlan != nil && userPlan.IsAvailable() {
return errors.New("there is an available user plan yet, you can not buy again")
}
// 如果是0价格则不允许购买
if price <= 0 {
return errors.New("invalid plan price")
}
// 先减少余额
account, err := accounts.SharedUserAccountDAO.FindUserAccountWithUserId(tx, userId)
if err != nil {
return err
}
if account == nil || account.Total < price {
return errors.New("no enough balance to buy the plan")
}
err = accounts.SharedUserAccountDAO.UpdateUserAccount(tx, int64(account.Id), -price, userconfigs.AccountEventTypeBuyNSPlan, "购买DNS套餐\""+plan.Name+"\"", maps.Map{
"nsPlanId": plan.Id,
})
if err != nil {
return err
}
// 创建套餐
userPlanId, err = nameservers.SharedNSUserPlanDAO.CreateUserPlan(tx, userId, req.PlanId, dayFrom, dayTo, req.Period)
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
return &pb.BuyNSUserPlanResponse{UserPlanId: userPlanId}, nil
}