gameshieldbackend.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/AlekSi/pointer"
  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/viper"
  10. "strconv"
  11. "strings"
  12. )
  13. type GameShieldBackendService interface {
  14. GetGameShieldBackend(ctx context.Context, id int64) (*model.GameShieldBackend, error)
  15. GameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, int, error)
  16. AddGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error)
  17. EditGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error)
  18. DeleteGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error)
  19. GetGameShieldRequired(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (*v1.GetGameShieldRequiredResponse, int, error)
  20. }
  21. func NewGameShieldBackendService(
  22. service *Service,
  23. gameShieldBackendRepository repository.GameShieldBackendRepository,
  24. gameShieldRepository repository.GameShieldRepository,
  25. crawlerService CrawlerService,
  26. gameShieldPublicIpService GameShieldPublicIpService,
  27. duedate DuedateService,
  28. formatter FormatterService,
  29. parser ParserService,
  30. required RequiredService,
  31. conf *viper.Viper,
  32. shieldService GameShieldService,
  33. hostService HostService,
  34. ) GameShieldBackendService {
  35. return &gameShieldBackendService{
  36. Service: service,
  37. gameShieldBackendRepository: gameShieldBackendRepository,
  38. gameShieldRepository: gameShieldRepository,
  39. crawlerService: crawlerService,
  40. gameShieldPublicIpService: gameShieldPublicIpService,
  41. duedate: duedate,
  42. formatter: formatter,
  43. parser: parser,
  44. required: required,
  45. Url: conf.GetString("crawler.Url"),
  46. shieldService: shieldService,
  47. hostService: hostService,
  48. }
  49. }
  50. type gameShieldBackendService struct {
  51. *Service
  52. gameShieldBackendRepository repository.GameShieldBackendRepository
  53. crawlerService CrawlerService
  54. gameShieldRepository repository.GameShieldRepository
  55. gameShieldPublicIpService GameShieldPublicIpService
  56. duedate DuedateService
  57. formatter FormatterService
  58. Url string
  59. parser ParserService
  60. required RequiredService
  61. shieldService GameShieldService
  62. hostService HostService
  63. }
  64. func (s *gameShieldBackendService) GetGameShieldRequired(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (*v1.GetGameShieldRequiredResponse, int, error) {
  65. var res v1.GetGameShieldRequiredResponse
  66. var err error
  67. var count int
  68. if req.Uid == 0 {
  69. return nil, 0, fmt.Errorf("uid is required")
  70. }
  71. res.ExpiredAt, err = s.duedate.NextDueDate(ctx, req.Uid, req.HostId)
  72. if err != nil {
  73. return nil, 0, err
  74. }
  75. gameShield, err := s.gameShieldRepository.GetGameShieldByHostId(ctx, req.HostId)
  76. if err != nil {
  77. return nil, 0, err
  78. }
  79. res.RuleId = gameShield.RuleId
  80. oldBackend, err := s.gameShieldBackendRepository.GetGameShieldBackendByHostId(ctx, req.HostId)
  81. if err != nil {
  82. return nil, 0, err
  83. }
  84. if len(oldBackend) != 0 {
  85. count = oldBackend[0].KeySort
  86. }
  87. OldBackend, err := s.formatter.OldFormat(ctx, &oldBackend)
  88. res.Backend, err = s.formatter.FormatBackendData(ctx, req, OldBackend, count)
  89. if err != nil {
  90. return nil, 0, err
  91. }
  92. res.Cookie, err = s.crawlerService.GetLoginCookie(ctx)
  93. if err != nil {
  94. return nil, 0, err
  95. }
  96. res.DunName = gameShield.DunName
  97. return &res, count, nil
  98. }
  99. func (s *gameShieldBackendService) GetGameShieldBackend(ctx context.Context, id int64) (*model.GameShieldBackend, error) {
  100. res, err := s.gameShieldBackendRepository.GetGameShieldBackendById(ctx, id)
  101. if err != nil {
  102. return nil, err
  103. }
  104. return res, nil
  105. }
  106. func (s *gameShieldBackendService) GameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, int, error) {
  107. require, count, err := s.GetGameShieldRequired(ctx, req)
  108. if err != nil {
  109. return "", 0, err
  110. }
  111. tokenUrl := s.Url + "admin/info/rule/edit?&__goadmin_edit_pk=" + strconv.Itoa(require.RuleId) + "_" + require.DunName
  112. tokens, err := s.crawlerService.GetFormTokens(ctx, tokenUrl, require.Cookie)
  113. if err != nil {
  114. return "", 0, err
  115. }
  116. formData := map[string]interface{}{
  117. "app_name": require.DunName,
  118. "gateway_group_id": 4,
  119. "backend": require.Backend,
  120. "rule_id": require.RuleId,
  121. "expired_at": require.ExpiredAt,
  122. "max_device_count": "max-bandwidth 1000K",
  123. "sdk_args": "",
  124. "__go_admin_previous_": tokens["previous"],
  125. "__go_admin_t_": tokens["t"],
  126. }
  127. sendUrl := s.Url + "admin/edit/rule"
  128. respBody, err := s.crawlerService.SendFormData(ctx, sendUrl, require.Cookie, formData)
  129. if err != nil {
  130. return "", 0, err
  131. }
  132. // 解析响应内容中的 alert 消息
  133. res, err := s.parser.ParseAlert(string(respBody))
  134. if err != nil {
  135. return "", 0, err
  136. }
  137. if res != "" {
  138. return "", 0, fmt.Errorf(res)
  139. }
  140. KeyAndField, err := s.required.GetKeyAndField(ctx, require.DunName, "rule_id")
  141. if err != nil {
  142. return "", 0, err
  143. }
  144. timeBase, err := s.gameShieldRepository.GetGameShieldNextduedate(ctx, int64(req.Uid), req.HostId)
  145. if err != nil {
  146. return "", 0, err
  147. }
  148. timestampSec, err := strconv.ParseInt(timeBase, 10, 64)
  149. if err != nil {
  150. return "", 0, err
  151. }
  152. if err := s.gameShieldRepository.UpdateGameShieldByHostId(ctx, &model.GameShield{HostId: req.HostId, Key: KeyAndField.Key, ExpireTime: timestampSec}); err != nil {
  153. return "", 0, err
  154. }
  155. return res, count, nil
  156. }
  157. func (s *gameShieldBackendService) AddGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error) {
  158. res, count, err := s.GameShieldBackend(ctx, req)
  159. if err != nil {
  160. return "", err
  161. }
  162. saveData, err := s.formatter.TidyFormatBackendData(ctx, req, count)
  163. if err != nil {
  164. return "", err
  165. }
  166. if err := s.SaveGameShieldBackend(ctx, saveData, req.HostId); err != nil {
  167. return "", err
  168. }
  169. return res, nil
  170. }
  171. func (s *gameShieldBackendService) EditGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error) {
  172. // 1. 获取当前所有数据库记录
  173. currentData, err := s.gameShieldBackendRepository.GetGameShieldBackendByHostId(ctx, req.HostId)
  174. if err != nil {
  175. return "", fmt.Errorf("获取当前配置失败: %w", err)
  176. }
  177. // 2. 创建当前记录的副本用于模拟修改
  178. simulatedData := make([]model.GameShieldBackend, len(currentData))
  179. copy(simulatedData, currentData)
  180. // 3. 创建ID到索引的映射,方便查找和修改
  181. idToIndex := make(map[int64]int)
  182. for i, item := range simulatedData {
  183. idToIndex[int64(item.Id)] = i
  184. }
  185. // 4. 在副本上应用修改
  186. for _, v := range req.Items {
  187. if v.Id == 0 {
  188. return "", fmt.Errorf("id 不能为空")
  189. }
  190. // 查找对应记录
  191. idx, exists := idToIndex[int64(v.Id)]
  192. if !exists {
  193. return "", fmt.Errorf("ID为%d的记录不存在", v.Id)
  194. }
  195. // 更新记录(只更新需要修改的字段)
  196. simulatedData[idx].SourceMachineIP = v.SourceMachineIP
  197. simulatedData[idx].ConnectPort = v.ConnectPort
  198. simulatedData[idx].Protocol = v.Protocol
  199. simulatedData[idx].SdkPort = v.SdkPort
  200. simulatedData[idx].SdkIp = v.SdkIp
  201. simulatedData[idx].Type = v.Type
  202. //获取指针的值
  203. simulatedData[idx].MaxBandwidth = pointer.GetInt(v.MaxBandwidth)
  204. }
  205. // 5. 使用模拟修改后的数据进行验证
  206. // 转换数据格式
  207. simulatedBackend, err := s.formatter.OldFormat(ctx, &simulatedData)
  208. if err != nil {
  209. return "", fmt.Errorf("格式化模拟数据失败: %w", err)
  210. }
  211. // 验证修改后的配置
  212. err = s.formatter.ValidateBackendData(ctx, simulatedBackend, req.HostId)
  213. if err != nil {
  214. return "", fmt.Errorf("验证失败: %w", err)
  215. }
  216. // 6. 验证通过,执行实际的数据库修改
  217. for _, v := range req.Items {
  218. if err := s.gameShieldBackendRepository.EditGameShieldBackend(ctx, &v); err != nil {
  219. return "", fmt.Errorf("修改数据失败(ID:%d): %w", v.Id, err)
  220. }
  221. }
  222. // 7. 更新远程配置
  223. res, _, err := s.GameShieldBackend(ctx, &v1.GameShieldBackendArrayRequest{
  224. HostId: req.HostId,
  225. Uid: req.Uid,
  226. Items: nil,
  227. })
  228. if err != nil {
  229. return "", fmt.Errorf("更新配置失败: %w", err)
  230. }
  231. return res, nil
  232. }
  233. func (s *gameShieldBackendService) DeleteGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error) {
  234. for _, v := range req.Items {
  235. if err := s.gameShieldBackendRepository.DeleteGameShieldBackend(ctx, int64(v.Id)); err != nil {
  236. return "", err
  237. }
  238. }
  239. res, _, err := s.GameShieldBackend(ctx, &v1.GameShieldBackendArrayRequest{HostId: req.HostId, Uid: req.Uid, Items: nil})
  240. if err != nil {
  241. return "", err
  242. }
  243. return res, nil
  244. }
  245. func (s *gameShieldBackendService) SaveGameShieldBackend(ctx context.Context, req map[string]v1.SendGameShieldBackend, hostId int) error {
  246. for k, v := range req {
  247. parts := strings.Split(v.Addr[0], ":")
  248. keyName := strings.Split(k, "key")[1]
  249. key, err := strconv.Atoi(keyName)
  250. if err != nil {
  251. return err
  252. }
  253. if v.Type != "pc" {
  254. v.SdkIp = "127.0.0.1"
  255. }
  256. if err := s.gameShieldBackendRepository.AddGameShieldBackend(ctx,
  257. &model.GameShieldBackend{
  258. HostId: hostId,
  259. KeySort: key,
  260. SourceMachineIP: parts[0],
  261. Protocol: v.Protocol,
  262. ProxyAddr: v.ProxyAddr,
  263. ConnectPort: parts[1],
  264. SdkIp: v.SdkIp,
  265. SdkPort: strconv.Itoa(v.SdkPort),
  266. Type: v.Type,
  267. // 可以添加其他字段
  268. }); err != nil {
  269. return err
  270. }
  271. }
  272. return nil
  273. }