webforwarding.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. package service
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
  7. "github.com/go-nunu/nunu-layout-advanced/internal/model"
  8. "github.com/go-nunu/nunu-layout-advanced/internal/repository"
  9. "github.com/spf13/cast"
  10. "golang.org/x/sync/errgroup"
  11. "strconv"
  12. "strings"
  13. )
  14. type WebForwardingService interface {
  15. GetWebForwarding(ctx context.Context, req v1.GetForwardingRequest) (v1.WebForwardingDataRequest, error)
  16. AddWebForwarding(ctx context.Context, req *v1.WebForwardingRequest) error
  17. EditWebForwarding(ctx context.Context, req *v1.WebForwardingRequest) error
  18. DeleteWebForwarding(ctx context.Context, Id int) error
  19. }
  20. func NewWebForwardingService(
  21. service *Service,
  22. required RequiredService,
  23. webForwardingRepository repository.WebForwardingRepository,
  24. crawler CrawlerService,
  25. parser ParserService,
  26. wafformatter WafFormatterService,
  27. ) WebForwardingService {
  28. return &webForwardingService{
  29. Service: service,
  30. webForwardingRepository: webForwardingRepository,
  31. required: required,
  32. parser: parser,
  33. crawler: crawler,
  34. wafformatter: wafformatter,
  35. }
  36. }
  37. type webForwardingService struct {
  38. *Service
  39. webForwardingRepository repository.WebForwardingRepository
  40. required RequiredService
  41. parser ParserService
  42. crawler CrawlerService
  43. wafformatter WafFormatterService
  44. }
  45. func (s *webForwardingService) require(ctx context.Context,req v1.GlobalRequire) (v1.GlobalRequire, error) {
  46. var err error
  47. var res v1.GlobalRequire
  48. g, gCtx := errgroup.WithContext(ctx)
  49. g.Go(func() error {
  50. result, e := s.wafformatter.require(gCtx, req, "web")
  51. if e != nil {
  52. return e
  53. }
  54. res = result
  55. return nil
  56. })
  57. g.Go(func() error {
  58. e := s.wafformatter.validateWafDomainCount(gCtx, req)
  59. if e != nil {
  60. return e
  61. }
  62. return nil
  63. })
  64. if err = g.Wait(); err != nil {
  65. return v1.GlobalRequire{}, err
  66. }
  67. return res, nil
  68. }
  69. func (s *webForwardingService) GetWebForwarding(ctx context.Context, req v1.GetForwardingRequest) (v1.WebForwardingDataRequest, error) {
  70. var webForwarding model.WebForwarding
  71. var backend model.WebForwardingRule
  72. var err error
  73. g, gCtx := errgroup.WithContext(ctx)
  74. g.Go(func() error {
  75. res, e := s.webForwardingRepository.GetWebForwarding(gCtx, int64(req.Id))
  76. if e != nil {
  77. // 直接返回错误,errgroup 会捕获它
  78. return fmt.Errorf("GetWebForwarding failed: %w", e)
  79. }
  80. if res != nil {
  81. webForwarding = *res
  82. }
  83. return nil
  84. })
  85. g.Go(func() error {
  86. res, e := s.webForwardingRepository.GetWebForwardingByID(ctx, req.Id)
  87. if e != nil {
  88. return fmt.Errorf("GetWebForwardingByID failed: %w", e)
  89. }
  90. if res != nil {
  91. backend = *res
  92. }
  93. return nil
  94. })
  95. if err := g.Wait(); err != nil {
  96. return v1.WebForwardingDataRequest{}, err
  97. }
  98. portInt, err := cast.ToIntE(webForwarding.Port)
  99. if err != nil {
  100. return v1.WebForwardingDataRequest{}, err
  101. }
  102. return v1.WebForwardingDataRequest{
  103. Id: webForwarding.Id,
  104. WafWebId: webForwarding.WafWebId,
  105. Tag: webForwarding.Tag,
  106. Port: portInt,
  107. Domain: webForwarding.Domain,
  108. CustomHost: webForwarding.CustomHost,
  109. WafWebLimitId: webForwarding.WebLimitRuleId,
  110. WafGatewayGroupId: webForwarding.WafGatewayGroupId,
  111. CcCount: webForwarding.CcCount,
  112. CcDuration: webForwarding.CcDuration,
  113. CcBlockCount: webForwarding.CcBlockCount,
  114. CcBlockDuration: webForwarding.CcBlockDuration,
  115. Cc4xxCount: webForwarding.Cc4xxCount,
  116. Cc4xxDuration: webForwarding.Cc4xxDuration,
  117. Cc4xxBlockCount: webForwarding.Cc4xxBlockCount,
  118. Cc4xxBlockDuration: webForwarding.Cc4xxBlockDuration,
  119. Cc5xxCount: webForwarding.Cc5xxCount,
  120. Cc5xxDuration: webForwarding.Cc5xxDuration,
  121. Cc5xxBlockCount: webForwarding.Cc5xxBlockCount,
  122. Cc5xxBlockDuration: webForwarding.Cc5xxBlockDuration,
  123. IsHttps: webForwarding.IsHttps,
  124. Comment: webForwarding.Comment,
  125. BackendList: backend.BackendList,
  126. AllowIpList: backend.AllowIpList,
  127. DenyIpList: backend.DenyIpList,
  128. AccessRule: backend.AccessRule,
  129. }, nil
  130. }
  131. // buildWafFormData 辅助函数,用于构建通用的 formData
  132. func (s *webForwardingService) buildWafFormData(req *v1.WebForwardingDataSend, require v1.GlobalRequire) map[string]interface{} {
  133. // 将BackendList序列化为JSON字符串
  134. backendJSON, err := json.MarshalIndent(req.BackendList, "", " ")
  135. var backendStr interface{}
  136. if err != nil {
  137. // 如果序列化失败,使用空数组
  138. backendStr = "[]"
  139. } else {
  140. // 成功序列化后,使用JSON字符串
  141. backendStr = string(backendJSON)
  142. }
  143. return map[string]interface{}{
  144. "waf_web_id": req.WafWebId,
  145. "tag": require.Tag,
  146. "port": req.Port,
  147. "domain": req.Domain,
  148. "custom_host": req.CustomHost,
  149. "waf_gateway_group_id": require.WafGatewayGroupId,
  150. "waf_web_limit_id": require.LimitRuleId,
  151. "cc_count": req.CcCount,
  152. "cc_duration": req.CcDuration,
  153. "cc_block_count": req.CcBlockCount,
  154. "cc_block_duration": req.CcBlockDuration,
  155. "cc_4xx_count": req.Cc4xxCount,
  156. "cc_4xx_duration": req.Cc4xxDuration,
  157. "cc_4xx_block_count": req.Cc4xxBlockCount,
  158. "cc_4xx_block_duration": req.Cc4xxBlockDuration,
  159. "cc_5xx_count": req.Cc5xxCount,
  160. "cc_5xx_duration": req.Cc5xxDuration,
  161. "cc_5xx_block_count": req.Cc5xxBlockCount,
  162. "cc_5xx_block_duration": req.Cc5xxBlockDuration,
  163. "backend": backendStr,
  164. "allow_ip_list": req.AllowIpList,
  165. "deny_ip_list": req.DenyIpList,
  166. "access_rule": req.AccessRule,
  167. "is_https": req.IsHttps,
  168. "comment": req.Comment,
  169. }
  170. }
  171. // buildWebForwardingModel 辅助函数,用于构建通用的 WebForwarding 模型
  172. // ruleId 是从 WAF 系统获取的 ID
  173. func (s *webForwardingService) buildWebForwardingModel(req *v1.WebForwardingDataRequest,ruleId int, require v1.GlobalRequire) *model.WebForwarding {
  174. return &model.WebForwarding{
  175. HostId: require.HostId,
  176. WafWebId: ruleId,
  177. Tag: require.Tag,
  178. Port: strconv.Itoa(req.Port),
  179. Domain: req.Domain,
  180. CustomHost: req.CustomHost,
  181. WafGatewayGroupId: require.WafGatewayGroupId,
  182. WebLimitRuleId: require.LimitRuleId,
  183. CcCount: req.CcCount,
  184. CcDuration: req.CcDuration,
  185. CcBlockCount: req.CcBlockCount,
  186. CcBlockDuration: req.CcBlockDuration,
  187. Cc4xxCount: req.Cc4xxCount,
  188. Cc4xxDuration: req.Cc4xxDuration,
  189. Cc4xxBlockCount: req.Cc4xxBlockCount,
  190. Cc4xxBlockDuration: req.Cc4xxBlockDuration,
  191. Cc5xxCount: req.Cc5xxCount,
  192. Cc5xxDuration: req.Cc5xxDuration,
  193. Cc5xxBlockCount: req.Cc5xxBlockCount,
  194. Cc5xxBlockDuration: req.Cc5xxBlockDuration,
  195. IsHttps: req.IsHttps,
  196. Comment: req.Comment,
  197. }
  198. }
  199. func (s *webForwardingService) buildWebRuleModel(reqData *v1.WebForwardingDataRequest, require v1.GlobalRequire, localDbId int) *model.WebForwardingRule {
  200. return &model.WebForwardingRule{
  201. Uid: require.Uid,
  202. HostId: require.HostId,
  203. WebId: localDbId, // 关联到本地数据库的主记录 ID
  204. BackendList: reqData.BackendList,
  205. AllowIpList: reqData.AllowIpList,
  206. DenyIpList: reqData.DenyIpList,
  207. AccessRule: reqData.AccessRule,
  208. }
  209. }
  210. func (s *webForwardingService) prepareWafData(ctx context.Context, req *v1.WebForwardingRequest) (v1.GlobalRequire, map[string]interface{}, error) {
  211. // 1. 获取必要的全局信息
  212. require, err := s.require(ctx, v1.GlobalRequire{
  213. HostId: req.HostId,
  214. Uid: req.Uid,
  215. Comment: req.WebForwardingData.Comment,
  216. Domain: req.WebForwardingData.Domain,
  217. })
  218. if err != nil {
  219. return v1.GlobalRequire{}, nil, err
  220. }
  221. if require.WafGatewayGroupId == 0 || require.LimitRuleId == 0 {
  222. return v1.GlobalRequire{}, nil, fmt.Errorf("请先配置实例")
  223. }
  224. // 2. 将字符串切片拼接成字符串,用于 WAF API
  225. allowIpListStr := strings.Join(req.WebForwardingData.AllowIpList, "\n")
  226. denyIpListStr := strings.Join(req.WebForwardingData.DenyIpList, "\n")
  227. // 3. 创建用于构建 WAF 表单的数据结构
  228. formDataBase := v1.WebForwardingDataSend{
  229. Tag: require.Tag,
  230. WafWebId: req.WebForwardingData.WafWebId,
  231. WafGatewayGroupId: require.WafGatewayGroupId,
  232. WafWebLimitId: require.LimitRuleId,
  233. Port: req.WebForwardingData.Port,
  234. Domain: req.WebForwardingData.Domain,
  235. CustomHost: req.WebForwardingData.CustomHost,
  236. CcCount: req.WebForwardingData.CcCount,
  237. CcDuration: req.WebForwardingData.CcDuration,
  238. CcBlockCount: req.WebForwardingData.CcBlockCount,
  239. CcBlockDuration: req.WebForwardingData.CcBlockDuration,
  240. Cc4xxCount: req.WebForwardingData.Cc4xxCount,
  241. Cc4xxDuration: req.WebForwardingData.Cc4xxDuration,
  242. Cc4xxBlockCount: req.WebForwardingData.Cc4xxBlockCount,
  243. Cc4xxBlockDuration: req.WebForwardingData.Cc4xxBlockDuration,
  244. Cc5xxCount: req.WebForwardingData.Cc5xxCount,
  245. Cc5xxDuration: req.WebForwardingData.Cc5xxDuration,
  246. Cc5xxBlockCount: req.WebForwardingData.Cc5xxBlockCount,
  247. Cc5xxBlockDuration: req.WebForwardingData.Cc5xxBlockDuration,
  248. IsHttps: req.WebForwardingData.IsHttps,
  249. BackendList: req.WebForwardingData.BackendList,
  250. AllowIpList: allowIpListStr,
  251. DenyIpList: denyIpListStr,
  252. AccessRule: req.WebForwardingData.AccessRule,
  253. Comment: req.WebForwardingData.Comment,
  254. }
  255. // 4. 构建 WAF 表单数据映射
  256. formData := s.buildWafFormData(&formDataBase, require)
  257. return require, formData, nil
  258. }
  259. func (s *webForwardingService) AddWebForwarding(ctx context.Context, req *v1.WebForwardingRequest) error {
  260. require, formData, err := s.prepareWafData(ctx, req)
  261. if err != nil {
  262. return err
  263. }
  264. err = s.wafformatter.validateWafPortCount(ctx, require.HostId)
  265. if err != nil {
  266. return err
  267. }
  268. wafWebId, err := s.wafformatter.sendFormData(ctx, "admin/info/waf_web/new", "admin/new/waf_web", formData)
  269. if err != nil {
  270. return err
  271. }
  272. webModel := s.buildWebForwardingModel(&req.WebForwardingData, wafWebId, require)
  273. id, err := s.webForwardingRepository.AddWebForwarding(ctx, webModel)
  274. if err != nil {
  275. return err
  276. }
  277. webRuleModel := s.buildWebRuleModel(&req.WebForwardingData, require, id)
  278. if _, err = s.webForwardingRepository.AddWebForwardingIps(ctx, *webRuleModel); err != nil {
  279. return err
  280. }
  281. return nil
  282. }
  283. func (s *webForwardingService) EditWebForwarding(ctx context.Context, req *v1.WebForwardingRequest) error {
  284. WafWebId, err := s.webForwardingRepository.GetWebForwardingWafWebIdById(ctx, req.WebForwardingData.Id)
  285. if err != nil {
  286. return err
  287. }
  288. req.WebForwardingData.WafWebId = WafWebId
  289. require, formData, err := s.prepareWafData(ctx, req)
  290. if err != nil {
  291. return err
  292. }
  293. _, err = s.wafformatter.sendFormData(ctx, "admin/info/waf_web/edit?&__goadmin_edit_pk="+strconv.Itoa(req.WebForwardingData.WafWebId), "admin/edit/waf_web", formData)
  294. if err != nil {
  295. return err
  296. }
  297. webModel := s.buildWebForwardingModel(&req.WebForwardingData, req.WebForwardingData.WafWebId, require)
  298. webModel.Id = req.WebForwardingData.Id
  299. if err = s.webForwardingRepository.EditWebForwarding(ctx, webModel); err != nil {
  300. return err
  301. }
  302. webRuleModel := s.buildWebRuleModel(&req.WebForwardingData, require, req.WebForwardingData.Id)
  303. if err = s.webForwardingRepository.EditWebForwardingIps(ctx, *webRuleModel); err != nil {
  304. return err
  305. }
  306. return nil
  307. }
  308. func (s *webForwardingService) DeleteWebForwarding(ctx context.Context, Id int) error {
  309. wafWebId, err := s.webForwardingRepository.GetWebForwardingWafWebIdById(ctx, Id)
  310. if err != nil {
  311. return err
  312. }
  313. _, err = s.crawler.DeleteRule(ctx, wafWebId, "admin/delete/waf_web?page=1&__pageSize=10&__sort=waf_web_id&__sort_type=desc")
  314. if err != nil {
  315. return err
  316. }
  317. if err = s.webForwardingRepository.DeleteWebForwarding(ctx, int64(Id)); err != nil {
  318. return err
  319. }
  320. return nil
  321. }