ddns 4.1.0b1__py2.py3-none-any.whl → 4.1.0b2__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ddns/provider/_base.py CHANGED
@@ -1,20 +1,18 @@
1
1
  # coding=utf-8
2
2
  """
3
3
  ## SimpleProvider 简单DNS抽象基类
4
-
5
4
  * set_record()
6
5
 
7
6
  ## BaseProvider 标准DNS抽象基类
8
7
  定义所有 DNS 服务商 API 类应继承的抽象基类,统一接口,便于扩展适配多服务商。
9
-
10
- Abstract base class for DNS provider APIs.
11
8
  Defines a unified interface to support extension and adaptation across providers.
12
9
  * _query_zone_id
13
10
  * _query_record
14
11
  * _update_record
15
12
  * _create_record
13
+
16
14
  ┌──────────────────────────────────────────────────┐
17
- 用户调用 set_record(domain, value...)
15
+ 调用 set_record(domain, value...)
18
16
  └──────────────────────────────────────────────────┘
19
17
 
20
18
 
@@ -58,121 +56,30 @@ Defines a unified interface to support extension and adaptation across providers
58
56
  """
59
57
 
60
58
  from abc import ABCMeta, abstractmethod
61
- from hashlib import sha256
62
- from hmac import HMAC
63
59
  from json import loads as jsondecode, dumps as jsonencode
64
60
  from logging import Logger, getLogger # noqa:F401 # type: ignore[no-redef]
65
- from os import environ
66
- from ..util.http import send_http_request
67
-
68
- try: # python 3
69
- from urllib.parse import quote, urlencode
70
- except ImportError: # python 2
71
- from urllib import urlencode, quote # type: ignore[no-redef,import-untyped]
61
+ from ..util.http import send_http_request, quote, urlencode
72
62
 
73
63
  TYPE_FORM = "application/x-www-form-urlencoded"
74
64
  TYPE_JSON = "application/json"
75
65
 
76
66
 
77
- def hmac_sha256(key, message):
78
- # type: (str | bytes, str | bytes) -> HMAC
79
- """
80
- 计算 HMAC-SHA256 签名对象
81
-
82
- Compute HMAC-SHA256 signature object.
83
-
84
- Args:
85
- key (str | bytes): 签名密钥 / Signing key
86
- message (str | bytes): 待签名消息 / Message to sign
87
-
88
- Returns:
89
- HMAC: HMAC签名对象,可调用.digest()获取字节或.hexdigest()获取十六进制字符串
90
- HMAC signature object, call .digest() for bytes or .hexdigest() for hex string
91
- """
92
- # Python 2/3 compatible encoding - avoid double encoding in Python 2
93
- if not isinstance(key, bytes):
94
- key = key.encode("utf-8")
95
- if not isinstance(message, bytes):
96
- message = message.encode("utf-8")
97
- return HMAC(key, message, sha256)
98
-
99
-
100
- def sha256_hash(data):
101
- # type: (str | bytes) -> str
67
+ def encode_params(params):
68
+ # type: (dict|list|str|bytes|None) -> str
102
69
  """
103
- 计算 SHA256 哈希值
104
-
105
- Compute SHA256 hash.
70
+ 编码参数为 URL 查询字符串,参数顺序会排序
106
71
 
107
72
  Args:
108
- data (str | bytes): 待哈希数据 / Data to hash
109
-
73
+ params (dict|list|str|bytes|None): 参数字典、列表或字符串
110
74
  Returns:
111
- str: 十六进制哈希字符串 / Hexadecimal hash string
75
+ str: 编码后的查询字符串
112
76
  """
