package httpdns import ( "context" "errors" "fmt" "strings" "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" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) // 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, req.AutoRemoteStart, req.AccessLogIsOn, req.TimeZone) 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 } // Compatibility fallback: // If protobuf schemas between edge-admin and edge-api are inconsistent, // these newly-added fields may be lost on the wire. Read gRPC metadata as fallback. if md, ok := metadata.FromIncomingContext(ctx); ok { if values := md.Get("x-httpdns-auto-remote-start"); len(values) > 0 { raw := strings.ToLower(strings.TrimSpace(values[0])) req.AutoRemoteStart = raw == "1" || raw == "true" || raw == "on" || raw == "yes" || raw == "enabled" } if values := md.Get("x-httpdns-access-log-is-on"); len(values) > 0 { raw := strings.ToLower(strings.TrimSpace(values[0])) req.AccessLogIsOn = raw == "1" || raw == "true" || raw == "on" || raw == "yes" || raw == "enabled" } if values := md.Get("x-httpdns-time-zone"); len(values) > 0 { raw := strings.TrimSpace(values[0]) if len(raw) > 0 { req.TimeZone = raw } } } err = this.RunTx(func(tx *dbs.Tx) error { // 先读取旧的 TLS 配置,用于判断是否真正发生了变化 var oldTLSJSON string oldCluster, findErr := models.SharedHTTPDNSClusterDAO.FindEnabledCluster(tx, req.ClusterId) if findErr == nil && oldCluster != nil { oldTLSJSON = string(oldCluster.TLSPolicy) } err = models.SharedHTTPDNSClusterDAO.UpdateCluster(tx, req.ClusterId, req.Name, req.ServiceDomain, req.DefaultTTL, req.FallbackTimeoutMs, req.InstallDir, req.TlsPolicyJSON, req.IsOn, req.IsDefault, req.AutoRemoteStart, req.AccessLogIsOn, req.TimeZone) if err != nil { return err } taskType := models.HTTPDNSNodeTaskTypeConfigChanged if len(req.TlsPolicyJSON) > 0 && string(req.TlsPolicyJSON) != oldTLSJSON { 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 } if cluster != nil { _ = grpc.SetHeader(ctx, metadata.Pairs( "x-httpdns-auto-remote-start", fmt.Sprintf("%t", cluster.AutoRemoteStart > 0), "x-httpdns-access-log-is-on", fmt.Sprintf("%t", cluster.AccessLogIsOn > 0), "x-httpdns-time-zone", cluster.TimeZone, )) } 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) isNode := false if validateErr != nil { if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil { return nil, validateErr } isNode = true } clusters, err := models.SharedHTTPDNSClusterDAO.FindAllEnabledClusters(this.NullTx()) if err != nil { return nil, err } var pbClusters []*pb.HTTPDNSCluster for _, cluster := range clusters { if isNode { // 节点调用时解析证书引用,嵌入实际 PEM 数据 pbClusters = append(pbClusters, toPBClusterWithResolvedCerts(this.NullTx(), cluster)) } else { 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 }