管理端全部功能跑通
This commit is contained in:
125
EdgeAPI/internal/rpc/services/httpdns/converters.go
Normal file
125
EdgeAPI/internal/rpc/services/httpdns/converters.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package httpdns
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
func toPBCluster(cluster *models.HTTPDNSCluster) *pb.HTTPDNSCluster {
|
||||
if cluster == nil {
|
||||
return nil
|
||||
}
|
||||
return &pb.HTTPDNSCluster{
|
||||
Id: int64(cluster.Id),
|
||||
IsOn: cluster.IsOn,
|
||||
IsDefault: cluster.IsDefault,
|
||||
Name: cluster.Name,
|
||||
ServiceDomain: cluster.ServiceDomain,
|
||||
DefaultTTL: cluster.DefaultTTL,
|
||||
FallbackTimeoutMs: cluster.FallbackTimeoutMs,
|
||||
InstallDir: cluster.InstallDir,
|
||||
TlsPolicyJSON: cluster.TLSPolicy,
|
||||
CreatedAt: int64(cluster.CreatedAt),
|
||||
UpdatedAt: int64(cluster.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
func toPBNode(node *models.HTTPDNSNode) *pb.HTTPDNSNode {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
return &pb.HTTPDNSNode{
|
||||
Id: int64(node.Id),
|
||||
ClusterId: int64(node.ClusterId),
|
||||
Name: node.Name,
|
||||
IsOn: node.IsOn,
|
||||
IsUp: node.IsUp,
|
||||
IsInstalled: node.IsInstalled,
|
||||
IsActive: node.IsActive,
|
||||
UniqueId: node.UniqueId,
|
||||
Secret: node.Secret,
|
||||
InstallDir: node.InstallDir,
|
||||
StatusJSON: node.Status,
|
||||
InstallStatusJSON: node.InstallStatus,
|
||||
CreatedAt: int64(node.CreatedAt),
|
||||
UpdatedAt: int64(node.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
func toPBApp(app *models.HTTPDNSApp, secret *models.HTTPDNSAppSecret) *pb.HTTPDNSApp {
|
||||
if app == nil {
|
||||
return nil
|
||||
}
|
||||
var signEnabled bool
|
||||
var signSecret string
|
||||
var signUpdatedAt int64
|
||||
if secret != nil {
|
||||
signEnabled = secret.SignEnabled
|
||||
signSecret = secret.SignSecret
|
||||
signUpdatedAt = int64(secret.SignUpdatedAt)
|
||||
}
|
||||
return &pb.HTTPDNSApp{
|
||||
Id: int64(app.Id),
|
||||
Name: app.Name,
|
||||
AppId: app.AppId,
|
||||
IsOn: app.IsOn,
|
||||
PrimaryClusterId: int64(app.PrimaryClusterId),
|
||||
BackupClusterId: int64(app.BackupClusterId),
|
||||
SniMode: app.SNIMode,
|
||||
SignEnabled: signEnabled,
|
||||
SignSecret: signSecret,
|
||||
SignUpdatedAt: signUpdatedAt,
|
||||
CreatedAt: int64(app.CreatedAt),
|
||||
UpdatedAt: int64(app.UpdatedAt),
|
||||
UserId: int64(app.UserId),
|
||||
}
|
||||
}
|
||||
|
||||
func toPBDomain(domain *models.HTTPDNSDomain, ruleCount int64) *pb.HTTPDNSDomain {
|
||||
if domain == nil {
|
||||
return nil
|
||||
}
|
||||
return &pb.HTTPDNSDomain{
|
||||
Id: int64(domain.Id),
|
||||
AppId: int64(domain.AppId),
|
||||
Domain: domain.Domain,
|
||||
IsOn: domain.IsOn,
|
||||
CreatedAt: int64(domain.CreatedAt),
|
||||
UpdatedAt: int64(domain.UpdatedAt),
|
||||
RuleCount: ruleCount,
|
||||
}
|
||||
}
|
||||
|
||||
func toPBRule(rule *models.HTTPDNSCustomRule, records []*models.HTTPDNSCustomRuleRecord) *pb.HTTPDNSCustomRule {
|
||||
if rule == nil {
|
||||
return nil
|
||||
}
|
||||
var pbRecords []*pb.HTTPDNSRuleRecord
|
||||
for _, record := range records {
|
||||
pbRecords = append(pbRecords, &pb.HTTPDNSRuleRecord{
|
||||
Id: int64(record.Id),
|
||||
RuleId: int64(record.RuleId),
|
||||
RecordType: record.RecordType,
|
||||
RecordValue: record.RecordValue,
|
||||
Weight: record.Weight,
|
||||
Sort: record.Sort,
|
||||
})
|
||||
}
|
||||
return &pb.HTTPDNSCustomRule{
|
||||
Id: int64(rule.Id),
|
||||
AppId: int64(rule.AppId),
|
||||
DomainId: int64(rule.DomainId),
|
||||
RuleName: rule.RuleName,
|
||||
LineScope: rule.LineScope,
|
||||
LineCarrier: rule.LineCarrier,
|
||||
LineRegion: rule.LineRegion,
|
||||
LineProvince: rule.LineProvince,
|
||||
LineContinent: rule.LineContinent,
|
||||
LineCountry: rule.LineCountry,
|
||||
Ttl: rule.TTL,
|
||||
IsOn: rule.IsOn,
|
||||
Priority: rule.Priority,
|
||||
UpdatedAt: int64(rule.UpdatedAt),
|
||||
Records: pbRecords,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
package httpdns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/clickhouse"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type HTTPDNSAccessLogService struct {
|
||||
services.BaseService
|
||||
pb.UnimplementedHTTPDNSAccessLogServiceServer
|
||||
}
|
||||
|
||||
func (s *HTTPDNSAccessLogService) CreateHTTPDNSAccessLogs(ctx context.Context, req *pb.CreateHTTPDNSAccessLogsRequest) (*pb.CreateHTTPDNSAccessLogsResponse, error) {
|
||||
nodeIdInContext, err := s.ValidateHTTPDNSNode(ctx)
|
||||
if err != nil {
|
||||
_, err = s.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(req.GetLogs()) == 0 {
|
||||
return &pb.CreateHTTPDNSAccessLogsResponse{}, nil
|
||||
}
|
||||
|
||||
mysqlLogs := make([]*models.HTTPDNSAccessLog, 0, len(req.GetLogs()))
|
||||
chLogs := make([]*pb.HTTPDNSAccessLog, 0, len(req.GetLogs()))
|
||||
seen := map[string]struct{}{}
|
||||
for _, item := range req.GetLogs() {
|
||||
if item == nil {
|
||||
continue
|
||||
}
|
||||
nodeId := item.GetNodeId()
|
||||
// When called by HTTPDNS node, trust node id parsed from RPC token.
|
||||
if nodeIdInContext > 0 {
|
||||
nodeId = nodeIdInContext
|
||||
}
|
||||
clusterId := item.GetClusterId()
|
||||
if clusterId <= 0 && nodeId > 0 {
|
||||
clusterId, _ = models.SharedHTTPDNSNodeDAO.FindNodeClusterId(s.NullTx(), nodeId)
|
||||
}
|
||||
|
||||
key := item.GetRequestId() + "#" + strconv.FormatInt(nodeId, 10)
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
|
||||
createdAt := item.GetCreatedAt()
|
||||
if createdAt <= 0 {
|
||||
createdAt = time.Now().Unix()
|
||||
}
|
||||
day := item.GetDay()
|
||||
if len(day) == 0 {
|
||||
day = timeutil.Format("Ymd")
|
||||
}
|
||||
|
||||
mysqlLogs = append(mysqlLogs, &models.HTTPDNSAccessLog{
|
||||
RequestId: item.GetRequestId(),
|
||||
ClusterId: uint32(clusterId),
|
||||
NodeId: uint32(nodeId),
|
||||
AppId: item.GetAppId(),
|
||||
AppName: item.GetAppName(),
|
||||
Domain: item.GetDomain(),
|
||||
QType: item.GetQtype(),
|
||||
ClientIP: item.GetClientIP(),
|
||||
ClientRegion: item.GetClientRegion(),
|
||||
Carrier: item.GetCarrier(),
|
||||
SDKVersion: item.GetSdkVersion(),
|
||||
OS: item.GetOs(),
|
||||
ResultIPs: item.GetResultIPs(),
|
||||
Status: item.GetStatus(),
|
||||
ErrorCode: item.GetErrorCode(),
|
||||
CostMs: item.GetCostMs(),
|
||||
CreatedAt: uint64(createdAt),
|
||||
Day: day,
|
||||
Summary: item.GetSummary(),
|
||||
})
|
||||
|
||||
chLogs = append(chLogs, &pb.HTTPDNSAccessLog{
|
||||
RequestId: item.GetRequestId(),
|
||||
ClusterId: clusterId,
|
||||
NodeId: nodeId,
|
||||
AppId: item.GetAppId(),
|
||||
AppName: item.GetAppName(),
|
||||
Domain: item.GetDomain(),
|
||||
Qtype: item.GetQtype(),
|
||||
ClientIP: item.GetClientIP(),
|
||||
ClientRegion: item.GetClientRegion(),
|
||||
Carrier: item.GetCarrier(),
|
||||
SdkVersion: item.GetSdkVersion(),
|
||||
Os: item.GetOs(),
|
||||
ResultIPs: item.GetResultIPs(),
|
||||
Status: item.GetStatus(),
|
||||
ErrorCode: item.GetErrorCode(),
|
||||
CostMs: item.GetCostMs(),
|
||||
CreatedAt: createdAt,
|
||||
Day: day,
|
||||
Summary: item.GetSummary(),
|
||||
})
|
||||
}
|
||||
|
||||
if s.canWriteHTTPDNSAccessLogsToMySQL() {
|
||||
for _, item := range mysqlLogs {
|
||||
err = models.SharedHTTPDNSAccessLogDAO.CreateLog(s.NullTx(), item)
|
||||
if err != nil {
|
||||
if models.CheckSQLDuplicateErr(err) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
store := clickhouse.NewHTTPDNSAccessLogsStore()
|
||||
if s.canWriteHTTPDNSAccessLogsToClickHouse() && store.Client().IsConfigured() && len(chLogs) > 0 {
|
||||
err = store.Insert(ctx, chLogs)
|
||||
if err != nil {
|
||||
log.Println("[HTTPDNS_ACCESS_LOG]write clickhouse failed, keep mysql success:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.CreateHTTPDNSAccessLogsResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *HTTPDNSAccessLogService) ListHTTPDNSAccessLogs(ctx context.Context, req *pb.ListHTTPDNSAccessLogsRequest) (*pb.ListHTTPDNSAccessLogsResponse, error) {
|
||||
_, _, err := s.ValidateAdminAndUser(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
store := clickhouse.NewHTTPDNSAccessLogsStore()
|
||||
canReadFromClickHouse := s.shouldReadHTTPDNSAccessLogsFromClickHouse() && store.Client().IsConfigured()
|
||||
canReadFromMySQL := s.shouldReadHTTPDNSAccessLogsFromMySQL()
|
||||
if canReadFromClickHouse {
|
||||
resp, listErr := s.listFromClickHouse(ctx, store, req)
|
||||
if listErr == nil {
|
||||
return resp, nil
|
||||
}
|
||||
log.Println("[HTTPDNS_ACCESS_LOG]read clickhouse failed, fallback mysql:", listErr.Error())
|
||||
if !canReadFromMySQL {
|
||||
return nil, listErr
|
||||
}
|
||||
}
|
||||
|
||||
if !canReadFromMySQL {
|
||||
return &pb.ListHTTPDNSAccessLogsResponse{
|
||||
Logs: []*pb.HTTPDNSAccessLog{},
|
||||
Total: 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
total, err := models.SharedHTTPDNSAccessLogDAO.CountLogs(s.NullTx(), req.GetDay(), req.GetClusterId(), req.GetNodeId(), req.GetAppId(), req.GetDomain(), req.GetStatus(), req.GetKeyword())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logs, err := models.SharedHTTPDNSAccessLogDAO.ListLogs(s.NullTx(), req.GetDay(), req.GetClusterId(), req.GetNodeId(), req.GetAppId(), req.GetDomain(), req.GetStatus(), req.GetKeyword(), req.GetOffset(), req.GetSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]*pb.HTTPDNSAccessLog, 0, len(logs))
|
||||
for _, item := range logs {
|
||||
if item == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
clusterName, _ := models.SharedHTTPDNSClusterDAO.FindEnabledClusterName(s.NullTx(), int64(item.ClusterId))
|
||||
nodeName := ""
|
||||
node, _ := models.SharedHTTPDNSNodeDAO.FindEnabledNode(s.NullTx(), int64(item.NodeId))
|
||||
if node != nil {
|
||||
nodeName = node.Name
|
||||
}
|
||||
|
||||
result = append(result, &pb.HTTPDNSAccessLog{
|
||||
Id: int64(item.Id),
|
||||
RequestId: item.RequestId,
|
||||
ClusterId: int64(item.ClusterId),
|
||||
NodeId: int64(item.NodeId),
|
||||
AppId: item.AppId,
|
||||
AppName: item.AppName,
|
||||
Domain: item.Domain,
|
||||
Qtype: item.QType,
|
||||
ClientIP: item.ClientIP,
|
||||
ClientRegion: item.ClientRegion,
|
||||
Carrier: item.Carrier,
|
||||
SdkVersion: item.SDKVersion,
|
||||
Os: item.OS,
|
||||
ResultIPs: item.ResultIPs,
|
||||
Status: item.Status,
|
||||
ErrorCode: item.ErrorCode,
|
||||
CostMs: item.CostMs,
|
||||
CreatedAt: int64(item.CreatedAt),
|
||||
Day: item.Day,
|
||||
Summary: item.Summary,
|
||||
NodeName: nodeName,
|
||||
ClusterName: clusterName,
|
||||
})
|
||||
}
|
||||
|
||||
return &pb.ListHTTPDNSAccessLogsResponse{
|
||||
Logs: result,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *HTTPDNSAccessLogService) listFromClickHouse(ctx context.Context, store *clickhouse.HTTPDNSAccessLogsStore, req *pb.ListHTTPDNSAccessLogsRequest) (*pb.ListHTTPDNSAccessLogsResponse, error) {
|
||||
filter := clickhouse.HTTPDNSAccessLogListFilter{
|
||||
Day: req.GetDay(),
|
||||
ClusterId: req.GetClusterId(),
|
||||
NodeId: req.GetNodeId(),
|
||||
AppId: req.GetAppId(),
|
||||
Domain: req.GetDomain(),
|
||||
Status: req.GetStatus(),
|
||||
Keyword: req.GetKeyword(),
|
||||
Offset: req.GetOffset(),
|
||||
Size: req.GetSize(),
|
||||
}
|
||||
|
||||
total, err := store.Count(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows, err := store.List(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]*pb.HTTPDNSAccessLog, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
item := clickhouse.HTTPDNSRowToPB(row)
|
||||
if item == nil {
|
||||
continue
|
||||
}
|
||||
clusterName, _ := models.SharedHTTPDNSClusterDAO.FindEnabledClusterName(s.NullTx(), item.GetClusterId())
|
||||
nodeName := ""
|
||||
node, _ := models.SharedHTTPDNSNodeDAO.FindEnabledNode(s.NullTx(), item.GetNodeId())
|
||||
if node != nil {
|
||||
nodeName = node.Name
|
||||
}
|
||||
item.ClusterName = clusterName
|
||||
item.NodeName = nodeName
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
return &pb.ListHTTPDNSAccessLogsResponse{
|
||||
Logs: result,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//go:build !plus
|
||||
|
||||
package httpdns
|
||||
|
||||
func (s *HTTPDNSAccessLogService) canWriteHTTPDNSAccessLogsToMySQL() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *HTTPDNSAccessLogService) canWriteHTTPDNSAccessLogsToClickHouse() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *HTTPDNSAccessLogService) shouldReadHTTPDNSAccessLogsFromClickHouse() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *HTTPDNSAccessLogService) shouldReadHTTPDNSAccessLogsFromMySQL() bool {
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
//go:build plus
|
||||
|
||||
package httpdns
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
)
|
||||
|
||||
var (
|
||||
httpDNSAccessLogWriteTargetsLocker sync.RWMutex
|
||||
httpDNSAccessLogWriteTargetsCache = &serverconfigs.AccessLogWriteTargets{
|
||||
File: true,
|
||||
MySQL: true,
|
||||
ClickHouse: false,
|
||||
}
|
||||
httpDNSAccessLogWriteTargetsExpireAt int64
|
||||
)
|
||||
|
||||
const httpDNSAccessLogWriteTargetsCacheTTL = 10 * time.Second
|
||||
|
||||
func (s *HTTPDNSAccessLogService) canWriteHTTPDNSAccessLogsToMySQL() bool {
|
||||
targets := s.readHTTPDNSAccessLogWriteTargets()
|
||||
if targets == nil {
|
||||
return true
|
||||
}
|
||||
return targets.MySQL
|
||||
}
|
||||
|
||||
func (s *HTTPDNSAccessLogService) canWriteHTTPDNSAccessLogsToClickHouse() bool {
|
||||
targets := s.readHTTPDNSAccessLogWriteTargets()
|
||||
if targets == nil {
|
||||
return false
|
||||
}
|
||||
return targets.ClickHouse
|
||||
}
|
||||
|
||||
func (s *HTTPDNSAccessLogService) shouldReadHTTPDNSAccessLogsFromClickHouse() bool {
|
||||
targets := s.readHTTPDNSAccessLogWriteTargets()
|
||||
if targets == nil {
|
||||
return false
|
||||
}
|
||||
return targets.ClickHouse
|
||||
}
|
||||
|
||||
func (s *HTTPDNSAccessLogService) shouldReadHTTPDNSAccessLogsFromMySQL() bool {
|
||||
targets := s.readHTTPDNSAccessLogWriteTargets()
|
||||
if targets == nil {
|
||||
return true
|
||||
}
|
||||
return targets.MySQL
|
||||
}
|
||||
|
||||
func (s *HTTPDNSAccessLogService) readHTTPDNSAccessLogWriteTargets() *serverconfigs.AccessLogWriteTargets {
|
||||
now := time.Now().Unix()
|
||||
|
||||
httpDNSAccessLogWriteTargetsLocker.RLock()
|
||||
if now < httpDNSAccessLogWriteTargetsExpireAt && httpDNSAccessLogWriteTargetsCache != nil {
|
||||
targets := *httpDNSAccessLogWriteTargetsCache
|
||||
httpDNSAccessLogWriteTargetsLocker.RUnlock()
|
||||
return &targets
|
||||
}
|
||||
httpDNSAccessLogWriteTargetsLocker.RUnlock()
|
||||
|
||||
httpDNSAccessLogWriteTargetsLocker.Lock()
|
||||
defer httpDNSAccessLogWriteTargetsLocker.Unlock()
|
||||
|
||||
// double-check
|
||||
now = time.Now().Unix()
|
||||
if now < httpDNSAccessLogWriteTargetsExpireAt && httpDNSAccessLogWriteTargetsCache != nil {
|
||||
targets := *httpDNSAccessLogWriteTargetsCache
|
||||
return &targets
|
||||
}
|
||||
|
||||
targets := s.loadHTTPDNSAccessLogWriteTargetsFromPolicy()
|
||||
if targets == nil {
|
||||
targets = &serverconfigs.AccessLogWriteTargets{
|
||||
File: true,
|
||||
MySQL: true,
|
||||
ClickHouse: false,
|
||||
}
|
||||
}
|
||||
|
||||
httpDNSAccessLogWriteTargetsCache = targets
|
||||
httpDNSAccessLogWriteTargetsExpireAt = time.Now().Add(httpDNSAccessLogWriteTargetsCacheTTL).Unix()
|
||||
|
||||
copyTargets := *targets
|
||||
return ©Targets
|
||||
}
|
||||
|
||||
func (s *HTTPDNSAccessLogService) loadHTTPDNSAccessLogWriteTargetsFromPolicy() *serverconfigs.AccessLogWriteTargets {
|
||||
tx := s.NullTx()
|
||||
publicPolicyId, err := models.SharedHTTPAccessLogPolicyDAO.FindCurrentPublicPolicyId(tx)
|
||||
if err != nil {
|
||||
log.Println("[HTTPDNS_ACCESS_LOG]load public access log policy failed:", err.Error())
|
||||
return nil
|
||||
}
|
||||
if publicPolicyId <= 0 {
|
||||
return &serverconfigs.AccessLogWriteTargets{
|
||||
File: true,
|
||||
MySQL: true,
|
||||
ClickHouse: false,
|
||||
}
|
||||
}
|
||||
|
||||
policy, err := models.SharedHTTPAccessLogPolicyDAO.FindEnabledHTTPAccessLogPolicy(tx, publicPolicyId)
|
||||
if err != nil {
|
||||
log.Println("[HTTPDNS_ACCESS_LOG]load access log policy detail failed:", err.Error())
|
||||
return nil
|
||||
}
|
||||
if policy == nil {
|
||||
return &serverconfigs.AccessLogWriteTargets{
|
||||
File: true,
|
||||
MySQL: true,
|
||||
ClickHouse: false,
|
||||
}
|
||||
}
|
||||
|
||||
return serverconfigs.ParseWriteTargetsFromPolicy(policy.WriteTargets, policy.Type, policy.DisableDefaultDB)
|
||||
}
|
||||
246
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_app.go
Normal file
246
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_app.go
Normal file
@@ -0,0 +1,246 @@
|
||||
package httpdns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"strings"
|
||||
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
)
|
||||
|
||||
// HTTPDNSAppService HTTPDNS应用服务
|
||||
type HTTPDNSAppService struct {
|
||||
services.BaseService
|
||||
pb.UnimplementedHTTPDNSAppServiceServer
|
||||
}
|
||||
|
||||
func (this *HTTPDNSAppService) CreateHTTPDNSApp(ctx context.Context, req *pb.CreateHTTPDNSAppRequest) (*pb.CreateHTTPDNSAppResponse, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(req.Name) == 0 || len(req.AppId) == 0 {
|
||||
return nil, errors.New("required 'name' and 'appId'")
|
||||
}
|
||||
if req.PrimaryClusterId <= 0 {
|
||||
return nil, errors.New("required 'primaryClusterId'")
|
||||
}
|
||||
var appDbId int64
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
exists, err := models.SharedHTTPDNSAppDAO.FindEnabledAppWithAppId(tx, strings.TrimSpace(req.AppId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists != nil {
|
||||
return errors.New("appId already exists")
|
||||
}
|
||||
|
||||
appDbId, err = models.SharedHTTPDNSAppDAO.CreateApp(tx, req.Name, strings.TrimSpace(req.AppId), req.PrimaryClusterId, req.BackupClusterId, req.IsOn, req.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, err = models.SharedHTTPDNSAppSecretDAO.InitAppSecret(tx, appDbId, req.SignEnabled)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByAppDbId(tx, appDbId, models.HTTPDNSNodeTaskTypeAppChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CreateHTTPDNSAppResponse{AppDbId: appDbId}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSAppService) UpdateHTTPDNSApp(ctx context.Context, req *pb.UpdateHTTPDNSAppRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
oldApp, err := models.SharedHTTPDNSAppDAO.FindEnabledApp(tx, req.AppDbId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oldApp == nil {
|
||||
return errors.New("app not found")
|
||||
}
|
||||
|
||||
err = models.SharedHTTPDNSAppDAO.UpdateApp(tx, req.AppDbId, req.Name, req.PrimaryClusterId, req.BackupClusterId, req.IsOn, req.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = notifyHTTPDNSAppTasksByApp(tx, oldApp, models.HTTPDNSNodeTaskTypeAppChanged)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByAppDbId(tx, req.AppDbId, models.HTTPDNSNodeTaskTypeAppChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSAppService) DeleteHTTPDNSApp(ctx context.Context, req *pb.DeleteHTTPDNSAppRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
app, err := models.SharedHTTPDNSAppDAO.FindEnabledApp(tx, req.AppDbId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if app == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 1) 先停用规则记录
|
||||
rules, err := models.SharedHTTPDNSCustomRuleDAO.ListEnabledRulesWithAppId(tx, req.AppDbId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rule := range rules {
|
||||
err = models.SharedHTTPDNSCustomRuleRecordDAO.DisableRecordsWithRuleId(tx, int64(rule.Id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 2) 停用规则、域名、密钥
|
||||
err = models.SharedHTTPDNSCustomRuleDAO.DisableRulesWithAppId(tx, req.AppDbId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = models.SharedHTTPDNSDomainDAO.DisableDomainsWithAppId(tx, req.AppDbId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = models.SharedHTTPDNSAppSecretDAO.DisableAppSecret(tx, req.AppDbId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3) 删除该应用的 MySQL 访问日志,避免残留
|
||||
err = models.SharedHTTPDNSAccessLogDAO.DeleteLogsWithAppId(tx, app.AppId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4) 最后停用应用
|
||||
err = models.SharedHTTPDNSAppDAO.DisableApp(tx, req.AppDbId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return notifyHTTPDNSAppTasksByApp(tx, app, models.HTTPDNSNodeTaskTypeAppChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSAppService) FindHTTPDNSApp(ctx context.Context, req *pb.FindHTTPDNSAppRequest) (*pb.FindHTTPDNSAppResponse, error) {
|
||||
_, _, err := this.ValidateAdminAndUser(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app, err := models.SharedHTTPDNSAppDAO.FindEnabledApp(this.NullTx(), req.AppDbId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret, err := models.SharedHTTPDNSAppSecretDAO.FindEnabledAppSecret(this.NullTx(), req.AppDbId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.FindHTTPDNSAppResponse{App: toPBApp(app, secret)}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSAppService) ListHTTPDNSApps(ctx context.Context, req *pb.ListHTTPDNSAppsRequest) (*pb.ListHTTPDNSAppsResponse, error) {
|
||||
_, _, err := this.ValidateAdminAndUser(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apps, err := models.SharedHTTPDNSAppDAO.ListEnabledApps(this.NullTx(), req.Offset, req.Size, req.Keyword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbApps []*pb.HTTPDNSApp
|
||||
for _, app := range apps {
|
||||
secret, err := models.SharedHTTPDNSAppSecretDAO.FindEnabledAppSecret(this.NullTx(), int64(app.Id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pbApps = append(pbApps, toPBApp(app, secret))
|
||||
}
|
||||
return &pb.ListHTTPDNSAppsResponse{Apps: pbApps}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSAppService) FindAllHTTPDNSApps(ctx context.Context, req *pb.FindAllHTTPDNSAppsRequest) (*pb.FindAllHTTPDNSAppsResponse, error) {
|
||||
_, _, validateErr := this.ValidateAdminAndUser(ctx, true)
|
||||
if validateErr != nil {
|
||||
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
||||
return nil, validateErr
|
||||
}
|
||||
}
|
||||
apps, err := models.SharedHTTPDNSAppDAO.FindAllEnabledApps(this.NullTx())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbApps []*pb.HTTPDNSApp
|
||||
for _, app := range apps {
|
||||
secret, err := models.SharedHTTPDNSAppSecretDAO.FindEnabledAppSecret(this.NullTx(), int64(app.Id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pbApps = append(pbApps, toPBApp(app, secret))
|
||||
}
|
||||
return &pb.FindAllHTTPDNSAppsResponse{Apps: pbApps}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSAppService) UpdateHTTPDNSAppSignEnabled(ctx context.Context, req *pb.UpdateHTTPDNSAppSignEnabledRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
err := models.SharedHTTPDNSAppSecretDAO.UpdateSignEnabled(tx, req.AppDbId, req.SignEnabled)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByAppDbId(tx, req.AppDbId, models.HTTPDNSNodeTaskTypeAppChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSAppService) ResetHTTPDNSAppSignSecret(ctx context.Context, req *pb.ResetHTTPDNSAppSignSecretRequest) (*pb.ResetHTTPDNSAppSignSecretResponse, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var signSecret string
|
||||
var updatedAt int64
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
var err error
|
||||
signSecret, updatedAt, err = models.SharedHTTPDNSAppSecretDAO.ResetSignSecret(tx, req.AppDbId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByAppDbId(tx, req.AppDbId, models.HTTPDNSNodeTaskTypeAppChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.ResetHTTPDNSAppSignSecretResponse{
|
||||
SignSecret: signSecret,
|
||||
UpdatedAt: updatedAt,
|
||||
}, nil
|
||||
}
|
||||
170
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_cluster.go
Normal file
170
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_cluster.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package httpdns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
)
|
||||
|
||||
// HTTPDNSClusterService HTTPDNS集群服务
|
||||
type HTTPDNSClusterService struct {
|
||||
services.BaseService
|
||||
pb.UnimplementedHTTPDNSClusterServiceServer
|
||||
}
|
||||
|
||||
func (this *HTTPDNSClusterService) CreateHTTPDNSCluster(ctx context.Context, req *pb.CreateHTTPDNSClusterRequest) (*pb.CreateHTTPDNSClusterResponse, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(req.Name) == 0 {
|
||||
return nil, errors.New("required 'name'")
|
||||
}
|
||||
var clusterId int64
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
clusterId, err = models.SharedHTTPDNSClusterDAO.CreateCluster(tx, req.Name, req.ServiceDomain, req.DefaultTTL, req.FallbackTimeoutMs, req.InstallDir, req.TlsPolicyJSON, req.IsOn, req.IsDefault)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSClusterTask(tx, clusterId, models.HTTPDNSNodeTaskTypeConfigChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CreateHTTPDNSClusterResponse{ClusterId: clusterId}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSClusterService) UpdateHTTPDNSCluster(ctx context.Context, req *pb.UpdateHTTPDNSClusterRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
err = models.SharedHTTPDNSClusterDAO.UpdateCluster(tx, req.ClusterId, req.Name, req.ServiceDomain, req.DefaultTTL, req.FallbackTimeoutMs, req.InstallDir, req.TlsPolicyJSON, req.IsOn, req.IsDefault)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
taskType := models.HTTPDNSNodeTaskTypeConfigChanged
|
||||
if len(req.TlsPolicyJSON) > 0 {
|
||||
taskType = models.HTTPDNSNodeTaskTypeTLSChanged
|
||||
}
|
||||
return notifyHTTPDNSClusterTask(tx, req.ClusterId, taskType)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSClusterService) DeleteHTTPDNSCluster(ctx context.Context, req *pb.DeleteHTTPDNSClusterRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
err = models.SharedHTTPDNSClusterDAO.DisableCluster(tx, req.ClusterId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSClusterTask(tx, req.ClusterId, models.HTTPDNSNodeTaskTypeConfigChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSClusterService) FindHTTPDNSCluster(ctx context.Context, req *pb.FindHTTPDNSClusterRequest) (*pb.FindHTTPDNSClusterResponse, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cluster, err := models.SharedHTTPDNSClusterDAO.FindEnabledCluster(this.NullTx(), req.ClusterId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.FindHTTPDNSClusterResponse{Cluster: toPBCluster(cluster)}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSClusterService) ListHTTPDNSClusters(ctx context.Context, req *pb.ListHTTPDNSClustersRequest) (*pb.ListHTTPDNSClustersResponse, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clusters, err := models.SharedHTTPDNSClusterDAO.ListEnabledClusters(this.NullTx(), req.Offset, req.Size, req.Keyword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbClusters []*pb.HTTPDNSCluster
|
||||
for _, cluster := range clusters {
|
||||
pbClusters = append(pbClusters, toPBCluster(cluster))
|
||||
}
|
||||
return &pb.ListHTTPDNSClustersResponse{Clusters: pbClusters}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSClusterService) FindAllHTTPDNSClusters(ctx context.Context, req *pb.FindAllHTTPDNSClustersRequest) (*pb.FindAllHTTPDNSClustersResponse, error) {
|
||||
_, _, validateErr := this.ValidateAdminAndUser(ctx, true)
|
||||
if validateErr != nil {
|
||||
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
||||
return nil, validateErr
|
||||
}
|
||||
}
|
||||
clusters, err := models.SharedHTTPDNSClusterDAO.FindAllEnabledClusters(this.NullTx())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbClusters []*pb.HTTPDNSCluster
|
||||
for _, cluster := range clusters {
|
||||
pbClusters = append(pbClusters, toPBCluster(cluster))
|
||||
}
|
||||
return &pb.FindAllHTTPDNSClustersResponse{Clusters: pbClusters}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSClusterService) UpdateHTTPDNSClusterDefault(ctx context.Context, req *pb.UpdateHTTPDNSClusterDefaultRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
err = models.SharedHTTPDNSClusterDAO.UpdateDefaultCluster(tx, req.ClusterId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clusters, err := models.SharedHTTPDNSClusterDAO.FindAllEnabledClusters(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, cluster := range clusters {
|
||||
err = notifyHTTPDNSClusterTask(tx, int64(cluster.Id), models.HTTPDNSNodeTaskTypeConfigChanged)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSClusterService) ListHTTPDNSNodesWithClusterId(ctx context.Context, req *pb.ListHTTPDNSNodesWithClusterIdRequest) (*pb.ListHTTPDNSNodesWithClusterIdResponse, error) {
|
||||
_, _, err := this.ValidateAdminAndUser(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes, err := models.SharedHTTPDNSNodeDAO.ListEnabledNodes(this.NullTx(), req.ClusterId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbNodes []*pb.HTTPDNSNode
|
||||
for _, node := range nodes {
|
||||
pbNodes = append(pbNodes, toPBNode(node))
|
||||
}
|
||||
return &pb.ListHTTPDNSNodesWithClusterIdResponse{Nodes: pbNodes}, nil
|
||||
}
|
||||
112
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_domain.go
Normal file
112
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_domain.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package httpdns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
)
|
||||
|
||||
// HTTPDNSDomainService HTTPDNS域名服务
|
||||
type HTTPDNSDomainService struct {
|
||||
services.BaseService
|
||||
pb.UnimplementedHTTPDNSDomainServiceServer
|
||||
}
|
||||
|
||||
func (this *HTTPDNSDomainService) CreateHTTPDNSDomain(ctx context.Context, req *pb.CreateHTTPDNSDomainRequest) (*pb.CreateHTTPDNSDomainResponse, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.AppDbId <= 0 || len(req.Domain) == 0 {
|
||||
return nil, errors.New("required 'appDbId' and 'domain'")
|
||||
}
|
||||
var domainId int64
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
domainId, err = models.SharedHTTPDNSDomainDAO.CreateDomain(tx, req.AppDbId, req.Domain, req.IsOn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByAppDbId(tx, req.AppDbId, models.HTTPDNSNodeTaskTypeDomainChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CreateHTTPDNSDomainResponse{DomainId: domainId}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSDomainService) DeleteHTTPDNSDomain(ctx context.Context, req *pb.DeleteHTTPDNSDomainRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
domain, err := models.SharedHTTPDNSDomainDAO.FindEnabledDomain(tx, req.DomainId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if domain == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = models.SharedHTTPDNSDomainDAO.DisableDomain(tx, req.DomainId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(domain.AppId), models.HTTPDNSNodeTaskTypeDomainChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSDomainService) UpdateHTTPDNSDomainStatus(ctx context.Context, req *pb.UpdateHTTPDNSDomainStatusRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
domain, err := models.SharedHTTPDNSDomainDAO.FindEnabledDomain(tx, req.DomainId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if domain == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = models.SharedHTTPDNSDomainDAO.UpdateDomainStatus(tx, req.DomainId, req.IsOn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(domain.AppId), models.HTTPDNSNodeTaskTypeDomainChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSDomainService) ListHTTPDNSDomainsWithAppId(ctx context.Context, req *pb.ListHTTPDNSDomainsWithAppIdRequest) (*pb.ListHTTPDNSDomainsWithAppIdResponse, error) {
|
||||
_, _, validateErr := this.ValidateAdminAndUser(ctx, true)
|
||||
if validateErr != nil {
|
||||
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
||||
return nil, validateErr
|
||||
}
|
||||
}
|
||||
domains, err := models.SharedHTTPDNSDomainDAO.ListEnabledDomainsWithAppId(this.NullTx(), req.AppDbId, req.Keyword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbDomains []*pb.HTTPDNSDomain
|
||||
for _, domain := range domains {
|
||||
ruleCount, err := models.SharedHTTPDNSCustomRuleDAO.CountEnabledRulesWithDomainId(this.NullTx(), int64(domain.Id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pbDomains = append(pbDomains, toPBDomain(domain, ruleCount))
|
||||
}
|
||||
return &pb.ListHTTPDNSDomainsWithAppIdResponse{Domains: pbDomains}, nil
|
||||
}
|
||||
185
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_node.go
Normal file
185
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_node.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package httpdns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/installers"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
)
|
||||
|
||||
// HTTPDNSNodeService HTTPDNS节点服务
|
||||
type HTTPDNSNodeService struct {
|
||||
services.BaseService
|
||||
pb.UnimplementedHTTPDNSNodeServiceServer
|
||||
}
|
||||
|
||||
func (this *HTTPDNSNodeService) CreateHTTPDNSNode(ctx context.Context, req *pb.CreateHTTPDNSNodeRequest) (*pb.CreateHTTPDNSNodeResponse, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.ClusterId <= 0 {
|
||||
return nil, errors.New("required 'clusterId'")
|
||||
}
|
||||
var nodeId int64
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
nodeId, err = models.SharedHTTPDNSNodeDAO.CreateNode(tx, req.ClusterId, req.Name, req.InstallDir, req.IsOn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSClusterTask(tx, req.ClusterId, models.HTTPDNSNodeTaskTypeConfigChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CreateHTTPDNSNodeResponse{NodeId: nodeId}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSNodeService) UpdateHTTPDNSNode(ctx context.Context, req *pb.UpdateHTTPDNSNodeRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
node, err := models.SharedHTTPDNSNodeDAO.FindEnabledNode(tx, req.NodeId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if node == nil {
|
||||
return errors.New("node not found")
|
||||
}
|
||||
|
||||
err = models.SharedHTTPDNSNodeDAO.UpdateNode(tx, req.NodeId, req.Name, req.InstallDir, req.IsOn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSClusterTask(tx, int64(node.ClusterId), models.HTTPDNSNodeTaskTypeConfigChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSNodeService) DeleteHTTPDNSNode(ctx context.Context, req *pb.DeleteHTTPDNSNodeRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
node, err := models.SharedHTTPDNSNodeDAO.FindEnabledNode(tx, req.NodeId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = models.SharedHTTPDNSNodeDAO.DisableNode(tx, req.NodeId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSClusterTask(tx, int64(node.ClusterId), models.HTTPDNSNodeTaskTypeConfigChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSNodeService) FindHTTPDNSNode(ctx context.Context, req *pb.FindHTTPDNSNodeRequest) (*pb.FindHTTPDNSNodeResponse, error) {
|
||||
nodeId := req.NodeId
|
||||
if nodeId <= 0 {
|
||||
parsedNodeId, nodeErr := this.ValidateHTTPDNSNode(ctx)
|
||||
if nodeErr != nil {
|
||||
return nil, errors.New("invalid 'nodeId'")
|
||||
}
|
||||
nodeId = parsedNodeId
|
||||
} else {
|
||||
_, _, validateErr := this.ValidateAdminAndUser(ctx, true)
|
||||
if validateErr != nil {
|
||||
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
||||
return nil, validateErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node, err := models.SharedHTTPDNSNodeDAO.FindEnabledNode(this.NullTx(), nodeId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.FindHTTPDNSNodeResponse{Node: toPBNode(node)}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSNodeService) ListHTTPDNSNodes(ctx context.Context, req *pb.ListHTTPDNSNodesRequest) (*pb.ListHTTPDNSNodesResponse, error) {
|
||||
_, _, err := this.ValidateAdminAndUser(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes, err := models.SharedHTTPDNSNodeDAO.ListEnabledNodes(this.NullTx(), req.ClusterId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbNodes []*pb.HTTPDNSNode
|
||||
for _, node := range nodes {
|
||||
pbNodes = append(pbNodes, toPBNode(node))
|
||||
}
|
||||
return &pb.ListHTTPDNSNodesResponse{Nodes: pbNodes}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSNodeService) UpdateHTTPDNSNodeStatus(ctx context.Context, req *pb.UpdateHTTPDNSNodeStatusRequest) (*pb.RPCSuccess, error) {
|
||||
nodeId := req.GetNodeId()
|
||||
isAdminCaller := false
|
||||
if nodeId > 0 {
|
||||
if _, adminErr := this.ValidateAdmin(ctx); adminErr == nil {
|
||||
isAdminCaller = true
|
||||
}
|
||||
}
|
||||
if !isAdminCaller {
|
||||
if nodeId <= 0 {
|
||||
parsedNodeId, err := this.ValidateHTTPDNSNode(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeId = parsedNodeId
|
||||
}
|
||||
}
|
||||
if nodeId <= 0 {
|
||||
return nil, errors.New("invalid 'nodeId'")
|
||||
}
|
||||
|
||||
err := models.SharedHTTPDNSNodeDAO.UpdateNodeStatus(this.NullTx(), nodeId, req.GetIsUp(), req.GetIsInstalled(), req.GetIsActive(), req.GetStatusJSON(), req.GetInstallStatusJSON())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isAdminCaller && shouldTriggerHTTPDNSInstall(req.GetInstallStatusJSON()) {
|
||||
goman.New(func() {
|
||||
installErr := installers.SharedHTTPDNSNodeQueue().InstallNodeProcess(nodeId, false)
|
||||
if installErr != nil {
|
||||
logs.Println("[RPC][HTTPDNS]install node failed:", installErr.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func shouldTriggerHTTPDNSInstall(installStatusJSON []byte) bool {
|
||||
if len(installStatusJSON) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
installStatus := &models.NodeInstallStatus{}
|
||||
err := json.Unmarshal(installStatusJSON, installStatus)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return installStatus.IsRunning && !installStatus.IsFinished
|
||||
}
|
||||
193
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_rule.go
Normal file
193
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_rule.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package httpdns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
)
|
||||
|
||||
// HTTPDNSRuleService HTTPDNS规则服务
|
||||
type HTTPDNSRuleService struct {
|
||||
services.BaseService
|
||||
pb.UnimplementedHTTPDNSRuleServiceServer
|
||||
}
|
||||
|
||||
func (this *HTTPDNSRuleService) CreateHTTPDNSCustomRule(ctx context.Context, req *pb.CreateHTTPDNSCustomRuleRequest) (*pb.CreateHTTPDNSCustomRuleResponse, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.Rule == nil {
|
||||
return nil, errors.New("required 'rule'")
|
||||
}
|
||||
var ruleId int64
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
rule := &models.HTTPDNSCustomRule{
|
||||
AppId: uint32(req.Rule.AppId),
|
||||
DomainId: uint32(req.Rule.DomainId),
|
||||
RuleName: req.Rule.RuleName,
|
||||
LineScope: req.Rule.LineScope,
|
||||
LineCarrier: req.Rule.LineCarrier,
|
||||
LineRegion: req.Rule.LineRegion,
|
||||
LineProvince: req.Rule.LineProvince,
|
||||
LineContinent: req.Rule.LineContinent,
|
||||
LineCountry: req.Rule.LineCountry,
|
||||
TTL: req.Rule.Ttl,
|
||||
IsOn: req.Rule.IsOn,
|
||||
Priority: req.Rule.Priority,
|
||||
}
|
||||
ruleId, err = models.SharedHTTPDNSCustomRuleDAO.CreateRule(tx, rule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, record := range req.Rule.Records {
|
||||
_, err := models.SharedHTTPDNSCustomRuleRecordDAO.CreateRecord(tx, ruleId, record.RecordType, record.RecordValue, record.Weight, record.Sort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByAppDbId(tx, req.Rule.AppId, models.HTTPDNSNodeTaskTypeRuleChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CreateHTTPDNSCustomRuleResponse{RuleId: ruleId}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSRuleService) UpdateHTTPDNSCustomRule(ctx context.Context, req *pb.UpdateHTTPDNSCustomRuleRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.Rule == nil || req.Rule.Id <= 0 {
|
||||
return nil, errors.New("invalid 'rule.id'")
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
oldRule, err := models.SharedHTTPDNSCustomRuleDAO.FindEnabledRule(tx, req.Rule.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oldRule == nil {
|
||||
return errors.New("rule not found")
|
||||
}
|
||||
|
||||
rule := &models.HTTPDNSCustomRule{
|
||||
Id: uint32(req.Rule.Id),
|
||||
RuleName: req.Rule.RuleName,
|
||||
LineScope: req.Rule.LineScope,
|
||||
LineCarrier: req.Rule.LineCarrier,
|
||||
LineRegion: req.Rule.LineRegion,
|
||||
LineProvince: req.Rule.LineProvince,
|
||||
LineContinent: req.Rule.LineContinent,
|
||||
LineCountry: req.Rule.LineCountry,
|
||||
TTL: req.Rule.Ttl,
|
||||
IsOn: req.Rule.IsOn,
|
||||
Priority: req.Rule.Priority,
|
||||
}
|
||||
err = models.SharedHTTPDNSCustomRuleDAO.UpdateRule(tx, rule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = models.SharedHTTPDNSCustomRuleRecordDAO.DisableRecordsWithRuleId(tx, req.Rule.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, record := range req.Rule.Records {
|
||||
_, err := models.SharedHTTPDNSCustomRuleRecordDAO.CreateRecord(tx, req.Rule.Id, record.RecordType, record.RecordValue, record.Weight, record.Sort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = notifyHTTPDNSAppTasksByAppDbId(tx, int64(oldRule.AppId), models.HTTPDNSNodeTaskTypeRuleChanged)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetAppDbId := req.Rule.AppId
|
||||
if targetAppDbId <= 0 {
|
||||
targetAppDbId = int64(oldRule.AppId)
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByAppDbId(tx, targetAppDbId, models.HTTPDNSNodeTaskTypeRuleChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSRuleService) DeleteHTTPDNSCustomRule(ctx context.Context, req *pb.DeleteHTTPDNSCustomRuleRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
rule, err := models.SharedHTTPDNSCustomRuleDAO.FindEnabledRule(tx, req.RuleId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rule == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = models.SharedHTTPDNSCustomRuleDAO.DisableRule(tx, req.RuleId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(rule.AppId), models.HTTPDNSNodeTaskTypeRuleChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSRuleService) UpdateHTTPDNSCustomRuleStatus(ctx context.Context, req *pb.UpdateHTTPDNSCustomRuleStatusRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||
rule, err := models.SharedHTTPDNSCustomRuleDAO.FindEnabledRule(tx, req.RuleId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rule == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = models.SharedHTTPDNSCustomRuleDAO.UpdateRuleStatus(tx, req.RuleId, req.IsOn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(rule.AppId), models.HTTPDNSNodeTaskTypeRuleChanged)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
func (this *HTTPDNSRuleService) ListHTTPDNSCustomRulesWithDomainId(ctx context.Context, req *pb.ListHTTPDNSCustomRulesWithDomainIdRequest) (*pb.ListHTTPDNSCustomRulesWithDomainIdResponse, error) {
|
||||
_, _, validateErr := this.ValidateAdminAndUser(ctx, true)
|
||||
if validateErr != nil {
|
||||
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
||||
return nil, validateErr
|
||||
}
|
||||
}
|
||||
rules, err := models.SharedHTTPDNSCustomRuleDAO.ListEnabledRulesWithDomainId(this.NullTx(), req.DomainId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbRules []*pb.HTTPDNSCustomRule
|
||||
for _, rule := range rules {
|
||||
records, err := models.SharedHTTPDNSCustomRuleRecordDAO.ListEnabledRecordsWithRuleId(this.NullTx(), int64(rule.Id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pbRules = append(pbRules, toPBRule(rule, records))
|
||||
}
|
||||
return &pb.ListHTTPDNSCustomRulesWithDomainIdResponse{Rules: pbRules}, nil
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package httpdns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTTPDNSRuntimeLogService HTTPDNS运行日志服务
|
||||
type HTTPDNSRuntimeLogService struct {
|
||||
services.BaseService
|
||||
pb.UnimplementedHTTPDNSRuntimeLogServiceServer
|
||||
}
|
||||
|
||||
func (this *HTTPDNSRuntimeLogService) CreateHTTPDNSRuntimeLogs(ctx context.Context, req *pb.CreateHTTPDNSRuntimeLogsRequest) (*pb.CreateHTTPDNSRuntimeLogsResponse, error) {
|
||||
nodeIdInContext, err := this.ValidateHTTPDNSNode(ctx)
|
||||
if err != nil {
|
||||
_, err = this.ValidateAdmin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, item := range req.Logs {
|
||||
createdAt := item.CreatedAt
|
||||
if createdAt <= 0 {
|
||||
createdAt = time.Now().Unix()
|
||||
}
|
||||
day := item.Day
|
||||
if len(day) == 0 {
|
||||
day = timeutil.Format("Ymd")
|
||||
}
|
||||
nodeId := item.NodeId
|
||||
// When called by HTTPDNS node, trust node id parsed from RPC token.
|
||||
if nodeIdInContext > 0 {
|
||||
nodeId = nodeIdInContext
|
||||
}
|
||||
|
||||
clusterId := item.ClusterId
|
||||
if clusterId <= 0 && nodeId > 0 {
|
||||
clusterId, _ = models.SharedHTTPDNSNodeDAO.FindNodeClusterId(this.NullTx(), nodeId)
|
||||
}
|
||||
|
||||
log := &models.HTTPDNSRuntimeLog{
|
||||
ClusterId: uint32(clusterId),
|
||||
NodeId: uint32(nodeId),
|
||||
Level: item.Level,
|
||||
Type: item.Type,
|
||||
Module: item.Module,
|
||||
Description: item.Description,
|
||||
Count: item.Count,
|
||||
RequestId: item.RequestId,
|
||||
CreatedAt: uint64(createdAt),
|
||||
Day: day,
|
||||
}
|
||||
err := models.SharedHTTPDNSRuntimeLogDAO.CreateLog(this.NullTx(), log)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &pb.CreateHTTPDNSRuntimeLogsResponse{}, nil
|
||||
}
|
||||
|
||||
func (this *HTTPDNSRuntimeLogService) ListHTTPDNSRuntimeLogs(ctx context.Context, req *pb.ListHTTPDNSRuntimeLogsRequest) (*pb.ListHTTPDNSRuntimeLogsResponse, error) {
|
||||
_, _, err := this.ValidateAdminAndUser(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
total, err := models.SharedHTTPDNSRuntimeLogDAO.CountLogs(this.NullTx(), req.Day, req.ClusterId, req.NodeId, req.Level, req.Keyword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logs, err := models.SharedHTTPDNSRuntimeLogDAO.ListLogs(this.NullTx(), req.Day, req.ClusterId, req.NodeId, req.Level, req.Keyword, req.Offset, req.Size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbLogs []*pb.HTTPDNSRuntimeLog
|
||||
for _, item := range logs {
|
||||
clusterName, _ := models.SharedHTTPDNSClusterDAO.FindEnabledClusterName(this.NullTx(), int64(item.ClusterId))
|
||||
nodeName := ""
|
||||
node, _ := models.SharedHTTPDNSNodeDAO.FindEnabledNode(this.NullTx(), int64(item.NodeId))
|
||||
if node != nil {
|
||||
nodeName = node.Name
|
||||
}
|
||||
pbLogs = append(pbLogs, &pb.HTTPDNSRuntimeLog{
|
||||
Id: int64(item.Id),
|
||||
ClusterId: int64(item.ClusterId),
|
||||
NodeId: int64(item.NodeId),
|
||||
Level: item.Level,
|
||||
Type: item.Type,
|
||||
Module: item.Module,
|
||||
Description: item.Description,
|
||||
Count: item.Count,
|
||||
RequestId: item.RequestId,
|
||||
CreatedAt: int64(item.CreatedAt),
|
||||
Day: item.Day,
|
||||
ClusterName: clusterName,
|
||||
NodeName: nodeName,
|
||||
})
|
||||
}
|
||||
return &pb.ListHTTPDNSRuntimeLogsResponse{
|
||||
Logs: pbLogs,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
252
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_sandbox.go
Normal file
252
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_sandbox.go
Normal file
@@ -0,0 +1,252 @@
|
||||
package httpdns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTTPDNSSandboxService HTTPDNS解析测试服务
|
||||
type HTTPDNSSandboxService struct {
|
||||
services.BaseService
|
||||
pb.UnimplementedHTTPDNSSandboxServiceServer
|
||||
}
|
||||
|
||||
// nodeResolveResponse 节点返回的 JSON 结构(对齐 EdgeHttpDNS resolve_server.go)
|
||||
type nodeResolveResponse struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
RequestID string `json:"requestId"`
|
||||
Data *nodeResolveData `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type nodeResolveData struct {
|
||||
Domain string `json:"domain"`
|
||||
QType string `json:"qtype"`
|
||||
TTL int32 `json:"ttl"`
|
||||
Records []*nodeResolveRecord `json:"records"`
|
||||
Client *nodeClientInfo `json:"client"`
|
||||
Summary string `json:"summary"`
|
||||
}
|
||||
|
||||
type nodeResolveRecord struct {
|
||||
Type string `json:"type"`
|
||||
IP string `json:"ip"`
|
||||
Weight int32 `json:"weight"`
|
||||
Line string `json:"line"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type nodeClientInfo struct {
|
||||
IP string `json:"ip"`
|
||||
Region string `json:"region"`
|
||||
Carrier string `json:"carrier"`
|
||||
Country string `json:"country"`
|
||||
}
|
||||
|
||||
func (this *HTTPDNSSandboxService) TestHTTPDNSResolve(ctx context.Context, req *pb.TestHTTPDNSResolveRequest) (*pb.TestHTTPDNSResolveResponse, error) {
|
||||
_, _, err := this.ValidateAdminAndUser(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(req.AppId) == 0 || len(req.Domain) == 0 {
|
||||
return nil, errors.New("appId 和 domain 不能为空")
|
||||
}
|
||||
|
||||
app, err := models.SharedHTTPDNSAppDAO.FindEnabledAppWithAppId(this.NullTx(), req.AppId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if app == nil || !app.IsOn {
|
||||
return &pb.TestHTTPDNSResolveResponse{
|
||||
Code: "APP_NOT_FOUND_OR_DISABLED",
|
||||
Message: "找不到指定的应用,或该应用已下线",
|
||||
RequestId: "rid-" + rands.HexString(12),
|
||||
}, nil
|
||||
}
|
||||
if req.ClusterId > 0 && req.ClusterId != int64(app.PrimaryClusterId) && req.ClusterId != int64(app.BackupClusterId) {
|
||||
return &pb.TestHTTPDNSResolveResponse{
|
||||
Code: "APP_CLUSTER_MISMATCH",
|
||||
Message: "当前应用未绑定到该集群 (主集群: " + strconv.FormatInt(int64(app.PrimaryClusterId), 10) + ", 备用集群: " + strconv.FormatInt(int64(app.BackupClusterId), 10) + ")",
|
||||
RequestId: "rid-" + rands.HexString(12),
|
||||
}, nil
|
||||
}
|
||||
|
||||
qtype := strings.ToUpper(strings.TrimSpace(req.Qtype))
|
||||
if qtype == "" {
|
||||
qtype = "A"
|
||||
}
|
||||
|
||||
// 获取集群服务域名
|
||||
clusterId := req.ClusterId
|
||||
if clusterId <= 0 {
|
||||
clusterId = int64(app.PrimaryClusterId)
|
||||
}
|
||||
cluster, err := models.SharedHTTPDNSClusterDAO.FindEnabledCluster(this.NullTx(), clusterId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cluster == nil {
|
||||
return &pb.TestHTTPDNSResolveResponse{
|
||||
Code: "CLUSTER_NOT_FOUND",
|
||||
Message: "找不到指定的集群",
|
||||
RequestId: "rid-" + rands.HexString(12),
|
||||
}, nil
|
||||
}
|
||||
|
||||
serviceDomain := strings.TrimSpace(cluster.ServiceDomain)
|
||||
if len(serviceDomain) == 0 {
|
||||
return &pb.TestHTTPDNSResolveResponse{
|
||||
Code: "NO_SERVICE_DOMAIN",
|
||||
Message: "该集群未配置服务域名",
|
||||
RequestId: "rid-" + rands.HexString(12),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 构造请求转发到 EdgeHttpDNS 节点
|
||||
secret, err := models.SharedHTTPDNSAppSecretDAO.FindEnabledAppSecret(this.NullTx(), int64(app.Id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("appId", req.AppId)
|
||||
query.Set("dn", req.Domain)
|
||||
query.Set("qtype", qtype)
|
||||
if len(req.ClientIP) > 0 {
|
||||
query.Set("cip", req.ClientIP)
|
||||
}
|
||||
if len(req.Sid) > 0 {
|
||||
query.Set("sid", req.Sid)
|
||||
}
|
||||
if len(req.SdkVersion) > 0 {
|
||||
query.Set("sdk_version", req.SdkVersion)
|
||||
}
|
||||
if len(req.Os) > 0 {
|
||||
query.Set("os", req.Os)
|
||||
}
|
||||
|
||||
// 应用开启验签时,沙盒自动生成签名参数,避免测试请求被拒绝
|
||||
if secret != nil && secret.SignEnabled {
|
||||
signSecret := strings.TrimSpace(secret.SignSecret)
|
||||
if len(signSecret) == 0 {
|
||||
return &pb.TestHTTPDNSResolveResponse{
|
||||
Code: "SIGN_INVALID",
|
||||
Message: "应用开启了请求验签,但未配置有效加签 Secret",
|
||||
RequestId: "rid-" + rands.HexString(12),
|
||||
Domain: req.Domain,
|
||||
Qtype: qtype,
|
||||
}, nil
|
||||
}
|
||||
|
||||
exp := strconv.FormatInt(time.Now().Unix()+300, 10)
|
||||
nonce := "sandbox-" + rands.HexString(16)
|
||||
sign := buildSandboxResolveSign(signSecret, req.AppId, req.Domain, qtype, exp, nonce)
|
||||
|
||||
query.Set("exp", exp)
|
||||
query.Set("nonce", nonce)
|
||||
query.Set("sign", sign)
|
||||
}
|
||||
|
||||
resolveURL := "https://" + serviceDomain + "/resolve?" + query.Encode()
|
||||
|
||||
httpClient := &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true, // 沙盒测试环境允许自签名证书
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, resolveURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("构建请求失败: %w", err)
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return &pb.TestHTTPDNSResolveResponse{
|
||||
Code: "NODE_UNREACHABLE",
|
||||
Message: "无法连接到 HTTPDNS 节点: " + err.Error(),
|
||||
RequestId: "rid-" + rands.HexString(12),
|
||||
Domain: req.Domain,
|
||||
Qtype: qtype,
|
||||
}, nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 64*1024))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取节点响应失败: %w", err)
|
||||
}
|
||||
|
||||
// 解析节点返回的 JSON
|
||||
var nodeResp nodeResolveResponse
|
||||
if err := json.Unmarshal(body, &nodeResp); err != nil {
|
||||
return &pb.TestHTTPDNSResolveResponse{
|
||||
Code: "PARSE_ERROR",
|
||||
Message: "解析节点返回数据失败: " + err.Error(),
|
||||
RequestId: "rid-" + rands.HexString(12),
|
||||
Domain: req.Domain,
|
||||
Qtype: qtype,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 映射节点响应到 protobuf 响应
|
||||
pbResp := &pb.TestHTTPDNSResolveResponse{
|
||||
Code: nodeResp.Code,
|
||||
Message: nodeResp.Message,
|
||||
RequestId: nodeResp.RequestID,
|
||||
Domain: req.Domain,
|
||||
Qtype: qtype,
|
||||
}
|
||||
|
||||
if nodeResp.Data != nil {
|
||||
pbResp.Ttl = nodeResp.Data.TTL
|
||||
pbResp.Summary = nodeResp.Data.Summary
|
||||
|
||||
if nodeResp.Data.Client != nil {
|
||||
pbResp.ClientIP = nodeResp.Data.Client.IP
|
||||
pbResp.ClientRegion = nodeResp.Data.Client.Region
|
||||
pbResp.ClientCarrier = nodeResp.Data.Client.Carrier
|
||||
pbResp.ClientCountry = nodeResp.Data.Client.Country
|
||||
}
|
||||
|
||||
for _, rec := range nodeResp.Data.Records {
|
||||
pbResp.Records = append(pbResp.Records, &pb.HTTPDNSResolveRecord{
|
||||
Type: rec.Type,
|
||||
Ip: rec.IP,
|
||||
Ttl: nodeResp.Data.TTL,
|
||||
Weight: rec.Weight,
|
||||
Line: rec.Line,
|
||||
Region: rec.Region,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return pbResp, nil
|
||||
}
|
||||
|
||||
func buildSandboxResolveSign(signSecret string, appID string, domain string, qtype string, exp string, nonce string) string {
|
||||
raw := strings.TrimSpace(appID) + "|" + strings.ToLower(strings.TrimSpace(domain)) + "|" + strings.ToUpper(strings.TrimSpace(qtype)) + "|" + strings.TrimSpace(exp) + "|" + strings.TrimSpace(nonce)
|
||||
mac := hmac.New(sha256.New, []byte(strings.TrimSpace(signSecret)))
|
||||
_, _ = mac.Write([]byte(raw))
|
||||
return hex.EncodeToString(mac.Sum(nil))
|
||||
}
|
||||
49
EdgeAPI/internal/rpc/services/httpdns/task_notify.go
Normal file
49
EdgeAPI/internal/rpc/services/httpdns/task_notify.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package httpdns
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
)
|
||||
|
||||
func notifyHTTPDNSClusterTask(tx *dbs.Tx, clusterId int64, taskType models.NodeTaskType) error {
|
||||
if clusterId <= 0 {
|
||||
return nil
|
||||
}
|
||||
return models.SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleHTTPDNS, clusterId, 0, 0, taskType)
|
||||
}
|
||||
|
||||
func notifyHTTPDNSAppTasksByApp(tx *dbs.Tx, app *models.HTTPDNSApp, taskType models.NodeTaskType) error {
|
||||
if app == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
primaryClusterId := int64(app.PrimaryClusterId)
|
||||
backupClusterId := int64(app.BackupClusterId)
|
||||
|
||||
err := notifyHTTPDNSClusterTask(tx, primaryClusterId, taskType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if backupClusterId > 0 && backupClusterId != primaryClusterId {
|
||||
err = notifyHTTPDNSClusterTask(tx, backupClusterId, taskType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func notifyHTTPDNSAppTasksByAppDbId(tx *dbs.Tx, appDbId int64, taskType models.NodeTaskType) error {
|
||||
if appDbId <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
app, err := models.SharedHTTPDNSAppDAO.FindEnabledApp(tx, appDbId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notifyHTTPDNSAppTasksByApp(tx, app, taskType)
|
||||
}
|
||||
@@ -83,6 +83,12 @@ func (this *BaseService) ValidateNSNode(ctx context.Context) (nodeId int64, err
|
||||
return
|
||||
}
|
||||
|
||||
// ValidateHTTPDNSNode 校验HTTPDNS节点
|
||||
func (this *BaseService) ValidateHTTPDNSNode(ctx context.Context) (nodeId int64, err error) {
|
||||
_, _, nodeId, err = rpcutils.ValidateRequest(ctx, rpcutils.UserTypeHTTPDNS)
|
||||
return
|
||||
}
|
||||
|
||||
// ValidateUserNode 校验用户节点
|
||||
func (this *BaseService) ValidateUserNode(ctx context.Context, canRest bool) (userId int64, err error) {
|
||||
// 不允许REST调用
|
||||
@@ -105,7 +111,7 @@ func (this *BaseService) ValidateAuthorityNode(ctx context.Context) (nodeId int6
|
||||
func (this *BaseService) ValidateNodeId(ctx context.Context, roles ...rpcutils.UserType) (role rpcutils.UserType, nodeIntId int64, err error) {
|
||||
// 默认包含大部分节点
|
||||
if len(roles) == 0 {
|
||||
roles = []rpcutils.UserType{rpcutils.UserTypeNode, rpcutils.UserTypeCluster, rpcutils.UserTypeAdmin, rpcutils.UserTypeUser, rpcutils.UserTypeDNS, rpcutils.UserTypeReport, rpcutils.UserTypeLog, rpcutils.UserTypeAPI}
|
||||
roles = []rpcutils.UserType{rpcutils.UserTypeNode, rpcutils.UserTypeCluster, rpcutils.UserTypeAdmin, rpcutils.UserTypeUser, rpcutils.UserTypeDNS, rpcutils.UserTypeHTTPDNS, rpcutils.UserTypeReport, rpcutils.UserTypeLog, rpcutils.UserTypeAPI}
|
||||
}
|
||||
|
||||
if ctx == nil {
|
||||
@@ -191,6 +197,8 @@ func (this *BaseService) ValidateNodeId(ctx context.Context, roles ...rpcutils.U
|
||||
nodeIntId = 0
|
||||
case rpcutils.UserTypeDNS:
|
||||
nodeIntId, err = models.SharedNSNodeDAO.FindEnabledNodeIdWithUniqueId(nil, nodeId)
|
||||
case rpcutils.UserTypeHTTPDNS:
|
||||
nodeIntId, err = models.SharedHTTPDNSNodeDAO.FindEnabledNodeIdWithUniqueId(nil, nodeId)
|
||||
case rpcutils.UserTypeReport:
|
||||
nodeIntId, err = models.SharedReportNodeDAO.FindEnabledNodeIdWithUniqueId(nil, nodeId)
|
||||
case rpcutils.UserTypeAuthority:
|
||||
|
||||
@@ -19,7 +19,7 @@ type NodeTaskService struct {
|
||||
|
||||
// FindNodeTasks 获取单节点同步任务
|
||||
func (this *NodeTaskService) FindNodeTasks(ctx context.Context, req *pb.FindNodeTasksRequest) (*pb.FindNodeTasksResponse, error) {
|
||||
nodeType, nodeId, err := this.ValidateNodeId(ctx, rpcutils.UserTypeNode, rpcutils.UserTypeDNS)
|
||||
nodeType, nodeId, err := this.ValidateNodeId(ctx, rpcutils.UserTypeNode, rpcutils.UserTypeDNS, rpcutils.UserTypeHTTPDNS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func (this *NodeTaskService) FindNodeTasks(ctx context.Context, req *pb.FindNode
|
||||
|
||||
// ReportNodeTaskDone 报告同步任务结果
|
||||
func (this *NodeTaskService) ReportNodeTaskDone(ctx context.Context, req *pb.ReportNodeTaskDoneRequest) (*pb.RPCSuccess, error) {
|
||||
_, _, err := this.ValidateNodeId(ctx, rpcutils.UserTypeNode, rpcutils.UserTypeDNS)
|
||||
_, _, err := this.ValidateNodeId(ctx, rpcutils.UserTypeNode, rpcutils.UserTypeDNS, rpcutils.UserTypeHTTPDNS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ const (
|
||||
UserTypeCluster = "cluster"
|
||||
UserTypeStat = "stat"
|
||||
UserTypeDNS = "dns"
|
||||
UserTypeHTTPDNS = "httpdns"
|
||||
UserTypeLog = "log"
|
||||
UserTypeAPI = "api"
|
||||
UserTypeAuthority = "authority"
|
||||
|
||||
@@ -142,6 +142,16 @@ func ValidateRequest(ctx context.Context, userTypes ...UserType) (userType UserT
|
||||
return UserTypeUser, 0, 0, errors.New("context: not found node with id '" + nodeId + "'")
|
||||
}
|
||||
resultNodeId = nodeIntId
|
||||
case UserTypeHTTPDNS:
|
||||
nodeIntId, err := models.SharedHTTPDNSNodeDAO.FindEnabledNodeIdWithUniqueId(nil, nodeId)
|
||||
if err != nil {
|
||||
return UserTypeHTTPDNS, nodeIntId, 0, errors.New("context: " + err.Error())
|
||||
}
|
||||
if nodeIntId <= 0 {
|
||||
return UserTypeHTTPDNS, nodeIntId, 0, errors.New("context: not found node with id '" + nodeId + "'")
|
||||
}
|
||||
nodeUserId = nodeIntId
|
||||
resultNodeId = nodeIntId
|
||||
}
|
||||
|
||||
if nodeUserId > 0 {
|
||||
|
||||
@@ -171,6 +171,16 @@ func ValidateRequest(ctx context.Context, userTypes ...UserType) (userType UserT
|
||||
}
|
||||
nodeUserId = nodeIntId
|
||||
resultNodeId = nodeIntId
|
||||
case UserTypeHTTPDNS:
|
||||
nodeIntId, err := models.SharedHTTPDNSNodeDAO.FindEnabledNodeIdWithUniqueId(nil, nodeId)
|
||||
if err != nil {
|
||||
return UserTypeHTTPDNS, nodeIntId, 0, errors.New("context: " + err.Error())
|
||||
}
|
||||
if nodeIntId <= 0 {
|
||||
return UserTypeHTTPDNS, nodeIntId, 0, errors.New("context: not found node with id '" + nodeId + "'")
|
||||
}
|
||||
nodeUserId = nodeIntId
|
||||
resultNodeId = nodeIntId
|
||||
case UserTypeReport:
|
||||
nodeIntId, err := models.SharedReportNodeDAO.FindEnabledNodeIdWithUniqueId(nil, nodeId)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user