113
- # Python 2/3 compatible encoding - avoid double encoding in Python 2
114
- if not isinstance(data, bytes):
115
- data = data.encode("utf-8")
116
- return sha256(data).hexdigest()
117
-
118
-
119
- def hmac_sha256_authorization(
120
- secret_key, # type: str | bytes
121
- method, # type: str
122
- path, # type: str
123
- query, # type: str
124
- headers, # type: dict[str, str]
125
- body_hash, # type: str
126
- signing_string_format, # type: str
127
- authorization_format, # type: str
128
- ):
129
- # type: (...) -> str
130
- """
131
- HMAC-SHA256 云服务商通用认证签名生成器
132
-
133
- Universal cloud provider authentication signature generator using HMAC-SHA256.
134
-
135
- 通用的云服务商API认证签名生成函数,使用HMAC-SHA256算法生成符合各云服务商规范的Authorization头部。
136
-
137
- 模板变量格式:{HashedCanonicalRequest}, {SignedHeaders}, {Signature}
138
-
139
- Args:
140
- secret_key (str | bytes): 签名密钥,已经过密钥派生处理 / Signing key (already derived if needed)
141
- method (str): HTTP请求方法 / HTTP request method (GET, POST, etc.)
142
- path (str): API请求路径 / API request path
143
- query (str): URL查询字符串 / URL query string
144
- headers (dict[str, str]): HTTP请求头部 / HTTP request headers
145
- body_hash (str): 请求体的SHA256哈希值 / SHA256 hash of request payload
146
- signing_string_format (str): 待签名字符串模板,包含 {HashedCanonicalRequest} 占位符
147
- authorization_format (str): Authorization头部模板,包含 {SignedHeaders}, {Signature} 占位符
148
-
149
- Returns:
150
- str: 完整的Authorization头部值 / Complete Authorization header value
151
- """
152
- # 1. 构建规范化头部 - 所有传入的头部都参与签名
153
- headers_to_sign = {k.lower(): str(v).strip() for k, v in headers.items()}
154
- signed_headers_list = sorted(headers_to_sign.keys())
155
-
156
- # 2. 构建规范请求字符串
157
- canonical_headers = ""
158
- for header_name in signed_headers_list:
159
- canonical_headers += "{}:{}\n".format(header_name, headers_to_sign[header_name])
160
-
161
- # 构建完整的规范请求字符串
162
- signed_headers = ";".join(signed_headers_list)
163
- canonical_request = "\n".join([method.upper(), path, query, canonical_headers, signed_headers, body_hash])
164
-
165
- # 3. 构建待签名字符串 - 只需要替换 HashedCanonicalRequest
166
- hashed_canonical_request = sha256_hash(canonical_request)
167
- string_to_sign = signing_string_format.format(HashedCanonicalRequest=hashed_canonical_request)
168
-
169
- # 4. 计算最终签名
170
- signature = hmac_sha256(secret_key, string_to_sign).hexdigest()
171
-
172
- # 5. 构建Authorization头部 - 只需要替换 SignedHeaders 和 Signature
173
- authorization = authorization_format.format(SignedHeaders=signed_headers, Signature=signature)
174
-
175
- return authorization
77
+ if not params:
78
+ return ""
79
+ elif isinstance(params, (str, bytes)):
80
+ return params # type: ignore[return-value]
81
+ items = params.items() if isinstance(params, dict) else params
82
+ return urlencode(sorted(items), doseq=True)
176
83
 
177
84
 
178
85
  class SimpleProvider(object):
@@ -188,43 +95,43 @@ class SimpleProvider(object):
188
95
  __metaclass__ = ABCMeta
189
96
 
190
97
  # API endpoint domain (to be defined in subclass)
191
- API = "" # type: str # https://exampledns.com
98
+ endpoint = "" # type: str # https://exampledns.com
192
99
  # Content-Type for requests (to be defined in subclass)
193
100
  content_type = TYPE_FORM # type: Literal["application/x-www-form-urlencoded"] | Literal["application/json"]
194
101
  # 默认 accept 头部, 空则不设置
195
102
  accept = TYPE_JSON # type: str | None
196
103
  # Decode Response as JSON by default
197
- decode_response = True
198
- # 是否验证 SSL 证书,默认为 True
199
- verify_ssl = "auto" # type: bool | str
200
-
201
- # 版本
202
- version = environ.get("DDNS_VERSION", "0.0.0")
104
+ decode_response = True # type: bool
105
+ # UA 可自定义, 可在子类中覆盖,空则不设置
106
+ user_agent = "DDNS/{version} (ddns@newfuture.cc)"
203
107
  # Description
