ddns 4.1.0b1__py2.py3-none-any.whl → 4.1.0b3__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.

Potentially problematic release.


This version of ddns might be problematic. Click here for more details.

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 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
67
+ def encode_params(params):
68
+ # type: (dict|list|str|bytes|None) -> str
79
69
  """
80
- 计算 HMAC-SHA256 签名对象
81
-
82
- Compute HMAC-SHA256 signature object.
70
+ 编码参数为 URL 查询字符串,参数顺序会排序
83
71
 
84
72
  Args:
85
- key (str | bytes): 签名密钥 / Signing key
86
- message (str | bytes): 待签名消息 / Message to sign
87
-
73
+ params (dict|list|str|bytes|None): 参数字典、列表或字符串
88
74
  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
75
+ str: 编码后的查询字符串
102
76
  """
103
- 计算 SHA256 哈希值
104
-
105
- Compute SHA256 hash.
106
-
107
- Args:
108
- data (str | bytes): 待哈希数据 / Data to hash
109
-
110
- Returns:
111
- str: 十六进制哈希字符串 / Hexadecimal hash string
112
- """
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,45 @@ 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
203
105
  # Description
204
- remark = "Managed by [DDNS v{}](https://ddns.newfuture.cc)".format(version)
106
+ remark = "Managed by [DDNS](https://ddns.newfuture.cc)"
205
107
 
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
108
+ def __init__(self, id, token, logger=None, ssl="auto", proxy=None, endpoint=None, **options):
109
+ # type: (str, str, Logger | None, bool|str, list[str]|None, str|None, **object) -> None
208
110
  """
209
111
  初始化服务商对象
210
112
 
211
113
  Initialize provider instance.
212
114
 
213
115
  Args:
214
- auth_id (str): 身份认证 ID / Authentication ID
215
- auth_token (str): 密钥 / Authentication Token
216
- options (dict): 其它参数,如代理、调试等 / Additional options
116
+ id (str): 身份认证 ID / Authentication ID
117
+ token (str): 密钥 / Authentication Token
118
+ proxy (list[str | None] | None): 代理配置,支持代理列表
119
+ options (dict): 其它参数 / Additional options
217
120
  """
218
- self.auth_id = auth_id # type: str
219
- self.auth_token = auth_token # type: str
121
+ self.id = id
122
+ self.token = token
123
+ if endpoint:
124
+ self.endpoint = endpoint
125
+
126
+ # 处理代理配置
127
+ self._proxy = proxy # 代理列表或None
128
+
129
+ self._ssl = ssl
130
+
220
131
  self.options = options
221
132
  name = self.__class__.__name__
222
133
  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
134
+
226
135
  self._zone_map = {} # type: dict[str, str]
227
- self.logger.debug("%s initialized with: %s", self.__class__.__name__, auth_id)
136
+ self.logger.debug("%s initialized with: %s", self.__class__.__name__, id)
228
137
  self._validate() # 验证身份认证信息
229
138
 
230
139
  @abstractmethod
@@ -248,22 +157,6 @@ class SimpleProvider(object):
248
157
  """
249
158
  raise NotImplementedError("This set_record should be implemented by subclasses")
250
159
 
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
160
  def _validate(self):
268
161
  # type: () -> None
269
162
  """
@@ -271,11 +164,11 @@ class SimpleProvider(object):
271
164
 
272
165
  Validate authentication credentials.
273
166
  """
274
- if not self.auth_id:
167
+ if not self.id:
275
168
  raise ValueError("id must be configured")
276
- if not self.auth_token:
169
+ if not self.token:
277
170
  raise ValueError("token must be configured")
278
- if not self.API:
171
+ if not self.endpoint:
279
172
  raise ValueError("API endpoint must be defined in {}".format(self.__class__.__name__))
280
173
 
281
174
  def _http(self, method, url, params=None, body=None, queries=None, headers=None): # noqa: C901
@@ -314,13 +207,13 @@ class SimpleProvider(object):
314
207
 
315
208
  # 构建查询字符串
316
209
  if len(query_params) > 0:
317
- url += ("&" if "?" in url else "?") + self._encode(query_params)
210
+ url += ("&" if "?" in url else "?") + encode_params(query_params)
318
211
 
319
212
  # 构建完整URL
320
213
  if not url.startswith("http://") and not url.startswith("https://"):
321
- if not url.startswith("/") and self.API.endswith("/"):
214
+ if not url.startswith("/") and self.endpoint.endswith("/"):
322
215
  url = "/" + url
323
- url = self.API + url
216
+ url = self.endpoint + url
324
217
 
325
218
  # 记录请求日志
