aidedweb.go 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  1. package waf
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "net"
  7. v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
  8. "github.com/go-nunu/nunu-layout-advanced/internal/model"
  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/internal/service/api/flexCdn"
  12. )
  13. // AidedWebService Web转发辅助服务接口
  14. type AidedWebService interface {
  15. // 验证相关
  16. ValidateAddRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) error
  17. ValidateEditRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, oldData *model.WebForwarding) error
  18. ValidateDeletePermission(oldData *model.WebForwarding, hostId int) error
  19. // CDN网站管理
  20. CreateCdnWebsite(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, formData v1.Website) (int64, error)
  21. UpdateCdnConfiguration(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse, formData v1.Website) error
  22. DeleteCdnServer(ctx context.Context, cdnWebId int) error
  23. // 源站管理
  24. AddOriginsToWebsite(ctx context.Context, req *v1.WebForwardingRequest, webId int64) (map[string]int64, error)
  25. UpdateOriginServers(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, ipData *model.WebForwardingRule) error
  26. // 功能配置管理
  27. ConfigureWebsocket(ctx context.Context, webId int64) error
  28. ConfigureProxyProtocol(ctx context.Context, req *v1.WebForwardingRequest, webId int64) error
  29. ConfigureCCProtection(ctx context.Context, req *v1.WebForwardingRequest, webId int64) error
  30. ConfigureWafFirewall(ctx context.Context, webId int64, groupId int) error
  31. // 异步任务处理
  32. ProcessAsyncTasks(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse)
  33. ProcessIpWhitelistChanges(ctx context.Context, req *v1.WebForwardingRequest, ipData *model.WebForwardingRule) error
  34. ProcessDeleteIpWhitelist(ctx context.Context, id int) error
  35. ProcessDomainWhitelistChanges(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse) error
  36. ProcessDeleteDomainWhitelist(ctx context.Context, oldData *model.WebForwarding, uid int) error
  37. // 数据库操作
  38. SaveToDatabase(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, webId int64, cdnOriginIds map[string]int64) (int, error)
  39. UpdateDatabaseRecords(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse, ipData *model.WebForwardingRule) error
  40. CleanupDatabaseRecords(ctx context.Context, id int) error
  41. // SSL证书管理
  42. ProcessSSLCertificate(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, formData v1.Website) error
  43. ProcessSSLCertificateUpdate(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse) error
  44. CleanupSSLCertificate(ctx context.Context, oldData *model.WebForwarding) error
  45. // 数据准备辅助函数
  46. PrepareWafData(ctx context.Context, req *v1.WebForwardingRequest) (RequireResponse, v1.Website, error)
  47. BuildProxyConfig(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) (v1.TypeJSON, error)
  48. BulidFormData(ctx context.Context, formData v1.Website) (v1.WebsiteSend, error)
  49. // 协议判断辅助函数
  50. GetProtocolType(isHttps int) string
  51. IsHttpsProtocol(isHttps int) bool
  52. // 模型构建辅助函数
  53. BuildWebForwardingModel(req *v1.WebForwardingDataRequest, ruleId int, require RequireResponse) *model.WebForwarding
  54. BuildWebRuleModel(reqData *v1.WebForwardingDataRequest, require RequireResponse, localDbId int, cdnOriginIds map[string]int64) *model.WebForwardingRule
  55. // 列表差异处理辅助函数
  56. FindDifferenceList(oldList, newList []v1.BackendList) (added, removed []v1.BackendList)
  57. WashDifferentIp(newIpList []string, oldIpList []string) (addedDenyIps []string, removedDenyIps []string)
  58. // 日志配置辅助函数
  59. EditLog(ctx context.Context, webId int64) error
  60. // 废弃的方法(保持向后兼容)
  61. Require(ctx context.Context, req v1.GlobalRequire) (v1.GlobalRequire, error)
  62. ValidateWebForwardingRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) error
  63. CreateOriginServers(ctx context.Context, req *v1.WebForwardingRequest) (map[string]int64, error)
  64. }
  65. func NewAidedWebService(
  66. service *service.Service,
  67. webForwardingRepository waf.WebForwardingRepository,
  68. wafformatter WafFormatterService,
  69. sslCert flexCdn.SslCertService,
  70. cdn flexCdn.CdnService,
  71. proxy flexCdn.ProxyService,
  72. websocket flexCdn.WebsocketService,
  73. cc CcService,
  74. ccIpList CcIpListService,
  75. gatewayIp GatewayipService,
  76. globalLimitRep waf.GlobalLimitRepository,
  77. ) AidedWebService {
  78. return &aidedWebService{
  79. Service: service,
  80. webForwardingRepository: webForwardingRepository,
  81. wafformatter: wafformatter,
  82. sslCert: sslCert,
  83. cdn: cdn,
  84. proxy: proxy,
  85. websocket: websocket,
  86. cc: cc,
  87. ccIpList: ccIpList,
  88. gatewayIp: gatewayIp,
  89. globalLimitRep: globalLimitRep,
  90. }
  91. }
  92. type aidedWebService struct {
  93. *service.Service
  94. webForwardingRepository waf.WebForwardingRepository
  95. wafformatter WafFormatterService
  96. sslCert flexCdn.SslCertService
  97. cdn flexCdn.CdnService
  98. proxy flexCdn.ProxyService
  99. websocket flexCdn.WebsocketService
  100. cc CcService
  101. ccIpList CcIpListService
  102. gatewayIp GatewayipService
  103. globalLimitRep waf.GlobalLimitRepository
  104. }
  105. const (
  106. // 协议类型常量
  107. isHttps = 1
  108. isHttp = 0
  109. protocolHttps = "https"
  110. protocolHttp = "http"
  111. // 默认配置常量
  112. defaultNodeClusterId = 2
  113. proxyProtocolVersion = 1
  114. )
  115. // Require 验证函数(原require函数)
  116. func (s *aidedWebService) Require(ctx context.Context, req v1.GlobalRequire) (v1.GlobalRequire, error) {
  117. var res v1.GlobalRequire
  118. //g, gCtx := errgroup.WithContext(ctx)
  119. //g.Go(func() error {
  120. // result, e := s.wafformatter.require(gCtx, req, "web")
  121. // if e != nil {
  122. // return e
  123. // }
  124. // res = result
  125. // return nil
  126. //})
  127. //g.Go(func() error {
  128. // e := s.wafformatter.validateWafDomainCount(gCtx, req)
  129. // if e != nil {
  130. // return e
  131. // }
  132. // return nil
  133. //})
  134. //if err = g.Wait(); err != nil {
  135. // return v1.GlobalRequire{}, err
  136. //}
  137. return res, nil
  138. }
  139. // BuildWebForwardingModel 辅助函数,用于构建通用的 WebForwarding 模型
  140. // ruleId 是从 WAF 系统获取的 ID
  141. func (s *aidedWebService) BuildWebForwardingModel(req *v1.WebForwardingDataRequest, ruleId int, require RequireResponse) *model.WebForwarding {
  142. return &model.WebForwarding{
  143. HostId: require.HostId,
  144. CdnWebId: ruleId,
  145. Port: req.Port,
  146. Domain: req.Domain,
  147. IsHttps: req.IsHttps,
  148. Comment: req.Comment,
  149. HttpsCert: req.HttpsCert,
  150. HttpsKey: req.HttpsKey,
  151. SslCertId: int(req.SslCertId),
  152. SslPolicyId: int(req.SslPolicyId),
  153. Cc: req.CcConfig.IsOn,
  154. ThresholdMethod: req.CcConfig.ThresholdMethod,
  155. Level: req.CcConfig.Level,
  156. Limit5s: req.CcConfig.Limit5s,
  157. Limit60s: req.CcConfig.Limit60s,
  158. Limit300s: req.CcConfig.Limit300s,
  159. Proxy: req.Proxy,
  160. }
  161. }
  162. // BuildWebRuleModel 构建WebForwardingRule模型
  163. func (s *aidedWebService) BuildWebRuleModel(reqData *v1.WebForwardingDataRequest, require RequireResponse, localDbId int, cdnOriginIds map[string]int64) *model.WebForwardingRule {
  164. return &model.WebForwardingRule{
  165. Uid: require.Uid,
  166. HostId: require.HostId,
  167. WebId: localDbId,
  168. CdnOriginIds: cdnOriginIds,
  169. BackendList: reqData.BackendList,
  170. }
  171. }
  172. // PrepareWafData 准备WAF数据
  173. // 职责:协调整个流程,负责获取前置配置和组装最终的 formData。
  174. func (s *aidedWebService) PrepareWafData(ctx context.Context, req *v1.WebForwardingRequest) (RequireResponse, v1.Website, error) {
  175. // 1. 获取基础配置
  176. require, err := s.wafformatter.Require(ctx, v1.GlobalRequire{
  177. HostId: req.HostId,
  178. Uid: req.Uid,
  179. Comment: req.WebForwardingData.Comment,
  180. })
  181. if err != nil {
  182. return RequireResponse{}, v1.Website{}, fmt.Errorf("获取WAF前置配置失败: %w", err)
  183. }
  184. if require.Uid == 0 {
  185. return RequireResponse{}, v1.Website{}, fmt.Errorf("请先配置实例")
  186. }
  187. // 2. 调用辅助函数,构建核心的代理配置 (将复杂逻辑封装起来)
  188. byteData, err := s.BuildProxyConfig(ctx, req, require)
  189. if err != nil {
  190. return RequireResponse{}, v1.Website{}, err // 错误信息在辅助函数中已经包装好了
  191. }
  192. type serverNames struct {
  193. ServerNames string `json:"name" form:"name"`
  194. Type string `json:"type" form:"type"`
  195. }
  196. var serverName []serverNames
  197. var serverJson []byte
  198. if req.WebForwardingData.Domain != "" {
  199. serverName = append(serverName, serverNames{
  200. ServerNames: req.WebForwardingData.Domain,
  201. Type: "full",
  202. })
  203. serverJson, err = json.Marshal(serverName)
  204. if err != nil {
  205. return RequireResponse{}, v1.Website{}, err
  206. }
  207. }
  208. // 3. 组装最终的 WAF 表单数据
  209. formData := v1.Website{
  210. UserId: int64(require.CdnUid),
  211. Type: "httpProxy",
  212. Name: require.Tag,
  213. ServerNamesJSON: serverJson,
  214. Description: req.WebForwardingData.Comment,
  215. ServerGroupIds: []int64{int64(require.GroupId)},
  216. NodeClusterId: defaultNodeClusterId,
  217. }
  218. // 4. 根据协议类型,填充 HttpJSON 和 HttpsJSON 字段
  219. if req.WebForwardingData.IsHttps == isHttps {
  220. formData.HttpJSON = v1.TypeJSON{IsOn: false}
  221. formData.HttpsJSON = byteData
  222. } else {
  223. formData.HttpJSON = byteData
  224. formData.HttpsJSON = v1.TypeJSON{IsOn: false}
  225. }
  226. return require, formData, nil
  227. }
  228. // BuildProxyConfig 构建代理配置
  229. // 职责:专门负责处理 HTTP/HTTPS 的差异,并生成对应的 JSON 配置。
  230. func (s *aidedWebService) BuildProxyConfig(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) (v1.TypeJSON, error) {
  231. var (
  232. jsonData v1.TypeJSON
  233. apiType string
  234. )
  235. jsonData.IsOn = true
  236. apiType = protocolHttps
  237. jsonData.SslPolicyRef.SslPolicyId = req.WebForwardingData.SslPolicyId
  238. // 判断协议类型,并处理 HTTPS 的特殊逻辑(证书)
  239. if req.WebForwardingData.IsHttps == isHttps {
  240. // 处理证书信息
  241. if jsonData.SslPolicyRef.SslPolicyId == 0 {
  242. sslPolicyId, err := s.sslCert.AddSslPolicy(ctx, nil)
  243. if err != nil {
  244. return v1.TypeJSON{}, err
  245. }
  246. jsonData.SslPolicyRef.SslPolicyId = sslPolicyId
  247. }
  248. jsonData.SslPolicyRef.IsOn = true
  249. } else {
  250. apiType = protocolHttp
  251. jsonData.SslPolicyRef = v1.SslPolicyRef{
  252. IsOn: false,
  253. SslPolicyId: req.WebForwardingData.SslCertId,
  254. }
  255. }
  256. // 填充通用的 Listen 配置
  257. for _, v := range require.GatewayIps {
  258. jsonData.Listen = append(jsonData.Listen, v1.Listen{
  259. Protocol: apiType,
  260. Host: v,
  261. Port: req.WebForwardingData.Port,
  262. })
  263. }
  264. return jsonData, nil
  265. }
  266. // FindDifferenceList 查找两个列表的差异
  267. func (s *aidedWebService) FindDifferenceList(oldList, newList []v1.BackendList) (added, removed []v1.BackendList) {
  268. diff := make(map[v1.BackendList]int)
  269. // 1. 遍历旧列表,为每个元素计数 +1
  270. for _, item := range oldList {
  271. diff[item]++
  272. }
  273. // 2. 遍历新列表,为每个元素计数 -1
  274. for _, item := range newList {
  275. diff[item]--
  276. }
  277. // 3. 遍历 diff map 来找出差异
  278. for item, count := range diff {
  279. if count > 0 {
  280. // 如果 count > 0,说明这个元素在 oldList 中但不在 newList 中
  281. removed = append(removed, item)
  282. } else if count < 0 {
  283. // 如果 count < 0,说明这个元素在 newList 中但不在 oldList 中
  284. added = append(added, item)
  285. }
  286. // 如果 count == 0,说明元素在两个列表中都存在,不做任何操作
  287. }
  288. return added, removed
  289. }
  290. // WashDifferentIp 清洗IP差异
  291. func (s *aidedWebService) WashDifferentIp(newIpList []string, oldIpList []string) (addedDenyIps []string, removedDenyIps []string) {
  292. var newAllowIps []string
  293. var oldAllowIps []string
  294. if len(oldIpList) > 0 {
  295. for _, v := range oldIpList {
  296. if net.ParseIP(v) != nil {
  297. oldAllowIps = append(oldAllowIps, v)
  298. }
  299. }
  300. }
  301. if len(newIpList) > 0 {
  302. for _, v := range newIpList {
  303. if net.ParseIP(v) != nil {
  304. newAllowIps = append(newAllowIps, v)
  305. }
  306. }
  307. }
  308. addedDenyIps, removedDenyIps = s.wafformatter.findIpDifferences(oldAllowIps, newAllowIps)
  309. return addedDenyIps, removedDenyIps
  310. }
  311. // EditLog 修改日志配置
  312. func (s *aidedWebService) EditLog(ctx context.Context, webId int64) error {
  313. webConfigId, err := s.webForwardingRepository.GetWebConfigId(ctx, webId)
  314. if err != nil {
  315. return err
  316. }
  317. if err := s.cdn.EditWebLog(ctx, webConfigId, v1.WebLog{
  318. IsPrior: false,
  319. IsOn: true,
  320. Fields: []int64{1, 2, 6, 7},
  321. Status1: true,
  322. Status2: true,
  323. Status3: true,
  324. Status4: true,
  325. Status5: true,
  326. FirewallOnly: false,
  327. EnableClientClosed: false,
  328. }); err != nil {
  329. return err
  330. }
  331. return nil
  332. }
  333. // BulidFormData 构建表单数据
  334. func (s *aidedWebService) BulidFormData(ctx context.Context, formData v1.Website) (v1.WebsiteSend, error) {
  335. httpJSON, err := json.Marshal(formData.HttpJSON)
  336. if err != nil {
  337. return v1.WebsiteSend{}, err
  338. }
  339. httpsJSON, err := json.Marshal(formData.HttpsJSON)
  340. if err != nil {
  341. return v1.WebsiteSend{}, err
  342. }
  343. formDataSend := v1.WebsiteSend{
  344. UserId: formData.UserId,
  345. AdminId: formData.AdminId,
  346. Type: formData.Type,
  347. Name: formData.Name,
  348. Description: formData.Description,
  349. ServerNamesJSON: formData.ServerNamesJSON,
  350. HttpJSON: httpJSON,
  351. HttpsJSON: httpsJSON,
  352. TcpJSON: formData.TcpJSON,
  353. TlsJSON: formData.TlsJSON,
  354. UdpJSON: formData.UdpJSON,
  355. WebId: formData.WebId,
  356. ReverseProxyJSON: formData.ReverseProxyJSON,
  357. ServerGroupIds: formData.ServerGroupIds,
  358. UserPlanId: formData.UserPlanId,
  359. NodeClusterId: formData.NodeClusterId,
  360. IncludeNodesJSON: formData.IncludeNodesJSON,
  361. ExcludeNodesJSON: formData.ExcludeNodesJSON,
  362. }
  363. return formDataSend, nil
  364. }
  365. // ValidateWebForwardingRequest 验证Web转发请求
  366. func (s *aidedWebService) ValidateWebForwardingRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) error {
  367. // 验证域名限制
  368. if err := s.wafformatter.validateWafDomainCount(ctx, v1.GlobalRequire{
  369. HostId: req.HostId,
  370. Domain: req.WebForwardingData.Domain,
  371. Comment: req.WebForwardingData.Comment,
  372. Uid: req.Uid,
  373. }); err != nil {
  374. return fmt.Errorf("域名数量验证失败: %w", err)
  375. }
  376. // 验证端口数量限制
  377. if err := s.wafformatter.validateWafPortCount(ctx, require.HostId); err != nil {
  378. return fmt.Errorf("端口数量验证失败: %w", err)
  379. }
  380. // 验证端口重复
  381. protocol := s.GetProtocolType(req.WebForwardingData.IsHttps)
  382. if err := s.wafformatter.VerifyPort(ctx, protocol, int64(req.WebForwardingData.Id), req.WebForwardingData.Port, int64(require.HostId), req.WebForwardingData.Domain); err != nil {
  383. return fmt.Errorf("端口 %d 验证失败: %w", req.WebForwardingData.Port, err)
  384. }
  385. return nil
  386. }
  387. // ProcessSSLCertificate 处理SSL证书
  388. func (s *aidedWebService) ProcessSSLCertificate(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, formData v1.Website) error {
  389. if !s.IsHttpsProtocol(req.WebForwardingData.IsHttps) {
  390. return nil // 非HTTPS协议不需要处理SSL证书
  391. }
  392. // 添加SSL证书
  393. sslCertId, err := s.sslCert.AddSSLCert(ctx, v1.SSL{
  394. Name: req.WebForwardingData.Domain,
  395. Domain: req.WebForwardingData.Domain,
  396. CertData: req.WebForwardingData.HttpsCert,
  397. KeyData: req.WebForwardingData.HttpsKey,
  398. CdnUserId: require.CdnUid,
  399. Description: req.WebForwardingData.Comment,
  400. })
  401. if err != nil {
  402. return fmt.Errorf("添加SSL证书失败: %w", err)
  403. }
  404. // 更新请求中的证书ID
  405. req.WebForwardingData.SslCertId = sslCertId
  406. req.WebForwardingData.SslPolicyId = formData.HttpsJSON.SslPolicyRef.SslPolicyId
  407. // 编辑SSL策略
  408. if err := s.sslCert.EditSslPolicy(ctx, formData.HttpsJSON.SslPolicyRef.SslPolicyId, []int64{sslCertId}, "add"); err != nil {
  409. return fmt.Errorf("编辑SSL策略失败: %w", err)
  410. }
  411. return nil
  412. }
  413. // CreateOriginServers 创建源站服务器
  414. func (s *aidedWebService) CreateOriginServers(ctx context.Context, req *v1.WebForwardingRequest) (map[string]int64, error) {
  415. cdnOriginIds := make(map[string]int64)
  416. for _, backend := range req.WebForwardingData.BackendList {
  417. apiType := s.GetProtocolType(backend.IsHttps)
  418. id, err := s.wafformatter.AddOrigin(ctx, v1.WebJson{
  419. ApiType: apiType,
  420. BackendList: backend.Addr,
  421. Host: backend.CustomHost,
  422. Comment: req.WebForwardingData.Comment,
  423. })
  424. if err != nil {
  425. return nil, fmt.Errorf("添加源站 %s 失败: %w", backend.Addr, err)
  426. }
  427. cdnOriginIds[backend.Addr] = id
  428. }
  429. return cdnOriginIds, nil
  430. }
  431. // GetProtocolType 获取协议类型字符串
  432. func (s *aidedWebService) GetProtocolType(isHttps int) string {
  433. if s.IsHttpsProtocol(isHttps) {
  434. return protocolHttps
  435. }
  436. return protocolHttp
  437. }
  438. // IsHttpsProtocol 判断是否为HTTPS协议
  439. func (s *aidedWebService) IsHttpsProtocol(httpsFlag int) bool {
  440. return httpsFlag == isHttps
  441. }
  442. // ValidateAddRequest 验证添加请求
  443. func (s *aidedWebService) ValidateAddRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) error {
  444. if err := s.wafformatter.validateWafDomainCount(ctx, v1.GlobalRequire{
  445. HostId: req.HostId,
  446. Domain: req.WebForwardingData.Domain,
  447. Comment: req.WebForwardingData.Comment,
  448. Uid: req.Uid,
  449. }); err != nil {
  450. return fmt.Errorf("域名数量验证失败: %w", err)
  451. }
  452. if err := s.wafformatter.validateWafPortCount(ctx, require.HostId); err != nil {
  453. return fmt.Errorf("端口数量验证失败: %w", err)
  454. }
  455. protocol := s.GetProtocolType(req.WebForwardingData.IsHttps)
  456. if err := s.wafformatter.VerifyPort(ctx, protocol, int64(req.WebForwardingData.Id), req.WebForwardingData.Port, int64(require.HostId), req.WebForwardingData.Domain); err != nil {
  457. return fmt.Errorf("端口 %d 验证失败: %w", req.WebForwardingData.Port, err)
  458. }
  459. return nil
  460. }
  461. // ValidateEditRequest 验证编辑请求
  462. func (s *aidedWebService) ValidateEditRequest(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, oldData *model.WebForwarding) error {
  463. if err := s.wafformatter.validateWafDomainCount(ctx, v1.GlobalRequire{
  464. HostId: req.HostId,
  465. Domain: req.WebForwardingData.Domain,
  466. Comment: req.WebForwardingData.Comment,
  467. Uid: req.Uid,
  468. }); err != nil {
  469. return fmt.Errorf("域名数量验证失败: %w", err)
  470. }
  471. protocol := s.GetProtocolType(req.WebForwardingData.IsHttps)
  472. if err := s.wafformatter.VerifyPort(ctx, protocol, int64(req.WebForwardingData.Id), req.WebForwardingData.Port, int64(require.HostId), req.WebForwardingData.Domain); err != nil {
  473. return fmt.Errorf("端口 %d 验证失败: %w", req.WebForwardingData.Port, err)
  474. }
  475. return nil
  476. }
  477. // ValidateDeletePermission 验证删除权限
  478. func (s *aidedWebService) ValidateDeletePermission(oldData *model.WebForwarding, hostId int) error {
  479. if oldData.HostId != hostId {
  480. return fmt.Errorf("用户权限不足")
  481. }
  482. return nil
  483. }
  484. // CreateCdnWebsite 创建CDN网站
  485. func (s *aidedWebService) CreateCdnWebsite(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, formData v1.Website) (int64, error) {
  486. formDataSend, err := s.BulidFormData(ctx, formData)
  487. if err != nil {
  488. return 0, fmt.Errorf("构建表单数据失败: %w", err)
  489. }
  490. webId, err := s.cdn.CreateWebsite(ctx, formDataSend)
  491. if err != nil {
  492. return 0, fmt.Errorf("创建CDN网站失败: %w", err)
  493. }
  494. return webId, nil
  495. }
  496. // UpdateCdnConfiguration 更新CDN配置
  497. func (s *aidedWebService) UpdateCdnConfiguration(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse, formData v1.Website) error {
  498. // 修改网站端口、协议或证书
  499. if oldData.Port != req.WebForwardingData.Port || oldData.IsHttps != req.WebForwardingData.IsHttps ||
  500. oldData.HttpsCert != req.WebForwardingData.HttpsCert || oldData.HttpsKey != req.WebForwardingData.HttpsKey {
  501. if err := s.updateWebsiteProtocolAndCert(ctx, req, oldData, require, formData); err != nil {
  502. return err
  503. }
  504. }
  505. // 修改网站域名
  506. if oldData.Domain != req.WebForwardingData.Domain {
  507. if err := s.updateWebsiteDomain(ctx, req, oldData); err != nil {
  508. return err
  509. }
  510. }
  511. // 修改网站名字
  512. if oldData.Comment != req.WebForwardingData.Comment {
  513. if err := s.updateWebsiteBasicInfo(ctx, oldData, require); err != nil {
  514. return err
  515. }
  516. }
  517. return nil
  518. }
  519. // DeleteCdnServer 删除CDN服务器
  520. func (s *aidedWebService) DeleteCdnServer(ctx context.Context, cdnWebId int) error {
  521. if err := s.cdn.DelServer(ctx, int64(cdnWebId)); err != nil {
  522. return fmt.Errorf("删除CDN服务器失败: %w", err)
  523. }
  524. return nil
  525. }
  526. // updateWebsiteProtocolAndCert 更新网站协议和证书
  527. func (s *aidedWebService) updateWebsiteProtocolAndCert(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse, formData v1.Website) error {
  528. // 修改证书
  529. if oldData.HttpsCert != req.WebForwardingData.HttpsCert || oldData.HttpsKey != req.WebForwardingData.HttpsKey {
  530. if err := s.sslCert.EditSSLCert(ctx, v1.SSL{
  531. Name: req.WebForwardingData.Domain,
  532. CertId: oldData.SslCertId,
  533. CertData: req.WebForwardingData.HttpsCert,
  534. KeyData: req.WebForwardingData.HttpsKey,
  535. CdnUserId: require.CdnUid,
  536. Domain: req.WebForwardingData.Domain,
  537. Description: req.WebForwardingData.Comment,
  538. }); err != nil {
  539. return fmt.Errorf("修改SSL证书失败: %w", err)
  540. }
  541. }
  542. // 切换协议
  543. var typeConfig, closeConfig v1.TypeJSON
  544. var apiType, closeType string
  545. if s.IsHttpsProtocol(req.WebForwardingData.IsHttps) {
  546. typeConfig = formData.HttpsJSON
  547. closeConfig = formData.HttpJSON
  548. apiType = s.GetProtocolType(req.WebForwardingData.IsHttps)
  549. closeType = s.GetProtocolType(0) // HTTP
  550. } else {
  551. typeConfig = formData.HttpJSON
  552. closeConfig = formData.HttpsJSON
  553. apiType = s.GetProtocolType(req.WebForwardingData.IsHttps)
  554. closeType = s.GetProtocolType(1) // HTTPS
  555. }
  556. typeJson, err := json.Marshal(typeConfig)
  557. if err != nil {
  558. return fmt.Errorf("序列化协议配置失败: %w", err)
  559. }
  560. closeJson, err := json.Marshal(closeConfig)
  561. if err != nil {
  562. return fmt.Errorf("序列化关闭协议配置失败: %w", err)
  563. }
  564. // 切换协议
  565. if err := s.cdn.EditServerType(ctx, v1.EditWebsite{
  566. Id: int64(oldData.CdnWebId),
  567. TypeJSON: typeJson,
  568. }, apiType); err != nil {
  569. return fmt.Errorf("切换到%s协议失败: %w", apiType, err)
  570. }
  571. if err := s.cdn.EditServerType(ctx, v1.EditWebsite{
  572. Id: int64(oldData.CdnWebId),
  573. TypeJSON: closeJson,
  574. }, closeType); err != nil {
  575. return fmt.Errorf("关闭%s协议失败: %w", closeType, err)
  576. }
  577. return nil
  578. }
  579. // updateWebsiteDomain 更新网站域名
  580. func (s *aidedWebService) updateWebsiteDomain(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding) error {
  581. type serverName struct {
  582. Name string `json:"name" form:"name"`
  583. Type string `json:"type" form:"type"`
  584. }
  585. var serverData []serverName
  586. serverData = append(serverData, serverName{
  587. Name: req.WebForwardingData.Domain,
  588. Type: "full",
  589. })
  590. serverJson, err := json.Marshal(serverData)
  591. if err != nil {
  592. return fmt.Errorf("序列化服务器名称失败: %w", err)
  593. }
  594. if err := s.cdn.EditServerName(ctx, v1.EditServerNames{
  595. ServerId: int64(oldData.CdnWebId),
  596. ServerNamesJSON: serverJson,
  597. }); err != nil {
  598. return fmt.Errorf("更新服务器名称失败: %w", err)
  599. }
  600. return nil
  601. }
  602. // updateWebsiteBasicInfo 更新网站基本信息
  603. func (s *aidedWebService) updateWebsiteBasicInfo(ctx context.Context, oldData *model.WebForwarding, require RequireResponse) error {
  604. // 通过globalLimitRep获取节点ID,这是项目中现有的方法
  605. nodeId, err := s.globalLimitRep.GetNodeId(ctx, oldData.CdnWebId)
  606. if err != nil {
  607. return fmt.Errorf("获取节点ID失败: %w", err)
  608. }
  609. if err := s.cdn.EditServerBasic(ctx, int64(oldData.CdnWebId), require.Tag, nodeId); err != nil {
  610. return fmt.Errorf("更新服务器基本信息失败: %w", err)
  611. }
  612. return nil
  613. }
  614. // AddOriginsToWebsite 添加源站到网站
  615. func (s *aidedWebService) AddOriginsToWebsite(ctx context.Context, req *v1.WebForwardingRequest, webId int64) (map[string]int64, error) {
  616. cdnOriginIds, err := s.CreateOriginServers(ctx, req)
  617. if err != nil {
  618. return nil, fmt.Errorf("创建源站服务器失败: %w", err)
  619. }
  620. // 添加源站到网站
  621. for _, originId := range cdnOriginIds {
  622. if err := s.cdn.AddServerOrigin(ctx, webId, originId); err != nil {
  623. return nil, fmt.Errorf("添加源站到网站失败: %w", err)
  624. }
  625. }
  626. return cdnOriginIds, nil
  627. }
  628. // UpdateOriginServers 更新源站服务器
  629. func (s *aidedWebService) UpdateOriginServers(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, ipData *model.WebForwardingRule) error {
  630. addOrigins, delOrigins := s.FindDifferenceList(ipData.BackendList, req.WebForwardingData.BackendList)
  631. addedIds := make(map[string]int64)
  632. // 添加新源站
  633. for _, v := range addOrigins {
  634. apiType := s.GetProtocolType(v.IsHttps)
  635. id, err := s.wafformatter.AddOrigin(ctx, v1.WebJson{
  636. ApiType: apiType,
  637. BackendList: v.Addr,
  638. Host: v.CustomHost,
  639. Comment: req.WebForwardingData.Comment,
  640. })
  641. if err != nil {
  642. return fmt.Errorf("添加源站 %s 失败: %w", v.Addr, err)
  643. }
  644. addedIds[v.Addr] = id
  645. }
  646. // 将新源站添加到网站
  647. for _, v := range addedIds {
  648. if err := s.cdn.AddServerOrigin(ctx, int64(oldData.CdnWebId), v); err != nil {
  649. return fmt.Errorf("添加源站到网站失败: %w", err)
  650. }
  651. }
  652. // 删除旧源站
  653. for k, v := range ipData.CdnOriginIds {
  654. for _, ip := range delOrigins {
  655. if k == ip.Addr {
  656. if err := s.cdn.DelServerOrigin(ctx, int64(oldData.CdnWebId), v); err != nil {
  657. return fmt.Errorf("删除源站失败: %w", err)
  658. }
  659. delete(ipData.CdnOriginIds, k)
  660. }
  661. }
  662. }
  663. // 合并新的源站ID
  664. for k, v := range addedIds {
  665. ipData.CdnOriginIds[k] = v
  666. }
  667. return nil
  668. }
  669. // ConfigureWebsocket 配置WebSocket
  670. func (s *aidedWebService) ConfigureWebsocket(ctx context.Context, webId int64) error {
  671. websocketId, err := s.websocket.AddWebsocket(ctx)
  672. if err != nil {
  673. return fmt.Errorf("添加WebSocket失败: %w", err)
  674. }
  675. if err := s.websocket.EnableOrDisable(ctx, webId, websocketId, true, false); err != nil {
  676. return fmt.Errorf("启用WebSocket失败: %w", err)
  677. }
  678. return nil
  679. }
  680. // ConfigureProxyProtocol 配置代理协议
  681. func (s *aidedWebService) ConfigureProxyProtocol(ctx context.Context, req *v1.WebForwardingRequest, webId int64) error {
  682. if req.WebForwardingData.Proxy {
  683. if err := s.proxy.EditProxy(ctx, webId, v1.ProxyProtocolJSON{
  684. IsOn: true,
  685. Version: proxyProtocolVersion,
  686. }); err != nil {
  687. return fmt.Errorf("启用代理协议失败: %w", err)
  688. }
  689. }
  690. return nil
  691. }
  692. // ConfigureCCProtection 配置CC防护
  693. func (s *aidedWebService) ConfigureCCProtection(ctx context.Context, req *v1.WebForwardingRequest, webId int64) error {
  694. if req.WebForwardingData.CcConfig.IsOn {
  695. if err := s.cc.EditCcConfig(ctx, webId, req.WebForwardingData.CcConfig); err != nil {
  696. return fmt.Errorf("配置CC防护失败: %w", err)
  697. }
  698. }
  699. return nil
  700. }
  701. // ConfigureWafFirewall 配置WAF防火墙
  702. func (s *aidedWebService) ConfigureWafFirewall(ctx context.Context, webId int64, groupId int) error {
  703. if err := s.ccIpList.AddCcIpListPolicy(ctx, webId, int64(groupId)); err != nil {
  704. return fmt.Errorf("配置WAF防火墙失败: %w", err)
  705. }
  706. return nil
  707. }
  708. // ProcessAsyncTasks 处理异步任务
  709. func (s *aidedWebService) ProcessAsyncTasks(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse) {
  710. // 域名白名单处理
  711. if req.WebForwardingData.Domain != "" {
  712. go func() {
  713. doMain, err := s.wafformatter.ConvertToWildcardDomain(ctx, req.WebForwardingData.Domain)
  714. if err != nil {
  715. return
  716. }
  717. if len(require.GatewayIps) == 0 {
  718. return
  719. }
  720. firstIp, err := s.gatewayIp.GetGatewayipByHostIdFirst(ctx, int64(require.HostId), int64(require.Uid))
  721. if err != nil {
  722. return
  723. }
  724. s.wafformatter.PublishDomainWhitelistTask(doMain, firstIp, "add")
  725. }()
  726. }
  727. // 源站IP白名单处理
  728. if req.WebForwardingData.BackendList != nil {
  729. go func() {
  730. var ips []string
  731. for _, v := range req.WebForwardingData.BackendList {
  732. ip, _, err := net.SplitHostPort(v.Addr)
  733. if err != nil {
  734. continue
  735. }
  736. ips = append(ips, ip)
  737. }
  738. if len(ips) > 0 {
  739. s.wafformatter.PublishIpWhitelistTask(ips, "add", "", "white")
  740. }
  741. }()
  742. }
  743. }
  744. // ProcessIpWhitelistChanges 处理IP白名单变更
  745. func (s *aidedWebService) ProcessIpWhitelistChanges(ctx context.Context, req *v1.WebForwardingRequest, ipData *model.WebForwardingRule) error {
  746. var oldIps, newIps []string
  747. // 提取旧IP列表
  748. for _, v := range ipData.BackendList {
  749. ip, _, err := net.SplitHostPort(v.Addr)
  750. if err != nil {
  751. return fmt.Errorf("解析旧IP地址失败: %w", err)
  752. }
  753. oldIps = append(oldIps, ip)
  754. }
  755. // 提取新IP列表
  756. for _, v := range req.WebForwardingData.BackendList {
  757. ip, _, err := net.SplitHostPort(v.Addr)
  758. if err != nil {
  759. return fmt.Errorf("解析新IP地址失败: %w", err)
  760. }
  761. newIps = append(newIps, ip)
  762. }
  763. // 查找IP差异
  764. addedIps, removedIps := s.wafformatter.findIpDifferences(oldIps, newIps)
  765. // 异步处理添加的IP
  766. if len(addedIps) > 0 {
  767. go s.wafformatter.PublishIpWhitelistTask(addedIps, "add", "", "white")
  768. }
  769. // 异步处理删除的IP
  770. if len(removedIps) > 0 {
  771. go func() {
  772. ipsToDelist, err := s.wafformatter.WashDelIps(ctx, removedIps)
  773. if err != nil {
  774. return
  775. }
  776. if len(ipsToDelist) > 0 {
  777. s.wafformatter.PublishIpWhitelistTask(ipsToDelist, "del", "0", "white")
  778. }
  779. }()
  780. }
  781. return nil
  782. }
  783. // ProcessDeleteIpWhitelist 处理删除IP白名单
  784. func (s *aidedWebService) ProcessDeleteIpWhitelist(ctx context.Context, id int) error {
  785. ipData, err := s.webForwardingRepository.GetWebForwardingIpsByID(ctx, id)
  786. if err != nil {
  787. return fmt.Errorf("获取IP数据失败: %w", err)
  788. }
  789. if ipData != nil && len(ipData.BackendList) > 0 {
  790. var ips []string
  791. for _, v := range ipData.BackendList {
  792. ip, _, err := net.SplitHostPort(v.Addr)
  793. if err != nil {
  794. continue
  795. }
  796. ips = append(ips, ip)
  797. }
  798. if len(ips) > 0 {
  799. go func() {
  800. ipsToDelist, err := s.wafformatter.WashDelIps(ctx, ips)
  801. if err != nil {
  802. return
  803. }
  804. if len(ipsToDelist) > 0 {
  805. s.wafformatter.PublishIpWhitelistTask(ipsToDelist, "del", "0", "white")
  806. }
  807. }()
  808. }
  809. }
  810. return nil
  811. }
  812. // ProcessDomainWhitelistChanges 处理域名白名单变更
  813. func (s *aidedWebService) ProcessDomainWhitelistChanges(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse) error {
  814. if oldData.Domain != req.WebForwardingData.Domain {
  815. firstIp, err := s.gatewayIp.GetGatewayipByHostIdFirst(ctx, int64(req.HostId), int64(req.Uid))
  816. if err != nil {
  817. return fmt.Errorf("获取网关IP失败: %w", err)
  818. }
  819. newDomain, err := s.wafformatter.ConvertToWildcardDomain(ctx, req.WebForwardingData.Domain)
  820. if err != nil {
  821. return fmt.Errorf("转换新域名失败: %w", err)
  822. }
  823. oldDomain, err := s.wafformatter.ConvertToWildcardDomain(ctx, oldData.Domain)
  824. if err != nil {
  825. return fmt.Errorf("转换旧域名失败: %w", err)
  826. }
  827. if len(require.GatewayIps) == 0 {
  828. return fmt.Errorf("网关组不存在")
  829. }
  830. // 检查旧域名使用数量
  831. count, err := s.webForwardingRepository.GetDomainCount(ctx, req.HostId, oldData.Domain)
  832. if err != nil {
  833. return fmt.Errorf("获取域名使用数量失败: %w", err)
  834. }
  835. // 异步处理域名白名单变更
  836. go func() {
  837. if count < 2 {
  838. s.wafformatter.PublishDomainWhitelistTask(oldDomain, firstIp, "del")
  839. }
  840. s.wafformatter.PublishDomainWhitelistTask(newDomain, firstIp, "add")
  841. }()
  842. }
  843. return nil
  844. }
  845. // ProcessDeleteDomainWhitelist 处理删除域名白名单
  846. func (s *aidedWebService) ProcessDeleteDomainWhitelist(ctx context.Context, oldData *model.WebForwarding, uid int) error {
  847. if oldData.Domain != "" {
  848. firstIp, err := s.gatewayIp.GetGatewayipByHostIdFirst(ctx, int64(oldData.HostId), int64(uid))
  849. if err != nil {
  850. return fmt.Errorf("获取网关IP失败: %w", err)
  851. }
  852. doMain, err := s.wafformatter.ConvertToWildcardDomain(ctx, oldData.Domain)
  853. if err != nil {
  854. return fmt.Errorf("转换域名失败: %w", err)
  855. }
  856. go s.wafformatter.PublishDomainWhitelistTask(doMain, firstIp, "del")
  857. }
  858. return nil
  859. }
  860. // SaveToDatabase 保存到数据库
  861. func (s *aidedWebService) SaveToDatabase(ctx context.Context, req *v1.WebForwardingRequest, require RequireResponse, webId int64, cdnOriginIds map[string]int64) (int, error) {
  862. webModel := s.BuildWebForwardingModel(&req.WebForwardingData, int(webId), require)
  863. id, err := s.webForwardingRepository.AddWebForwarding(ctx, webModel)
  864. if err != nil {
  865. return 0, fmt.Errorf("添加Web转发记录失败: %w", err)
  866. }
  867. webRuleModel := s.BuildWebRuleModel(&req.WebForwardingData, require, id, cdnOriginIds)
  868. if _, err = s.webForwardingRepository.AddWebForwardingIps(ctx, *webRuleModel); err != nil {
  869. return 0, fmt.Errorf("添加Web转发规则失败: %w", err)
  870. }
  871. return id, nil
  872. }
  873. // UpdateDatabaseRecords 更新数据库记录
  874. func (s *aidedWebService) UpdateDatabaseRecords(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse, ipData *model.WebForwardingRule) error {
  875. webModel := s.BuildWebForwardingModel(&req.WebForwardingData, req.WebForwardingData.CdnWebId, require)
  876. webModel.Id = req.WebForwardingData.Id
  877. if err := s.webForwardingRepository.EditWebForwarding(ctx, webModel); err != nil {
  878. return fmt.Errorf("更新Web转发记录失败: %w", err)
  879. }
  880. webRuleModel := s.BuildWebRuleModel(&req.WebForwardingData, require, req.WebForwardingData.Id, ipData.CdnOriginIds)
  881. if err := s.webForwardingRepository.EditWebForwardingIps(ctx, *webRuleModel); err != nil {
  882. return fmt.Errorf("更新Web转发规则失败: %w", err)
  883. }
  884. return nil
  885. }
  886. // CleanupDatabaseRecords 清理数据库记录
  887. func (s *aidedWebService) CleanupDatabaseRecords(ctx context.Context, id int) error {
  888. if err := s.webForwardingRepository.DeleteWebForwarding(ctx, int64(id)); err != nil {
  889. return fmt.Errorf("删除Web转发记录失败: %w", err)
  890. }
  891. if err := s.webForwardingRepository.DeleteWebForwardingIpsById(ctx, id); err != nil {
  892. return fmt.Errorf("删除Web转发规则失败: %w", err)
  893. }
  894. return nil
  895. }
  896. // ProcessSSLCertificateUpdate 处理SSL证书更新
  897. func (s *aidedWebService) ProcessSSLCertificateUpdate(ctx context.Context, req *v1.WebForwardingRequest, oldData *model.WebForwarding, require RequireResponse) error {
  898. if !s.IsHttpsProtocol(req.WebForwardingData.IsHttps) {
  899. return nil // 非HTTPS协议不需要处理SSL证书
  900. }
  901. // 如果证书内容有变化
  902. if oldData.HttpsCert != req.WebForwardingData.HttpsCert || oldData.HttpsKey != req.WebForwardingData.HttpsKey {
  903. if err := s.sslCert.EditSSLCert(ctx, v1.SSL{
  904. Name: req.WebForwardingData.Domain,
  905. CertId: oldData.SslCertId,
  906. CertData: req.WebForwardingData.HttpsCert,
  907. KeyData: req.WebForwardingData.HttpsKey,
  908. CdnUserId: require.CdnUid,
  909. Domain: req.WebForwardingData.Domain,
  910. Description: req.WebForwardingData.Comment,
  911. }); err != nil {
  912. return fmt.Errorf("更新SSL证书失败: %w", err)
  913. }
  914. }
  915. return nil
  916. }
  917. // CleanupSSLCertificate 清理SSL证书
  918. func (s *aidedWebService) CleanupSSLCertificate(ctx context.Context, oldData *model.WebForwarding) error {
  919. if oldData.SslCertId != 0 {
  920. if err := s.cdn.DelSSLCert(ctx, int64(oldData.SslCertId)); err != nil {
  921. return fmt.Errorf("删除SSL证书失败: %w", err)
  922. }
  923. if err := s.sslCert.EditSslPolicy(ctx, int64(oldData.SslPolicyId), []int64{int64(oldData.SslCertId)}, "del"); err != nil {
  924. return fmt.Errorf("删除SSL策略失败: %w", err)
  925. }
  926. }
  927. return nil
  928. }