waflog.go 9.4 KB

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