204
- remark = "Managed by [DDNS v{}](https://ddns.newfuture.cc)".format(version)
108
+ remark = "Managed by [DDNS](https://ddns.newfuture.cc)"
205
109
 
206
- def __init__(self, auth_id, auth_token, logger=None, verify_ssl=None, **options):
207
- # type: (str, str, Logger | None, bool|str| None, **object) -> None
110
+ def __init__(self, id, token, logger=None, verify_ssl="auto", proxy=None, endpoint=None, **options):
111
+ # type: (str, str, Logger | None, bool|str, Sequence[str|None]|None, str|None, **object) -> None
208
112
  """
209
113
  初始化服务商对象
210
114
 
211
115
  Initialize provider instance.
212
116
 
213
117
  Args:
214
- auth_id (str): 身份认证 ID / Authentication ID
215
- auth_token (str): 密钥 / Authentication Token
216
- options (dict): 其它参数,如代理、调试等 / Additional options
118
+ id (str): 身份认证 ID / Authentication ID
119
+ token (str): 密钥 / Authentication Token
120
+ options (dict): 其它参数 / Additional options
217
121
  """
218
- self.auth_id = auth_id # type: str
219
- self.auth_token = auth_token # type: str
122
+ self.id = id
123
+ self.token = token
124
+ if endpoint:
125
+ self.endpoint = endpoint
126
+ self._proxy = proxy if proxy and len(proxy) else [None]
127
+ self._ssl = verify_ssl
128
+
220
129
  self.options = options
221
130
  name = self.__class__.__name__
222
131
  self.logger = (logger or getLogger()).getChild(name)
223
- self.proxy = None # type: str | None
224
- if verify_ssl is not None:
225
- self.verify_ssl = verify_ssl
132
+
226
133
  self._zone_map = {} # type: dict[str, str]
227
- self.logger.debug("%s initialized with: %s", self.__class__.__name__, auth_id)
134
+ self.logger.debug("%s initialized with: %s", self.__class__.__name__, id)
228
135
  self._validate() # 验证身份认证信息
229
136
 
230
137
  @abstractmethod
@@ -248,22 +155,6 @@ class SimpleProvider(object):
248
155
  """
249
156
  raise NotImplementedError("This set_record should be implemented by subclasses")
250
157
 
251
- def set_proxy(self, proxy_str):
252
- # type: (str | None) -> SimpleProvider
253
- """
254
- 设置代理服务器
255
-
256
- Set HTTPS proxy string.
257
-
258
- Args:
259
- proxy_str (str): 代理地址
260
-
261
- Returns:
262
- Self: 自身
263
- """
264
- self.proxy = proxy_str
265
- return self
266
-
267
158
  def _validate(self):
268
159
  # type: () -> None
269
160
  """
@@ -271,11 +162,11 @@ class SimpleProvider(object):
271
162
 
272
163
  Validate authentication credentials.
