gameshieldbackend.go 11 KB

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