waflog.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package admin
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. adminApi "github.com/go-nunu/nunu-layout-advanced/api/v1/admin"
  7. "github.com/go-nunu/nunu-layout-advanced/internal/model"
  8. adminRep "github.com/go-nunu/nunu-layout-advanced/internal/repository/admin"
  9. "github.com/go-nunu/nunu-layout-advanced/internal/repository/api/waf"
  10. "github.com/go-nunu/nunu-layout-advanced/internal/service"
  11. "github.com/go-nunu/nunu-layout-advanced/pkg/rabbitmq"
  12. amqp "github.com/rabbitmq/amqp091-go"
  13. "go.uber.org/zap"
  14. "strings"
  15. )
  16. type WafLogService interface {
  17. GetWafLog(ctx context.Context, id int64) (*model.WafLog, error)
  18. GetWafLogList(ctx context.Context) ([]model.WafLog, error)
  19. AddWafLog(ctx context.Context, req adminApi.WafLog) error
  20. BatchAddWafLog(ctx context.Context, reqs []*adminApi.WafLog) error
  21. PublishIpWafLogTask(ctx context.Context, req adminApi.WafLog)
  22. }
  23. func NewWafLogService(
  24. service *service.Service,
  25. wafLogRepository adminRep.WafLogRepository,
  26. globalLimitRepository waf.GlobalLimitRepository,
  27. mq *rabbitmq.RabbitMQ,
  28. ) WafLogService {
  29. return &wafLogService{
  30. Service: service,
  31. wafLogRepository: wafLogRepository,
  32. globalLimitRepository: globalLimitRepository,
  33. mq : mq,
  34. }
  35. }
  36. type wafLogService struct {
  37. *service.Service
  38. wafLogRepository adminRep.WafLogRepository
  39. globalLimitRepository waf.GlobalLimitRepository
  40. mq *rabbitmq.RabbitMQ
  41. }
  42. var ApiDescriptionMap = map[string]string{
  43. "/webForward/get": "获取web详情",
  44. "/webForward/getList" : "获取web列表",
  45. "/webForward/add" : "添加web",
  46. "/webForward/update" : "更新web",
  47. "/webForward/delete" : "删除web",
  48. "/tcpForward/add" : "添加tcp",
  49. "/tcpForward/update" : "更新tcp",
  50. "/tcpForward/delete" : "删除tcp",
  51. "/tcpForward/getList" : "获取tcp列表",
  52. "/tcpForward/get" : "获取tcp详情",
  53. "/udpForward/add" : "添加udp",
  54. "/udpForward/update" : "更新udp",
  55. "/udpForward/delete" : "删除udp",
  56. "/udpForward/getList" : "获取udp列表",
  57. "/udpForward/get" : "获取udp详情",
  58. "/globalLimit/add" : "添加实例",
  59. "/globalLimit/edit" : "编辑实例",
  60. "/globalLimit/delete" : "删除实例",
  61. "/allowAndDeny/get" : "获取黑白名单详情",
  62. "/allowAndDeny/getList" : "获取黑白名单列表",
  63. "/allowAndDeny/add" : "添加黑白名单",
  64. "/allowAndDeny/edit" : "编辑黑白名单",
  65. "/allowAndDeny/delete" : "删除黑白名单",
  66. "/cc/getList" : "获取CC列表",
  67. "/cc/editState" : "删除CC黑名单",
  68. "/ccIpList/getList" : "获取CC白名单列表",
  69. "/ccIpList/add" : "添加CC白名单",
  70. "/ccIpList/edit" : "编辑CC白名单",
  71. "/ccIpList/delete" : "删除CC白名单",
  72. }
  73. func (s *wafLogService) getFirstPathSegment(path string) (segment []string, ok bool) {
  74. // 1. 为了统一处理,先去掉路径最前面的 "/"
  75. // 这样 "/v1/admin" 会变成 "v1/admin",而 "v1/admin" 保持不变
  76. trimmedPath := strings.TrimPrefix(path, "/")
  77. // 如果去掉 "/" 后字符串为空(比如原路径是 "/" 或 ""),则无法提取
  78. if trimmedPath == "" {
  79. return nil, false
  80. }
  81. // 2. 使用 "/" 作为分隔符来切割字符串
  82. // "v1/admin/menus" 会被切割成一个字符串切片 (slice): ["v1", "admin", "menus"]
  83. parts := strings.Split(trimmedPath, "/")
  84. // 3. 只要切片不为空,第一个元素就是我们需要的值
  85. // len(parts) > 0 这个检查可以保证程序不会因为空切片而出错
  86. if len(parts) > 0 {
  87. return parts, true
  88. }
  89. return nil, false
  90. }
  91. func (s *wafLogService) GetWafLog(ctx context.Context, id int64) (*model.WafLog, error) {
  92. return s.wafLogRepository.GetWafLog(ctx, id)
  93. }
  94. func (s *wafLogService) GetWafLogList(ctx context.Context) ([]model.WafLog, error) {
  95. return s.wafLogRepository.GetWafLogList(ctx)
  96. }
  97. func (s *wafLogService) AddWafLog(ctx context.Context, req adminApi.WafLog) error {
  98. if req.Api != "" {
  99. api := strings.TrimPrefix(req.Api, "/v1")
  100. if _, ok := ApiDescriptionMap[api]; ok {
  101. req.ApiName = ApiDescriptionMap[api]
  102. }
  103. apiType, ok := s.getFirstPathSegment(req.Api)
  104. if ok {
  105. req.ApiType = apiType[len(apiType)-1]
  106. }
  107. }
  108. userInfo, err := s.globalLimitRepository.GetUserInfo(ctx, int64(req.Uid))
  109. if err != nil {
  110. return err
  111. }
  112. req.Name = userInfo.Username
  113. extraData, err := json.Marshal(req.ExtraData)
  114. if err != nil {
  115. return err
  116. }
  117. return s.wafLogRepository.AddWafLog(ctx, &model.WafLog{
  118. Uid: req.Uid,
  119. Name: req.Name,
  120. RequestIp: req.RequestIp,
  121. RuleId: req.RuleId,
  122. HostId: req.HostId,
  123. UserAgent: req.UserAgent,
  124. Api: req.Api,
  125. ApiType: req.ApiType,
  126. ApiName: req.ApiName,
  127. ExtraData: extraData,
  128. })
  129. }
  130. func (s *wafLogService) BatchAddWafLog(ctx context.Context, reqs []*adminApi.WafLog) error {
  131. if len(reqs) == 0 {
  132. return nil
  133. }
  134. wafLogs := make([]*model.WafLog, 0, len(reqs))
  135. for _, req := range reqs {
  136. if req.Api != "" {
  137. api := strings.TrimPrefix(req.Api, "/v1")
  138. if _, ok := ApiDescriptionMap[api]; ok {
  139. req.ApiName = ApiDescriptionMap[api]
  140. }
  141. apiType, ok := s.getFirstPathSegment(req.Api)
  142. if ok {
  143. req.ApiType = apiType[len(apiType)-1]
  144. }
  145. }
  146. userInfo, err := s.globalLimitRepository.GetUserInfo(ctx, int64(req.Uid))
  147. if err != nil {
  148. s.Logger.Error("获取用户信息失败", zap.Error(err), zap.Int("uid", req.Uid))
  149. continue
  150. }
  151. req.Name = userInfo.Username
  152. extraData, err := json.Marshal(req.ExtraData)
  153. if err != nil {
  154. s.Logger.Error("序列化额外数据失败", zap.Error(err))
  155. continue
  156. }
  157. wafLogs = append(wafLogs, &model.WafLog{
  158. Uid: req.Uid,
  159. Name: req.Name,
  160. RequestIp: req.RequestIp,
  161. RuleId: req.RuleId,
  162. HostId: req.HostId,
  163. UserAgent: req.UserAgent,
  164. Api: req.Api,
  165. ApiType: req.ApiType,
  166. ApiName: req.ApiName,
  167. ExtraData: extraData,
  168. })
  169. }
  170. // 调用repository层的批量插入方法
  171. return s.wafLogRepository.BatchAddWafLog(ctx, wafLogs)
  172. }
  173. func (s *wafLogService) PublishIpWafLogTask(ctx context.Context, req adminApi.WafLog) {
  174. payload := &req
  175. // Serialize the message
  176. msgBody, err := json.Marshal(payload)
  177. if err != nil {
  178. s.Logger.Error("序列化 WafLog 任务消息失败", zap.Error(err), zap.Int("hostId", payload.HostId), zap.Int("uid", payload.Uid), zap.Any("req", req))
  179. return
  180. }
  181. // Get task configuration
  182. taskCfg, ok := s.mq.GetTaskConfig("waf_log")
  183. if !ok {
  184. s.Logger.Error("无法获取“waf_Log”任务配置")
  185. return
  186. }
  187. // Construct the routing key dynamically based on the action
  188. routingKey := fmt.Sprintf("wafLog.%s", "add")
  189. // Construct the amqp.Publishing message
  190. publishingMsg := amqp.Publishing{
  191. ContentType: "application/json",
  192. Body: msgBody,
  193. DeliveryMode: amqp.Persistent, // Persistent message
  194. }
  195. // Publish the message
  196. err = s.mq.PublishWithCh(taskCfg.Exchange, routingKey, publishingMsg)
  197. if err != nil {
  198. s.Logger.Error("发布 WafLog 任务消息失败", zap.Error(err), zap.Int("hostId", payload.HostId), zap.Int("uid", payload.Uid), zap.Any("req", req))
  199. } else {
  200. s.Logger.Info("已成功发布 WafLog 任务消息", zap.Int("hostId", payload.HostId), zap.Int("uid", payload.Uid), zap.Any("req", req))
  201. }
  202. }