326
219
  self.logger.info("%s %s", method, self._mask_sensitive_data(url))
@@ -330,12 +223,7 @@ class SimpleProvider(object):
330
223
  if body:
331
224
  if "content-type" not in headers:
332
225
  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)
226
+ body_data = self._encode_body(body)
339
227
  self.logger.debug("body:\n%s", self._mask_sensitive_data(body_data))
340
228
 
341
229
  # 处理headers
@@ -344,16 +232,8 @@ class SimpleProvider(object):
344
232
  if len(headers) > 2:
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
+ # 直接传递代理列表给request函数
236
+ response = request(method, url, body_data, headers=headers, proxies=self._proxy, verify=self._ssl, retries=2)
357
237
  # 处理响应
358
238
  status_code = response.status
359
239
  if not (200 <= status_code < 300):
@@ -364,11 +244,11 @@ class SimpleProvider(object):
364
244
  if status_code >= 500 or status_code in (400, 401, 403):
365
245
  self.logger.error("HTTP error:\n%s", res)
366
246
  if status_code == 400:
367
- raise RuntimeError("请求参数错误 [400]: " + response.reason)
247
+ raise RuntimeError("参数错误 [400]: " + response.reason)
368
248
  elif status_code == 401:
369
249
  raise RuntimeError("认证失败 [401]: " + response.reason)
370
250
  elif status_code == 403:
371
- raise RuntimeError("权限不足 [403]: " + response.reason)
251
+ raise RuntimeError("禁止访问 [403]: " + response.reason)
372
252
  else:
373
253
  raise RuntimeError("服务器错误 [{}]: {}".format(status_code, response.reason))
374
254
 
@@ -382,36 +262,23 @@ class SimpleProvider(object):
382
262
  self.logger.error("fail to decode response: %s", e)
383
263
  return res
384
264
 
385
- @staticmethod
386
- def _encode(params):
387
- # type: (dict|list|str|bytes|None) -> str
265
+ def _encode_body(self, data):
266
+ # type: (dict | list | str | bytes | None) -> str
388
267
  """
389
- 编码参数为 URL 查询字符串
390
-
268
+ 自动编码数据为字符串或字节, 根据 content_type 选择编码方式。
391
269
  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): 待编码字符串
270
+ data (dict | list | str | bytes | None): 待编码数据
410
271
 
411
272
  Returns:
412
- str: 编码后的字符串
273
+ str | bytes | None: 编码后的数据
413
274
  """
414
- return quote(data, safe=safe)
275
+ if isinstance(data, (str, bytes)):
276
+ return data # type: ignore[return-value]
277
+ if not data:
278
+ return ""
279
+ if self.content_type == TYPE_FORM:
280
+ return encode_params(data)
281
+ return jsonencode(data)
415
282
 
416
283
  def _mask_sensitive_data(self, data):
417
284
  # type: (str | bytes | None) -> str | bytes | None
@@ -423,19 +290,19 @@ class SimpleProvider(object):
423
290
  Returns:
424
291
  str | bytes | None: 打码后的字符串
425
292
  """
426
- if not data or not self.auth_token:
293
+ if not data or not self.token:
427
294
  return data
428
295
 
429
296
  # 生成打码后的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="")
297
+ token_masked = self.token[:2] + "***" + self.token[-2:] if len(self.token) > 4 else "***"
298
+ token_encoded = quote(self.token, safe="")
432
299
 
433
300
  if isinstance(data, bytes): # 处理字节数据
434
- return data.replace(self.auth_token.encode(), token_masked.encode()).replace(
301
+ return data.replace(self.token.encode(), token_masked.encode()).replace(
435
302
  token_encoded.encode(), token_masked.encode()
436
303
  )
437
304
  if hasattr(data, "replace"): # 处理字符串数据
438
- return data.replace(self.auth_token, token_masked).replace(token_encoded, token_masked)
305
+ return data.replace(self.token, token_masked).replace(token_encoded, token_masked)
439
306
  return data
440
307
 
441
308
 
@@ -472,7 +339,7 @@ class BaseProvider(SimpleProvider):
472
339
  """
473
340
  domain = domain.lower()
474
341
  self.logger.info("%s => %s(%s)", domain, value, record_type)
475
- sub, main = split_custom_domain(domain)
342
+ sub, main = _split_custom_domain(domain)
476
343
  try:
477
344
  if sub is not None:
478
345
  # 使用自定义分隔符格式
@@ -619,7 +486,7 @@ class BaseProvider(SimpleProvider):
619
486
  return None, None, main
620
487
 
621
488
 
622
- def split_custom_domain(domain):
489
+ def _split_custom_domain(domain):
623
490
  # type: (str) -> tuple[str | None, str]
