换成单集群模式

This commit is contained in:
robin
2026-03-02 20:07:53 +08:00
parent 5d0b7c7e91
commit 2a76d1773d
432 changed files with 5681 additions and 5095 deletions

View File

@@ -1,6 +1,8 @@
package httpdns
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
@@ -58,20 +60,23 @@ func toPBApp(app *models.HTTPDNSApp, secret *models.HTTPDNSAppSecret) *pb.HTTPDN
signSecret = secret.SignSecret
signUpdatedAt = int64(secret.SignUpdatedAt)
}
// 构建 clusterIdsJSON
clusterIds := models.SharedHTTPDNSAppDAO.ReadAppClusterIds(app)
clusterIdsJSON, _ := json.Marshal(clusterIds)
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),
Id: int64(app.Id),
Name: app.Name,
AppId: app.AppId,
IsOn: app.IsOn,
SniMode: app.SNIMode,
SignEnabled: signEnabled,
SignSecret: signSecret,
SignUpdatedAt: signUpdatedAt,
CreatedAt: int64(app.CreatedAt),
UpdatedAt: int64(app.UpdatedAt),
UserId: int64(app.UserId),
ClusterIdsJSON: clusterIdsJSON,
}
}

View File

@@ -2,12 +2,13 @@ package httpdns
import (
"context"
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/types"
"strings"
"time"
@@ -66,25 +67,35 @@ func (this *HTTPDNSAppService) CreateHTTPDNSApp(ctx context.Context, req *pb.Cre
return errors.New("appId already exists")
}
primaryClusterId := req.PrimaryClusterId
backupClusterId := req.BackupClusterId
if primaryClusterId <= 0 || backupClusterId <= 0 {
defaultPrimaryClusterId, defaultBackupClusterId, err := readHTTPDNSDefaultClusterIds(tx)
if err != nil {
return err
// 使用 clusterIdsJSON若为空则优先从用户关联集群获取再 fallback 到全局默认
clusterIdsJSON := req.ClusterIdsJSON
if len(clusterIdsJSON) == 0 || string(clusterIdsJSON) == "[]" || string(clusterIdsJSON) == "null" {
// 优先读取用户关联的 HTTPDNS 集群
if req.UserId > 0 {
user, userErr := models.SharedUserDAO.FindEnabledUser(tx, req.UserId, nil)
if userErr != nil {
return userErr
}
if user != nil && len(user.HttpdnsClusterIds) > 0 {
var userClusterIds []int64
if json.Unmarshal([]byte(user.HttpdnsClusterIds), &userClusterIds) == nil && len(userClusterIds) > 0 {
clusterIdsJSON, _ = json.Marshal(userClusterIds)
}
}
}
if primaryClusterId <= 0 {
primaryClusterId = defaultPrimaryClusterId
// fallback 到全局默认
if len(clusterIdsJSON) == 0 || string(clusterIdsJSON) == "[]" || string(clusterIdsJSON) == "null" {
defaultClusterIds, defaultErr := readHTTPDNSDefaultClusterIdList(tx)
if defaultErr != nil {
return defaultErr
}
if len(defaultClusterIds) > 0 {
clusterIdsJSON, _ = json.Marshal(defaultClusterIds)
}
}
if backupClusterId <= 0 {
backupClusterId = defaultBackupClusterId
}
}
if primaryClusterId > 0 && backupClusterId == primaryClusterId {
backupClusterId = 0
}
appDbId, err = models.SharedHTTPDNSAppDAO.CreateApp(tx, appName, appId, primaryClusterId, backupClusterId, req.IsOn, req.UserId)
appDbId, err = models.SharedHTTPDNSAppDAO.CreateApp(tx, appName, appId, clusterIdsJSON, req.IsOn, req.UserId)
if err != nil {
return err
}
@@ -100,44 +111,47 @@ func (this *HTTPDNSAppService) CreateHTTPDNSApp(ctx context.Context, req *pb.Cre
return &pb.CreateHTTPDNSAppResponse{AppDbId: appDbId}, nil
}
func readHTTPDNSDefaultClusterIds(tx *dbs.Tx) (primaryClusterId int64, backupClusterId int64, err error) {
primaryClusterId, err = models.SharedHTTPDNSClusterDAO.FindDefaultPrimaryClusterId(tx)
// readHTTPDNSDefaultClusterIdList reads default cluster IDs from UserRegisterConfig.
func readHTTPDNSDefaultClusterIdList(tx *dbs.Tx) ([]int64, error) {
// 优先从 UserRegisterConfig 中读取
configJSON, err := models.SharedSysSettingDAO.ReadSetting(tx, systemconfigs.SettingCodeUserRegisterConfig)
if err != nil {
return 0, 0, err
return nil, err
}
backupClusterId = 0
backupValueJSON, err := models.SharedSysSettingDAO.ReadSetting(tx, systemconfigs.SettingCodeHTTPDNSDefaultBackupClusterId)
if err != nil {
return 0, 0, err
}
if len(backupValueJSON) > 0 {
backupClusterId = types.Int64(string(backupValueJSON))
}
if backupClusterId > 0 {
backupCluster, err := models.SharedHTTPDNSClusterDAO.FindEnabledCluster(tx, backupClusterId)
if err != nil {
return 0, 0, err
}
if backupCluster == nil || !backupCluster.IsOn {
backupClusterId = 0
if len(configJSON) > 0 {
var config userconfigs.UserRegisterConfig
if err := json.Unmarshal(configJSON, &config); err == nil {
if len(config.HTTPDNSDefaultClusterIds) > 0 {
// 验证集群有效性
var validIds []int64
for _, id := range config.HTTPDNSDefaultClusterIds {
if id <= 0 {
continue
}
cluster, err := models.SharedHTTPDNSClusterDAO.FindEnabledCluster(tx, id)
if err != nil {
return nil, err
}
if cluster != nil && cluster.IsOn {
validIds = append(validIds, id)
}
}
if len(validIds) > 0 {
return validIds, nil
}
}
}
}
// fallback默认主集群
primaryClusterId, err := models.SharedHTTPDNSClusterDAO.FindDefaultPrimaryClusterId(tx)
if err != nil {
return nil, err
}
if primaryClusterId > 0 {
primaryCluster, err := models.SharedHTTPDNSClusterDAO.FindEnabledCluster(tx, primaryClusterId)
if err != nil {
return 0, 0, err
}
if primaryCluster == nil || !primaryCluster.IsOn {
primaryClusterId = 0
}
return []int64{primaryClusterId}, nil
}
if primaryClusterId > 0 && backupClusterId == primaryClusterId {
backupClusterId = 0
}
return primaryClusterId, backupClusterId, nil
return nil, nil
}
func (this *HTTPDNSAppService) UpdateHTTPDNSApp(ctx context.Context, req *pb.UpdateHTTPDNSAppRequest) (*pb.RPCSuccess, error) {
@@ -161,13 +175,8 @@ func (this *HTTPDNSAppService) UpdateHTTPDNSApp(ctx context.Context, req *pb.Upd
if userId > 0 {
targetUserId = userId
}
primaryClusterId := req.PrimaryClusterId
backupClusterId := req.BackupClusterId
if primaryClusterId > 0 && backupClusterId == primaryClusterId {
backupClusterId = 0
}
err = models.SharedHTTPDNSAppDAO.UpdateApp(tx, req.AppDbId, req.Name, primaryClusterId, backupClusterId, req.IsOn, targetUserId)
err = models.SharedHTTPDNSAppDAO.UpdateApp(tx, req.AppDbId, req.Name, req.ClusterIdsJSON, req.IsOn, targetUserId)
if err != nil {
return err
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/installers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/logs"
@@ -114,7 +115,26 @@ func (this *HTTPDNSNodeService) FindHTTPDNSNode(ctx context.Context, req *pb.Fin
if err != nil {
return nil, err
}
return &pb.FindHTTPDNSNodeResponse{Node: toPBNode(node)}, nil
pbNode := toPBNode(node)
// 认证信息
if pbNode != nil {
login, loginErr := models.SharedNodeLoginDAO.FindEnabledNodeLoginWithNodeId(this.NullTx(), nodeconfigs.NodeRoleHTTPDNS, nodeId)
if loginErr != nil {
return nil, loginErr
}
if login != nil {
pbNode.NodeLogin = &pb.NodeLogin{
Id: int64(login.Id),
Name: login.Name,
Type: login.Type,
Params: login.Params,
}
}
}
return &pb.FindHTTPDNSNodeResponse{Node: pbNode}, nil
}
func (this *HTTPDNSNodeService) ListHTTPDNSNodes(ctx context.Context, req *pb.ListHTTPDNSNodesRequest) (*pb.ListHTTPDNSNodesResponse, error) {
@@ -171,6 +191,31 @@ func (this *HTTPDNSNodeService) UpdateHTTPDNSNodeStatus(ctx context.Context, req
return this.Success()
}
// UpdateHTTPDNSNodeLogin 修改HTTPDNS节点登录信息
func (this *HTTPDNSNodeService) UpdateHTTPDNSNodeLogin(ctx context.Context, req *pb.UpdateHTTPDNSNodeLoginRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
if req.NodeLogin.Id <= 0 {
loginId, createErr := models.SharedNodeLoginDAO.CreateNodeLogin(tx, nodeconfigs.NodeRoleHTTPDNS, req.NodeId, req.NodeLogin.Name, req.NodeLogin.Type, req.NodeLogin.Params)
if createErr != nil {
return nil, createErr
}
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()
}
func shouldTriggerHTTPDNSInstall(installStatusJSON []byte) bool {
if len(installStatusJSON) == 0 {
return false

View File

@@ -83,12 +83,23 @@ func (this *HTTPDNSSandboxService) TestHTTPDNSResolve(ctx context.Context, req *
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
// 检查集群是否绑定
appClusterIds := models.SharedHTTPDNSAppDAO.ReadAppClusterIds(app)
if req.ClusterId > 0 {
var found bool
for _, cid := range appClusterIds {
if cid == req.ClusterId {
found = true
break
}
}
if !found {
return &pb.TestHTTPDNSResolveResponse{
Code: "APP_CLUSTER_MISMATCH",
Message: "当前应用未绑定到该集群",
RequestId: "rid-" + rands.HexString(12),
}, nil
}
}
qtype := strings.ToUpper(strings.TrimSpace(req.Qtype))
@@ -98,8 +109,8 @@ func (this *HTTPDNSSandboxService) TestHTTPDNSResolve(ctx context.Context, req *
// 获取集群服务域名
clusterId := req.ClusterId
if clusterId <= 0 {
clusterId = int64(app.PrimaryClusterId)
if clusterId <= 0 && len(appClusterIds) > 0 {
clusterId = appClusterIds[0]
}
cluster, err := models.SharedHTTPDNSClusterDAO.FindEnabledCluster(this.NullTx(), clusterId)
if err != nil {
@@ -128,6 +139,25 @@ func (this *HTTPDNSSandboxService) TestHTTPDNSResolve(ctx context.Context, req *
return nil, err
}
port := "443"
if len(cluster.TLSPolicy) > 0 {
var tlsConfig map[string]interface{}
if err := json.Unmarshal(cluster.TLSPolicy, &tlsConfig); err == nil {
if listenRaw, ok := tlsConfig["listen"]; ok && listenRaw != nil {
if data, err := json.Marshal(listenRaw); err == nil {
var listenAddresses []map[string]interface{}
if err := json.Unmarshal(data, &listenAddresses); err == nil {
if len(listenAddresses) > 0 {
if portRange, ok := listenAddresses[0]["portRange"].(string); ok && len(portRange) > 0 {
port = portRange
}
}
}
}
}
}
}
query := url.Values{}
query.Set("appId", req.AppId)
query.Set("dn", req.Domain)
@@ -167,7 +197,7 @@ func (this *HTTPDNSSandboxService) TestHTTPDNSResolve(ctx context.Context, req *
query.Set("sign", sign)
}
resolveURL := "https://" + serviceDomain + "/resolve?" + query.Encode()
resolveURL := "https://" + serviceDomain + ":" + port + "/resolve?" + query.Encode()
httpClient := &http.Client{
Timeout: 5 * time.Second,

View File

@@ -18,16 +18,14 @@ func notifyHTTPDNSAppTasksByApp(tx *dbs.Tx, app *models.HTTPDNSApp, taskType mod
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)
clusterIds := models.SharedHTTPDNSAppDAO.ReadAppClusterIds(app)
notified := map[int64]bool{}
for _, clusterId := range clusterIds {
if clusterId <= 0 || notified[clusterId] {
continue
}
notified[clusterId] = true
err := notifyHTTPDNSClusterTask(tx, clusterId, taskType)
if err != nil {
return err
}

View File

@@ -71,7 +71,7 @@ func (this *UserService) UpdateUser(ctx context.Context, req *pb.UpdateUserReque
return nil, err
}
err = models.SharedUserDAO.UpdateUser(tx, req.UserId, req.Username, req.Password, req.Fullname, req.Mobile, req.Tel, req.Email, req.Remark, req.IsOn, req.NodeClusterId, req.BandwidthAlgo)
err = models.SharedUserDAO.UpdateUser(tx, req.UserId, req.Username, req.Password, req.Fullname, req.Mobile, req.Tel, req.Email, req.Remark, req.IsOn, req.NodeClusterId, req.BandwidthAlgo, req.HttpdnsClusterIdsJSON)
if err != nil {
return nil, err
}
@@ -242,6 +242,20 @@ func (this *UserService) FindEnabledUser(ctx context.Context, req *pb.FindEnable
}
}
// 用户功能列表
var pbFeatures []*pb.UserFeature
userFeatures, err := models.SharedUserDAO.FindUserFeatures(tx, req.UserId)
if err != nil {
return nil, err
}
for _, f := range userFeatures {
pbFeatures = append(pbFeatures, &pb.UserFeature{
Name: f.Name,
Code: f.Code,
Description: f.Description,
})
}
return &pb.FindEnabledUserResponse{
User: &pb.User{
Id: int64(user.Id),
@@ -265,6 +279,8 @@ func (this *UserService) FindEnabledUser(ctx context.Context, req *pb.FindEnable
BandwidthAlgo: user.BandwidthAlgo,
OtpLogin: pbOtpAuth,
Lang: user.Lang,
Features: pbFeatures,
HttpdnsClusterIdsJSON: user.HttpdnsClusterIds,
},
}, nil
}

View File

@@ -5,11 +5,13 @@ package users
import (
"context"
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
)
// FindUserPriceInfo 读取用户计费信息
@@ -142,13 +144,27 @@ func (this *UserService) RegisterUser(ctx context.Context, req *pb.RegisterUserR
return errors.New("the username exists already")
}
features := registerConfig.Features
if registerConfig.HTTPDNSIsOn && !lists.ContainsString(features, userconfigs.UserFeatureCodeHTTPDNS) {
features = append(features, userconfigs.UserFeatureCodeHTTPDNS)
}
// 创建用户
userId, err := models.SharedUserDAO.CreateUser(tx, req.Username, req.Password, req.Fullname, req.Mobile, "", req.Email, "", req.Source, registerConfig.ClusterId, registerConfig.Features, req.Ip, !registerConfig.RequireVerification)
userId, err := models.SharedUserDAO.CreateUser(tx, req.Username, req.Password, req.Fullname, req.Mobile, "", req.Email, "", req.Source, registerConfig.ClusterId, features, req.Ip, !registerConfig.RequireVerification)
if err != nil {
return err
}
createdUserId = userId
// 自动关联默认 HTTPDNS 集群
if registerConfig.HTTPDNSIsOn && len(registerConfig.HTTPDNSDefaultClusterIds) > 0 {
httpdnsJSON, _ := json.Marshal(registerConfig.HTTPDNSDefaultClusterIds)
err = models.SharedUserDAO.UpdateUserHttpdnsClusterIds(tx, userId, httpdnsJSON)
if err != nil {
return err
}
}
// 发送激活邮件
if len(req.Email) > 0 && registerConfig.EmailVerification.IsOn {
_, err := models.SharedUserEmailVerificationDAO.CreateVerification(tx, userId, req.Email)