gameshieldbackend.go 11 KB

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