273
164
  """
274
- if not self.auth_id:
165
+ if not self.id:
275
166
  raise ValueError("id must be configured")
276
- if not self.auth_token:
167
+ if not self.token:
277
168
  raise ValueError("token must be configured")
278
- if not self.API:
169
+ if not self.endpoint:
279
170
  raise ValueError("API endpoint must be defined in {}".format(self.__class__.__name__))
280
171
 
281
172
  def _http(self, method, url, params=None, body=None, queries=None, headers=None): # noqa: C901
@@ -314,13 +205,13 @@ class SimpleProvider(object):
314
205
 
315
206
  # 构建查询字符串
316
207
  if len(query_params) > 0:
317
- url += ("&" if "?" in url else "?") + self._encode(query_params)
208
+ url += ("&" if "?" in url else "?") + encode_params(query_params)
318
209
 
319
210
  # 构建完整URL
320
211
  if not url.startswith("http://") and not url.startswith("https://"):
321
- if not url.startswith("/") and self.API.endswith("/"):
212
+ if not url.startswith("/") and self.endpoint.endswith("/"):
322
213
  url = "/" + url
323
- url = self.API + url
214
+ url = self.endpoint + url
324
215
 
325
216
  # 记录请求日志
326
217
  self.logger.info("%s %s", method, self._mask_sensitive_data(url))
@@ -330,30 +221,32 @@ class SimpleProvider(object):
330
221
  if body:
331
222
  if "content-type" not in headers:
332
223
  headers["content-type"] = self.content_type
333
- if isinstance(body, (str, bytes)):
334
- body_data = body
335
- elif self.content_type == TYPE_FORM:
336
- body_data = self._encode(body)
337
- else:
338
- body_data = jsonencode(body)
224
+ body_data = self._encode_body(body)
339
225
  self.logger.debug("body:\n%s", self._mask_sensitive_data(body_data))
340
226
 
341
227
  # 处理headers
342
228
  if self.accept and "accept" not in headers and "Accept" not in headers:
343
229
  headers["accept"] = self.accept
344
- if len(headers) > 2:
230
+ if "user-agent" not in headers and "User-Agent" not in headers and self.user_agent:
231
+ headers["user-agent"] = self.user_agent
232
+ if len(headers) > 3:
345
233
  self.logger.debug("headers:\n%s", {k: self._mask_sensitive_data(v) for k, v in headers.items()})
346
234
 
347
- response = send_http_request(
348
- url=url,
349
- method=method,
350
- body=body_data,
351
- headers=headers,
352
- proxy=self.proxy,
353
- max_redirects=5,
354
- verify_ssl=self.verify_ssl,
355
- )
356
-
235
+ response = None # type: Any
236
+ for p in self._proxy:
237
+ if p:
238
+ self.logger.debug("Using proxy: %s", p)
239
+ try:
240
+ response = send_http_request(
241
+ method, url, body=body_data, headers=headers, proxy=p, verify_ssl=self._ssl
242
+ )
243
+ break # 成功发送请求,跳出循环
244
+ except Exception as e:
245
+ self.logger.warning("Failed to send request: %s", e)
246
+ if not response:
247
+ if len(self._proxy) > 1:
248
+ self.logger.error("Failed to send request via all proxies: %s", self._proxy)
249
+ raise RuntimeError("Failed to send request to {}".format(url))
357
250
  # 处理响应
358
251
  status_code = response.status
359
252
  if not (200 <= status_code < 300):
@@ -368,7 +261,7 @@ class SimpleProvider(object):
368
261
  elif status_code == 401:
369
262
  raise RuntimeError("认证失败 [401]: " + response.reason)
370
263
  elif status_code == 403:
371
- raise RuntimeError("权限不足 [403]: " + response.reason)
264
+ raise RuntimeError("禁止访问 [403]: " + response.reason)
372
265
  else:
373
266
  raise RuntimeError("服务器错误 [{}]: {}".format(status_code, response.reason))
374
267
 
@@ -382,36 +275,23 @@ class SimpleProvider(object):
382
275
  self.logger.error("fail to decode response: %s", e)
383
276
  return res
384
277
 
385
- @staticmethod
386
- def _encode(params):
387
- # type: (dict|list|str|bytes|None) -> str
278
+ def _encode_body(self, data):
279
+ # type: (dict | list | str | bytes | None) -> str
388
280
  """
389
- 编码参数为 URL 查询字符串
390
-
281
+ 自动编码数据为字符串或字节, 根据 content_type 选择编码方式。
391
282
  Args:
392
- params (dict|list|str|bytes|None): 参数字典、列表或字符串
393
- Returns:
394
- str: 编码后的查询字符串
395
- """
396
- if not params:
397
- return ""
398
- elif isinstance(params, (str, bytes)):
399
- return params # type: ignore[return-value]
400
- return urlencode(params, doseq=True)
401
-
402
- @staticmethod
403
- def _quote(data, safe="/"):
404
- # type: (str, str) -> str
405
- """
406
- 对字符串进行 URL 编码
407
-
408
- Args:
409
- data (str): 待编码字符串
283
+ data (dict | list | str | bytes | None): 待编码数据
410
284
 
