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;若为空则从用户关联集群获取 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 len(clusterIdsJSON) == 0 || string(clusterIdsJSON) == "[]" || string(clusterIdsJSON) == "null" { return errors.New("用户尚未分配 HTTPDNS 集群,无法创建应用") } 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 } } } } 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 }