package waf import ( "context" "encoding/json" "fmt" "net" v1 "github.com/go-nunu/nunu-layout-advanced/api/v1" "github.com/go-nunu/nunu-layout-advanced/internal/model" "github.com/go-nunu/nunu-layout-advanced/internal/repository/api/waf" "github.com/go-nunu/nunu-layout-advanced/internal/service" "github.com/go-nunu/nunu-layout-advanced/internal/service/api/flexCdn" ) // AidedWebService Web转发辅助服务接口 type AidedWebService interface { // 验证相关 ValidateAddRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) error ValidateEditRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, oldData *model.WebForwarding) error ValidateDeletePermission(oldData *model.WebForwarding, hostId int) error // CDN网站管理 CreateCdnWebsite(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, formData v1.Website) (int64, error) UpdateCdnConfiguration(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse, formData v1.Website) error DeleteCdnServer(ctx context.Context, cdnWebId int) error // 源站管理 AddOriginsToWebsite(ctx context.Context, req *v1.WebForwardingRequest, webId int64) (map[string]int64, error) UpdateOriginServers(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, ipData *model.WebForwardingRule) error // 功能配置管理 ConfigureWebsocket(ctx context.Context, webId int64) error ConfigureProxyProtocol(ctx context.Context, req *v1.WebForwardingRequest, webId int64) error ConfigureCCProtection(ctx context.Context, req *v1.WebForwardingRequest, webId int64) error ConfigureWafFirewall(ctx context.Context, webId int64, groupId int) error // 异步任务处理 ProcessAsyncTasks(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) ProcessIpWhitelistChanges(ctx context.Context, req *v1.WebForwardingRequest, ipData *model.WebForwardingRule) error ProcessDeleteIpWhitelist(ctx context.Context, id int) error ProcessDomainWhitelistChanges(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse) error ProcessDeleteDomainWhitelist(ctx context.Context, oldData *model.WebForwarding, uid int) error // 数据库操作 SaveToDatabase(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, webId int64, cdnOriginIds map[string]int64) (int, error) UpdateDatabaseRecords(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse, ipData *model.WebForwardingRule) error CleanupDatabaseRecords(ctx context.Context, id int) error // SSL证书管理 ProcessSSLCertificate(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, formData v1.Website) error ProcessSSLCertificateUpdate(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse) error CleanupSSLCertificate(ctx context.Context, oldData *model.WebForwarding) error // 数据准备辅助函数 PrepareWafData(ctx context.Context, req *v1.WebForwardingRequest) (RequireResponse, v1.Website, error) BuildProxyConfig(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) (v1.TypeJSON, error) BulidFormData(ctx context.Context, formData v1.Website) (v1.WebsiteSend, error) // 协议判断辅助函数 GetProtocolType(isHttps int) string IsHttpsProtocol(isHttps int) bool // 模型构建辅助函数 BuildWebForwardingModel(req *v1.WebForwardingDataRequest, ruleId int, require RequireResponse) *model.WebForwarding BuildWebRuleModel(reqData *v1.WebForwardingDataRequest, require RequireResponse, localDbId int, cdnOriginIds map[string]int64) *model.WebForwardingRule // 列表差异处理辅助函数 FindDifferenceList(oldList, newList []v1.BackendList) (added, removed []v1.BackendList) WashDifferentIp(newIpList []string, oldIpList []string) (addedDenyIps []string, removedDenyIps []string) // 日志配置辅助函数 EditLog(ctx context.Context, webId int64) error // 废弃的方法(保持向后兼容) Require(ctx context.Context, req v1.GlobalRequire) (v1.GlobalRequire, error) ValidateWebForwardingRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) error CreateOriginServers(ctx context.Context, req *v1.WebForwardingRequest) (map[string]int64, error) } func NewAidedWebService( service *service.Service, webForwardingRepository waf.WebForwardingRepository, wafformatter WafFormatterService, sslCert flexCdn.SslCertService, cdn flexCdn.CdnService, proxy flexCdn.ProxyService, websocket flexCdn.WebsocketService, cc CcService, ccIpList CcIpListService, gatewayIp GatewayipService, globalLimitRep waf.GlobalLimitRepository, ) AidedWebService { return &aidedWebService{ Service: service, webForwardingRepository: webForwardingRepository, wafformatter: wafformatter, sslCert: sslCert, cdn: cdn, proxy: proxy, websocket: websocket, cc: cc, ccIpList: ccIpList, gatewayIp: gatewayIp, globalLimitRep: globalLimitRep, } } type aidedWebService struct { *service.Service webForwardingRepository waf.WebForwardingRepository wafformatter WafFormatterService sslCert flexCdn.SslCertService cdn flexCdn.CdnService proxy flexCdn.ProxyService websocket flexCdn.WebsocketService cc CcService ccIpList CcIpListService gatewayIp GatewayipService globalLimitRep waf.GlobalLimitRepository } const ( // 协议类型常量 isHttps = 1 isHttp = 0 protocolHttps = "https" protocolHttp = "http" // 默认配置常量 defaultNodeClusterId = 2 proxyProtocolVersion = 1 ) // Require 验证函数(原require函数) func (s *aidedWebService) Require(ctx context.Context, req v1.GlobalRequire) (v1.GlobalRequire, error) { var res v1.GlobalRequire //g, gCtx := errgroup.WithContext(ctx) //g.Go(func() error { // result, e := s.wafformatter.require(gCtx, req, "web") // if e != nil { // return e // } // res = result // return nil //}) //g.Go(func() error { // e := s.wafformatter.validateWafDomainCount(gCtx, req) // if e != nil { // return e // } // return nil //}) //if err = g.Wait(); err != nil { // return v1.GlobalRequire{}, err //} return res, nil } // BuildWebForwardingModel 辅助函数,用于构建通用的 WebForwarding 模型 // ruleId 是从 WAF 系统获取的 ID func (s *aidedWebService) BuildWebForwardingModel(req *v1.WebForwardingDataRequest, ruleId int, require RequireResponse) *model.WebForwarding { return &model.WebForwarding{ HostId: require.HostId, CdnWebId: ruleId, Port: req.Port, Domain: req.Domain, IsHttps: req.IsHttps, Comment: req.Comment, HttpsCert: req.HttpsCert, HttpsKey: req.HttpsKey, SslCertId: int(req.SslCertId), SslPolicyId: int(req.SslPolicyId), Cc: req.CcConfig.IsOn, ThresholdMethod: req.CcConfig.ThresholdMethod, Level: req.CcConfig.Level, Limit5s: req.CcConfig.Limit5s, Limit60s: req.CcConfig.Limit60s, Limit300s: req.CcConfig.Limit300s, Proxy: req.Proxy, } } // BuildWebRuleModel 构建WebForwardingRule模型 func (s *aidedWebService) BuildWebRuleModel(reqData *v1.WebForwardingDataRequest, require RequireResponse, localDbId int, cdnOriginIds map[string]int64) *model.WebForwardingRule { return &model.WebForwardingRule{ Uid: require.Uid, HostId: require.HostId, WebId: localDbId, CdnOriginIds: cdnOriginIds, BackendList: reqData.BackendList, } } // PrepareWafData 准备WAF数据 // 职责:协调整个流程,负责获取前置配置和组装最终的 formData。 func (s *aidedWebService) PrepareWafData(ctx context.Context, req *v1.WebForwardingRequest) (RequireResponse, v1.Website, error) { // 1. 获取基础配置 require, err := s.wafformatter.Require(ctx, v1.GlobalRequire{ HostId: req.HostId, Uid: req.Uid, Comment: req.WebForwardingData.Comment, }) if err != nil { return RequireResponse{}, v1.Website{}, fmt.Errorf("获取WAF前置配置失败: %w", err) } if require.Uid == 0 { return RequireResponse{}, v1.Website{}, fmt.Errorf("请先配置实例") } // 2. 调用辅助函数,构建核心的代理配置 (将复杂逻辑封装起来) byteData, err := s.BuildProxyConfig(ctx, req, require) if err != nil { return RequireResponse{}, v1.Website{}, err // 错误信息在辅助函数中已经包装好了 } req.WebForwardingData.SslPolicyId = byteData.SslPolicyRef.SslPolicyId type serverNames struct { ServerNames string `json:"name" form:"name"` Type string `json:"type" form:"type"` } var serverName []serverNames var serverJson []byte if req.WebForwardingData.Domain != "" { serverName = append(serverName, serverNames{ ServerNames: req.WebForwardingData.Domain, Type: "full", }) serverJson, err = json.Marshal(serverName) if err != nil { return RequireResponse{}, v1.Website{}, err } } // 3. 组装最终的 WAF 表单数据 formData := v1.Website{ UserId: int64(require.CdnUid), Type: "httpProxy", Name: require.Tag, ServerNamesJSON: serverJson, Description: req.WebForwardingData.Comment, ServerGroupIds: []int64{int64(require.GroupId)}, NodeClusterId: defaultNodeClusterId, } // 4. 根据协议类型,填充 HttpJSON 和 HttpsJSON 字段 if req.WebForwardingData.IsHttps == isHttps { formData.HttpJSON = v1.TypeJSON{IsOn: false} formData.HttpsJSON = byteData } else { formData.HttpJSON = byteData formData.HttpsJSON = v1.TypeJSON{IsOn: false} } return require, formData, nil } // BuildProxyConfig 构建代理配置 // 职责:专门负责处理 HTTP/HTTPS 的差异,并生成对应的 JSON 配置。 func (s *aidedWebService) BuildProxyConfig(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) (v1.TypeJSON, error) { var ( jsonData v1.TypeJSON apiType string ) jsonData.IsOn = true apiType = protocolHttps jsonData.SslPolicyRef.SslPolicyId = req.WebForwardingData.SslPolicyId // 判断协议类型,并处理 HTTPS 的特殊逻辑(证书) if req.WebForwardingData.IsHttps == isHttps { // 处理证书信息 if jsonData.SslPolicyRef.SslPolicyId == 0 { sslPolicyId, err := s.sslCert.AddSslPolicy(ctx, nil) if err != nil { return v1.TypeJSON{}, err } jsonData.SslPolicyRef.SslPolicyId = sslPolicyId } jsonData.SslPolicyRef.IsOn = true } else { apiType = protocolHttp jsonData.SslPolicyRef = v1.SslPolicyRef{ IsOn: false, SslPolicyId: req.WebForwardingData.SslCertId, } } // 填充通用的 Listen 配置 for _, v := range require.GatewayIps { jsonData.Listen = append(jsonData.Listen, v1.Listen{ Protocol: apiType, Host: v, Port: req.WebForwardingData.Port, }) } return jsonData, nil } // FindDifferenceList 查找两个列表的差异 func (s *aidedWebService) FindDifferenceList(oldList, newList []v1.BackendList) (added, removed []v1.BackendList) { diff := make(map[v1.BackendList]int) // 1. 遍历旧列表,为每个元素计数 +1 for _, item := range oldList { diff[item]++ } // 2. 遍历新列表,为每个元素计数 -1 for _, item := range newList { diff[item]-- } // 3. 遍历 diff map 来找出差异 for item, count := range diff { if count > 0 { // 如果 count > 0,说明这个元素在 oldList 中但不在 newList 中 removed = append(removed, item) } else if count < 0 { // 如果 count < 0,说明这个元素在 newList 中但不在 oldList 中 added = append(added, item) } // 如果 count == 0,说明元素在两个列表中都存在,不做任何操作 } return added, removed } // WashDifferentIp 清洗IP差异 func (s *aidedWebService) WashDifferentIp(newIpList []string, oldIpList []string) (addedDenyIps []string, removedDenyIps []string) { var newAllowIps []string var oldAllowIps []string if len(oldIpList) > 0 { for _, v := range oldIpList { if net.ParseIP(v) != nil { oldAllowIps = append(oldAllowIps, v) } } } if len(newIpList) > 0 { for _, v := range newIpList { if net.ParseIP(v) != nil { newAllowIps = append(newAllowIps, v) } } } addedDenyIps, removedDenyIps = s.wafformatter.findIpDifferences(oldAllowIps, newAllowIps) return addedDenyIps, removedDenyIps } // EditLog 修改日志配置 func (s *aidedWebService) EditLog(ctx context.Context, webId int64) error { webConfigId, err := s.webForwardingRepository.GetWebConfigId(ctx, webId) if err != nil { return err } if err := s.cdn.EditWebLog(ctx, webConfigId, v1.WebLog{ IsPrior: false, IsOn: true, Fields: []int64{1, 2, 6, 7}, Status1: true, Status2: true, Status3: true, Status4: true, Status5: true, FirewallOnly: false, EnableClientClosed: false, }); err != nil { return err } return nil } // BulidFormData 构建表单数据 func (s *aidedWebService) BulidFormData(ctx context.Context, formData v1.Website) (v1.WebsiteSend, error) { httpJSON, err := json.Marshal(formData.HttpJSON) if err != nil { return v1.WebsiteSend{}, err } httpsJSON, err := json.Marshal(formData.HttpsJSON) if err != nil { return v1.WebsiteSend{}, err } formDataSend := v1.WebsiteSend{ UserId: formData.UserId, AdminId: formData.AdminId, Type: formData.Type, Name: formData.Name, Description: formData.Description, ServerNamesJSON: formData.ServerNamesJSON, HttpJSON: httpJSON, HttpsJSON: httpsJSON, TcpJSON: formData.TcpJSON, TlsJSON: formData.TlsJSON, UdpJSON: formData.UdpJSON, WebId: formData.WebId, ReverseProxyJSON: formData.ReverseProxyJSON, ServerGroupIds: formData.ServerGroupIds, UserPlanId: formData.UserPlanId, NodeClusterId: formData.NodeClusterId, IncludeNodesJSON: formData.IncludeNodesJSON, ExcludeNodesJSON: formData.ExcludeNodesJSON, } return formDataSend, nil } // ValidateWebForwardingRequest 验证Web转发请求 func (s *aidedWebService) ValidateWebForwardingRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) error { // 验证域名限制 if err := s.wafformatter.validateWafDomainCount(ctx, v1.GlobalRequire{ HostId: req.HostId, Domain: req.WebForwardingData.Domain, Comment: req.WebForwardingData.Comment, Uid: req.Uid, }); err != nil { return fmt.Errorf("域名数量验证失败: %w", err) } // 验证端口数量限制 if err := s.wafformatter.validateWafPortCount(ctx, require.HostId); err != nil { return fmt.Errorf("端口数量验证失败: %w", err) } // 验证端口重复 protocol := s.GetProtocolType(req.WebForwardingData.IsHttps) if err := s.wafformatter.VerifyPort(ctx, protocol, int64(req.WebForwardingData.Id), req.WebForwardingData.Port, int64(require.HostId), req.WebForwardingData.Domain); err != nil { return fmt.Errorf("端口 %d 验证失败: %w", req.WebForwardingData.Port, err) } return nil } // ProcessSSLCertificate 处理SSL证书 func (s *aidedWebService) ProcessSSLCertificate(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, formData v1.Website) error { if !s.IsHttpsProtocol(req.WebForwardingData.IsHttps) { return nil // 非HTTPS协议不需要处理SSL证书 } // 添加SSL证书 sslCertId, err := s.sslCert.AddSSLCert(ctx, v1.SSL{ Name: req.WebForwardingData.Domain, Domain: req.WebForwardingData.Domain, CertData: req.WebForwardingData.HttpsCert, KeyData: req.WebForwardingData.HttpsKey, CdnUserId: require.CdnUid, Description: req.WebForwardingData.Comment, }) if err != nil { return fmt.Errorf("添加SSL证书失败: %w", err) } // 更新请求中的证书ID req.WebForwardingData.SslCertId = sslCertId req.WebForwardingData.SslPolicyId = formData.HttpsJSON.SslPolicyRef.SslPolicyId // 编辑SSL策略 if err := s.sslCert.EditSslPolicy(ctx, formData.HttpsJSON.SslPolicyRef.SslPolicyId, []int64{sslCertId}, "add"); err != nil { return fmt.Errorf("编辑SSL策略失败: %w", err) } return nil } // CreateOriginServers 创建源站服务器 func (s *aidedWebService) CreateOriginServers(ctx context.Context, req *v1.WebForwardingRequest) (map[string]int64, error) { cdnOriginIds := make(map[string]int64) for _, backend := range req.WebForwardingData.BackendList { apiType := s.GetProtocolType(backend.IsHttps) id, err := s.wafformatter.AddOrigin(ctx, v1.WebJson{ ApiType: apiType, BackendList: backend.Addr, Host: backend.CustomHost, Comment: req.WebForwardingData.Comment, }) if err != nil { return nil, fmt.Errorf("添加源站 %s 失败: %w", backend.Addr, err) } cdnOriginIds[backend.Addr] = id } return cdnOriginIds, nil } // GetProtocolType 获取协议类型字符串 func (s *aidedWebService) GetProtocolType(isHttps int) string { if s.IsHttpsProtocol(isHttps) { return protocolHttps } return protocolHttp } // IsHttpsProtocol 判断是否为HTTPS协议 func (s *aidedWebService) IsHttpsProtocol(httpsFlag int) bool { return httpsFlag == isHttps } // ValidateAddRequest 验证添加请求 func (s *aidedWebService) ValidateAddRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) error { if err := s.wafformatter.validateWafDomainCount(ctx, v1.GlobalRequire{ HostId: req.HostId, Domain: req.WebForwardingData.Domain, Comment: req.WebForwardingData.Comment, Uid: req.Uid, }); err != nil { return fmt.Errorf("域名数量验证失败: %w", err) } if err := s.wafformatter.validateWafPortCount(ctx, require.HostId); err != nil { return fmt.Errorf("端口数量验证失败: %w", err) } protocol := s.GetProtocolType(req.WebForwardingData.IsHttps) if err := s.wafformatter.VerifyPort(ctx, protocol, int64(req.WebForwardingData.Id), req.WebForwardingData.Port, int64(require.HostId), req.WebForwardingData.Domain); err != nil { return fmt.Errorf("端口 %d 验证失败: %w", req.WebForwardingData.Port, err) } return nil } // ValidateEditRequest 验证编辑请求 func (s *aidedWebService) ValidateEditRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, oldData *model.WebForwarding) error { if err := s.wafformatter.validateWafDomainCount(ctx, v1.GlobalRequire{ HostId: req.HostId, Domain: req.WebForwardingData.Domain, Comment: req.WebForwardingData.Comment, Uid: req.Uid, }); err != nil { return fmt.Errorf("域名数量验证失败: %w", err) } protocol := s.GetProtocolType(req.WebForwardingData.IsHttps) if err := s.wafformatter.VerifyPort(ctx, protocol, int64(req.WebForwardingData.Id), req.WebForwardingData.Port, int64(require.HostId), req.WebForwardingData.Domain); err != nil { return fmt.Errorf("端口 %d 验证失败: %w", req.WebForwardingData.Port, err) } return nil } // ValidateDeletePermission 验证删除权限 func (s *aidedWebService) ValidateDeletePermission(oldData *model.WebForwarding, hostId int) error { if oldData.HostId != hostId { return fmt.Errorf("用户权限不足") } return nil } // CreateCdnWebsite 创建CDN网站 func (s *aidedWebService) CreateCdnWebsite(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, formData v1.Website) (int64, error) { formDataSend, err := s.BulidFormData(ctx, formData) if err != nil { return 0, fmt.Errorf("构建表单数据失败: %w", err) } webId, err := s.cdn.CreateWebsite(ctx, formDataSend) if err != nil { return 0, fmt.Errorf("创建CDN网站失败: %w", err) } return webId, nil } // UpdateCdnConfiguration 更新CDN配置 func (s *aidedWebService) UpdateCdnConfiguration(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse, formData v1.Website) error { // 修改网站端口、协议或证书 if oldData.Port != req.WebForwardingData.Port || oldData.IsHttps != req.WebForwardingData.IsHttps || oldData.HttpsCert != req.WebForwardingData.HttpsCert || oldData.HttpsKey != req.WebForwardingData.HttpsKey { if err := s.updateWebsiteProtocolAndCert(ctx, req, oldData, require, formData); err != nil { return err } } // 修改网站域名 if oldData.Domain != req.WebForwardingData.Domain { if err := s.updateWebsiteDomain(ctx, req, oldData); err != nil { return err } } // 修改网站名字 if oldData.Comment != req.WebForwardingData.Comment { if err := s.updateWebsiteBasicInfo(ctx, oldData, require); err != nil { return err } } return nil } // DeleteCdnServer 删除CDN服务器 func (s *aidedWebService) DeleteCdnServer(ctx context.Context, cdnWebId int) error { if err := s.cdn.DelServer(ctx, int64(cdnWebId)); err != nil { return fmt.Errorf("删除CDN服务器失败: %w", err) } return nil } // updateWebsiteProtocolAndCert 更新网站协议和证书 func (s *aidedWebService) updateWebsiteProtocolAndCert(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse, formData v1.Website) error { // 切换协议 var typeConfig, closeConfig v1.TypeJSON var apiType, closeType string if s.IsHttpsProtocol(req.WebForwardingData.IsHttps) { typeConfig = formData.HttpsJSON closeConfig = formData.HttpJSON apiType = s.GetProtocolType(req.WebForwardingData.IsHttps) closeType = s.GetProtocolType(0) // HTTP } else { typeConfig = formData.HttpJSON closeConfig = formData.HttpsJSON apiType = s.GetProtocolType(req.WebForwardingData.IsHttps) closeType = s.GetProtocolType(1) // HTTPS } typeJson, err := json.Marshal(typeConfig) if err != nil { return fmt.Errorf("序列化协议配置失败: %w", err) } closeJson, err := json.Marshal(closeConfig) if err != nil { return fmt.Errorf("序列化关闭协议配置失败: %w", err) } // 切换协议 if err := s.cdn.EditServerType(ctx, v1.EditWebsite{ Id: int64(oldData.CdnWebId), TypeJSON: typeJson, }, apiType); err != nil { return fmt.Errorf("切换到%s协议失败: %w", apiType, err) } if err := s.cdn.EditServerType(ctx, v1.EditWebsite{ Id: int64(oldData.CdnWebId), TypeJSON: closeJson, }, closeType); err != nil { return fmt.Errorf("关闭%s协议失败: %w", closeType, err) } return nil } // updateWebsiteDomain 更新网站域名 func (s *aidedWebService) updateWebsiteDomain(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding) error { type serverName struct { Name string `json:"name" form:"name"` Type string `json:"type" form:"type"` } var serverData []serverName serverData = append(serverData, serverName{ Name: req.WebForwardingData.Domain, Type: "full", }) serverJson, err := json.Marshal(serverData) if err != nil { return fmt.Errorf("序列化服务器名称失败: %w", err) } if err := s.cdn.EditServerName(ctx, v1.EditServerNames{ ServerId: int64(oldData.CdnWebId), ServerNamesJSON: serverJson, }); err != nil { return fmt.Errorf("更新服务器名称失败: %w", err) } return nil } // updateWebsiteBasicInfo 更新网站基本信息 func (s *aidedWebService) updateWebsiteBasicInfo(ctx context.Context, oldData *model.WebForwarding, require RequireResponse) error { // 通过globalLimitRep获取节点ID,这是项目中现有的方法 nodeId, err := s.globalLimitRep.GetNodeId(ctx, oldData.CdnWebId) if err != nil { return fmt.Errorf("获取节点ID失败: %w", err) } if err := s.cdn.EditServerBasic(ctx, int64(oldData.CdnWebId), require.Tag, nodeId); err != nil { return fmt.Errorf("更新服务器基本信息失败: %w", err) } return nil } // AddOriginsToWebsite 添加源站到网站 func (s *aidedWebService) AddOriginsToWebsite(ctx context.Context, req *v1.WebForwardingRequest, webId int64) (map[string]int64, error) { cdnOriginIds, err := s.CreateOriginServers(ctx, req) if err != nil { return nil, fmt.Errorf("创建源站服务器失败: %w", err) } // 添加源站到网站 for _, originId := range cdnOriginIds { if err := s.cdn.AddServerOrigin(ctx, webId, originId); err != nil { return nil, fmt.Errorf("添加源站到网站失败: %w", err) } } return cdnOriginIds, nil } // UpdateOriginServers 更新源站服务器 func (s *aidedWebService) UpdateOriginServers(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, ipData *model.WebForwardingRule) error { addOrigins, delOrigins := s.FindDifferenceList(ipData.BackendList, req.WebForwardingData.BackendList) addedIds := make(map[string]int64) // 添加新源站 for _, v := range addOrigins { apiType := s.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 fmt.Errorf("添加源站 %s 失败: %w", v.Addr, err) } addedIds[v.Addr] = id } // 将新源站添加到网站 for _, v := range addedIds { if err := s.cdn.AddServerOrigin(ctx, int64(oldData.CdnWebId), v); err != nil { return fmt.Errorf("添加源站到网站失败: %w", err) } } // 删除旧源站 for k, v := range ipData.CdnOriginIds { for _, ip := range delOrigins { if k == ip.Addr { if err := s.cdn.DelServerOrigin(ctx, int64(oldData.CdnWebId), v); err != nil { return fmt.Errorf("删除源站失败: %w", err) } delete(ipData.CdnOriginIds, k) } } } // 合并新的源站ID for k, v := range addedIds { ipData.CdnOriginIds[k] = v } return nil } // ConfigureWebsocket 配置WebSocket func (s *aidedWebService) ConfigureWebsocket(ctx context.Context, webId int64) error { websocketId, err := s.websocket.AddWebsocket(ctx) if err != nil { return fmt.Errorf("添加WebSocket失败: %w", err) } if err := s.websocket.EnableOrDisable(ctx, webId, websocketId, true, false); err != nil { return fmt.Errorf("启用WebSocket失败: %w", err) } return nil } // ConfigureProxyProtocol 配置代理协议 func (s *aidedWebService) ConfigureProxyProtocol(ctx context.Context, req *v1.WebForwardingRequest, webId int64) error { if req.WebForwardingData.Proxy { if err := s.proxy.EditProxy(ctx, webId, v1.ProxyProtocolJSON{ IsOn: true, Version: proxyProtocolVersion, }); err != nil { return fmt.Errorf("启用代理协议失败: %w", err) } } return nil } // ConfigureCCProtection 配置CC防护 func (s *aidedWebService) ConfigureCCProtection(ctx context.Context, req *v1.WebForwardingRequest, webId int64) error { if req.WebForwardingData.CcConfig.IsOn { if err := s.cc.EditCcConfig(ctx, webId, req.WebForwardingData.CcConfig); err != nil { return fmt.Errorf("配置CC防护失败: %w", err) } } return nil } // ConfigureWafFirewall 配置WAF防火墙 func (s *aidedWebService) ConfigureWafFirewall(ctx context.Context, webId int64, groupId int) error { if err := s.ccIpList.AddCcIpListPolicy(ctx, webId, int64(groupId)); err != nil { return fmt.Errorf("配置WAF防火墙失败: %w", err) } return nil } // ProcessAsyncTasks 处理异步任务 func (s *aidedWebService) ProcessAsyncTasks(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) { // 域名白名单处理 if req.WebForwardingData.Domain != "" { go func() { doMain, err := s.wafformatter.ConvertToWildcardDomain(ctx, req.WebForwardingData.Domain) if err != nil { return } if len(require.GatewayIps) == 0 { return } firstIp, err := s.gatewayIp.GetGatewayipByHostIdFirst(ctx, int64(require.HostId), int64(require.Uid)) if err != nil { return } s.wafformatter.PublishDomainWhitelistTask(doMain, firstIp, "add") }() } // 源站IP白名单处理 if req.WebForwardingData.BackendList != nil { go func() { var ips []string for _, v := range req.WebForwardingData.BackendList { ip, _, err := net.SplitHostPort(v.Addr) if err != nil { continue } ips = append(ips, ip) } if len(ips) > 0 { s.wafformatter.PublishIpWhitelistTask(ips, "add", "", "white") } }() } } // ProcessIpWhitelistChanges 处理IP白名单变更 func (s *aidedWebService) ProcessIpWhitelistChanges(ctx context.Context, req *v1.WebForwardingRequest, ipData *model.WebForwardingRule) error { var oldIps, newIps []string // 提取旧IP列表 for _, v := range ipData.BackendList { ip, _, err := net.SplitHostPort(v.Addr) if err != nil { return fmt.Errorf("解析旧IP地址失败: %w", err) } oldIps = append(oldIps, ip) } // 提取新IP列表 for _, v := range req.WebForwardingData.BackendList { ip, _, err := net.SplitHostPort(v.Addr) if err != nil { return fmt.Errorf("解析新IP地址失败: %w", err) } newIps = append(newIps, ip) } // 查找IP差异 addedIps, removedIps := s.wafformatter.findIpDifferences(oldIps, newIps) // 异步处理添加的IP if len(addedIps) > 0 { go s.wafformatter.PublishIpWhitelistTask(addedIps, "add", "", "white") } // 异步处理删除的IP if len(removedIps) > 0 { go func() { ipsToDelist, err := s.wafformatter.WashDelIps(ctx, removedIps) if err != nil { return } if len(ipsToDelist) > 0 { s.wafformatter.PublishIpWhitelistTask(ipsToDelist, "del", "0", "white") } }() } return nil } // ProcessDeleteIpWhitelist 处理删除IP白名单 func (s *aidedWebService) ProcessDeleteIpWhitelist(ctx context.Context, id int) error { ipData, err := s.webForwardingRepository.GetWebForwardingIpsByID(ctx, id) if err != nil { return fmt.Errorf("获取IP数据失败: %w", err) } if ipData != nil && len(ipData.BackendList) > 0 { var ips []string for _, v := range ipData.BackendList { ip, _, err := net.SplitHostPort(v.Addr) if err != nil { continue } ips = append(ips, ip) } if len(ips) > 0 { go func() { ipsToDelist, err := s.wafformatter.WashDelIps(ctx, ips) if err != nil { return } if len(ipsToDelist) > 0 { s.wafformatter.PublishIpWhitelistTask(ipsToDelist, "del", "0", "white") } }() } } return nil } // ProcessDomainWhitelistChanges 处理域名白名单变更 func (s *aidedWebService) ProcessDomainWhitelistChanges(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse) error { if oldData.Domain != req.WebForwardingData.Domain { firstIp, err := s.gatewayIp.GetGatewayipByHostIdFirst(ctx, int64(req.HostId), int64(req.Uid)) if err != nil { return fmt.Errorf("获取网关IP失败: %w", err) } newDomain, err := s.wafformatter.ConvertToWildcardDomain(ctx, req.WebForwardingData.Domain) if err != nil { return fmt.Errorf("转换新域名失败: %w", err) } oldDomain, err := s.wafformatter.ConvertToWildcardDomain(ctx, oldData.Domain) if err != nil { return fmt.Errorf("转换旧域名失败: %w", err) } if len(require.GatewayIps) == 0 { return fmt.Errorf("网关组不存在") } // 检查旧域名使用数量 count, err := s.webForwardingRepository.GetDomainCount(ctx, req.HostId, oldData.Domain) if err != nil { return fmt.Errorf("获取域名使用数量失败: %w", err) } // 异步处理域名白名单变更 go func() { if count < 2 { s.wafformatter.PublishDomainWhitelistTask(oldDomain, firstIp, "del") } s.wafformatter.PublishDomainWhitelistTask(newDomain, firstIp, "add") }() } return nil } // ProcessDeleteDomainWhitelist 处理删除域名白名单 func (s *aidedWebService) ProcessDeleteDomainWhitelist(ctx context.Context, oldData *model.WebForwarding, uid int) error { if oldData.Domain != "" { firstIp, err := s.gatewayIp.GetGatewayipByHostIdFirst(ctx, int64(oldData.HostId), int64(uid)) if err != nil { return fmt.Errorf("获取网关IP失败: %w", err) } doMain, err := s.wafformatter.ConvertToWildcardDomain(ctx, oldData.Domain) if err != nil { return fmt.Errorf("转换域名失败: %w", err) } go s.wafformatter.PublishDomainWhitelistTask(doMain, firstIp, "del") } return nil } // SaveToDatabase 保存到数据库 func (s *aidedWebService) SaveToDatabase(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, webId int64, cdnOriginIds map[string]int64) (int, error) { webModel := s.BuildWebForwardingModel(&req.WebForwardingData, int(webId), require) id, err := s.webForwardingRepository.AddWebForwarding(ctx, webModel) if err != nil { return 0, fmt.Errorf("添加Web转发记录失败: %w", err) } webRuleModel := s.BuildWebRuleModel(&req.WebForwardingData, require, id, cdnOriginIds) if _, err = s.webForwardingRepository.AddWebForwardingIps(ctx, *webRuleModel); err != nil { return 0, fmt.Errorf("添加Web转发规则失败: %w", err) } return id, nil } // UpdateDatabaseRecords 更新数据库记录 func (s *aidedWebService) UpdateDatabaseRecords(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse, ipData *model.WebForwardingRule) error { webModel := s.BuildWebForwardingModel(&req.WebForwardingData, req.WebForwardingData.CdnWebId, require) webModel.Id = req.WebForwardingData.Id if err := s.webForwardingRepository.EditWebForwarding(ctx, webModel); err != nil { return fmt.Errorf("更新Web转发记录失败: %w", err) } webRuleModel := s.BuildWebRuleModel(&req.WebForwardingData, require, req.WebForwardingData.Id, ipData.CdnOriginIds) if err := s.webForwardingRepository.EditWebForwardingIps(ctx, *webRuleModel); err != nil { return fmt.Errorf("更新Web转发规则失败: %w", err) } return nil } // CleanupDatabaseRecords 清理数据库记录 func (s *aidedWebService) CleanupDatabaseRecords(ctx context.Context, id int) error { if err := s.webForwardingRepository.DeleteWebForwarding(ctx, int64(id)); err != nil { return fmt.Errorf("删除Web转发记录失败: %w", err) } if err := s.webForwardingRepository.DeleteWebForwardingIpsById(ctx, id); err != nil { return fmt.Errorf("删除Web转发规则失败: %w", err) } return nil } // ProcessSSLCertificateUpdate 处理SSL证书更新 func (s *aidedWebService) ProcessSSLCertificateUpdate(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse) error { if !s.IsHttpsProtocol(req.WebForwardingData.IsHttps) { return nil // 非HTTPS协议不需要处理SSL证书 } // 如果证书ID为0 if oldData.SslCertId == 0 { err := s.ProcessSSLCertificate(ctx, req, require, v1.Website{ HttpsJSON: v1.TypeJSON{ SslPolicyRef: v1.SslPolicyRef{ SslPolicyId: req.WebForwardingData.SslPolicyId, }, }, }) if err != nil { return fmt.Errorf("处理SSL证书失败: %w", err) } return nil } // 如果证书内容有变化 if oldData.HttpsCert != req.WebForwardingData.HttpsCert || oldData.HttpsKey != req.WebForwardingData.HttpsKey { if 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, }); err != nil { return fmt.Errorf("更新SSL证书失败: %w", err) } } return nil } // CleanupSSLCertificate 清理SSL证书 func (s *aidedWebService) CleanupSSLCertificate(ctx context.Context, oldData *model.WebForwarding) error { if oldData.SslCertId != 0 { if err := s.cdn.DelSSLCert(ctx, int64(oldData.SslCertId)); err != nil { return fmt.Errorf("删除SSL证书失败: %w", err) } if err := s.sslCert.EditSslPolicy(ctx, int64(oldData.SslPolicyId), []int64{int64(oldData.SslCertId)}, "del"); err != nil { return fmt.Errorf("删除SSL策略失败: %w", err) } } return nil }