You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

http_request.py 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import base64
  2. import hashlib
  3. import hmac
  4. import logging
  5. import os
  6. import time
  7. import httpx
  8. logger = logging.getLogger(__name__)
  9. class NacosHttpClient:
  10. def __init__(self):
  11. self.username = os.getenv("DIFY_ENV_NACOS_USERNAME")
  12. self.password = os.getenv("DIFY_ENV_NACOS_PASSWORD")
  13. self.ak = os.getenv("DIFY_ENV_NACOS_ACCESS_KEY")
  14. self.sk = os.getenv("DIFY_ENV_NACOS_SECRET_KEY")
  15. self.server = os.getenv("DIFY_ENV_NACOS_SERVER_ADDR", "localhost:8848")
  16. self.token: str | None = None
  17. self.token_ttl = 18000
  18. self.token_expire_time: float = 0
  19. def http_request(
  20. self, url: str, method: str = "GET", headers: dict[str, str] | None = None, params: dict[str, str] | None = None
  21. ) -> str:
  22. if headers is None:
  23. headers = {}
  24. if params is None:
  25. params = {}
  26. try:
  27. self._inject_auth_info(headers, params)
  28. response = httpx.request(method, url="http://" + self.server + url, headers=headers, params=params)
  29. response.raise_for_status()
  30. return response.text
  31. except httpx.RequestError as e:
  32. return f"Request to Nacos failed: {e}"
  33. def _inject_auth_info(self, headers: dict[str, str], params: dict[str, str], module: str = "config") -> None:
  34. headers.update({"User-Agent": "Nacos-Http-Client-In-Dify:v0.0.1"})
  35. if module == "login":
  36. return
  37. ts = str(int(time.time() * 1000))
  38. if self.ak and self.sk:
  39. sign_str = self.get_sign_str(params["group"], params["tenant"], ts)
  40. headers["Spas-AccessKey"] = self.ak
  41. headers["Spas-Signature"] = self.__do_sign(sign_str, self.sk)
  42. headers["timeStamp"] = ts
  43. if self.username and self.password:
  44. self.get_access_token(force_refresh=False)
  45. if self.token is not None:
  46. params["accessToken"] = self.token
  47. def __do_sign(self, sign_str: str, sk: str) -> str:
  48. return (
  49. base64.encodebytes(hmac.new(sk.encode(), sign_str.encode(), digestmod=hashlib.sha1).digest())
  50. .decode()
  51. .strip()
  52. )
  53. def get_sign_str(self, group: str, tenant: str, ts: str) -> str:
  54. sign_str = ""
  55. if tenant:
  56. sign_str = tenant + "+"
  57. if group:
  58. sign_str = sign_str + group + "+"
  59. sign_str += ts # Directly concatenate ts without conditional checks, because the nacos auth header forced it.
  60. return sign_str
  61. def get_access_token(self, force_refresh: bool = False) -> str | None:
  62. current_time = time.time()
  63. if self.token and not force_refresh and self.token_expire_time > current_time:
  64. return self.token
  65. params = {"username": self.username, "password": self.password}
  66. url = "http://" + self.server + "/nacos/v1/auth/login"
  67. try:
  68. resp = httpx.request("POST", url, headers=None, params=params)
  69. resp.raise_for_status()
  70. response_data = resp.json()
  71. self.token = response_data.get("accessToken")
  72. self.token_ttl = response_data.get("tokenTtl", 18000)
  73. self.token_expire_time = current_time + self.token_ttl - 10
  74. return self.token
  75. except Exception:
  76. logger.exception("[get-access-token] exception occur")
  77. raise