411
285
  Returns:
412
- str: 编码后的字符串
286
+ str | bytes | None: 编码后的数据
413
287
  """
414
- return quote(data, safe=safe)
288
+ if isinstance(data, (str, bytes)):
289
+ return data # type: ignore[return-value]
290
+ if not data:
291
+ return ""
292
+ if self.content_type == TYPE_FORM:
293
+ return encode_params(data)
294
+ return jsonencode(data)
415
295
 
416
296
  def _mask_sensitive_data(self, data):
417
297
  # type: (str | bytes | None) -> str | bytes | None
@@ -423,19 +303,19 @@ class SimpleProvider(object):
423
303
  Returns:
424
304
  str | bytes | None: 打码后的字符串
425
305
  """
426
- if not data or not self.auth_token:
306
+ if not data or not self.token:
427
307
  return data
428
308
 
429
309
  # 生成打码后的token
430
- token_masked = self.auth_token[:2] + "***" + self.auth_token[-2:] if len(self.auth_token) > 4 else "***"
431
- token_encoded = quote(self.auth_token, safe="")
310
+ token_masked = self.token[:2] + "***" + self.token[-2:] if len(self.token) > 4 else "***"
311
+ token_encoded = quote(self.token, safe="")
432
312
 
433
313
  if isinstance(data, bytes): # 处理字节数据
