aidedweb.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. package web
  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/api/waf"
  9. "github.com/go-nunu/nunu-layout-advanced/internal/service"
  10. "github.com/go-nunu/nunu-layout-advanced/internal/service/api/flexCdn"
  11. waf2 "github.com/go-nunu/nunu-layout-advanced/internal/service/api/waf"
  12. "github.com/go-nunu/nunu-layout-advanced/internal/service/api/waf/common"
  13. )
  14. // AidedWebService Web转发辅助服务接口
  15. type AidedWebService interface {
  16. // 数据准备辅助函数
  17. PrepareWafData(ctx context.Context, req *v1.WebForwardingRequest) (common.RequireResponse, v1.Website, error)
  18. BuildProxyConfig(ctx context.Context, req *v1.WebForwardingRequest, gatewayIps []string) (v1.TypeJSON, error)
  19. BulidFormData(ctx context.Context, formData v1.Website) (v1.WebsiteSend, error)
  20. // 协议判断辅助函数
  21. GetProtocolType(isHttps int) string
  22. IsHttpsProtocol(isHttps int) bool
  23. // 模型构建辅助函数
  24. BuildWebForwardingModel(req *v1.WebForwardingDataRequest, ruleId int, require common.RequireResponse) *model.WebForwarding
  25. BuildWebRuleModel(reqData *v1.WebForwardingDataRequest, require common.RequireResponse, localDbId int, cdnOriginIds map[string]int64) *model.WebForwardingRule
  26. Helper
  27. Valid
  28. Log
  29. Database
  30. SSLCert
  31. Configure
  32. CdnWeb
  33. Origin
  34. Process
  35. }
  36. func NewAidedWebService(
  37. service *service.Service,
  38. webForwardingRepository waf.WebForwardingRepository,
  39. wafformatter common.WafFormatterService,
  40. sslCert flexCdn.SslCertService,
  41. cdn flexCdn.CdnService,
  42. proxy flexCdn.ProxyService,
  43. websocket flexCdn.WebsocketService,
  44. cc waf2.CcService,
  45. ccIpList waf2.CcIpListService,
  46. gatewayIp common.GatewayipService,
  47. globalLimitRep waf.GlobalLimitRepository,
  48. ) AidedWebService {
  49. return &aidedWebService{
  50. Service: service,
  51. webForwardingRepository: webForwardingRepository,
  52. wafformatter: wafformatter,
  53. sslCert: sslCert,
  54. cdn: cdn,
  55. proxy: proxy,
  56. websocket: websocket,
  57. cc: cc,
  58. ccIpList: ccIpList,
  59. gatewayIp: gatewayIp,
  60. globalLimitRep: globalLimitRep,
  61. }
  62. }
  63. type aidedWebService struct {
  64. *service.Service
  65. webForwardingRepository waf.WebForwardingRepository
  66. wafformatter common.WafFormatterService
  67. sslCert flexCdn.SslCertService
  68. cdn flexCdn.CdnService
  69. proxy flexCdn.ProxyService
  70. websocket flexCdn.WebsocketService
  71. cc waf2.CcService
  72. ccIpList waf2.CcIpListService
  73. gatewayIp common.GatewayipService
  74. globalLimitRep waf.GlobalLimitRepository
  75. }
  76. const (
  77. // 协议类型常量
  78. isHttps = 1
  79. isHttp = 0
  80. protocolHttps = "https"
  81. protocolHttp = "http"
  82. // 默认配置常量
  83. defaultNodeClusterId = 2
  84. proxyProtocolVersion = 1
  85. )
  86. // BuildWebForwardingModel 辅助函数,用于构建通用的 WebForwarding 模型
  87. // ruleId 是从 WAF 系统获取的 ID
  88. func (s *aidedWebService) BuildWebForwardingModel(req *v1.WebForwardingDataRequest, ruleId int, require common.RequireResponse) *model.WebForwarding {
  89. return &model.WebForwarding{
  90. HostId: require.HostId,
  91. CdnWebId: ruleId,
  92. Port: req.Port,
  93. Domain: req.Domain,
  94. IsHttps: req.IsHttps,
  95. Comment: req.Comment,
  96. HttpsCert: req.HttpsCert,
  97. HttpsKey: req.HttpsKey,
  98. SslCertId: int(req.SslCertId),
  99. SslPolicyId: int(req.SslPolicyId),
  100. Cc: req.CcConfig.IsOn,
  101. ThresholdMethod: req.CcConfig.ThresholdMethod,
  102. Level: req.CcConfig.Level,
  103. Limit5s: req.CcConfig.Limit5s,
  104. Limit60s: req.CcConfig.Limit60s,
  105. Limit300s: req.CcConfig.Limit300s,
  106. Proxy: req.Proxy,
  107. }
  108. }
  109. // BuildWebRuleModel 构建WebForwardingRule模型
  110. func (s *aidedWebService) BuildWebRuleModel(reqData *v1.WebForwardingDataRequest, require common.RequireResponse, localDbId int, cdnOriginIds map[string]int64) *model.WebForwardingRule {
  111. return &model.WebForwardingRule{
  112. Uid: require.Uid,
  113. HostId: require.HostId,
  114. WebId: localDbId,
  115. CdnOriginIds: cdnOriginIds,
  116. BackendList: reqData.BackendList,
  117. }
  118. }
  119. // getRequire 获取前置配置
  120. func (s *aidedWebService) getRequire (ctx context.Context, req *v1.WebForwardingRequest) (common.RequireResponse, error) {
  121. // 1. 获取基础配置
  122. require, err := s.wafformatter.Require(ctx, v1.GlobalRequire{
  123. HostId: req.HostId,
  124. Uid: req.Uid,
  125. Comment: req.WebForwardingData.Comment,
  126. })
  127. if err != nil {
  128. return common.RequireResponse{}, fmt.Errorf("获取WAF前置配置失败: %w", err)
  129. }
  130. if require.Uid == 0 {
  131. return common.RequireResponse{}, fmt.Errorf("请先配置实例")
  132. }
  133. return require, nil
  134. }
  135. // PrepareWafData 准备WAF数据
  136. // 职责:协调整个流程,负责获取前置配置和组装最终的 formData。
  137. func (s *aidedWebService) PrepareWafData(ctx context.Context, req *v1.WebForwardingRequest) (common.RequireResponse, v1.Website, error) {
  138. // 1. 获取前置配置
  139. require, err := s.getRequire(ctx, req)
  140. if err != nil {
  141. return common.RequireResponse{}, v1.Website{}, err
  142. }
  143. // 2. 调用辅助函数,构建核心的代理配置 (将复杂逻辑封装起来)
  144. byteData, err := s.BuildProxyConfig(ctx, req, require.GatewayIps)
  145. if err != nil {
  146. return common.RequireResponse{}, v1.Website{}, err // 错误信息在辅助函数中已经包装好了
  147. }
  148. type serverNames struct {
  149. ServerNames string `json:"name" form:"name"`
  150. Type string `json:"type" form:"type"`
  151. }
  152. var serverName []serverNames
  153. var serverJson []byte
  154. if req.WebForwardingData.Domain != "" {
  155. serverName = append(serverName, serverNames{
  156. ServerNames: req.WebForwardingData.Domain,
  157. Type: "full",
  158. })
  159. serverJson, err = json.Marshal(serverName)
  160. if err != nil {
  161. return common.RequireResponse{}, v1.Website{}, err
  162. }
  163. }
  164. // 3. 组装最终的 WAF 表单数据
  165. formData := v1.Website{
  166. UserId: int64(require.CdnUid),
  167. Type: "httpProxy",
  168. Name: require.Tag,
  169. ServerNamesJSON: serverJson,
  170. Description: req.WebForwardingData.Comment,
  171. ServerGroupIds: []int64{int64(require.GroupId)},
  172. NodeClusterId: defaultNodeClusterId,
  173. }
  174. // 4. 根据协议类型,填充 HttpJSON 和 HttpsJSON 字段
  175. if req.WebForwardingData.IsHttps == isHttps {
  176. formData.HttpJSON = v1.TypeJSON{IsOn: false}
  177. formData.HttpsJSON = byteData
  178. } else {
  179. formData.HttpJSON = byteData
  180. formData.HttpsJSON = v1.TypeJSON{IsOn: false}
  181. }
  182. return require, formData, nil
  183. }
  184. func (s *aidedWebService) buildSslPolicy(ctx context.Context, data *v1.WebForwardingDataRequest) (v1.SslPolicyRef, error) {
  185. // 如果不是 HTTPS,直接返回关闭状态的 SSL 策略
  186. if data.IsHttps != isHttps {
  187. return v1.SslPolicyRef{
  188. IsOn: false,
  189. SslPolicyId: data.SslPolicyId,
  190. }, nil
  191. }
  192. // --- 以下是 HTTPS 的逻辑 ---
  193. sslPolicyID := data.SslPolicyId
  194. // 如果请求中没有提供 SSL 策略 ID,则为其创建一个新的
  195. if sslPolicyID == 0 {
  196. var err error
  197. sslPolicyID, err = s.sslCert.AddSslPolicy(ctx, nil)
  198. if err != nil {
  199. // 如果创建失败,返回零值和错误
  200. return v1.SslPolicyRef{}, err
  201. }
  202. }
  203. // 返回开启状态的 HTTPS 策略
  204. return v1.SslPolicyRef{
  205. IsOn: true,
  206. SslPolicyId: sslPolicyID,
  207. }, nil
  208. }
  209. // BuildProxyConfig 构建代理配置
  210. // 职责:专门负责处理 HTTP/HTTPS 的差异,并生成对应的 JSON 配置。
  211. func (s *aidedWebService) BuildProxyConfig(ctx context.Context, req *v1.WebForwardingRequest, gatewayIps []string) (v1.TypeJSON, error) {
  212. // 第一步:构建 SSL 策略。所有复杂的 if/else 都被封装在辅助函数中
  213. sslPolicy, err := s.buildSslPolicy(ctx, &req.WebForwardingData)
  214. if err != nil {
  215. return v1.TypeJSON{}, err
  216. }
  217. // 更新请求中的 SSL 策略
  218. req.WebForwardingData.SslPolicyId = sslPolicy.SslPolicyId
  219. // 第二步:根据协议类型确定 apiType
  220. apiType := protocolHttp
  221. if req.WebForwardingData.IsHttps == isHttps {
  222. apiType = protocolHttps
  223. }
  224. // 第三步:构建通用的 Listen 配置
  225. listenConfigs := make([]v1.Listen, 0, len(gatewayIps))
  226. for _, ip := range gatewayIps {
  227. listenConfigs = append(listenConfigs, v1.Listen{
  228. Protocol: apiType,
  229. Host: ip,
  230. Port: req.WebForwardingData.Port,
  231. })
  232. }
  233. // 第四步:组装并返回最终结果
  234. jsonData := v1.TypeJSON{
  235. IsOn: true,
  236. SslPolicyRef: sslPolicy,
  237. Listen: listenConfigs,
  238. }
  239. return jsonData, nil
  240. }
  241. // BulidFormData 构建表单数据
  242. func (s *aidedWebService) BulidFormData(ctx context.Context, formData v1.Website) (v1.WebsiteSend, error) {
  243. httpJSON, err := json.Marshal(formData.HttpJSON)
  244. if err != nil {
  245. return v1.WebsiteSend{}, err
  246. }
  247. httpsJSON, err := json.Marshal(formData.HttpsJSON)
  248. if err != nil {
  249. return v1.WebsiteSend{}, err
  250. }
  251. formDataSend := v1.WebsiteSend{
  252. UserId: formData.UserId,
  253. AdminId: formData.AdminId,
  254. Type: formData.Type,
  255. Name: formData.Name,
  256. Description: formData.Description,
  257. ServerNamesJSON: formData.ServerNamesJSON,
  258. HttpJSON: httpJSON,
  259. HttpsJSON: httpsJSON,
  260. TcpJSON: formData.TcpJSON,
  261. TlsJSON: formData.TlsJSON,
  262. UdpJSON: formData.UdpJSON,
  263. WebId: formData.WebId,
  264. ReverseProxyJSON: formData.ReverseProxyJSON,
  265. ServerGroupIds: formData.ServerGroupIds,
  266. UserPlanId: formData.UserPlanId,
  267. NodeClusterId: formData.NodeClusterId,
  268. IncludeNodesJSON: formData.IncludeNodesJSON,
  269. ExcludeNodesJSON: formData.ExcludeNodesJSON,
  270. }
  271. return formDataSend, nil
  272. }
  273. // GetProtocolType 获取协议类型字符串
  274. func (s *aidedWebService) GetProtocolType(isHttps int) string {
  275. if s.IsHttpsProtocol(isHttps) {
  276. return protocolHttps
  277. }
  278. return protocolHttp
  279. }
  280. // IsHttpsProtocol 判断是否为HTTPS协议
  281. func (s *aidedWebService) IsHttpsProtocol(httpsFlag int) bool {
  282. return httpsFlag == isHttps
  283. }
  284. // updateWebsiteProtocolAndCert 更新网站协议和证书
  285. func (s *aidedWebService) updateWebsiteProtocolAndCert(ctx context.Context, isHttps int, cdnWebId int64, formData v1.Website) error {
  286. // 切换协议
  287. var typeConfig, closeConfig v1.TypeJSON
  288. var apiType, closeType string
  289. if s.IsHttpsProtocol(isHttps) {
  290. typeConfig = formData.HttpsJSON
  291. closeConfig = formData.HttpJSON
  292. apiType = s.GetProtocolType(isHttps)
  293. closeType = s.GetProtocolType(0) // HTTP
  294. } else {
  295. typeConfig = formData.HttpJSON
  296. closeConfig = formData.HttpsJSON
  297. apiType = s.GetProtocolType(isHttps)
  298. closeType = s.GetProtocolType(1) // HTTPS
  299. }
  300. typeJson, err := json.Marshal(typeConfig)
  301. if err != nil {
  302. return fmt.Errorf("序列化协议配置失败: %w", err)
  303. }
  304. closeJson, err := json.Marshal(closeConfig)
  305. if err != nil {
  306. return fmt.Errorf("序列化关闭协议配置失败: %w", err)
  307. }
  308. // 切换协议
  309. if err := s.cdn.EditServerType(ctx, v1.EditWebsite{
  310. Id: cdnWebId,
  311. TypeJSON: typeJson,
  312. }, apiType); err != nil {
  313. return fmt.Errorf("切换到%s协议失败: %w", apiType, err)
  314. }
  315. if err := s.cdn.EditServerType(ctx, v1.EditWebsite{
  316. Id: cdnWebId,
  317. TypeJSON: closeJson,
  318. }, closeType); err != nil {
  319. return fmt.Errorf("关闭%s协议失败: %w", closeType, err)
  320. }
  321. return nil
  322. }
  323. // updateWebsiteDomain 更新网站域名
  324. func (s *aidedWebService) updateWebsiteDomain(ctx context.Context, domain string, cdnWebId int64) error {
  325. type serverName struct {
  326. Name string `json:"name" form:"name"`
  327. Type string `json:"type" form:"type"`
  328. }
  329. var serverData []serverName
  330. serverData = append(serverData, serverName{
  331. Name: domain,
  332. Type: "full",
  333. })
  334. serverJson, err := json.Marshal(serverData)
  335. if err != nil {
  336. return fmt.Errorf("序列化服务器名称失败: %w", err)
  337. }
  338. if err := s.cdn.EditServerName(ctx, v1.EditServerNames{
  339. ServerId: cdnWebId,
  340. ServerNamesJSON: serverJson,
  341. }); err != nil {
  342. return fmt.Errorf("更新服务器名称失败: %w", err)
  343. }
  344. return nil
  345. }
  346. // updateWebsiteBasicInfo 更新网站基本信息
  347. func (s *aidedWebService) updateWebsiteBasicInfo(ctx context.Context, cdnWebId int64, tag string) error {
  348. // 通过globalLimitRep获取节点ID,这是项目中现有的方法
  349. nodeId, err := s.globalLimitRep.GetNodeId(ctx, int(cdnWebId))
  350. if err != nil {
  351. return fmt.Errorf("获取节点ID失败: %w", err)
  352. }
  353. if err := s.cdn.EditServerBasic(ctx, cdnWebId, tag, nodeId); err != nil {
  354. return fmt.Errorf("更新服务器基本信息失败: %w", err)
  355. }
  356. return nil
  357. }