aidedtcp.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. package tcp
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. waf2 "github.com/go-nunu/nunu-layout-advanced/internal/service/api/waf/common"
  7. "maps"
  8. "net"
  9. "strconv"
  10. v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
  11. "github.com/go-nunu/nunu-layout-advanced/internal/model"
  12. "github.com/go-nunu/nunu-layout-advanced/internal/repository/api/waf"
  13. "github.com/go-nunu/nunu-layout-advanced/internal/service"
  14. "github.com/go-nunu/nunu-layout-advanced/internal/service/api/flexCdn"
  15. )
  16. // AidedTcpService TCP转发辅助服务接口
  17. type AidedTcpService interface {
  18. // 验证相关
  19. ValidateAddRequest(ctx context.Context, req *v1.TcpForwardingRequest, require waf2.RequireResponse) error
  20. ValidateEditRequest(ctx context.Context, req *v1.TcpForwardingRequest, require waf2.RequireResponse, oldData *model.Tcpforwarding) error
  21. ValidateDeletePermission(oldData *model.Tcpforwarding, hostId int) error
  22. // CDN操作相关
  23. CreateCdnWebsite(ctx context.Context, formData v1.WebsiteSend) (int64, error)
  24. UpdateCdnConfiguration(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, require waf2.RequireResponse, formData v1.WebsiteSend) error
  25. DeleteCdnServer(ctx context.Context, cdnWebId int) error
  26. // 源站操作相关
  27. AddOriginsToWebsite(ctx context.Context, req *v1.TcpForwardingRequest, tcpId int64) (map[string]int64, error)
  28. UpdateOriginServers(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, ipData *model.TcpForwardingRule) error
  29. // 代理协议配置
  30. ConfigureProxyProtocol(ctx context.Context, req *v1.TcpForwardingRequest, tcpId int64) error
  31. // 异步任务处理
  32. ProcessAsyncTasks(req *v1.TcpForwardingRequest)
  33. ProcessIpWhitelistChanges(ctx context.Context, req *v1.TcpForwardingRequest, ipData *model.TcpForwardingRule) error
  34. ProcessDeleteIpWhitelist(ctx context.Context, id int) error
  35. // 数据准备和配置
  36. PrepareWafData(ctx context.Context, req *v1.TcpForwardingRequest) (waf2.RequireResponse, v1.WebsiteSend, error)
  37. BuildTcpListenConfig(gatewayIps []string, portStr string) ([]byte, error)
  38. // 模型构建
  39. BuildTcpForwardingModel(req *v1.TcpForwardingDataRequest, ruleId int, require waf2.RequireResponse) *model.Tcpforwarding
  40. BuildTcpRuleModel(reqData *v1.TcpForwardingDataRequest, require waf2.RequireResponse, localDbId int, cdnOriginIds map[string]int64) *model.TcpForwardingRule
  41. // 数据库操作
  42. SaveToDatabase(ctx context.Context, req *v1.TcpForwardingRequest, require waf2.RequireResponse, tcpId int64, cdnOriginIds map[string]int64) (int, error)
  43. UpdateDatabaseRecords(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, require waf2.RequireResponse, ipData *model.TcpForwardingRule) error
  44. CleanupDatabaseRecords(ctx context.Context, id int) error
  45. // 工具函数
  46. ExtractIpsFromBackends(backends []string) []string
  47. }
  48. type aidedTcpService struct {
  49. *service.Service
  50. wafformatter waf2.WafFormatterService
  51. cdn flexCdn.CdnService
  52. proxy flexCdn.ProxyService
  53. globalRep waf.GlobalLimitRepository
  54. tcpRepository waf.TcpforwardingRepository
  55. }
  56. func NewAidedTcpService(
  57. service *service.Service,
  58. wafformatter waf2.WafFormatterService,
  59. cdn flexCdn.CdnService,
  60. proxy flexCdn.ProxyService,
  61. globalRep waf.GlobalLimitRepository,
  62. tcpRepository waf.TcpforwardingRepository,
  63. ) AidedTcpService {
  64. return &aidedTcpService{
  65. Service: service,
  66. wafformatter: wafformatter,
  67. cdn: cdn,
  68. proxy: proxy,
  69. globalRep: globalRep,
  70. tcpRepository: tcpRepository,
  71. }
  72. }
  73. // ValidateAddRequest 验证添加 TCP 转发请求的合法性
  74. // 该函数验证以下内容:
  75. // 1. 验证 WAF 端口数量限制(防止超出配额)
  76. // 2. 验证端口号是否已被占用(确保端口唯一性)
  77. func (s *aidedTcpService) ValidateAddRequest(ctx context.Context, req *v1.TcpForwardingRequest, require waf2.RequireResponse) error {
  78. // 验证端口数量限制
  79. if err := s.wafformatter.ValidateWafPortCount(ctx, require.HostId); err != nil {
  80. return fmt.Errorf("端口数量验证失败: %w", err)
  81. }
  82. // 验证端口占用情况
  83. if err := s.wafformatter.VerifyPort(ctx, "tcp", int64(req.TcpForwardingData.Id), req.TcpForwardingData.Port, int64(require.HostId), ""); err != nil {
  84. return fmt.Errorf("端口 %d 已被占用或不合法: %w", req.TcpForwardingData.Port, err)
  85. }
  86. return nil
  87. }
  88. // ValidateEditRequest 验证编辑 TCP 转发请求的合法性
  89. // 该函数仅在端口发生变更时才验证端口冲突,提高性能并避免不必要的检查
  90. func (s *aidedTcpService) ValidateEditRequest(ctx context.Context, req *v1.TcpForwardingRequest, require waf2.RequireResponse, oldData *model.Tcpforwarding) error {
  91. // 只有端口发生变更时才需要验证端口冲突
  92. if oldData.Port != req.TcpForwardingData.Port {
  93. if err := s.wafformatter.VerifyPort(ctx, "tcp", int64(req.TcpForwardingData.Id), req.TcpForwardingData.Port, int64(require.HostId), ""); err != nil {
  94. return fmt.Errorf("新端口 %d 已被占用或不合法: %w", req.TcpForwardingData.Port, err)
  95. }
  96. }
  97. return nil
  98. }
  99. // ValidateDeletePermission 验证删除 TCP 转发配置的权限
  100. // 该函数确保用户只能删除属于自己主机的配置,防止越权操作
  101. func (s *aidedTcpService) ValidateDeletePermission(oldData *model.Tcpforwarding, hostId int) error {
  102. if oldData == nil {
  103. return fmt.Errorf("TCP转发配置数据不存在")
  104. }
  105. if oldData.HostId != hostId {
  106. return fmt.Errorf("用户权限不足,无法删除不属于自己主机的配置(主机ID: %d)", hostId)
  107. }
  108. return nil
  109. }
  110. // CreateCdnWebsite 在 CDN 系统中创建 TCP 代理网站
  111. // 该函数调用 CDN 服务接口创建一个新的 TCP 代理网站,返回网站ID用于后续配置
  112. func (s *aidedTcpService) CreateCdnWebsite(ctx context.Context, formData v1.WebsiteSend) (int64, error) {
  113. tcpId, err := s.cdn.CreateWebsite(ctx, formData)
  114. if err != nil {
  115. return 0, fmt.Errorf("在CDN系统中创建 TCP 代理网站失败: %w", err)
  116. }
  117. if tcpId <= 0 {
  118. return 0, fmt.Errorf("CDN系统返回了无效的网站ID: %d", tcpId)
  119. }
  120. return tcpId, nil
  121. }
  122. // UpdateCdnConfiguration 更新 CDN 系统中的 TCP 代理配置
  123. // 该函数根据变更内容智能更新:
  124. // 1. 端口变更 - 更新网站监听配置
  125. // 2. 名称变更 - 更新网站基本信息
  126. // 3. 代理协议变更 - 开启/关闭 Proxy Protocol
  127. func (s *aidedTcpService) UpdateCdnConfiguration(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, require waf2.RequireResponse, formData v1.WebsiteSend) error {
  128. // 更新网站端口
  129. if oldData.Port != req.TcpForwardingData.Port {
  130. if err := s.cdn.EditServerType(ctx, v1.EditWebsite{
  131. Id: int64(oldData.CdnWebId),
  132. TypeJSON: formData.TcpJSON,
  133. }, "tcp"); err != nil {
  134. return fmt.Errorf("更新网站端口失败: %w", err)
  135. }
  136. }
  137. // 更新网站名称
  138. if oldData.Comment != req.TcpForwardingData.Comment {
  139. nodeId, err := s.globalRep.GetNodeId(ctx, oldData.CdnWebId)
  140. if err != nil {
  141. return fmt.Errorf("获取节点ID失败: %w", err)
  142. }
  143. if err := s.cdn.EditServerBasic(ctx, int64(oldData.CdnWebId), require.Tag, nodeId); err != nil {
  144. return fmt.Errorf("更新网站名称失败: %w", err)
  145. }
  146. }
  147. // 更新代理协议
  148. if oldData.Proxy != req.TcpForwardingData.Proxy {
  149. if err := s.proxy.EditProxy(ctx, int64(oldData.CdnWebId), v1.ProxyProtocolJSON{
  150. IsOn: req.TcpForwardingData.Proxy,
  151. Version: 1,
  152. }); err != nil {
  153. return fmt.Errorf("更新代理协议失败: %w", err)
  154. }
  155. }
  156. return nil
  157. }
  158. // DeleteCdnServer 从 CDN 系统中删除 TCP 代理网站
  159. // 该函数删除 CDN 中的网站配置,释放端口资源和相关配置
  160. func (s *aidedTcpService) DeleteCdnServer(ctx context.Context, cdnWebId int) error {
  161. if err := s.cdn.DelServer(ctx, int64(cdnWebId)); err != nil {
  162. return fmt.Errorf("删除CDN服务器失败: %w", err)
  163. }
  164. return nil
  165. }
  166. // AddOriginsToWebsite 为 TCP 代理网站添加后端源站
  167. // 该函数执行以下操作:
  168. // 1. 批量创建后端服务器的源站记录
  169. // 2. 将源站关联到 TCP 代理网站
  170. // 3. 返回后端地址与源站ID的映射关系
  171. func (s *aidedTcpService) AddOriginsToWebsite(ctx context.Context, req *v1.TcpForwardingRequest, tcpId int64) (map[string]int64, error) {
  172. cdnOriginIds := make(map[string]int64)
  173. // 批量创建源站
  174. for _, backend := range req.TcpForwardingData.BackendList {
  175. id, err := s.wafformatter.AddOrigin(ctx, v1.WebJson{
  176. ApiType: "tcp",
  177. BackendList: backend,
  178. Comment: req.TcpForwardingData.Comment,
  179. })
  180. if err != nil {
  181. return nil, fmt.Errorf("添加源站失败 %s: %w", backend, err)
  182. }
  183. cdnOriginIds[backend] = id
  184. }
  185. // 批量关联源站到网站
  186. for _, originId := range cdnOriginIds {
  187. if err := s.cdn.AddServerOrigin(ctx, tcpId, originId); err != nil {
  188. return nil, fmt.Errorf("关联源站到网站失败: %w", err)
  189. }
  190. }
  191. return cdnOriginIds, nil
  192. }
  193. // UpdateOriginServers 更新 TCP 代理的后端源站配置
  194. // 该函数智能对比新旧后端列表,只处理变更的部分:
  195. // 1. 添加新增的后端服务器
  196. // 2. 删除不再需要的后端服务器
  197. // 3. 更新源站ID映射关系
  198. func (s *aidedTcpService) UpdateOriginServers(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, ipData *model.TcpForwardingRule) error {
  199. addOrigins, delOrigins := s.wafformatter.FindIpDifferences(ipData.BackendList, req.TcpForwardingData.BackendList)
  200. // 添加新源站
  201. addedIds := make(map[string]int64)
  202. for _, origin := range addOrigins {
  203. id, err := s.wafformatter.AddOrigin(ctx, v1.WebJson{
  204. ApiType: "tcp",
  205. BackendList: origin,
  206. Comment: req.TcpForwardingData.Comment,
  207. })
  208. if err != nil {
  209. return fmt.Errorf("添加源站失败 %s: %w", origin, err)
  210. }
  211. addedIds[origin] = id
  212. }
  213. // 关联新源站到网站
  214. for _, originId := range addedIds {
  215. if err := s.cdn.AddServerOrigin(ctx, int64(oldData.CdnWebId), originId); err != nil {
  216. return fmt.Errorf("关联源站到网站失败: %w", err)
  217. }
  218. }
  219. // 更新源站ID映射
  220. maps.Copy(ipData.CdnOriginIds, addedIds)
  221. // 删除不需要的源站
  222. for backend, originId := range ipData.CdnOriginIds {
  223. for _, delOrigin := range delOrigins {
  224. if backend == delOrigin {
  225. if err := s.cdn.DelServerOrigin(ctx, int64(oldData.CdnWebId), originId); err != nil {
  226. return fmt.Errorf("删除源站失败: %w", err)
  227. }
  228. delete(ipData.CdnOriginIds, backend)
  229. break
  230. }
  231. }
  232. }
  233. return nil
  234. }
  235. // ConfigureProxyProtocol 配置 TCP 代理的 Proxy Protocol 协议
  236. // 该函数根据请求参数决定是否开启 Proxy Protocol,用于传递客户端真实IP
  237. func (s *aidedTcpService) ConfigureProxyProtocol(ctx context.Context, req *v1.TcpForwardingRequest, tcpId int64) error {
  238. // 如果不需要开启 Proxy Protocol,直接返回
  239. if !req.TcpForwardingData.Proxy {
  240. return nil
  241. }
  242. // 配置 Proxy Protocol v1
  243. if err := s.proxy.EditProxy(ctx, tcpId, v1.ProxyProtocolJSON{
  244. IsOn: true,
  245. Version: 1,
  246. }); err != nil {
  247. return fmt.Errorf("开启 Proxy Protocol 失败: %w", err)
  248. }
  249. return nil
  250. }
  251. // ProcessAsyncTasks 处理 TCP 转发相关的异步任务
  252. // 该函数主要处理新增后端服务器时的IP白名单添加任务,确保后端服务器能正常访问
  253. func (s *aidedTcpService) ProcessAsyncTasks(req *v1.TcpForwardingRequest) {
  254. // 检查是否有后端服务器需要处理
  255. if req == nil || len(req.TcpForwardingData.BackendList) == 0 {
  256. return
  257. }
  258. // 提取后端 IP 地址
  259. ips := s.ExtractIpsFromBackends(req.TcpForwardingData.BackendList)
  260. if len(ips) > 0 {
  261. // 异步添加到白名单
  262. go s.wafformatter.PublishIpWhitelistTask(ips, "add", "", "white")
  263. }
  264. }
  265. // ProcessIpWhitelistChanges 处理 TCP 转发编辑时的IP白名单变更
  266. // 该函数对比新旧后端列表,智能处理IP白名单:
  267. // 1. 将新增的后端 IP 加入白名单
  268. // 2. 将不再使用的后端 IP 从白名单移除
  269. func (s *aidedTcpService) ProcessIpWhitelistChanges(ctx context.Context, req *v1.TcpForwardingRequest, ipData *model.TcpForwardingRule) error {
  270. addedIps, removedIps, err := s.wafformatter.WashEditWafIp(ctx, req.TcpForwardingData.BackendList, ipData.BackendList)
  271. if err != nil {
  272. return fmt.Errorf("处理IP变更失败: %w", err)
  273. }
  274. // 处理新增IP
  275. if len(addedIps) > 0 {
  276. go s.wafformatter.PublishIpWhitelistTask(addedIps, "add", "", "white")
  277. }
  278. // 处理移除IP
  279. if len(removedIps) > 0 {
  280. ipsToDelist, err := s.wafformatter.WashDelIps(ctx, removedIps)
  281. if err != nil {
  282. return fmt.Errorf("处理移除IP失败: %w", err)
  283. }
  284. if len(ipsToDelist) > 0 {
  285. go s.wafformatter.PublishIpWhitelistTask(ipsToDelist, "del", "0", "white")
  286. }
  287. }
  288. return nil
  289. }
  290. // ProcessDeleteIpWhitelist 处理 TCP 转发删除时的IP白名单清理
  291. // 该函数在删除 TCP 转发配置时,智能清理不再需要的后端 IP 白名单条目
  292. func (s *aidedTcpService) ProcessDeleteIpWhitelist(ctx context.Context, id int) error {
  293. ipData, err := s.tcpRepository.GetTcpForwardingIpsByID(ctx, id)
  294. if err != nil {
  295. return fmt.Errorf("获取IP数据失败: %w", err)
  296. }
  297. if ipData == nil || len(ipData.BackendList) == 0 {
  298. return nil
  299. }
  300. ips, err := s.wafformatter.WashDeleteWafIp(ctx, ipData.BackendList)
  301. if err != nil {
  302. return fmt.Errorf("处理删除IP失败: %w", err)
  303. }
  304. if len(ips) > 0 {
  305. ipsToDelist, err := s.wafformatter.WashDelIps(ctx, ips)
  306. if err != nil {
  307. return fmt.Errorf("清理IP列表失败: %w", err)
  308. }
  309. if len(ipsToDelist) > 0 {
  310. go s.wafformatter.PublishIpWhitelistTask(ipsToDelist, "del", "0", "white")
  311. }
  312. }
  313. return nil
  314. }
  315. // ExtractIpsFromBackends 从后端服务器地址列表中提取纯IP地址
  316. // 该函数解析 "IP:端口" 格式的后端地址,提取出纯IP地址用于白名单处理
  317. func (s *aidedTcpService) ExtractIpsFromBackends(backends []string) []string {
  318. var ips []string
  319. for _, backend := range backends {
  320. // 跳过空字符串
  321. if backend == "" {
  322. continue
  323. }
  324. // 解析 "IP:端口" 格式
  325. if ip, _, err := net.SplitHostPort(backend); err == nil && ip != "" {
  326. ips = append(ips, ip)
  327. }
  328. }
  329. return ips
  330. }
  331. // PrepareWafData 准备WAF配置数据
  332. // 该函数的作用是根据请求参数准备创建CDN网站所需的所有配置数据,包括:
  333. // 1. 获取全局配置信息(用户、主机、网关等)
  334. // 2. 构建 TCP 代理的监听配置 JSON
  335. // 3. 组装 CDN 创建网站的表单数据
  336. func (s *aidedTcpService) PrepareWafData(ctx context.Context, req *v1.TcpForwardingRequest) (waf2.RequireResponse, v1.WebsiteSend, error) {
  337. // 获取全局配置信息,包括用户信息、网关IP等
  338. require, err := s.wafformatter.Require(ctx, v1.GlobalRequire{
  339. HostId: req.HostId,
  340. Uid: req.Uid,
  341. Comment: req.TcpForwardingData.Comment,
  342. })
  343. if err != nil {
  344. return waf2.RequireResponse{}, v1.WebsiteSend{}, fmt.Errorf("获取全局配置信息失败: %w", err)
  345. }
  346. // 验证实例配置是否完整
  347. if require.Uid == 0 {
  348. return waf2.RequireResponse{}, v1.WebsiteSend{}, fmt.Errorf("请先配置实例,确保用户信息和网关配置正确")
  349. }
  350. // 构建 TCP 监听配置
  351. tcpConfig, err := s.BuildTcpListenConfig(require.GatewayIps, req.TcpForwardingData.Port)
  352. if err != nil {
  353. return waf2.RequireResponse{}, v1.WebsiteSend{}, fmt.Errorf("构建 TCP 监听配置失败: %w", err)
  354. }
  355. // 组装 CDN 创建网站的表单数据
  356. formData := v1.WebsiteSend{
  357. UserId: int64(require.CdnUid),
  358. Type: "tcpProxy",
  359. Name: require.Tag,
  360. Description: req.TcpForwardingData.Comment,
  361. TcpJSON: tcpConfig,
  362. ServerGroupIds: []int64{int64(require.GroupId)},
  363. NodeClusterId: 2, // 默认节点集群ID
  364. }
  365. return require, formData, nil
  366. }
  367. // BuildTcpListenConfig 构建 TCP 监听配置 JSON
  368. // 该函数将网关IP列表和端口转换为 CDN 所需的 JSON 配置格式
  369. func (s *aidedTcpService) BuildTcpListenConfig(gatewayIps []string, portStr string) ([]byte, error) {
  370. if len(gatewayIps) == 0 {
  371. return nil, fmt.Errorf("网关IP列表不能为空")
  372. }
  373. // 验证端口字符串不为空
  374. if portStr == "" {
  375. return nil, fmt.Errorf("端口号不能为空")
  376. }
  377. // 转换端口字符串为整数并验证范围
  378. port, err := strconv.Atoi(portStr)
  379. if err != nil {
  380. return nil, fmt.Errorf("端口号格式不合法: %s", portStr)
  381. }
  382. if port <= 0 || port > 65535 {
  383. return nil, fmt.Errorf("端口号不合法: %d,必须在 1-65535 范围内", port)
  384. }
  385. // 构建监听配置
  386. var listenConfigs []v1.Listen
  387. for _, gatewayIp := range gatewayIps {
  388. if gatewayIp == "" {
  389. continue // 跳过空的IP
  390. }
  391. listenConfigs = append(listenConfigs, v1.Listen{
  392. Protocol: "tcp",
  393. Host: gatewayIp,
  394. Port: portStr,
  395. })
  396. }
  397. if len(listenConfigs) == 0 {
  398. return nil, fmt.Errorf("没有有效的网关IP配置")
  399. }
  400. // 组装最终的 JSON 配置
  401. tcpJSON := v1.TypeJSON{
  402. IsOn: true,
  403. Listen: listenConfigs,
  404. }
  405. byteData, err := json.Marshal(tcpJSON)
  406. if err != nil {
  407. return nil, fmt.Errorf("序列化 TCP 配置失败: %w", err)
  408. }
  409. return byteData, nil
  410. }
  411. // BuildTcpForwardingModel 构建 TCP 转发主记录模型
  412. // 该函数将请求数据转换为数据库模型,包含主机、CDN网站、端口等信息
  413. func (s *aidedTcpService) BuildTcpForwardingModel(req *v1.TcpForwardingDataRequest, ruleId int, require waf2.RequireResponse) *model.Tcpforwarding {
  414. return &model.Tcpforwarding{
  415. HostId: require.HostId,
  416. CdnWebId: ruleId,
  417. Port: req.Port,
  418. Comment: req.Comment,
  419. Proxy: req.Proxy,
  420. }
  421. }
  422. // BuildTcpRuleModel 构建 TCP 转发规则记录模型
  423. // 该函数构建包含后端服务器列表和 CDN 源站ID映射的规则记录
  424. func (s *aidedTcpService) BuildTcpRuleModel(reqData *v1.TcpForwardingDataRequest, require waf2.RequireResponse, localDbId int, cdnOriginIds map[string]int64) *model.TcpForwardingRule {
  425. return &model.TcpForwardingRule{
  426. Uid: require.Uid,
  427. HostId: require.HostId,
  428. TcpId: localDbId,
  429. CdnOriginIds: cdnOriginIds,
  430. BackendList: reqData.BackendList,
  431. }
  432. }
  433. // SaveToDatabase 保存 TCP 转发配置到数据库
  434. // 该函数分别保存主记录(基本信息)和规则记录(后端服务器和源站ID映射)
  435. func (s *aidedTcpService) SaveToDatabase(ctx context.Context, req *v1.TcpForwardingRequest, require waf2.RequireResponse, tcpId int64, cdnOriginIds map[string]int64) (int, error) {
  436. // 保存主记录
  437. tcpModel := s.BuildTcpForwardingModel(&req.TcpForwardingData, int(tcpId), require)
  438. id, err := s.tcpRepository.AddTcpforwarding(ctx, tcpModel)
  439. if err != nil {
  440. return 0, fmt.Errorf("保存TCP转发记录失败: %w", err)
  441. }
  442. // 保存规则记录
  443. tcpRuleModel := s.BuildTcpRuleModel(&req.TcpForwardingData, require, id, cdnOriginIds)
  444. if _, err := s.tcpRepository.AddTcpforwardingIps(ctx, *tcpRuleModel); err != nil {
  445. return 0, fmt.Errorf("保存TCP转发规则失败: %w", err)
  446. }
  447. return id, nil
  448. }
  449. // UpdateDatabaseRecords 更新数据库记录
  450. // 该函数更新 TCP 转发的主记录和规则记录,同步最新的配置变更
  451. func (s *aidedTcpService) UpdateDatabaseRecords(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, require waf2.RequireResponse, ipData *model.TcpForwardingRule) error {
  452. // 更新主记录
  453. tcpModel := s.BuildTcpForwardingModel(&req.TcpForwardingData, oldData.CdnWebId, require)
  454. tcpModel.Id = req.TcpForwardingData.Id
  455. if err := s.tcpRepository.EditTcpforwarding(ctx, tcpModel); err != nil {
  456. return fmt.Errorf("更新TCP转发记录失败: %w", err)
  457. }
  458. // 更新规则记录
  459. tcpRuleModel := s.BuildTcpRuleModel(&req.TcpForwardingData, require, req.TcpForwardingData.Id, ipData.CdnOriginIds)
  460. if err := s.tcpRepository.EditTcpforwardingIps(ctx, *tcpRuleModel); err != nil {
  461. return fmt.Errorf("更新TCP转发规则失败: %w", err)
  462. }
  463. return nil
  464. }
  465. // CleanupDatabaseRecords 清理数据库记录
  466. // 该函数删除 TCP 转发相关的所有数据库记录,包括主记录和规则记录
  467. func (s *aidedTcpService) CleanupDatabaseRecords(ctx context.Context, id int) error {
  468. if err := s.tcpRepository.DeleteTcpforwarding(ctx, int64(id)); err != nil {
  469. return fmt.Errorf("删除TCP转发主记录失败: %w", err)
  470. }
  471. if err := s.tcpRepository.DeleteTcpForwardingIpsById(ctx, id); err != nil {
  472. return fmt.Errorf("删除TCP转发规则记录失败: %w", err)
  473. }
  474. return nil
  475. }