1
0

nord.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. package service
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "time"
  8. "github.com/mhsanaei/3x-ui/v3/util/common"
  9. )
  10. type NordService struct {
  11. SettingService
  12. }
  13. var nordHTTPClient = &http.Client{Timeout: 15 * time.Second}
  14. // maxResponseSize limits the maximum size of NordVPN API responses (10 MB).
  15. const maxResponseSize = 10 << 20
  16. func (s *NordService) GetCountries() (string, error) {
  17. resp, err := nordHTTPClient.Get("https://api.nordvpn.com/v1/countries")
  18. if err != nil {
  19. return "", err
  20. }
  21. defer resp.Body.Close()
  22. if resp.StatusCode != http.StatusOK {
  23. return "", common.NewErrorf("NordVPN API error: %s", resp.Status)
  24. }
  25. body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
  26. if err != nil {
  27. return "", err
  28. }
  29. return string(body), nil
  30. }
  31. func (s *NordService) GetServers(countryId string) (string, error) {
  32. // Validate countryId is numeric to prevent URL injection
  33. for _, c := range countryId {
  34. if c < '0' || c > '9' {
  35. return "", common.NewError("invalid country ID")
  36. }
  37. }
  38. url := fmt.Sprintf("https://api.nordvpn.com/v2/servers?limit=0&filters[servers_technologies][id]=35&filters[country_id]=%s", countryId)
  39. resp, err := nordHTTPClient.Get(url)
  40. if err != nil {
  41. return "", err
  42. }
  43. defer resp.Body.Close()
  44. if resp.StatusCode != http.StatusOK {
  45. return "", common.NewErrorf("NordVPN API error: %s", resp.Status)
  46. }
  47. body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
  48. if err != nil {
  49. return "", err
  50. }
  51. var data map[string]any
  52. if err := json.Unmarshal(body, &data); err != nil {
  53. return string(body), nil
  54. }
  55. servers, ok := data["servers"].([]any)
  56. if !ok {
  57. return string(body), nil
  58. }
  59. var filtered []any
  60. for _, s := range servers {
  61. if server, ok := s.(map[string]any); ok {
  62. if load, ok := server["load"].(float64); ok && load > 7 {
  63. filtered = append(filtered, s)
  64. }
  65. }
  66. }
  67. data["servers"] = filtered
  68. result, _ := json.Marshal(data)
  69. return string(result), nil
  70. }
  71. func (s *NordService) SetKey(privateKey string) (string, error) {
  72. if privateKey == "" {
  73. return "", common.NewError("private key cannot be empty")
  74. }
  75. nordData := map[string]string{
  76. "private_key": privateKey,
  77. "token": "",
  78. }
  79. data, _ := json.Marshal(nordData)
  80. err := s.SettingService.SetNord(string(data))
  81. if err != nil {
  82. return "", err
  83. }
  84. return string(data), nil
  85. }
  86. func (s *NordService) GetCredentials(token string) (string, error) {
  87. url := "https://api.nordvpn.com/v1/users/services/credentials"
  88. req, err := http.NewRequest("GET", url, nil)
  89. if err != nil {
  90. return "", err
  91. }
  92. req.SetBasicAuth("token", token)
  93. resp, err := nordHTTPClient.Do(req)
  94. if err != nil {
  95. return "", err
  96. }
  97. defer resp.Body.Close()
  98. if resp.StatusCode != http.StatusOK {
  99. return "", common.NewErrorf("NordVPN API error: %s", resp.Status)
  100. }
  101. body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
  102. if err != nil {
  103. return "", err
  104. }
  105. var creds map[string]any
  106. if err := json.Unmarshal(body, &creds); err != nil {
  107. return "", err
  108. }
  109. privateKey, ok := creds["nordlynx_private_key"].(string)
  110. if !ok || privateKey == "" {
  111. return "", common.NewError("failed to retrieve NordLynx private key")
  112. }
  113. nordData := map[string]string{
  114. "private_key": privateKey,
  115. "token": token,
  116. }
  117. data, _ := json.Marshal(nordData)
  118. err = s.SettingService.SetNord(string(data))
  119. if err != nil {
  120. return "", err
  121. }
  122. return string(data), nil
  123. }
  124. func (s *NordService) GetNordData() (string, error) {
  125. return s.SettingService.GetNord()
  126. }
  127. func (s *NordService) DelNordData() error {
  128. return s.SettingService.SetNord("")
  129. }