|
2 | 2 | # -*- coding:utf-8 -*- |
3 | 3 | from argparse import Action, ArgumentParser, Namespace, RawTextHelpFormatter |
4 | 4 | from json import load as loadjson, dump as dumpjson |
5 | | -from os import stat, environ |
| 5 | +from os import stat, environ, path |
6 | 6 | from logging import error, getLevelName |
7 | 7 |
|
8 | | -from time import time |
9 | | - |
10 | 8 | import sys |
11 | 9 |
|
12 | 10 |
|
@@ -46,89 +44,77 @@ def init_config(description, doc, version): |
46 | 44 | epilog=doc, formatter_class=RawTextHelpFormatter) |
47 | 45 | parser.add_argument('-v', '--version', |
48 | 46 | action='version', version=version) |
49 | | - parser.add_argument('-c', '--config', help="run with config file [配置文件路径]") |
| 47 | + parser.add_argument('-c', '--config', help='run with config file [配置文件路径]') |
50 | 48 |
|
51 | 49 | # 参数定义 |
52 | | - parser.add_argument('--dns', help="DNS Provider [DNS服务提供商]", choices=[ |
| 50 | + parser.add_argument('--dns', help='DNS Provider [DNS服务提供商]', choices=[ |
53 | 51 | 'alidns', 'cloudflare', 'dnscom', 'dnspod', 'dnspod_com', 'he', 'huaweidns', 'callback']) |
54 | | - parser.add_argument('--id', help="api ID [授权账户]") |
55 | | - parser.add_argument('--token', help="api token or Secret key [授权访问凭证或密钥]") |
56 | | - parser.add_argument('--index4', nargs="*", action=ExtendAction, |
57 | | - help="list to get ipv4 [IPV4 获取方式]") |
58 | | - parser.add_argument('--index6', nargs="*", action=ExtendAction, |
59 | | - help="list to get ipv6 [IPV6获取方式]") |
60 | | - parser.add_argument('--ipv4', nargs="*", action=ExtendAction, |
61 | | - help="ipv4 domain list [IPV4域名列表]") |
62 | | - parser.add_argument('--ipv6', nargs="*", action=ExtendAction, |
63 | | - help="ipv6 domain list [IPV6域名列表]") |
64 | | - parser.add_argument('--ttl', type=int, help="ttl for DNS [DNS 解析 TTL 时间]") |
65 | | - parser.add_argument('--proxy', nargs="*", action=ExtendAction, |
66 | | - help="https proxy [设置http 代理,多代理逐个尝试直到成功]") |
| 52 | + parser.add_argument('--id', help='api ID [授权账户]') |
| 53 | + parser.add_argument('--token', help='api token or Secret key [授权访问凭证或密钥]') |
| 54 | + parser.add_argument('--index4', nargs='*', action=ExtendAction, |
| 55 | + help='list to get ipv4 [IPV4 获取方式]') |
| 56 | + parser.add_argument('--index6', nargs='*', action=ExtendAction, |
| 57 | + help='list to get ipv6 [IPV6获取方式]') |
| 58 | + parser.add_argument('--ipv4', nargs='*', action=ExtendAction, |
| 59 | + help='ipv4 domain list [IPV4域名列表]') |
| 60 | + parser.add_argument('--ipv6', nargs='*', action=ExtendAction, |
| 61 | + help='ipv6 domain list [IPV6域名列表]') |
| 62 | + parser.add_argument('--ttl', type=int, help='ttl for DNS [DNS 解析 TTL 时间]') |
| 63 | + parser.add_argument('--proxy', nargs='*', action=ExtendAction, |
| 64 | + help='https proxy [设置http 代理,多代理逐个尝试直到成功]') |
67 | 65 | parser.add_argument('--cache', type=str2bool, nargs='?', |
68 | | - const=True, help="cache flag [启用缓存,可配配置路径或开关]") |
69 | | - parser.add_argument('--log.file', metavar="LOG_FILE", |
70 | | - help="log file [日志文件,默认标准输出]") |
| 66 | + const=True, help='cache flag [启用缓存,可配配置路径或开关]') |
| 67 | + parser.add_argument('--log.file', metavar='LOG_FILE', |
| 68 | + help='log file [日志文件,默认标准输出]') |
71 | 69 | parser.add_argument('--log.level', type=log_level, |
72 | | - metavar="|".join(log_levels)) |
| 70 | + metavar='|'.join(log_levels)) |
73 | 71 |
|
74 | 72 | __cli_args = parser.parse_args() |
75 | | - is_configfile_optional = get_config("token") or get_config("id") |
| 73 | + is_configfile_required = not get_config("token") and not get_config("id") |
76 | 74 | config_file = get_config("config") |
77 | | - if not is_configfile_optional or config_file is not None: |
78 | | - __load_config(config_file or "config.json", is_configfile_optional) |
79 | | - __cli_args.config = config_file or "config.json" |
| 75 | + if not config_file: |
| 76 | + # 未指定配置文件且需要读取文件时,依次查找 |
| 77 | + cfgs = [ |
| 78 | + path.abspath('config.json'), |
| 79 | + path.expanduser('~/.ddns/config.json'), |
| 80 | + '/etc/ddns/config.json' |
| 81 | + ] |
| 82 | + config_file = next((cfg for cfg in cfgs if path.isfile(cfg)), cfgs[0]) |
| 83 | + |
| 84 | + if path.isfile(config_file): |
| 85 | + __load_config(config_file) |
| 86 | + __cli_args.config = config_file |
| 87 | + elif is_configfile_required: |
| 88 | + error('Config file is required, but not found: %s', config_file) |
| 89 | + # 如果需要配置文件但没有指定,则自动生成 |
| 90 | + if generate_config(config_file): |
| 91 | + sys.stdout.write( |
| 92 | + 'Default configure file %s is generated.\n' % config_file) |
| 93 | + sys.exit(1) |
| 94 | + else: |
| 95 | + sys.exit('fail to load config from file: %s\n' % config_file) |
80 | 96 |
|
81 | 97 |
|
82 | | -def __load_config(path="config.json", skip_auto_generation=False): |
| 98 | +def __load_config(config_path): |
83 | 99 | """ |
84 | 100 | 加载配置 |
85 | 101 | """ |
86 | 102 | global __config |
87 | 103 | try: |
88 | | - with open(path) as configfile: |
| 104 | + with open(config_path, 'r') as configfile: |
89 | 105 | __config = loadjson(configfile) |
90 | | - __config["config_modified_time"] = stat(path).st_mtime |
| 106 | + __config["config_modified_time"] = stat(config_path).st_mtime |
91 | 107 | if 'log' in __config: |
92 | 108 | if 'level' in __config['log'] and __config['log']['level'] is not None: |
93 | 109 | __config['log.level'] = log_level(__config['log']['level']) |
94 | 110 | if 'file' in __config['log']: |
95 | 111 | __config['log.file'] = __config['log']['file'] |
96 | 112 | elif 'log.level' in __config: |
97 | 113 | __config['log.level'] = log_level(__config['log.level']) |
98 | | - except IOError: |
99 | | - if skip_auto_generation: |
100 | | - __config["config_modified_time"] = time() |
101 | | - return |
102 | | - error(' Config file `%s` does not exist!' % path) |
103 | | - with open(path, 'w') as configfile: |
104 | | - configure = { |
105 | | - "$schema": "https://ddns.newfuture.cc/schema/v4.0.json", |
106 | | - "id": "YOUR ID or EMAIL for DNS Provider", |
107 | | - "token": "YOUR TOKEN or KEY for DNS Provider", |
108 | | - "dns": "dnspod", |
109 | | - "ipv4": [ |
110 | | - "newfuture.cc", |
111 | | - "ddns.newfuture.cc" |
112 | | - ], |
113 | | - "ipv6": [ |
114 | | - "newfuture.cc", |
115 | | - "ipv6.ddns.newfuture.cc" |
116 | | - ], |
117 | | - "index4": "default", |
118 | | - "index6": "default", |
119 | | - "ttl": None, |
120 | | - "proxy": None, |
121 | | - "log": { |
122 | | - "level": "INFO", |
123 | | - "file": None |
124 | | - } |
125 | | - } |
126 | | - dumpjson(configure, configfile, indent=2, sort_keys=True) |
127 | | - sys.stdout.write( |
128 | | - "New template configure file `%s` is generated.\n" % path) |
129 | | - sys.exit(1) |
130 | 114 | except Exception as e: |
131 | | - sys.exit('fail to load config from file: %s\n%s' % (path, e)) |
| 115 | + error('Failed to load config file `%s`: %s', config_path, e) |
| 116 | + raise |
| 117 | + # 重新抛出异常 |
132 | 118 |
|
133 | 119 |
|
134 | 120 | def get_config(key, default=None): |
@@ -167,3 +153,41 @@ def __call__(self, parser, namespace, values, option_string=None): |
167 | 153 | else: |
168 | 154 | items.append(values) |
169 | 155 | setattr(namespace, self.dest, items) |
| 156 | + |
| 157 | + |
| 158 | +def generate_config(config_path): |
| 159 | + """ |
| 160 | + 生成配置文件 |
| 161 | + """ |
| 162 | + configure = { |
| 163 | + '$schema': 'https://ddns.newfuture.cc/schema/v4.0.json', |
| 164 | + 'id': 'YOUR ID or EMAIL for DNS Provider', |
| 165 | + 'token': 'YOUR TOKEN or KEY for DNS Provider', |
| 166 | + 'dns': 'dnspod', |
| 167 | + 'ipv4': [ |
| 168 | + 'newfuture.cc', |
| 169 | + 'ddns.newfuture.cc' |
| 170 | + ], |
| 171 | + 'ipv6': [ |
| 172 | + 'newfuture.cc', |
| 173 | + 'ipv6.ddns.newfuture.cc' |
| 174 | + ], |
| 175 | + 'index4': 'default', |
| 176 | + 'index6': 'default', |
| 177 | + 'ttl': None, |
| 178 | + 'proxy': None, |
| 179 | + 'log': { |
| 180 | + 'level': 'INFO', |
| 181 | + 'file': None |
| 182 | + } |
| 183 | + } |
| 184 | + try: |
| 185 | + with open(config_path, 'w') as f: |
| 186 | + dumpjson(configure, f, indent=2, sort_keys=True) |
| 187 | + return True |
| 188 | + except IOError: |
| 189 | + error('Cannot open config file to write: `%s`!', config_path) |
| 190 | + return False |
| 191 | + except Exception as e: |
| 192 | + error('Failed to write config file `%s`: %s', config_path, e) |
| 193 | + return False |
0 commit comments