nord.go 3.3 KB

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