ddns 4.0.1__py2.py3-none-any.whl → 4.1.0__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/__builtins__.pyi +6 -0
- ddns/__init__.py +2 -9
- ddns/__main__.py +108 -144
- ddns/cache.py +183 -0
- ddns/ip.py +145 -0
- ddns/provider/__init__.py +79 -0
- ddns/provider/_base.py +526 -0
- ddns/provider/_signature.py +113 -0
- ddns/provider/alidns.py +145 -176
- ddns/provider/aliesa.py +130 -0
- ddns/provider/callback.py +66 -104
- ddns/provider/cloudflare.py +87 -151
- ddns/provider/debug.py +19 -0
- ddns/provider/dnscom.py +91 -168
- ddns/provider/dnspod.py +102 -174
- ddns/provider/dnspod_com.py +11 -8
- ddns/provider/edgeone.py +83 -0
- ddns/provider/he.py +41 -78
- ddns/provider/huaweidns.py +134 -256
- ddns/provider/namesilo.py +159 -0
- ddns/provider/noip.py +101 -0
- ddns/provider/tencentcloud.py +194 -0
- ddns/util/comment.py +88 -0
- ddns/util/fileio.py +113 -0
- ddns/util/http.py +322 -0
- ddns/util/try_run.py +37 -0
- ddns-4.1.0.dist-info/METADATA +327 -0
- ddns-4.1.0.dist-info/RECORD +33 -0
- ddns/util/cache.py +0 -139
- ddns/util/config.py +0 -317
- ddns/util/ip.py +0 -96
- ddns-4.0.1.dist-info/METADATA +0 -326
- ddns-4.0.1.dist-info/RECORD +0 -21
- {ddns-4.0.1.dist-info → ddns-4.1.0.dist-info}/WHEEL +0 -0
- {ddns-4.0.1.dist-info → ddns-4.1.0.dist-info}/entry_points.txt +0 -0
- {ddns-4.0.1.dist-info → ddns-4.1.0.dist-info}/licenses/LICENSE +0 -0
- {ddns-4.0.1.dist-info → ddns-4.1.0.dist-info}/top_level.txt +0 -0
ddns/provider/dnscom.py
CHANGED
|
@@ -1,180 +1,103 @@
|
|
|
1
1
|
# coding=utf-8
|
|
2
2
|
"""
|
|
3
|
-
DNSCOM API
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@author: Bigjin
|
|
7
|
-
@mailto: i@bigjin.com
|
|
3
|
+
DNSCOM/51dns API 接口解析操作库
|
|
4
|
+
www.51dns.com (原dns.com)
|
|
5
|
+
@author: Bigjin<i@bigjin.com>, NewFuture
|
|
8
6
|
"""
|
|
9
7
|
|
|
10
8
|
from hashlib import md5
|
|
11
|
-
from
|
|
12
|
-
from logging import debug, info, warning
|
|
13
|
-
from time import mktime
|
|
14
|
-
from datetime import datetime
|
|
9
|
+
from time import time
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
from http.client import HTTPSConnection
|
|
18
|
-
from urllib.parse import urlencode
|
|
19
|
-
except ImportError: # python 2
|
|
20
|
-
from httplib import HTTPSConnection
|
|
21
|
-
from urllib import urlencode
|
|
11
|
+
from ._base import TYPE_FORM, BaseProvider, encode_params
|
|
22
12
|
|
|
23
|
-
__author__ = 'Bigjin'
|
|
24
|
-
# __all__ = ["request", "ID", "TOKEN", "PROXY"]
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
class Config:
|
|
28
|
-
ID = "id"
|
|
29
|
-
TOKEN = "TOKEN"
|
|
30
|
-
PROXY = None # 代理设置
|
|
31
|
-
TTL = None
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class API:
|
|
35
|
-
# API 配置
|
|
36
|
-
SITE = "www.dns.com" # API endpoint
|
|
37
|
-
METHOD = "POST" # 请求方法
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def signature(params):
|
|
41
|
-
"""
|
|
42
|
-
计算签名,返回签名后的查询参数
|
|
43
|
-
"""
|
|
44
|
-
params.update({
|
|
45
|
-
'apiKey': Config.ID,
|
|
46
|
-
'timestamp': mktime(datetime.now().timetuple()),
|
|
47
|
-
})
|
|
48
|
-
query = urlencode(sorted(params.items()))
|
|
49
|
-
debug(query)
|
|
50
|
-
sign = query
|
|
51
|
-
debug("signString: %s", sign)
|
|
52
|
-
|
|
53
|
-
sign = md5((sign + Config.TOKEN).encode('utf-8')).hexdigest()
|
|
54
|
-
params["hash"] = sign
|
|
55
|
-
|
|
56
|
-
return params
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def request(action, param=None, **params):
|
|
60
|
-
"""
|
|
61
|
-
发送请求数据
|
|
62
|
-
"""
|
|
63
|
-
if param:
|
|
64
|
-
params.update(param)
|
|
65
|
-
params = dict((k, params[k]) for k in params if params[k] is not None)
|
|
66
|
-
params = signature(params)
|
|
67
|
-
info("%s/api/%s/ : params:%s", API.SITE, action, params)
|
|
68
|
-
|
|
69
|
-
if Config.PROXY:
|
|
70
|
-
conn = HTTPSConnection(Config.PROXY)
|
|
71
|
-
conn.set_tunnel(API.SITE, 443)
|
|
72
|
-
else:
|
|
73
|
-
conn = HTTPSConnection(API.SITE)
|
|
74
|
-
|
|
75
|
-
conn.request(API.METHOD, '/api/' + action + '/', urlencode(params),
|
|
76
|
-
{"Content-type": "application/x-www-form-urlencoded"})
|
|
77
|
-
response = conn.getresponse()
|
|
78
|
-
result = response.read().decode('utf8')
|
|
79
|
-
conn.close()
|
|
80
|
-
|
|
81
|
-
if response.status < 200 or response.status >= 300:
|
|
82
|
-
warning('%s : error[%d]:%s', action, response.status, result)
|
|
83
|
-
raise Exception(result)
|
|
84
|
-
else:
|
|
85
|
-
data = jsondecode(result)
|
|
86
|
-
debug('%s : result:%s', action, data)
|
|
87
|
-
if data.get('code') != 0:
|
|
88
|
-
raise Exception("api error:", data.get('message'))
|
|
89
|
-
data = data.get('data')
|
|
90
|
-
if data is None:
|
|
91
|
-
raise Exception('response data is none')
|
|
92
|
-
return data
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def get_domain_info(domain):
|
|
96
|
-
"""
|
|
97
|
-
切割域名获取主域名和对应ID
|
|
98
|
-
"""
|
|
99
|
-
if len(domain.split('.')) > 2:
|
|
100
|
-
domains = domain.split('.', 1)
|
|
101
|
-
sub = domains[0]
|
|
102
|
-
main = domains[1]
|
|
103
|
-
else:
|
|
104
|
-
sub = '' # 接口有bug 不能传 @ * 作为主机头,但是如果为空,默认为 @
|
|
105
|
-
main = domain
|
|
106
|
-
|
|
107
|
-
res = request("domain/getsingle", domainID=main)
|
|
108
|
-
domain_id = res.get('domainID')
|
|
109
|
-
return sub, main, domain_id
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def get_records(domain, domain_id, **conditions):
|
|
113
|
-
"""
|
|
114
|
-
获取记录ID
|
|
115
|
-
返回满足条件的所有记录[]
|
|
116
|
-
TODO 大于500翻页
|
|
14
|
+
class DnscomProvider(BaseProvider):
|
|
117
15
|
"""
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
get_records.keys = ("recordID", "record", "type", "viewID",
|
|
121
|
-
"TTL", "state", "value")
|
|
122
|
-
|
|
123
|
-
if domain not in get_records.records:
|
|
124
|
-
get_records.records[domain] = {}
|
|
125
|
-
data = request("record/list",
|
|
126
|
-
domainID=domain_id, pageSize=500)
|
|
127
|
-
if data.get('data'):
|
|
128
|
-
for record in data.get('data'):
|
|
129
|
-
get_records.records[domain][record["recordID"]] = {
|
|
130
|
-
k: v for (k, v) in record.items() if k in get_records.keys}
|
|
131
|
-
records = {}
|
|
132
|
-
for (rid, record) in get_records.records[domain].items():
|
|
133
|
-
for (k, value) in conditions.items():
|
|
134
|
-
if record.get(k) != value:
|
|
135
|
-
break
|
|
136
|
-
else: # for else push
|
|
137
|
-
records[rid] = record
|
|
138
|
-
return records
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def update_record(domain, value, record_type='A'):
|
|
142
|
-
"""
|
|
143
|
-
更新记录
|
|
16
|
+
DNSCOM/51dns API Provider
|
|
17
|
+
https://www.51dns.com/document/api/index.html
|
|
144
18
|
"""
|
|
145
|
-
info(">>>>>%s(%s)", domain, record_type)
|
|
146
|
-
sub, main, domain_id = get_domain_info(domain)
|
|
147
19
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
else:
|
|
166
|
-
res = request("record/create", domainID=domain_id,
|
|
167
|
-
value=value, host=sub, type=record_type, TTL=Config.TTL)
|
|
168
|
-
if res:
|
|
169
|
-
# update records INFO
|
|
170
|
-
rid = res.get('recordID')
|
|
171
|
-
get_records.records[main][rid] = {
|
|
172
|
-
'value': value,
|
|
173
|
-
"recordID": rid,
|
|
174
|
-
"record": sub,
|
|
175
|
-
"type": record_type
|
|
20
|
+
endpoint = "https://www.51dns.com"
|
|
21
|
+
content_type = TYPE_FORM
|
|
22
|
+
|
|
23
|
+
def _validate(self):
|
|
24
|
+
self.logger.warning(
|
|
25
|
+
"DNS.COM 缺少充分的真实环境测试,请及时在 GitHub Issues 中反馈: %s",
|
|
26
|
+
"https://github.com/NewFuture/DDNS/issues",
|
|
27
|
+
)
|
|
28
|
+
super(DnscomProvider, self)._validate()
|
|
29
|
+
|
|
30
|
+
def _signature(self, params):
|
|
31
|
+
"""https://www.51dns.com/document/api/70/72.html"""
|
|
32
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
33
|
+
params.update(
|
|
34
|
+
{
|
|
35
|
+
"apiKey": self.id,
|
|
36
|
+
"timestamp": time(), # 时间戳
|
|
176
37
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
38
|
+
)
|
|
39
|
+
query = encode_params(params)
|
|
40
|
+
sign = md5((query + self.token).encode("utf-8")).hexdigest()
|
|
41
|
+
params["hash"] = sign
|
|
42
|
+
return params
|
|
43
|
+
|
|
44
|
+
def _request(self, action, **params):
|
|
45
|
+
params = self._signature(params)
|
|
46
|
+
data = self._http("POST", "/api/{}/".format(action), body=params)
|
|
47
|
+
if data is None or not isinstance(data, dict):
|
|
48
|
+
raise Exception("response data is none")
|
|
49
|
+
if data.get("code", 0) != 0:
|
|
50
|
+
raise Exception("api error: " + str(data.get("message")))
|
|
51
|
+
return data.get("data")
|
|
52
|
+
|
|
53
|
+
def _query_zone_id(self, domain):
|
|
54
|
+
"""https://www.51dns.com/document/api/74/31.html"""
|
|
55
|
+
res = self._request("domain/getsingle", domainID=domain)
|
|
56
|
+
self.logger.debug("Queried domain: %s", res)
|
|
57
|
+
if res:
|
|
58
|
+
return res.get("domainID")
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
def _query_record(self, zone_id, subdomain, main_domain, record_type, line, extra):
|
|
62
|
+
"""https://www.51dns.com/document/api/4/47.html"""
|
|
63
|
+
records = self._request("record/list", domainID=zone_id, host=subdomain, pageSize=500)
|
|
64
|
+
records = records.get("data", []) if records else []
|
|
65
|
+
for record in records:
|
|
66
|
+
if (
|
|
67
|
+
record.get("record") == subdomain
|
|
68
|
+
and record.get("type") == record_type
|
|
69
|
+
and (line is None or record.get("viewID") == line)
|
|
70
|
+
):
|
|
71
|
+
return record
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
def _create_record(self, zone_id, subdomain, main_domain, value, record_type, ttl, line, extra):
|
|
75
|
+
"""https://www.51dns.com/document/api/4/12.html"""
|
|
76
|
+
extra["remark"] = extra.get("remark", self.remark)
|
|
77
|
+
res = self._request(
|
|
78
|
+
"record/create",
|
|
79
|
+
domainID=zone_id,
|
|
80
|
+
value=value,
|
|
81
|
+
host=subdomain,
|
|
82
|
+
type=record_type,
|
|
83
|
+
TTL=ttl,
|
|
84
|
+
viewID=line,
|
|
85
|
+
**extra
|
|
86
|
+
) # fmt: skip
|
|
87
|
+
if res and res.get("recordID"):
|
|
88
|
+
self.logger.info("Record created: %s", res)
|
|
89
|
+
return True
|
|
90
|
+
self.logger.error("Failed to create record: %s", res)
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
def _update_record(self, zone_id, old_record, value, record_type, ttl, line, extra):
|
|
94
|
+
"""https://www.51dns.com/document/api/4/45.html"""
|
|
95
|
+
extra["remark"] = extra.get("remark", self.remark)
|
|
96
|
+
res = self._request(
|
|
97
|
+
"record/modify", domainID=zone_id, recordID=old_record.get("recordID"), newvalue=value, newTTL=ttl
|
|
98
|
+
)
|
|
99
|
+
if res:
|
|
100
|
+
self.logger.info("Record updated: %s", res)
|
|
101
|
+
return True
|
|
102
|
+
self.logger.error("Failed to update record: %s", res)
|
|
103
|
+
return False
|
ddns/provider/dnspod.py
CHANGED
|
@@ -1,186 +1,114 @@
|
|
|
1
1
|
# coding=utf-8
|
|
2
2
|
"""
|
|
3
3
|
DNSPOD API
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@author: New Future
|
|
4
|
+
@doc: https://docs.dnspod.cn/api/
|
|
5
|
+
@author: NewFuture
|
|
7
6
|
"""
|
|
8
7
|
|
|
9
|
-
from
|
|
10
|
-
from logging import debug, info, warning
|
|
11
|
-
from os import environ
|
|
8
|
+
from ._base import BaseProvider, TYPE_FORM
|
|
12
9
|
|
|
13
|
-
try: # python 3
|
|
14
|
-
from http.client import HTTPSConnection
|
|
15
|
-
from urllib.parse import urlencode
|
|
16
|
-
except ImportError: # python 2
|
|
17
|
-
from httplib import HTTPSConnection
|
|
18
|
-
from urllib import urlencode
|
|
19
10
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class Config:
|
|
24
|
-
ID = "token id"
|
|
25
|
-
TOKEN = "token key"
|
|
26
|
-
PROXY = None # 代理设置
|
|
27
|
-
TTL = None
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class API:
|
|
31
|
-
# API 配置
|
|
32
|
-
SITE = "dnsapi.cn" # API endpoint
|
|
33
|
-
METHOD = "POST" # 请求方法
|
|
34
|
-
TOKEN_PARAM = "login_token" # token参数
|
|
35
|
-
DEFAULT = "默认" # 默认线路名
|
|
36
|
-
LENGTH = "length" # 添加参数
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def request(action, param=None, **params):
|
|
11
|
+
class DnspodProvider(BaseProvider):
|
|
40
12
|
"""
|
|
41
|
-
|
|
13
|
+
DNSPOD API
|
|
14
|
+
DNSPOD 接口解析操作库
|
|
42
15
|
"""
|
|
43
|
-
if param:
|
|
44
|
-
params.update(param)
|
|
45
|
-
params = dict((k, params[k]) for k in params if params[k] is not None)
|
|
46
|
-
params.update({API.TOKEN_PARAM: '***', 'format': 'json'})
|
|
47
|
-
info("%s/%s : %s", API.SITE, action, params)
|
|
48
|
-
params[API.TOKEN_PARAM] = "%s,%s" % (Config.ID, Config.TOKEN)
|
|
49
|
-
params[API.LENGTH] = "3000" # 添加参数
|
|
50
|
-
if Config.PROXY:
|
|
51
|
-
conn = HTTPSConnection(Config.PROXY)
|
|
52
|
-
conn.set_tunnel(API.SITE, 443)
|
|
53
|
-
else:
|
|
54
|
-
conn = HTTPSConnection(API.SITE)
|
|
55
|
-
|
|
56
|
-
conn.request(API.METHOD, '/' + action, urlencode(params), {
|
|
57
|
-
"Content-type": "application/x-www-form-urlencoded",
|
|
58
|
-
"User-Agent": "DDNS/%s (ddns@newfuture.cc)" % environ.get("DDNS_VERSION", "1.0.0")
|
|
59
|
-
})
|
|
60
|
-
response = conn.getresponse()
|
|
61
|
-
res = response.read().decode('utf8')
|
|
62
|
-
conn.close()
|
|
63
16
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
17
|
+
endpoint = "https://dnsapi.cn"
|
|
18
|
+
content_type = TYPE_FORM
|
|
19
|
+
|
|
20
|
+
DefaultLine = "默认"
|
|
21
|
+
|
|
22
|
+
def _request(self, action, extra=None, **params):
|
|
23
|
+
# type: (str, dict | None, **(str | int | bytes | bool | None)) -> dict
|
|
24
|
+
"""
|
|
25
|
+
发送请求数据
|
|
26
|
+
|
|
27
|
+
Send request to DNSPod API.
|
|
28
|
+
Args:
|
|
29
|
+
action (str): API 动作/Action
|
|
30
|
+
extra (dict|None): 额外参数/Extra params
|
|
31
|
+
params (dict): 其它参数/Other params
|
|
32
|
+
Returns:
|
|
33
|
+
dict: 响应数据/Response data
|
|
34
|
+
"""
|
|
35
|
+
# 过滤掉None参数
|
|
36
|
+
if extra:
|
|
37
|
+
params.update(extra)
|
|
38
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
39
|
+
params.update({"login_token": "{0},{1}".format(self.id, self.token), "format": "json"})
|
|
40
|
+
data = self._http("POST", "/" + action, body=params)
|
|
41
|
+
if data and data.get("status", {}).get("code") == "1": # 请求成功
|
|
42
|
+
return data
|
|
43
|
+
else: # 请求失败
|
|
44
|
+
error_msg = "Unknown error"
|
|
45
|
+
if data and isinstance(data, dict):
|
|
46
|
+
error_msg = data.get("status", {}).get("message", "Unknown error")
|
|
47
|
+
self.logger.warning("DNSPod API error: %s", error_msg)
|
|
73
48
|
return data
|
|
74
|
-
else:
|
|
75
|
-
raise Exception(data.get('status', {}))
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def get_domain_info(domain):
|
|
79
|
-
"""
|
|
80
|
-
切割域名获取主域名和对应ID
|
|
81
|
-
"""
|
|
82
|
-
domain_split = domain.split('.')
|
|
83
|
-
sub, did = None, None
|
|
84
|
-
main = domain_split.pop()
|
|
85
|
-
while domain_split: # 通过API判断,最后两个,三个递增
|
|
86
|
-
main = domain_split.pop() + '.' + main
|
|
87
|
-
did = get_domain_id(main)
|
|
88
|
-
if did:
|
|
89
|
-
sub = ".".join(domain_split) or '@'
|
|
90
|
-
# root domain根域名https://github.com/NewFuture/DDNS/issues/9
|
|
91
|
-
break
|
|
92
|
-
info('domain_id: %s, sub: %s', did, sub)
|
|
93
|
-
return did, sub
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def get_domain_id(domain):
|
|
97
|
-
"""
|
|
98
|
-
获取域名ID
|
|
99
|
-
http://www.dnspod.cn/docs/domains.html#domain-info
|
|
100
|
-
"""
|
|
101
|
-
if not hasattr(get_domain_id, "domain_list"):
|
|
102
|
-
get_domain_id.domain_list = {} # "静态变量"存储已查询过的id
|
|
103
|
-
|
|
104
|
-
if domain in get_domain_id.domain_list:
|
|
105
|
-
# 如果已经存在直接返回防止再次请求
|
|
106
|
-
return get_domain_id.domain_list[domain]
|
|
107
|
-
else:
|
|
108
|
-
try:
|
|
109
|
-
d_info = request('Domain.Info', domain=domain)
|
|
110
|
-
except Exception as e:
|
|
111
|
-
info("get_domain_id(%s) error: %s", domain, e)
|
|
112
|
-
return
|
|
113
|
-
did = d_info.get("domain", {}).get("id")
|
|
114
|
-
if did:
|
|
115
|
-
get_domain_id.domain_list[domain] = did
|
|
116
|
-
return did
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def get_records(did, **conditions):
|
|
120
|
-
"""
|
|
121
|
-
获取记录ID
|
|
122
|
-
返回满足条件的所有记录[]
|
|
123
|
-
TODO 大于3000翻页
|
|
124
|
-
http://www.dnspod.cn/docs/records.html#record-list
|
|
125
|
-
"""
|
|
126
|
-
if not hasattr(get_records, "records"):
|
|
127
|
-
get_records.records = {} # "静态变量"存储已查询过的id
|
|
128
|
-
get_records.keys = ("id", "name", "type", "line",
|
|
129
|
-
"line_id", "enabled", "mx", "value")
|
|
130
|
-
|
|
131
|
-
if did not in get_records.records:
|
|
132
|
-
get_records.records[did] = {}
|
|
133
|
-
data = request('Record.List', domain_id=did)
|
|
134
|
-
if data:
|
|
135
|
-
for record in data.get('records'):
|
|
136
|
-
get_records.records[did][record["id"]] = {
|
|
137
|
-
k: v for (k, v) in record.items() if k in get_records.keys}
|
|
138
|
-
|
|
139
|
-
records = {}
|
|
140
|
-
for (did, record) in get_records.records[did].items():
|
|
141
|
-
for (k, value) in conditions.items():
|
|
142
|
-
if record.get(k) != value:
|
|
143
|
-
break
|
|
144
|
-
else: # for else push
|
|
145
|
-
records[did] = record
|
|
146
|
-
return records
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def update_record(domain, value, record_type="A"):
|
|
150
|
-
"""
|
|
151
|
-
更新记录
|
|
152
|
-
"""
|
|
153
|
-
info(">>>>>%s(%s)", domain, record_type)
|
|
154
|
-
domainid, sub = get_domain_info(domain)
|
|
155
|
-
if not domainid:
|
|
156
|
-
raise Exception("invalid domain: [ %s ] " % domain)
|
|
157
49
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
#
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
50
|
+
def _create_record(self, zone_id, subdomain, main_domain, value, record_type, ttl, line, extra):
|
|
51
|
+
# type: (str, str, str, str, str, int | str | None, str | None, dict) -> bool
|
|
52
|
+
"""https://docs.dnspod.cn/api/add-record/"""
|
|
53
|
+
res = self._request(
|
|
54
|
+
"Record.Create",
|
|
55
|
+
extra=extra,
|
|
56
|
+
domain_id=zone_id,
|
|
57
|
+
sub_domain=subdomain,
|
|
58
|
+
value=value,
|
|
59
|
+
record_type=record_type,
|
|
60
|
+
record_line=line or self.DefaultLine,
|
|
61
|
+
ttl=ttl,
|
|
62
|
+
)
|
|
63
|
+
record = res and res.get("record")
|
|
64
|
+
if record: # 记录创建成功
|
|
65
|
+
self.logger.info("Record created: %s", record)
|
|
66
|
+
return True
|
|
67
|
+
else: # 记录创建失败
|
|
68
|
+
self.logger.error("Failed to create record: %s", res)
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
def _update_record(self, zone_id, old_record, value, record_type, ttl, line, extra):
|
|
72
|
+
# type: (str, dict, str, str, int | str | None, str | None, dict) -> bool
|
|
73
|
+
"""https://docs.dnspod.cn/api/modify-records/"""
|
|
74
|
+
record_line = (line or old_record.get("line") or self.DefaultLine).replace("Default", "default")
|
|
75
|
+
res = self._request(
|
|
76
|
+
"Record.Modify",
|
|
77
|
+
domain_id=zone_id,
|
|
78
|
+
record_id=old_record.get("id"),
|
|
79
|
+
sub_domain=old_record.get("name"),
|
|
80
|
+
record_type=record_type,
|
|
81
|
+
value=value,
|
|
82
|
+
record_line=record_line,
|
|
83
|
+
extra=extra,
|
|
84
|
+
)
|
|
85
|
+
record = res and res.get("record")
|
|
86
|
+
if record: # 记录更新成功
|
|
87
|
+
self.logger.debug("Record updated: %s", record)
|
|
88
|
+
return True
|
|
89
|
+
else: # 记录更新失败
|
|
90
|
+
self.logger.error("Failed to update record: %s", res)
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
def _query_zone_id(self, domain):
|
|
94
|
+
# type: (str) -> str | None
|
|
95
|
+
"""查询域名信息 https://docs.dnspod.cn/api/domain-info/"""
|
|
96
|
+
res = self._request("Domain.Info", domain=domain)
|
|
97
|
+
if res and isinstance(res, dict):
|
|
98
|
+
return res.get("domain", {}).get("id")
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
def _query_record(self, zone_id, subdomain, main_domain, record_type, line, extra):
|
|
102
|
+
# type: (str, str, str, str, str | None, dict) -> dict | None
|
|
103
|
+
"""查询记录 list 然后逐个查找 https://docs.dnspod.cn/api/record-list/"""
|
|
104
|
+
res = self._request("Record.List", domain_id=zone_id, sub_domain=subdomain, record_type=record_type, line=line)
|
|
105
|
+
# length="3000"
|
|
106
|
+
records = res.get("records", [])
|
|
107
|
+
n = len(records)
|
|
108
|
+
if not n:
|
|
109
|
+
self.logger.warning("No record found for [%s] %s<%s>(line: %s)", zone_id, subdomain, record_type, line)
|
|
110
|
+
return None
|
|
111
|
+
if n > 1:
|
|
112
|
+
self.logger.warning("%d records found for %s<%s>(%s):\n %s", n, subdomain, record_type, line, records)
|
|
113
|
+
return next((r for r in records if r.get("name") == subdomain), None)
|
|
114
|
+
return records[0]
|
ddns/provider/dnspod_com.py
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
# coding=utf-8
|
|
2
2
|
"""
|
|
3
|
-
DNSPOD API
|
|
4
|
-
DNSPOD 接口解析操作库
|
|
3
|
+
DNSPOD Global (国际版) API
|
|
5
4
|
http://www.dnspod.com/docs/domains.html
|
|
6
|
-
@author:
|
|
5
|
+
@author: NewFuture
|
|
7
6
|
"""
|
|
8
7
|
|
|
9
|
-
from .dnspod import
|
|
8
|
+
from .dnspod import DnspodProvider # noqa: F401
|
|
10
9
|
|
|
11
|
-
API.SITE = "api.dnspod.com" # noqa: F405
|
|
12
|
-
API.DEFAULT = "default" # noqa: F405
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
class DnspodComProvider(DnspodProvider):
|
|
12
|
+
"""
|
|
13
|
+
DNSPOD.com Provider (国际版)
|
|
14
|
+
This class extends the DnspodProvider to use the global DNSPOD API.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
endpoint = "https://api.dnspod.com"
|
|
18
|
+
DefaultLine = "default"
|
ddns/provider/edgeone.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Tencent Cloud EdgeOne API
|
|
4
|
+
腾讯云 EdgeOne (边缘安全速平台) API
|
|
5
|
+
API Documentation: https://cloud.tencent.com/document/api/1552/80731
|
|
6
|
+
@author: NewFuture
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ddns.provider._base import join_domain
|
|
10
|
+
from .tencentcloud import TencentCloudProvider
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EdgeOneProvider(TencentCloudProvider):
|
|
14
|
+
"""
|
|
15
|
+
腾讯云 EdgeOne API 提供商
|
|
16
|
+
Tencent Cloud EdgeOne API Provider
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
endpoint = "https://teo.tencentcloudapi.com"
|
|
20
|
+
# 腾讯云 EdgeOne API 配置
|
|
21
|
+
service = "teo"
|
|
22
|
+
version_date = "2022-09-01"
|
|
23
|
+
|
|
24
|
+
def _query_zone_id(self, domain):
|
|
25
|
+
# type: (str) -> str | None
|
|
26
|
+
"""查询域名的加速域名信息获取 ZoneId https://cloud.tencent.com/document/api/1552/80713"""
|
|
27
|
+
# 首先尝试直接查找域名
|
|
28
|
+
filters = [{"Name": "zone-name", "Values": [domain], "Fuzzy": False}] # type: Any
|
|
29
|
+
response = self._request("DescribeZones", Filters=filters)
|
|
30
|
+
|
|
31
|
+
if response and "Zones" in response:
|
|
32
|
+
for zone in response.get("Zones", []):
|
|
33
|
+
if zone.get("ZoneName") == domain:
|
|
34
|
+
zone_id = zone.get("ZoneId")
|
|
35
|
+
if zone_id:
|
|
36
|
+
self.logger.debug("Found acceleration domain %s with Zone ID: %s", domain, zone_id)
|
|
37
|
+
return zone_id
|
|
38
|
+
|
|
39
|
+
self.logger.debug("Acceleration domain not found for: %s", domain)
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
def _query_record(self, zone_id, subdomain, main_domain, record_type, line, extra):
|
|
43
|
+
# type: (str, str, str, str, str | None, dict) -> dict | None
|
|
44
|
+
"""查询加速域名信息 https://cloud.tencent.com/document/api/1552/86336"""
|
|
45
|
+
domain = join_domain(subdomain, main_domain)
|
|
46
|
+
filters = [{"Name": "domain-name", "Values": [domain], "Fuzzy": False}] # type: Any
|
|
47
|
+
response = self._request("DescribeAccelerationDomains", ZoneId=zone_id, Filters=filters)
|
|
48
|
+
|
|
49
|
+
if response and "AccelerationDomains" in response:
|
|
50
|
+
for domain_info in response.get("AccelerationDomains", []):
|
|
51
|
+
if domain_info.get("DomainName") == domain:
|
|
52
|
+
self.logger.debug("Found acceleration domain: %s", domain_info)
|
|
53
|
+
return domain_info
|
|
54
|
+
|
|
55
|
+
self.logger.warning("No acceleration domain found for: %s, response: %s", domain, response)
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
def _create_record(self, zone_id, subdomain, main_domain, value, record_type, ttl, line, extra):
|
|
59
|
+
# type: (str, str, str, str, str, int, str | None, dict) -> bool
|
|
60
|
+
"""创建新的加速域名记录 https://cloud.tencent.com/document/api/1552/86338"""
|
|
61
|
+
domain = join_domain(subdomain, main_domain)
|
|
62
|
+
origin = {"OriginType": "IP_DOMAIN", "Origin": value} # type: Any
|
|
63
|
+
res = self._request("CreateAccelerationDomain", ZoneId=zone_id, DomainName=domain, OriginInfo=origin, **extra)
|
|
64
|
+
if res:
|
|
65
|
+
self.logger.info("Acceleration domain created (%s)", res.get("RequestId"))
|
|
66
|
+
return True
|
|
67
|
+
|
|
68
|
+
self.logger.error("Failed to create acceleration domain, response: %s", res)
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
def _update_record(self, zone_id, old_record, value, record_type, ttl, line, extra):
|
|
72
|
+
"""更新加速域名的源站 IP 地址 https://cloud.tencent.com/document/api/1552/86335"""
|
|
73
|
+
domain = old_record.get("DomainName")
|
|
74
|
+
# 构建源站信息
|
|
75
|
+
backup = old_record.get("OriginDetail", {}).get("BackupOrigin", "")
|
|
76
|
+
origin = {"OriginType": "IP_DOMAIN", "Origin": value, "BackupOrigin": backup} # type: Any
|
|
77
|
+
response = self._request("ModifyAccelerationDomain", ZoneId=zone_id, DomainName=domain, OriginInfo=origin)
|
|
78
|
+
|
|
79
|
+
if response:
|
|
80
|
+
self.logger.info("Acceleration domain updated (%s)", response.get("RequestId"))
|
|
81
|
+
return True
|
|
82
|
+
self.logger.error("Failed to update acceleration domain origin, response: %s", response)
|
|
83
|
+
return False
|