434
- return data.replace(self.auth_token.encode(), token_masked.encode()).replace(
314
+ return data.replace(self.token.encode(), token_masked.encode()).replace(
435
315
  token_encoded.encode(), token_masked.encode()
436
316
  )
437
317
  if hasattr(data, "replace"): # 处理字符串数据
438
- return data.replace(self.auth_token, token_masked).replace(token_encoded, token_masked)
318
+ return data.replace(self.token, token_masked).replace(token_encoded, token_masked)
439
319
  return data
440
320
 
441
321
 
@@ -472,7 +352,7 @@ class BaseProvider(SimpleProvider):
472
352
  """
473
353
  domain = domain.lower()
474
354
  self.logger.info("%s => %s(%s)", domain, value, record_type)
475
- sub, main = split_custom_domain(domain)
355
+ sub, main = _split_custom_domain(domain)
476
356
  try:
477
357
  if sub is not None:
478
358
  # 使用自定义分隔符格式
@@ -619,7 +499,7 @@ class BaseProvider(SimpleProvider):
619
499
  return None, None, main
620
500
 
621
501
 
622
- def split_custom_domain(domain):
502
+ def _split_custom_domain(domain):
623
503
  # type: (str) -> tuple[str | None, str]
624
504
  """
625
505
  拆分支持 ~ 或 + 的自定义格式域名为 (子域, 主域)
@@ -0,0 +1,113 @@
1
+ # coding=utf-8
2
+ """
3
+ DNS Provider 签名和哈希算法模块
4
+
5
+ Signature and hash algorithms module for DNS providers.
6
+ Provides common cryptographic functions for cloud provider authentication.
7
+
8
+ @author: NewFuture
9
+ """
10
+
11
+ from hashlib import sha256
12
+ from hmac import HMAC
13
+
14
+
15
+ def hmac_sha256(key, message):
16
+ # type: (str | bytes, str | bytes) -> HMAC
17
+ """
18
+ 计算 HMAC-SHA256 签名对象
19
+
20
+ Compute HMAC-SHA256 signature object.
21
+
22
+ Args:
23
+ key (str | bytes): 签名密钥 / Signing key
24
+ message (str | bytes): 待签名消息 / Message to sign
25
+
26
+ Returns:
27
+ HMAC: HMAC签名对象,可调用.digest()获取字节或.hexdigest()获取十六进制字符串
28
+ HMAC signature object, call .digest() for bytes or .hexdigest() for hex string
29
+ """
30
+ # Python 2/3 compatible encoding - avoid double encoding in Python 2
31
+ if not isinstance(key, bytes):
32
+ key = key.encode("utf-8")
33
+ if not isinstance(message, bytes):
34
+ message = message.encode("utf-8")
35
+ return HMAC(key, message, sha256)
36
+
37
+
38
+ def sha256_hash(data):
39
+ # type: (str | bytes) -> str
40
+ """
41
+ 计算 SHA256 哈希值
42
+
43
+ Compute SHA256 hash.
44
+
45
+ Args:
46
+ data (str | bytes): 待哈希数据 / Data to hash
47
+
48
+ Returns:
49
+ str: 十六进制哈希字符串 / Hexadecimal hash string
50
+ """
51
+ # Python 2/3 compatible encoding - avoid double encoding in Python 2
52
+ if not isinstance(data, bytes):
53
+ data = data.encode("utf-8")
54
+ return sha256(data).hexdigest()
55
+
56
+
57
+ def hmac_sha256_authorization(
58
+ secret_key, # type: str | bytes
59
+ method, # type: str
60
+ path, # type: str
61
+ query, # type: str
62
+ headers, # type: dict[str, str]
63
+ body_hash, # type: str
64
+ signing_string_format, # type: str
65
+ authorization_format, # type: str
66
+ ):
67
+ # type: (...) -> str
68
+ """
69
+ HMAC-SHA256 云服务商通用认证签名生成器
70
+
71
+ Universal cloud provider authentication signature generator using HMAC-SHA256.
72
+
73
+ 通用的云服务商API认证签名生成函数,使用HMAC-SHA256算法生成符合各云服务商规范的Authorization头部。
74
+
75
+ 模板变量格式:{HashedCanonicalRequest}, {SignedHeaders}, {Signature}
76
+
77
+ Args:
78
+ secret_key (str | bytes): 签名密钥,已经过密钥派生处理 / Signing key (already derived if needed)
79
+ method (str): HTTP请求方法 / HTTP request method (GET, POST, etc.)
80
+ path (str): API请求路径 / API request path
81
+ query (str): URL查询字符串 / URL query string
82
+ headers (dict[str, str]): HTTP请求头部 / HTTP request headers
83
+ body_hash (str): 请求体的SHA256哈希值 / SHA256 hash of request payload
84
+ signing_string_format (str): 待签名字符串模板,包含 {HashedCanonicalRequest} 占位符
85
+ authorization_format (str): Authorization头部模板,包含 {SignedHeaders}, {Signature} 占位符
86
+
87
+ Returns:
88
+ str: 完整的Authorization头部值 / Complete Authorization header value
89
+ """
90
+ # 1. 构建规范化头部 - 所有传入的头部都参与签名
91
+ headers_to_sign = {k.lower(): str(v).strip() for k, v in headers.items()}
92
+ signed_headers_list = sorted(headers_to_sign.keys())
93
+
94
+ # 2. 构建规范请求字符串
95
+ canonical_headers = ""
96
+ for header_name in signed_headers_list:
97
+ canonical_headers += "{}:{}\n".format(header_name, headers_to_sign[header_name])
98
+
99
+ # 构建完整的规范请求字符串
100
+ signed_headers = ";".join(signed_headers_list)
101
+ canonical_request = "\n".join([method.upper(), path, query, canonical_headers, signed_headers, body_hash])
102
+
103
+ # 3. 构建待签名字符串 - 只需要替换 HashedCanonicalRequest
104
+ hashed_canonical_request = sha256_hash(canonical_request)
105
+ string_to_sign = signing_string_format.format(HashedCanonicalRequest=hashed_canonical_request)
106
+
107
+ # 4. 计算最终签名
108
+ signature = hmac_sha256(secret_key, string_to_sign).hexdigest()
109
+
110
+ # 5. 构建Authorization头部 - 只需要替换 SignedHeaders 和 Signature
111
+ authorization = authorization_format.format(SignedHeaders=signed_headers, Signature=signature)
112
+
113
+ return authorization
ddns/provider/alidns.py CHANGED
@@ -5,25 +5,37 @@ AliDNS API
5
5
  @author: NewFuture
6
6
  """
7
7
 
8
- from ._base import TYPE_FORM, BaseProvider, hmac_sha256_authorization, sha256_hash, join_domain
8
+ from ._base import TYPE_FORM, BaseProvider, join_domain, encode_params
9
+ from ._signature import hmac_sha256_authorization, sha256_hash
9
10
  from time import strftime, gmtime, time
10
11
 
11
12
 
12
- class AlidnsProvider(BaseProvider):
13
- API = "https://alidns.aliyuncs.com"
14
- content_type = TYPE_FORM # 阿里云DNS API使用表单格式
13
+ class AliBaseProvider(BaseProvider):
14
+ """阿里云基础Provider,提供通用的_request方法"""
15
15
 
16
+ endpoint = "https://alidns.aliyuncs.com"
17
+ content_type = TYPE_FORM # 阿里云DNS API使用表单格式
16
18
  api_version = "2015-01-09" # API版本,v3签名需要
17
19
 
18
- def _request(self, action, **params):
19
- # type: (str, **(str | int | bytes | bool | None)) -> dict
20
+ def _request(self, action, method="POST", **params):
21
+ # type: (str, str, **(Any)) -> dict
20
22
  """Aliyun v3 https://help.aliyun.com/zh/sdk/product-overview/v3-request-structure-and-signature"""
21
23
  params = {k: v for k, v in params.items() if v is not None}
22
- body_content = self._encode(params) if len(params) > 0 else ""
24
+
25
+ if method in ("GET", "DELETE"):
26
+ # For GET and DELETE requests, parameters go in query string
27
+ query_string = encode_params(params) if len(params) > 0 else ""
28
+ body_content = ""
29
+ else:
30
+ # For POST requests, parameters go in body
31
+ body_content = self._encode_body(params)
32
+ query_string = ""
33
+
34
+ path = "/"
23
35
  content_hash = sha256_hash(body_content)
24
36
  # 构造请求头部
25
37
  headers = {
26
- "host": self.API.split("://", 1)[1].strip("/"),
38
+ "host": self.endpoint.split("://", 1)[1].strip("/"),
27
39
  "content-type": self.content_type,
28
40
  "x-acs-action": action,
29
41
  "x-acs-content-sha256": content_hash,
@@ -34,20 +46,25 @@ class AlidnsProvider(BaseProvider):
34
46
 
35
47
  # 使用通用签名函数
36
48
  authorization = hmac_sha256_authorization(
37
- secret_key=self.auth_token,
38
- method="POST",
39
- path="/",
40
- query="",
49
+ secret_key=self.token,
50
+ method=method,
51
+ path=path,
52
+ query=query_string,
41
53
  headers=headers,
42
54
  body_hash=content_hash,
43
55
  signing_string_format="ACS3-HMAC-SHA256\n{HashedCanonicalRequest}",
44
56
  authorization_format=(
45
- "ACS3-HMAC-SHA256 Credential=" + self.auth_id + ",SignedHeaders={SignedHeaders},Signature={Signature}"
57
+ "ACS3-HMAC-SHA256 Credential=" + self.id + ",SignedHeaders={SignedHeaders},Signature={Signature}"
46
58
  ),
47
59
  )
48
60
  headers["Authorization"] = authorization
49
61
  # 对于v3签名的RPC API,参数在request body中
50
- return self._http("POST", "/", body=body_content, headers=headers)
62
+ path = path if not query_string else path + "?" + format(query_string)
63
+ return self._http(method, path, body=body_content, headers=headers)
64
+
65
+
66
+ class AlidnsProvider(AliBaseProvider):
67
+ """阿里云DNS Provider"""
51
68
 
52
69
  def _split_zone_and_sub(self, domain):
53
70
  # type: (str) -> tuple[str | None, str | None, str]