Forráskód Böngészése

refactor(aodun): 重构域名白名单功能并添加 IP白名单支持

- 重构了 AoDunService 接口,新增 IP 白名单相关的函数- 修改了域名白名单的实现,使用新的 API 接口- 增加了 IP白名单的消费者逻辑(已注释)
- 优化了日志输出和错误处理
fusu 1 hónapja
szülő
commit
bbc0441bdf

+ 6 - 1
api/v1/aodun.go

@@ -34,7 +34,12 @@ type DomainResponse struct {
 	Data        string `json:"data,omitempty"`
 }
 
-type IpGet struct {
+type IpGetResponse struct {
+	Code        int    `json:"code"`
+	Msg         string `json:"msg,omitempty"`
+	Data        []IpGetData `json:"data,omitempty"`
+}
+type IpGetData struct {
 	LongIntEndIP      int64  `json:"long_int_end_ip"`
 	Remark            string `json:"remark"`
 	Index             int    `json:"index"`

+ 63 - 6
config/local.yml

@@ -13,13 +13,23 @@ data:
   db:
     user:
       driver: mysql
-      dsn: 183_136_132_25:xGrNJphcmGcXiajE@tcp(183.136.132.25:3306)/183_136_132_25?charset=utf8mb4&parseTime=True&loc=Local
-#    user:
-#      driver: sqlite
-#      dsn: storage/nunu-test.db?_busy_timeout=5000
+      dsn: 	mofangtest:M3a4RewbedtyFsXc@tcp(110.42.96.15:3306)/mofangtest?charset=utf8mb4&parseTime=True&loc=Local
+      logLevel: "info"  # 记录所有日志
+    second:
+      driver: mysql
+      dsn: root:Mgrj9hMF3QQ3atX5hFIo@tcp(115.238.186.121:3306)/0panel?charset=utf8mb4&parseTime=True&loc=Local
+      logLevel: "info"
+  #    user:
+  #      driver: sqlite
+  #      dsn: storage/nunu-test.db?_busy_timeout=5000
   #    user:
   #      driver: postgres
   #      dsn: host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai
+  mongodb:
+    uri: "mongodb://nunuUser:61cba0aauim0zituxxgyd0zr@110.42.96.15:27017"
+    database: "nunutest"
+    timeout: 10s  # 连接超时时间
+    max_pool_size: 100  # 连接池大小
   redis:
     addr: 127.0.0.1:6350
     password: ""
@@ -47,8 +57,8 @@ log:
 crawler:
   username: "admin"
   password: "mr7c6r61jIRLGhcnT5j9"
-#  Url: "http://api.hongxingdun.net:8700/"
-#  keyUrl: "http://api.hongxingdun.net:13350/sdk/key?app_name="
+  #  Url: "http://api.hongxingdun.net:8700/"
+  #  keyUrl: "http://api.hongxingdun.net:13350/sdk/key?app_name="
   Url: "http://115.238.186.121:8700/"
   keyUrl: "http://115.238.186.121:13350/sdk/key?app_name="
 
@@ -73,3 +83,50 @@ ip_allowlist:
     - 127.0.0.1    # 本地开发
     - ::1          # IPv6本地回环地址
     - 183.136.132.25  # 示例局域网IP
+
+aodun:
+  username: "zznet_api"
+  password: "Nbgaofang.com!@#4"
+  clientId: "bd9d36fc-17e1-11ef-8a72-549f35180370"
+  Url: "https://115.238.184.13:16008"
+
+aodunIp:
+  clientId: "3f4ab936-0527-11ef-8065-801844e71378"
+  Url: "https://115.238.186.169:16008"
+
+domainWhite:
+  username: "zznet_api"
+  password: "Nbgaofang.com!@#4"
+# RabbitMQ Configuration
+rabbitmq:
+  host: "110.42.96.15"
+  port: 5672
+  username: "fusu"
+  password: "fusu12332ATQREW"
+  vhost: "/"
+  connection_timeout: 15s
+  tasks:
+    # IP白名单更新任务
+    ip_white:
+      exchange: "tasks_direct_exchange" # 使用一个统一的direct交换机
+      queue: "ip_white_queue"
+      routing_key: "task.ip_white.update"
+      consumer_count: 2
+      prefetch_count: 1
+
+    # 用户注册后续任务(例如:发送欢迎邮件)
+    user_register:
+      exchange: "tasks_direct_exchange"
+      queue: "user_register_queue"
+      routing_key: "task.user.register.notify"
+      consumer_count: 3
+      prefetch_count: 1
+
+    # 域名白名单任务
+    domain_whitelist:
+      exchange: "whitelist_topic_exchange" # Topic 类型的交换机
+      exchange_type: "topic"              # 显式指定交换机类型
+      queue: "domain_whitelist_queue"
+      routing_key: "whitelist.domain.*"   # 消费者监听的绑定键,能接收所有 domain 相关的任务
+      consumer_count: 3
+      prefetch_count: 1

+ 101 - 15
internal/job/whitelist.go

@@ -52,6 +52,7 @@ func (j *whitelistJob) DomainConsumer(ctx context.Context) {
 	// Define the message payload structure, now including an action field
 	type domainTaskPayload struct {
 		Domain string `json:"domain"`
+		Ip     string `json:"ip"`
 		Action string `json:"action"` // "add" or "del"
 	}
 
@@ -83,21 +84,9 @@ func (j *whitelistJob) DomainConsumer(ctx context.Context) {
 			// Call business logic based on the action
 			var processingErr error
 
-			// Call business logic based on the action
-			switch payload.Action {
-			case "add":
-				processingErr = j.aoDunService.AddDomainWhiteList(ctx, []string{payload.Domain})
-				if processingErr == nil {
-					j.logger.Info("Successfully processed 'add' domain whitelist task", zap.String("domain", payload.Domain))
-				}
-			case "del":
-				processingErr = j.aoDunService.DelDomainWhiteList(ctx, []string{payload.Domain})
-				if processingErr == nil {
-					j.logger.Info("Successfully processed 'delete' domain whitelist task", zap.String("domain", payload.Domain))
-				}
-			default:
-				j.logger.Warn("Received unknown action in domain whitelist task", zap.String("action", payload.Action), zap.String("domain", payload.Domain))
-				processingErr = fmt.Errorf("unknown action: %s", payload.Action)
+			processingErr = j.aoDunService.DomainWhiteList(ctx, payload.Domain,payload.Ip, payload.Action)
+			if processingErr == nil {
+				j.logger.Info("Successfully processed 'delete' domain whitelist task", zap.String("domain", payload.Domain))
 			}
 
 			// 在循环的最后,根据 processingErr 的状态统一处理 Ack/Nack
@@ -117,3 +106,100 @@ func (j *whitelistJob) DomainConsumer(ctx context.Context) {
 		}
 	}
 }
+
+
+//func (j *whitelistJob) IpConsumer(ctx context.Context) {
+//	taskName := "IP_whitelist"
+//	taskCfg, ok := j.Rabbitmq.GetTaskConfig(taskName)
+//	if !ok {
+//		j.logger.Error(fmt.Sprintf("未找到任务 '%s' 的配置", taskName))
+//		return
+//	}
+//
+//	consumerName := "IP_whitelist_consumer"
+//	j.logger.Info("正在启动域名白名单消费者...",
+//		zap.String("task", taskName),
+//		zap.String("queue", taskCfg.Queue),
+//		zap.String("consumer", consumerName),
+//	)
+//
+//	msgs, err := j.Rabbitmq.Consume(taskCfg.Queue, consumerName, taskCfg.PrefetchCount)
+//	if err != nil {
+//		j.logger.Error("启动IP白名单消费者失败", zap.Error(err))
+//		return
+//	}
+//
+//	// Define the message payload structure, now including an action field
+//	type ipTaskPayload struct {
+//		IP string `json:"IP"`
+//		Action string `json:"action"` // "add" or "del"
+//	}
+//
+//	for {
+//		select {
+//		case <-ctx.Done():
+//			j.logger.Info("IP白名单消费者正在关闭...", zap.String("task", taskName))
+//			return
+//		case d, ok := <-msgs:
+//			if !ok {
+//				j.logger.Warn("消息通道已关闭,IP白名单消费者退出。", zap.String("task", taskName))
+//				return
+//			}
+//
+//			// 解析消息
+//			var payload ipTaskPayload
+//			if err := json.Unmarshal(d.Body, &payload); err != nil {
+//				j.logger.Error("解析IP白名单消息失败", zap.Error(err), zap.ByteString("body", d.Body))
+//				// 消息格式错误,直接拒绝且不重新入队
+//				_ = d.Nack(false, false)
+//				continue
+//			}
+//
+//			j.logger.Info("收到IP白名单任务",
+//				zap.String("domain", payload.IP),
+//				zap.String("routing_key", d.RoutingKey),
+//			)
+//
+//			// Call business logic based on the action
+//			var processingErr error
+//
+//			// Call business logic based on the action
+//			switch payload.Action {
+//			case "add":
+//				processingErr = j.aoDunService.AddWhiteStaticList(ctx, []string{payload.IP})
+//				if processingErr == nil {
+//					j.logger.Info("Successfully processed 'add' IP whitelist task", zap.String("IP", payload.IP))
+//				}
+//			case "del":
+//				processingErr = j.aoDunService.DomainWhiteList(ctx, []string{payload.IP})
+//				if processingErr == nil {
+//					j.logger.Info("Successfully processed 'delete' IP whitelist task", zap.String("IP", payload.IP))
+//				}
+//			case "get":
+//				ids,processingErr = j.aoDunService.d(ctx)
+//				if processingErr == nil {
+//					j.logger.Info("Successfully processed 'get' IP whitelist task", zap.String("IP", payload.IP))
+//				}
+//
+//			default:
+//				j.logger.Warn("Received unknown action in IP whitelist task", zap.String("action", payload.Action), zap.String("IP", payload.IP))
+//				processingErr = fmt.Errorf("unknown action: %s", payload.Action)
+//			}
+//
+//			// 在循环的最后,根据 processingErr 的状态统一处理 Ack/Nack
+//			if processingErr != nil {
+//				j.logger.Error("处理IP白名单任务失败", zap.Error(processingErr), zap.String("IP", payload.IP))
+//				// 业务失败,拒绝消息并不重新入队
+//				if err := d.Nack(false, false); err != nil {
+//					j.logger.Error("消息 Nack 失败", zap.Error(err))
+//				}
+//			} else {
+//				// 业务处理成功,手动发送确认
+//				if err := d.Ack(false); err != nil {
+//					j.logger.Error("IP白名单任务消息确认失败", zap.Error(err))
+//				}
+//			}
+//
+//		}
+//	}
+//}

+ 9 - 0
internal/repository/gatewaygroupip.go

@@ -11,6 +11,7 @@ type GateWayGroupIpRepository interface {
 	EditGateWayGroupIp(ctx context.Context, req *model.GateWayGroupIp) error
 	DeleteGateWayGroupIp(ctx context.Context, req *model.GateWayGroupIp) error
 	GetGateWayGroupIpByGatewayGroupId(ctx context.Context, gatewayGroupId int) ([]model.GateWayGroupIp, error)
+	GetGateWayGroupFirstIpByGatewayGroupId(ctx context.Context, gatewayGroupId int) (string, error)
 }
 
 func NewGateWayGroupIpRepository(
@@ -61,4 +62,12 @@ func (r *gateWayGroupIpRepository) GetGateWayGroupIpByGatewayGroupId(ctx context
 	}
 	return res, nil
 
+}
+
+func (r *gateWayGroupIpRepository) GetGateWayGroupFirstIpByGatewayGroupId(ctx context.Context, gatewayGroupId int) (string, error) {
+	var res string
+	if err := r.DB(ctx).Model(&model.GateWayGroupIp{}).Where("gateway_group_id = ?", gatewayGroupId).Select("ip").First(&res).Error; err != nil {
+		return "", err
+	}
+	return res, nil
 }

+ 100 - 83
internal/service/aodun.go

@@ -10,13 +10,14 @@ import (
 	"github.com/spf13/viper"
 	"io"
 	"net/http"
-	"strings"
+	"net/url"
 	"time"
 )
 
 type AoDunService interface {
-	AddDomainWhiteList(ctx context.Context, req []string) error
-	DelDomainWhiteList(ctx context.Context, req []string) error
+	DomainWhiteList(ctx context.Context, domain string, ip string, apiType string) error
+	AddWhiteStaticList(ctx context.Context, req []v1.IpInfo) error
+	DelWhiteStaticList(ctx context.Context, req v1.DeleteIp) error
 }
 func NewAoDunService(
     service *Service,
@@ -29,6 +30,9 @@ func NewAoDunService(
 		clientID:          conf.GetString("aodun.clientID"),
 		username:          conf.GetString("aodun.username"),
 		password:          conf.GetString("aodun.password"),
+		domainUserName:    conf.GetString("domainWhite.username"),
+		domainPassword:    conf.GetString("domainWhite.password"),
+
 	}
 }
 
@@ -38,12 +42,10 @@ type aoDunService struct {
 	clientID string
 	username string
 	password string
+	domainUserName string
+	domainPassword string
 }
 
-func (s *aoDunService) DeleteDomainWhiteList(ctx context.Context, req []string) error {
-	//TODO implement me
-	panic("implement me")
-}
 
 func (s *aoDunService) sendFormData(ctx context.Context,apiUrl string,tokenType string,token string,formData map[string]interface{})  ([]byte,error) {
 	URL := s.Url + apiUrl
@@ -87,6 +89,54 @@ func (s *aoDunService) sendFormData(ctx context.Context,apiUrl string,tokenType
 }
 
 
+func (s *aoDunService) sendDomainFormData(ctx context.Context,domain string,ip string,apiType string)  ([]byte,error) {
+	var URL string
+	if apiType == "add" {
+		URL = "http://zapi.zzybgp.com/api/user/do_main"
+	} else {
+		URL = "http://zapi.zzybgp.com/api/user/do_main/delete"
+	}
+	formData := url.Values{}
+	formData.Set("username", s.domainUserName)
+	formData.Set("password", s.domainPassword)
+	formData.Add("do_main_list[name][]", domain)
+	formData.Add("do_main_list[ip][]", ip)
+	encodedData := formData.Encode()
+
+	req, err := http.NewRequest("POST", URL, bytes.NewBuffer([]byte(encodedData)))
+	if err != nil {
+		return nil, fmt.Errorf("创建 HTTP 请求失败: %w", err)
+	}
+
+	// 设置请求头 Content-Type 为 "application/x-www-form-urlencoded"
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // <--- 关键修改:忽略 SSL 验证
+	}
+
+	// 5. 使用 HTTP 客户端发送请求
+	client := &http.Client{
+		Transport: tr,
+		Timeout: 15 * time.Second, // 设置一个合理的超时时间,例如15秒
+	}
+	resp, err := client.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("发送 HTTP 请求失败: %w", err)
+	}
+	// defer 确保在函数返回前关闭响应体,防止资源泄露
+	defer resp.Body.Close()
+
+	// 6. 读取响应体内容
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("读取响应体失败: %w", err)
+	}
+	return body, nil
+}
+
+
 func (s *aoDunService) GetToken(ctx context.Context)  (string,string,error) {
 
 	formData := map[string]interface{}{
@@ -151,110 +201,77 @@ func (s *aoDunService) AddWhiteStaticList(ctx context.Context,req []v1.IpInfo) e
 	return nil
 }
 
-//func (s *aoDunService) GetWhiteStaticList(ctx context.Context,ip string) (int,error) {
-//	tokenType,token, err := s.GetToken(ctx)
-//	if err != nil {
-//		return 0, err
-//	}
-//
-//	formData := map[string]interface{}{
-//		"action" : "get",
-//		"bwflag" : "white",
-//		"page" : 1,
-//		"ids": ip,
-//	}
-//
-//	resBody, err := s.sendFormData(ctx,"/v1.0/firewall/static_bw_list",tokenType,token,formData)
-//	if err != nil {
-//		return nil, err
-//	}
-//	// 7. 将响应体 JSON 数据反序列化到 ResponsePayload 结构体
-//	var res IpResponse // 使用我们定义的 IpResponse 结构体
-//	if err := json.Unmarshal(resBody, &res); err != nil {
-//		// 如果反序列化失败,说明响应格式不符合预期
-//		return 0, fmt.Errorf("反序列化响应 JSON 失败 (内容: %s): %w", string(resBody), err)
-//	}
-//
-//	// 2. 检查 API 返回的 code,这是处理业务失败的关键
-//	if res.Code != 0 {
-//		// API 返回了错误码,例如 IP 不存在、参数错误等
-//		return 0, fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Msg)
-//	}
-//
-//	// 3. 检查 data 数组是否为空
-//	// 即使 code 为 0,也可能因为没有匹配的数据而返回一个空数组
-//	if len(res.Data) == 0 {
-//		return 0, fmt.Errorf("API 调用成功,但未找到与 IP '%s' 相关的记录", ip)
-//	}
-//
-//	// 4. 获取 ID 并返回
-//	// 假设我们总是取返回结果中的第一个元素的 ID
-//	id := res.Data[0].ID
-//	return id, nil // 成功!返回获取到的 id 和 nil 错误
-//}
-
-func (s *aoDunService) DelWhiteStaticList(ctx context.Context, req v1.DeleteIp) error {
-	tokenType, token, err := s.GetToken(ctx)
+func (s *aoDunService) GetWhiteStaticList(ctx context.Context,ip string) (int,error) {
+	tokenType,token, err := s.GetToken(ctx)
 	if err != nil {
-		return err
+		return 0, err
 	}
 
 	formData := map[string]interface{}{
-		"action": "del",
-		"bwflag": "white",
-		"flag":   0,
-		"ids":    req.Ids,
+		"action" : "get",
+		"bwflag" : "white",
+		"page" : 1,
+		"ids": ip,
 	}
 
-	resBody, err := s.sendFormData(ctx, "/v1.0/firewall/static_bw_list", tokenType, token, formData)
+	resBody, err := s.sendFormData(ctx,"/v1.0/firewall/static_bw_list",tokenType,token,formData)
 	if err != nil {
-		return err
+		return 0, err
 	}
-	var res v1.IpResponse
+	// 7. 将响应体 JSON 数据反序列化到 ResponsePayload 结构体
+	var res v1.IpGetResponse // 使用我们定义的 IpResponse 结构体
 	if err := json.Unmarshal(resBody, &res); err != nil {
-		return fmt.Errorf("反序列化响应 JSON 失败 ( 内容: %s): %w", string(resBody), err)
+		// 如果反序列化失败,说明响应格式不符合预期
+		return 0, fmt.Errorf("反序列化响应 JSON 失败 (内容: %s): %w", string(resBody), err)
 	}
+
+	// 2. 检查 API 返回的 code,这是处理业务失败的关键
 	if res.Code != 0 {
-		return fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Msg)
+		// API 返回了错误码,例如 IP 不存在、参数错误等
+		return 0, fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Msg)
 	}
-	return nil
+
+	// 3. 检查 data 数组是否为空
+	// 即使 code 为 0,也可能因为没有匹配的数据而返回一个空数组
+	if len(res.Data) == 0 {
+		return 0, fmt.Errorf("API 调用成功,但未找到与 IP '%s' 相关的记录", ip)
+	}
+
+	// 4. 获取 ID 并返回
+	// 假设我们总是取返回结果中的第一个元素的 ID
+	id := res.Data[0].ID
+	return id, nil // 成功!返回获取到的 id 和 nil 错误
 }
 
-func (s *aoDunService) AddDomainWhiteList(ctx context.Context, req []string) error {
+func (s *aoDunService) DelWhiteStaticList(ctx context.Context, req v1.DeleteIp) error {
 	tokenType, token, err := s.GetToken(ctx)
 	if err != nil {
 		return err
 	}
+
 	formData := map[string]interface{}{
-		"domain": req,
+		"action": "del",
+		"bwflag": "white",
+		"flag":   0,
+		"ids":    req.Ids,
 	}
-	resBody, err := s.sendFormData(ctx, "/v1.0/firewall/addDomainWhiteList", tokenType, token, formData)
+
+	resBody, err := s.sendFormData(ctx, "/v1.0/firewall/static_bw_list", tokenType, token, formData)
 	if err != nil {
 		return err
 	}
-	var res v1.DomainResponse
+	var res v1.IpResponse
 	if err := json.Unmarshal(resBody, &res); err != nil {
 		return fmt.Errorf("反序列化响应 JSON 失败 ( 内容: %s): %w", string(resBody), err)
 	}
 	if res.Code != 0 {
-		if strings.Contains(string(res.Msg), "重复列表") {
-			return nil
-		}
 		return fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Msg)
 	}
 	return nil
 }
 
-func (s *aoDunService) DelDomainWhiteList(ctx context.Context, req []string) error {
-	tokenType, token, err := s.GetToken(ctx)
-	if err != nil {
-		return err
-	}
-	formData := map[string]interface{}{
-		"type":   1,
-		"domain": req,
-	}
-	resBody, err := s.sendFormData(ctx, "/v1.0/firewall/delDomainWhiteList", tokenType, token, formData)
+func (s *aoDunService) DomainWhiteList(ctx context.Context, domain string, ip string, apiType string) error {
+	resBody, err := s.sendDomainFormData(ctx,domain,ip,apiType)
 	if err != nil {
 		return err
 	}
@@ -262,10 +279,10 @@ func (s *aoDunService) DelDomainWhiteList(ctx context.Context, req []string) err
 	if err := json.Unmarshal(resBody, &res); err != nil {
 		return fmt.Errorf("反序列化响应 JSON 失败 ( 内容: %s): %w", string(resBody), err)
 	}
-	if res.Code != 0 {
-		if strings.Contains(string(res.Msg), "重复列表") {
-			return nil
-		}
+	if res.Code != 200 && apiType == "add" {
+		return fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Msg)
+	}
+	if res.Code != 600 && apiType == "del" {
 		return fmt.Errorf("API 错误: code %d, msg '%s'", res.Code, res.Msg)
 	}
 	return nil

+ 37 - 11
internal/service/webforwarding.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"github.com/davecgh/go-spew/spew"
 	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"
@@ -33,6 +34,7 @@ func NewWebForwardingService(
 	wafformatter WafFormatterService,
 	aoDun AoDunService,
 	mq *rabbitmq.RabbitMQ,
+	gatewayGroupIpRep repository.GateWayGroupIpRepository,
 ) WebForwardingService {
 	return &webForwardingService{
 		Service:                 service,
@@ -43,6 +45,7 @@ func NewWebForwardingService(
 		wafformatter:            wafformatter,
 		aoDun:                   aoDun,
 		mq:                      mq,
+		gatewayGroupIpRep:        gatewayGroupIpRep,
 	}
 }
 
@@ -55,6 +58,7 @@ type webForwardingService struct {
 	wafformatter            WafFormatterService
 	aoDun                   AoDunService
 	mq                      *rabbitmq.RabbitMQ
+	gatewayGroupIpRep        repository.GateWayGroupIpRepository
 }
 
 func (s *webForwardingService) require(ctx context.Context,req v1.GlobalRequire) (v1.GlobalRequire, error) {
@@ -307,12 +311,21 @@ func (s *webForwardingService) AddWebForwarding(ctx context.Context, req *v1.Web
 	if err != nil {
 		return err
 	}
-	// 异步任务:将域名添加到白名单
-	doMain, err := s.wafformatter.ConvertToWildcardDomain(ctx, req.WebForwardingData.Domain)
-	if err != nil {
-		return err
+	spew.Dump(req.WebForwardingData.Domain)
+	if req.WebForwardingData.Domain != "" {
+		// 异步任务:将域名添加到白名单
+		doMain, err := s.wafformatter.ConvertToWildcardDomain(ctx, req.WebForwardingData.Domain)
+		if err != nil {
+			return err
+		}
+		ip, err := s.gatewayGroupIpRep.GetGateWayGroupFirstIpByGatewayGroupId(ctx, require.WafGatewayGroupId)
+		if err != nil {
+			return err
+		}
+		spew.Dump(ip,"=================================")
+		go s.publishDomainWhitelistTask(doMain,ip, "add")
 	}
-	go s.publishDomainWhitelistTask(doMain, "add")
+
 
 	webModel := s.buildWebForwardingModel(&req.WebForwardingData, wafWebId, require)
 
@@ -357,14 +370,17 @@ func (s *webForwardingService) EditWebForwarding(ctx context.Context, req *v1.We
 	if err != nil {
 		return err
 	}
-	// 异步任务:将域名添加到白名单
 	if webData.Domain != req.WebForwardingData.Domain {
+		Ip, err := s.gatewayGroupIpRep.GetGateWayGroupFirstIpByGatewayGroupId(ctx, webData.WafGatewayGroupId)
+			if err != nil {
+		}
+	// 异步任务:将域名添加到白名单
 		doMain, err := s.wafformatter.ConvertToWildcardDomain(ctx, req.WebForwardingData.Domain)
 		if err != nil {
 			return err
 		}
-		go s.publishDomainWhitelistTask(webData.Domain, "del")
-		go s.publishDomainWhitelistTask(doMain, "add")
+		go s.publishDomainWhitelistTask(webData.Domain, Ip, "del")
+		go s.publishDomainWhitelistTask(doMain, Ip, "add")
 	}
 
 
@@ -389,9 +405,17 @@ func (s *webForwardingService) DeleteWebForwarding(ctx context.Context, Ids []in
 		webData, err := s.webForwardingRepository.GetWebForwarding(ctx, int64(Id))
 		if err != nil {
 			return err
+		}
+		Ip, err := s.gatewayGroupIpRep.GetGateWayGroupFirstIpByGatewayGroupId(ctx, webData.WafGatewayGroupId)
+		if err != nil {
+
 		}
 		if webData.Domain != "" {
-			go s.publishDomainWhitelistTask(webData.Domain, "del")
+			doMain, err := s.wafformatter.ConvertToWildcardDomain(ctx, webData.Domain)
+			if err != nil {
+				return err
+			}
+			go s.publishDomainWhitelistTask(doMain,Ip, "del")
 		}
 		_, err = s.crawler.DeleteRule(ctx, wafWebId, "admin/delete/waf_web?page=1&__pageSize=10&__sort=waf_web_id&__sort_type=desc")
 		if err != nil {
@@ -546,21 +570,23 @@ func (s *webForwardingService) GetWebForwardingWafWebAllIps(ctx context.Context,
 
 // publishDomainWhitelistTask is a helper function to publish domain whitelist tasks to RabbitMQ.
 // It can handle different actions like "add" or "del".
-func (s *webForwardingService) publishDomainWhitelistTask(domain, action string) {
+func (s *webForwardingService) publishDomainWhitelistTask(domain, ip, action string) {
 	// Define message payload, including the action
 	type domainTaskPayload struct {
 		Domain string `json:"domain"`
+		Ip     string `json:"ip"`
 		Action string `json:"action"`
 	}
 	payload := domainTaskPayload{
 		Domain: domain,
+		Ip:     ip,
 		Action: action,
 	}
 
 	// Serialize the message
 	msgBody, err := json.Marshal(payload)
 	if err != nil {
-		s.logger.Error("Failed to serialize domain whitelist task message", zap.Error(err), zap.String("domain", domain), zap.String("action", action))
+		s.logger.Error("Failed to serialize domain whitelist task message", zap.Error(err), zap.String("domain", domain), zap.String("ip", ip), zap.String("action", action))
 		return
 	}