|
@@ -2,7 +2,6 @@ package waf
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
- "encoding/json"
|
|
|
"fmt"
|
|
|
v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
|
|
|
"github.com/go-nunu/nunu-layout-advanced/internal/model"
|
|
@@ -11,8 +10,6 @@ import (
|
|
|
"github.com/go-nunu/nunu-layout-advanced/internal/service/api/flexCdn"
|
|
|
"github.com/go-nunu/nunu-layout-advanced/pkg/rabbitmq"
|
|
|
"golang.org/x/sync/errgroup"
|
|
|
- "maps"
|
|
|
- "net"
|
|
|
"sort"
|
|
|
)
|
|
|
|
|
@@ -215,364 +212,121 @@ func (s *webForwardingService) AddWebForwarding(ctx context.Context, req *v1.Web
|
|
|
}
|
|
|
|
|
|
func (s *webForwardingService) EditWebForwarding(ctx context.Context, req *v1.WebForwardingRequest) error {
|
|
|
-
|
|
|
-
|
|
|
- err := s.wafformatter.validateWafDomainCount(ctx, v1.GlobalRequire{
|
|
|
- HostId: req.HostId,
|
|
|
- Domain: req.WebForwardingData.Domain,
|
|
|
- Comment: req.WebForwardingData.Comment,
|
|
|
- Uid: req.Uid,
|
|
|
- })
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
+ // 1. 获取原始数据
|
|
|
oldData, err := s.webForwardingRepository.GetWebForwarding(ctx, int64(req.WebForwardingData.Id))
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return fmt.Errorf("获取原始Web转发数据失败: %w", err)
|
|
|
}
|
|
|
+ // 继承旧的证书ID和策略ID,以便后续逻辑处理
|
|
|
req.WebForwardingData.SslCertId = int64(oldData.SslCertId)
|
|
|
req.WebForwardingData.SslPolicyId = int64(oldData.SslPolicyId)
|
|
|
+
|
|
|
+ // 2. 准备WAF数据和基础验证
|
|
|
require, formData, err := s.aidedWeb.PrepareWafData(ctx, req)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
-
|
|
|
- // 验证端口重复
|
|
|
- protocol := s.aidedWeb.GetProtocolType(req.WebForwardingData.IsHttps)
|
|
|
- err = s.wafformatter.VerifyPort(ctx, protocol, int64(req.WebForwardingData.Id), req.WebForwardingData.Port, int64(require.HostId), req.WebForwardingData.Domain)
|
|
|
- if err != nil {
|
|
|
+ if err := s.aidedWeb.ValidateEditRequest(ctx, req, require, oldData); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
- //修改网站端口
|
|
|
- if oldData.Port != req.WebForwardingData.Port || oldData.IsHttps != req.WebForwardingData.IsHttps || oldData.HttpsCert != req.WebForwardingData.HttpsCert || oldData.HttpsKey != req.WebForwardingData.HttpsKey {
|
|
|
- var apiType string
|
|
|
- var closeType string
|
|
|
-
|
|
|
- // 修改证书
|
|
|
- if oldData.HttpsCert != req.WebForwardingData.HttpsCert || oldData.HttpsKey != req.WebForwardingData.HttpsKey {
|
|
|
- err = s.sslCert.EditSSLCert(ctx, v1.SSL{
|
|
|
- Name: req.WebForwardingData.Domain,
|
|
|
- CertId: oldData.SslCertId,
|
|
|
- CertData: req.WebForwardingData.HttpsCert,
|
|
|
- KeyData: req.WebForwardingData.HttpsKey,
|
|
|
- CdnUserId: require.CdnUid,
|
|
|
- Domain: req.WebForwardingData.Domain,
|
|
|
- Description: req.WebForwardingData.Comment,
|
|
|
- })
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 切换协议
|
|
|
- var typeConfig v1.TypeJSON
|
|
|
- var closeConfig v1.TypeJSON
|
|
|
- if s.aidedWeb.IsHttpsProtocol(req.WebForwardingData.IsHttps) {
|
|
|
- typeConfig = formData.HttpsJSON
|
|
|
- closeConfig = formData.HttpJSON
|
|
|
- apiType = s.aidedWeb.GetProtocolType(req.WebForwardingData.IsHttps)
|
|
|
- closeType = s.aidedWeb.GetProtocolType(0) // HTTP
|
|
|
- } else {
|
|
|
- typeConfig = formData.HttpJSON
|
|
|
- closeConfig = formData.HttpsJSON
|
|
|
- apiType = s.aidedWeb.GetProtocolType(req.WebForwardingData.IsHttps)
|
|
|
- closeType = s.aidedWeb.GetProtocolType(1) // HTTPS
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- typeJson,err := json.Marshal(typeConfig)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- closeJson,err := json.Marshal(closeConfig)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- // 切换协议
|
|
|
- err = s.cdn.EditServerType(ctx, v1.EditWebsite{
|
|
|
- Id: int64(oldData.CdnWebId),
|
|
|
- TypeJSON: typeJson,
|
|
|
- }, apiType)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- err = s.cdn.EditServerType(ctx, v1.EditWebsite{
|
|
|
- Id: int64(oldData.CdnWebId),
|
|
|
- TypeJSON: closeJson,
|
|
|
- }, closeType)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //修改网站域名
|
|
|
- if oldData.Domain != req.WebForwardingData.Domain {
|
|
|
- type serverName struct {
|
|
|
- Name string `json:"name" form:"name"`
|
|
|
- Type string `json:"type" form:"type"`
|
|
|
- }
|
|
|
- var serverData []serverName
|
|
|
- var serverJson []byte
|
|
|
- serverData = append(serverData, serverName{
|
|
|
- Name: req.WebForwardingData.Domain,
|
|
|
- Type: "full",
|
|
|
- })
|
|
|
- serverJson, err = json.Marshal(serverData)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- err = s.cdn.EditServerName(ctx, v1.EditServerNames{
|
|
|
- ServerId: int64(oldData.CdnWebId),
|
|
|
- ServerNamesJSON: serverJson,
|
|
|
- })
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
+ // 3. 处理SSL证书更新
|
|
|
+ if err := s.aidedWeb.ProcessSSLCertificateUpdate(ctx, req, oldData, require); err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- //修改网站名字
|
|
|
- if oldData.Comment != req.WebForwardingData.Comment {
|
|
|
- nodeId,err := s.globalLimitRep.GetNodeId(ctx, oldData.CdnWebId)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- err = s.cdn.EditServerBasic(ctx, int64(oldData.CdnWebId), require.Tag,nodeId)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
+ // 4. 更新核心CDN配置(端口、协议、域名、备注等)
|
|
|
+ if err := s.aidedWeb.UpdateCdnConfiguration(ctx, req, oldData, require, formData); err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- //修改Proxy
|
|
|
+ // 5. 更新Proxy Protocol配置
|
|
|
if oldData.Proxy != req.WebForwardingData.Proxy {
|
|
|
- err = s.proxy.EditProxy(ctx, int64(oldData.CdnWebId), v1.ProxyProtocolJSON{
|
|
|
- IsOn: req.WebForwardingData.Proxy,
|
|
|
- Version: 1,
|
|
|
- })
|
|
|
- if err != nil {
|
|
|
+ if err := s.aidedWeb.ConfigureProxyProtocol(ctx, req, int64(oldData.CdnWebId)); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 修改CC配置
|
|
|
- err = s.cc.EditCcConfig(ctx, int64(oldData.CdnWebId), v1.CcConfigRequest{
|
|
|
- IsOn: req.WebForwardingData.CcConfig.IsOn,
|
|
|
- Level: req.WebForwardingData.CcConfig.Level,
|
|
|
- Limit5s: req.WebForwardingData.CcConfig.Limit5s,
|
|
|
- Limit60s: req.WebForwardingData.CcConfig.Limit60s,
|
|
|
- Limit300s: req.WebForwardingData.CcConfig.Limit300s,
|
|
|
- ThresholdMethod: req.WebForwardingData.CcConfig.ThresholdMethod,
|
|
|
- })
|
|
|
- if err != nil {
|
|
|
+ // 6. 更新CC防护配置
|
|
|
+ if err := s.aidedWeb.ConfigureCCProtection(ctx, req, int64(oldData.CdnWebId)); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- // 将域名添加到白名单
|
|
|
- webData, err := s.webForwardingRepository.GetWebForwarding(ctx, int64(req.WebForwardingData.Id))
|
|
|
- if err != nil {
|
|
|
+ // 7. 处理域名白名单变更
|
|
|
+ if err := s.aidedWeb.ProcessDomainWhitelistChanges(ctx, req, oldData, require); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- // 异步任务:将域名添加到白名单
|
|
|
- if webData.Domain != req.WebForwardingData.Domain {
|
|
|
- firstIp, err := s.gatewayIp.GetGatewayipByHostIdFirst(ctx, int64(req.HostId), int64(req.Uid))
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- doMain, err := s.wafformatter.ConvertToWildcardDomain(ctx, req.WebForwardingData.Domain)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- oldDomain, err := s.wafformatter.ConvertToWildcardDomain(ctx, webData.Domain)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- if len(require.GatewayIps) == 0 {
|
|
|
- return fmt.Errorf("网关组不存在")
|
|
|
- }
|
|
|
-
|
|
|
- // 查找域名数量,如果数量小于2,删除旧域名
|
|
|
- count, err := s.webForwardingRepository.GetDomainCount(ctx, req.HostId, webData.Domain)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- if count < 2 {
|
|
|
- go s.wafformatter.PublishDomainWhitelistTask(oldDomain, firstIp, "del")
|
|
|
- }
|
|
|
- go s.wafformatter.PublishDomainWhitelistTask(doMain, firstIp, "add")
|
|
|
- }
|
|
|
-
|
|
|
- // IP过白
|
|
|
+ // 8. 获取后端IP规则数据
|
|
|
ipData, err := s.webForwardingRepository.GetWebForwardingIpsByID(ctx, req.WebForwardingData.Id)
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return fmt.Errorf("获取Web转发IP规则失败: %w", err)
|
|
|
}
|
|
|
- var oldIps []string
|
|
|
- var newIps []string
|
|
|
- for _, v := range ipData.BackendList {
|
|
|
- ip, _, err := net.SplitHostPort(v.Addr)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- oldIps = append(oldIps, ip)
|
|
|
+
|
|
|
+ // 9. 处理源站IP白名单变更
|
|
|
+ if err := s.aidedWeb.ProcessIpWhitelistChanges(ctx, req, ipData); err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
- for _, v := range req.WebForwardingData.BackendList {
|
|
|
- ip, _, err := net.SplitHostPort(v.Addr)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- newIps = append(newIps, ip)
|
|
|
+
|
|
|
+ // 10. 更新CDN上的源站服务器
|
|
|
+ if err := s.aidedWeb.UpdateOriginServers(ctx, req, oldData, ipData); err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
- addedIps, removedIps := s.wafformatter.findIpDifferences(oldIps, newIps)
|
|
|
- if len(addedIps) > 0 {
|
|
|
- go s.wafformatter.PublishIpWhitelistTask(addedIps, "add", "", "white")
|
|
|
+
|
|
|
+ // 11. 更新本地数据库记录
|
|
|
+ if err := s.aidedWeb.UpdateDatabaseRecords(ctx, req, oldData, require, ipData); err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- // IP过白
|
|
|
- if len(removedIps) > 0 {
|
|
|
- // 1. 一次性获取所有相关IP的数量
|
|
|
- ipsToDelist, err := s.wafformatter.WashDelIps(ctx, removedIps)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
+ return nil
|
|
|
+}
|
|
|
|
|
|
- // 4. 如果有需要处理的IP,则批量发布一次任务
|
|
|
- if len(ipsToDelist) > 0 {
|
|
|
- go s.wafformatter.PublishIpWhitelistTask(ipsToDelist, "del", "0", "white")
|
|
|
+// DeleteWebForwarding 批量删除Web转发配置
|
|
|
+// 该函数遍历ID列表,对每个ID执行完整的、独立的删除流程
|
|
|
+func (s *webForwardingService) DeleteWebForwarding(ctx context.Context, req v1.DeleteWebForwardingRequest) error {
|
|
|
+ for _, id := range req.Ids {
|
|
|
+ if err := s.deleteSingleWebForwarding(ctx, id, req.HostId, req.Uid); err != nil {
|
|
|
+ // 增加错误上下文,方便定位问题
|
|
|
+ return fmt.Errorf("删除Web转发配置失败 ID:%d, %w", id, err)
|
|
|
}
|
|
|
}
|
|
|
+ return nil
|
|
|
+}
|
|
|
|
|
|
- //修改源站
|
|
|
- addOrigins, delOrigins := s.aidedWeb.FindDifferenceList(ipData.BackendList, req.WebForwardingData.BackendList)
|
|
|
- addedIds := make(map[string]int64)
|
|
|
- for _, v := range addOrigins {
|
|
|
- apiType := s.aidedWeb.GetProtocolType(v.IsHttps)
|
|
|
- id, err := s.wafformatter.AddOrigin(ctx, v1.WebJson{
|
|
|
- ApiType: apiType,
|
|
|
- BackendList: v.Addr,
|
|
|
- Host: v.CustomHost,
|
|
|
- Comment: req.WebForwardingData.Comment,
|
|
|
- })
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- addedIds[v.Addr] = id
|
|
|
+// deleteSingleWebForwarding 删除单个Web转发配置的完整流程
|
|
|
+func (s *webForwardingService) deleteSingleWebForwarding(ctx context.Context, id int, hostId int, uid int) error {
|
|
|
+ // 1. 获取并验证数据
|
|
|
+ oldData, err := s.webForwardingRepository.GetWebForwarding(ctx, int64(id))
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("获取Web转发数据失败: %w", err)
|
|
|
}
|
|
|
- for _, v := range addedIds {
|
|
|
- err = s.cdn.AddServerOrigin(ctx, int64(oldData.CdnWebId), v)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
+ if err := s.aidedWeb.ValidateDeletePermission(oldData, hostId); err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- for k, v := range ipData.CdnOriginIds {
|
|
|
- for _, ip := range delOrigins {
|
|
|
- if k == ip.Addr {
|
|
|
- err = s.cdn.DelServerOrigin(ctx, int64(oldData.CdnWebId), v)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- delete(ipData.CdnOriginIds, k)
|
|
|
- }
|
|
|
- }
|
|
|
+ // 2. 删除CDN服务器
|
|
|
+ if err := s.aidedWeb.DeleteCdnServer(ctx, oldData.CdnWebId); err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- maps.Copy(ipData.CdnOriginIds, addedIds)
|
|
|
-
|
|
|
- webModel := s.aidedWeb.BuildWebForwardingModel(&req.WebForwardingData, req.WebForwardingData.CdnWebId, require)
|
|
|
- webModel.Id = req.WebForwardingData.Id
|
|
|
- if err = s.webForwardingRepository.EditWebForwarding(ctx, webModel); err != nil {
|
|
|
+ // 3. 处理域名白名单清理
|
|
|
+ if err := s.aidedWeb.ProcessDeleteDomainWhitelist(ctx, oldData, uid); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- webRuleModel := s.aidedWeb.BuildWebRuleModel(&req.WebForwardingData, require, req.WebForwardingData.Id, ipData.CdnOriginIds)
|
|
|
- if err = s.webForwardingRepository.EditWebForwardingIps(ctx, *webRuleModel); err != nil {
|
|
|
+
|
|
|
+ // 4. 处理IP白名单清理
|
|
|
+ if err := s.aidedWeb.ProcessDeleteIpWhitelist(ctx, id); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *webForwardingService) DeleteWebForwarding(ctx context.Context, req v1.DeleteWebForwardingRequest) error {
|
|
|
- for _, Id := range req.Ids {
|
|
|
- oldData, err := s.webForwardingRepository.GetWebForwarding(ctx, int64(Id))
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- if oldData.HostId != req.HostId {
|
|
|
- return fmt.Errorf("用户权限不足")
|
|
|
- }
|
|
|
-
|
|
|
- err = s.cdn.DelServer(ctx, int64(oldData.CdnWebId))
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // 异步任务:将域名添加到白名单
|
|
|
- firstIp, err := s.gatewayIp.GetGatewayipByHostIdFirst(ctx, int64(oldData.HostId), int64(req.Uid))
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- if oldData.Domain != "" {
|
|
|
-
|
|
|
- doMain, err := s.wafformatter.ConvertToWildcardDomain(ctx, oldData.Domain)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- go s.wafformatter.PublishDomainWhitelistTask(doMain, firstIp, "del")
|
|
|
- }
|
|
|
- // IP过白
|
|
|
- ipData, err := s.webForwardingRepository.GetWebForwardingIpsByID(ctx, Id)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- var ips []string
|
|
|
- if ipData != nil && len(ipData.BackendList) > 0 {
|
|
|
- for _, v := range ipData.BackendList {
|
|
|
- ip, _, err := net.SplitHostPort(v.Addr)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- ips = append(ips, ip)
|
|
|
- }
|
|
|
- }
|
|
|
- if len(ips) > 0 {
|
|
|
- ipsToDelist, err := s.wafformatter.WashDelIps(ctx, ips)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // 4. 如果有需要处理的IP,则批量发布一次任务
|
|
|
- if len(ipsToDelist) > 0 {
|
|
|
- go s.wafformatter.PublishIpWhitelistTask(ipsToDelist, "del", "0", "white")
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 删除ssl
|
|
|
- if oldData.SslCertId != 0 {
|
|
|
- err = s.cdn.DelSSLCert(ctx, int64(oldData.SslCertId))
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- err = s.sslCert.EditSslPolicy(ctx, int64(oldData.SslPolicyId), []int64{int64(oldData.SslCertId)}, "del")
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
|
|
|
+ // 5. 清理SSL证书
|
|
|
+ if err := s.aidedWeb.CleanupSSLCertificate(ctx, oldData); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
|
|
|
- if err = s.webForwardingRepository.DeleteWebForwarding(ctx, int64(Id)); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- if err = s.webForwardingRepository.DeleteWebForwardingIpsById(ctx, Id); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
+ // 6. 清理数据库记录
|
|
|
+ if err := s.aidedWeb.CleanupDatabaseRecords(ctx, id); err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
return nil
|