webforwarding.go 12 KB

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