624
491
  """
625
492
  拆分支持 ~ 或 + 的自定义格式域名为 (子域, 主域)
@@ -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,38 @@ AliDNS API
5
5
  @author: NewFuture
6
6
  """
7
7
 
8
- from ._base import TYPE_FORM, BaseProvider, hmac_sha256_authorization, sha256_hash, join_domain
9
- from time import strftime, gmtime, time
8
+ from time import gmtime, strftime, time
10
9
 
10
+ from ._base import TYPE_FORM, BaseProvider, encode_params, join_domain
11
+ from ._signature import hmac_sha256_authorization, sha256_hash
11
12
 
12
- class AlidnsProvider(BaseProvider):
13
- API = "https://alidns.aliyuncs.com"
14
- content_type = TYPE_FORM # 阿里云DNS API使用表单格式
15
13
 
14
+ class AliBaseProvider(BaseProvider):
15
+ """阿里云基础Provider,提供通用的_request方法"""
16
+
17
+ endpoint = "https://alidns.aliyuncs.com"
18
+ content_type = TYPE_FORM # 阿里云DNS API使用表单格式
16
19
  api_version = "2015-01-09" # API版本,v3签名需要
17
20
 
18
- def _request(self, action, **params):
19
- # type: (str, **(str | int | bytes | bool | None)) -> dict
21
+ def _request(self, action, method="POST", **params):
22
+ # type: (str, str, **(Any)) -> dict
20
23
  """Aliyun v3 https://help.aliyun.com/zh/sdk/product-overview/v3-request-structure-and-signature"""
21
24
  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 ""
25
+
26
+ if method in ("GET", "DELETE"):
27
+ # For GET and DELETE requests, parameters go in query string
28
+ query_string = encode_params(params) if len(params) > 0 else ""
29
+ body_content = ""
30
+ else:
31
+ # For POST requests, parameters go in body
32
+ body_content = self._encode_body(params)
33
+ query_string = ""
34
+
35
+ path = "/"
23
36
  content_hash = sha256_hash(body_content)
24
37
  # 构造请求头部
25
38
  headers = {
26
- "host": self.API.split("://", 1)[1].strip("/"),
39
+ "host": self.endpoint.split("://", 1)[1].strip("/"),
27
40
  "content-type": self.content_type,
28
41
  "x-acs-action": action,
29
42
  "x-acs-content-sha256": content_hash,
@@ -34,20 +47,25 @@ class AlidnsProvider(BaseProvider):
34
47
 
35
48
  # 使用通用签名函数
36
49
  authorization = hmac_sha256_authorization(
37
- secret_key=self.auth_token,
38
- method="POST",
39
- path="/",
40
- query="",
50
+ secret_key=self.token,
51
+ method=method,
52
+ path=path,
53
+ query=query_string,
41
54
  headers=headers,
42
55
  body_hash=content_hash,
43
56
  signing_string_format="ACS3-HMAC-SHA256\n{HashedCanonicalRequest}",
44
57
  authorization_format=(
45
- "ACS3-HMAC-SHA256 Credential=" + self.auth_id + ",SignedHeaders={SignedHeaders},Signature={Signature}"
58
+ "ACS3-HMAC-SHA256 Credential=" + self.id + ",SignedHeaders={SignedHeaders},Signature={Signature}"
46
59
  ),
47
60
  )
48
61
  headers["Authorization"] = authorization
49
62
  # 对于v3签名的RPC API,参数在request body中
50
- return self._http("POST", "/", body=body_content, headers=headers)
63
+ path = path if not query_string else path + "?" + format(query_string)
64
+ return self._http(method, path, body=body_content, headers=headers)
65
+
66
+
67
+ class AlidnsProvider(AliBaseProvider):
68
+ """阿里云DNS Provider"""
51
69
 
52
70
  def _split_zone_and_sub(self, domain):
53
71
  # type: (str) -> tuple[str | None, str | None, str]
@@ -99,7 +117,7 @@ class AlidnsProvider(BaseProvider):
99
117
  TTL=ttl,
100
118
  Line=line,
101
119
  **extra
102
- )
120
+ ) # fmt: skip
103
121
  if data and data.get("RecordId"):
104
122
  self.logger.info("Record created: %s", data)
105
123
  return True
@@ -126,7 +144,7 @@ class AlidnsProvider(BaseProvider):
126
144
  TTL=ttl,
127
145
  Line=line or old_record.get("Line"),
128
146
  **extra
129
- )
147
+ ) # fmt: skip
130
148
  if data and data.get("RecordId"):
131
149
  self.logger.info("Record updated: %s", data)
132
150
  return True