384 lines
11 KiB
Go
384 lines
11 KiB
Go
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"
|
||
"strings"
|
||
"time"
|
||
|
||
"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) {
|
||
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if userId > 0 {
|
||
req.UserId = userId
|
||
}
|
||
appName := strings.TrimSpace(req.Name)
|
||
appId := strings.TrimSpace(req.AppId)
|
||
if len(appName) == 0 || len(appId) == 0 {
|
||
return nil, errors.New("required 'name' and 'appId'")
|
||
}
|
||
var appDbId int64
|
||
now := time.Now().Unix()
|
||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||
// 用户端防重复提交:短时间内同用户同应用名仅创建一次。
|
||
if req.UserId > 0 {
|
||
latest, err := models.SharedHTTPDNSAppDAO.FindLatestEnabledAppWithNameAndUser(tx, appName, req.UserId)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if latest != nil && int64(latest.CreatedAt) >= now-5 {
|
||
appDbId = int64(latest.Id)
|
||
secret, err := models.SharedHTTPDNSAppSecretDAO.FindEnabledAppSecret(tx, appDbId)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if secret == nil {
|
||
_, _, err = models.SharedHTTPDNSAppSecretDAO.InitAppSecret(tx, appDbId, req.SignEnabled)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
}
|
||
|
||
exists, err := models.SharedHTTPDNSAppDAO.FindEnabledAppWithAppId(tx, appId)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if exists != nil {
|
||
return errors.New("appId already exists")
|
||
}
|
||
|
||
// 使用 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)
|
||
}
|
||
}
|
||
}
|
||
// 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)
|
||
}
|
||
}
|
||
}
|
||
|
||
appDbId, err = models.SharedHTTPDNSAppDAO.CreateApp(tx, appName, appId, clusterIdsJSON, 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
|
||
}
|
||
|
||
// 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 nil, err
|
||
}
|
||
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 {
|
||
return []int64{primaryClusterId}, nil
|
||
}
|
||
return nil, nil
|
||
}
|
||
|
||
func (this *HTTPDNSAppService) UpdateHTTPDNSApp(ctx context.Context, req *pb.UpdateHTTPDNSAppRequest) (*pb.RPCSuccess, error) {
|
||
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||
oldApp, err := ensureAppAccess(tx, req.AppDbId, userId)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if oldApp == nil {
|
||
return errors.New("app not found")
|
||
}
|
||
|
||
targetUserId := req.UserId
|
||
if targetUserId <= 0 {
|
||
targetUserId = oldApp.UserId
|
||
}
|
||
if userId > 0 {
|
||
targetUserId = userId
|
||
}
|
||
|
||
err = models.SharedHTTPDNSAppDAO.UpdateApp(tx, req.AppDbId, req.Name, req.ClusterIdsJSON, req.IsOn, targetUserId)
|
||
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) {
|
||
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||
app, err := ensureAppAccess(tx, req.AppDbId, userId)
|
||
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) {
|
||
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
app, err := ensureAppAccess(this.NullTx(), req.AppDbId, userId)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if app == nil {
|
||
return &pb.FindHTTPDNSAppResponse{}, nil
|
||
}
|
||
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) {
|
||
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
var apps []*models.HTTPDNSApp
|
||
if userId > 0 {
|
||
apps, err = models.SharedHTTPDNSAppDAO.ListEnabledAppsWithUser(this.NullTx(), userId, req.Offset, req.Size, req.Keyword)
|
||
} else {
|
||
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) {
|
||
_, userId, validateErr := this.ValidateAdminAndUser(ctx, true)
|
||
if validateErr != nil {
|
||
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
||
return nil, validateErr
|
||
}
|
||
}
|
||
var apps []*models.HTTPDNSApp
|
||
var err error
|
||
if validateErr == nil && userId > 0 {
|
||
apps, err = models.SharedHTTPDNSAppDAO.FindAllEnabledAppsWithUser(this.NullTx(), userId)
|
||
} else {
|
||
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) {
|
||
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||
app, err := ensureAppAccess(tx, req.AppDbId, userId)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if app == nil {
|
||
return errors.New("app not found")
|
||
}
|
||
|
||
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) {
|
||
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
var signSecret string
|
||
var updatedAt int64
|
||
err = this.RunTx(func(tx *dbs.Tx) error {
|
||
app, err := ensureAppAccess(tx, req.AppDbId, userId)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if app == nil {
|
||
return errors.New("app not found")
|
||
}
|
||
|
||
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
|
||
}
|