zxtoolbox 1.1.0__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.
zxtoolbox/__init__.py ADDED
@@ -0,0 +1,28 @@
1
+ from textwrap import dedent
2
+
3
+ __version__ = "0.1.0"
4
+
5
+
6
+ def cowsay(msg):
7
+ """
8
+ copy https://github.com/cs01/pycowsay/blob/master/pycowsay/main.py code
9
+ :param msg:
10
+ :return:
11
+ """
12
+ phrase = " ".join(msg)
13
+ topbar = "-" * len(phrase)
14
+ bottombar = "-" * len(phrase)
15
+ output = dedent(
16
+ """
17
+ %s
18
+ < %s >
19
+ %s
20
+ \ ^__^
21
+ \ (oo)\_______
22
+ (__)\ )\/\\
23
+ ||----w |
24
+ || ||
25
+ """
26
+ % (topbar, phrase, bottombar)
27
+ )
28
+ print(output)
zxtoolbox/cli.py ADDED
@@ -0,0 +1,419 @@
1
+ """ZX Toolbox CLI 入口。
2
+
3
+ 子命令结构:
4
+ zxtool ci [options] - 计算机信息(默认显示简短信息)
5
+ zxtool le <subcommand> - Let's Encrypt 证书管理
6
+ zxtool ssl <subcommand> - 自签 SSL 证书生成
7
+ zxtool totp -k <key> - TOTP 双因素认证解析
8
+ zxtool video -u <url> - 在线视频下载
9
+ zxtool mkdocs <subcommand> - MkDocs 项目管理
10
+ zxtool config <subcommand> - 配置文件管理
11
+ zxtool git <subcommand> - Git 仓库管理(config/pull)
12
+ """
13
+
14
+ import argparse
15
+ import json
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ import zxtoolbox.computer_info as cpi
20
+ import zxtoolbox.pyopt_2fa as opt2fa
21
+ import zxtoolbox.video_download as vd
22
+ import zxtoolbox.ssl_cert as ssl
23
+ import zxtoolbox.git_config as gc
24
+ import zxtoolbox.config_manager as cm
25
+
26
+
27
+ def main():
28
+ parser = argparse.ArgumentParser(
29
+ description="ZX Toolbox - 跨平台工具集合",
30
+ formatter_class=argparse.RawDescriptionHelpFormatter,
31
+ )
32
+ subparsers = parser.add_subparsers(dest="command", help="可用命令")
33
+
34
+ # ========== ci 子命令 - 计算机信息 ==========
35
+ ci_parser = subparsers.add_parser("ci", help="显示计算机信息(默认简短信息)")
36
+ ci_parser.add_argument(
37
+ "-a", "--all", action="store_true", help="显示详细信息"
38
+ )
39
+
40
+ # ========== totp 子命令 - TOTP 解析 ==========
41
+ totp_parser = subparsers.add_parser("totp", help="TOTP 双因素认证解析")
42
+ totp_parser.add_argument(
43
+ "-k", "--key", type=str, required=True, help="TOTP 待解析的 key"
44
+ )
45
+
46
+ # ========== video 子命令 - 视频下载 ==========
47
+ video_parser = subparsers.add_parser("video", help="在线视频下载")
48
+ video_parser.add_argument(
49
+ "-u", "--url", type=str, required=True, help="视频 URL 地址"
50
+ )
51
+ video_parser.add_argument(
52
+ "-o", "--output", type=str, default=None, help="视频输出路径"
53
+ )
54
+
55
+ # ========== ssl 子命令 - SSL 证书生成 ==========
56
+ ssl_parser = subparsers.add_parser("ssl", help="自签泛域名 SSL 证书生成")
57
+ ssl_subparsers = ssl_parser.add_subparsers(dest="ssl_command", help="SSL 子命令")
58
+
59
+ # ssl init
60
+ ssl_init_parser = ssl_subparsers.add_parser("init", help="初始化输出目录结构")
61
+ ssl_init_parser.add_argument(
62
+ "--output", type=str, default=None, help="输出目录路径(默认: ./out)"
63
+ )
64
+
65
+ # ssl root
66
+ ssl_root_parser = ssl_subparsers.add_parser("root", help="生成 Root CA 证书")
67
+ ssl_root_parser.add_argument(
68
+ "--output", type=str, default=None, help="输出目录路径(默认: ./out)"
69
+ )
70
+ ssl_root_parser.add_argument(
71
+ "--force", action="store_true", help="强制重新生成(覆盖已有证书)"
72
+ )
73
+
74
+ # ssl cert
75
+ ssl_cert_parser = ssl_subparsers.add_parser("cert", help="生成域名证书")
76
+ ssl_cert_parser.add_argument(
77
+ "-d", "--domain", nargs="+", required=True,
78
+ help="域名列表,如 example.dev another.dev"
79
+ )
80
+ ssl_cert_parser.add_argument(
81
+ "--output", type=str, default=None, help="输出目录路径(默认: ./out)"
82
+ )
83
+
84
+ # ========== mkdocs 子命令 ==========
85
+ mkdocs_parser = subparsers.add_parser("mkdocs", help="MkDocs 项目管理")
86
+ mkdocs_subparsers = mkdocs_parser.add_subparsers(
87
+ dest="mkdocs_command", help="MkDocs 子命令"
88
+ )
89
+
90
+ # mkdocs create
91
+ mkdocs_create_parser = mkdocs_subparsers.add_parser("create", help="创建新的 MkDocs 项目")
92
+ mkdocs_create_parser.add_argument("project_dir", help="项目目录路径")
93
+ mkdocs_create_parser.add_argument(
94
+ "--name", type=str, default=None, help="站点名称(默认使用目录名)"
95
+ )
96
+
97
+ # mkdocs build
98
+ mkdocs_build_parser = mkdocs_subparsers.add_parser(
99
+ "build", help="构建 MkDocs 项目到指定目录"
100
+ )
101
+ mkdocs_build_parser.add_argument("project_dir", help="MkDocs 项目目录")
102
+ mkdocs_build_parser.add_argument("-o", "--output", type=str, default=None, help="输出目录")
103
+ mkdocs_build_parser.add_argument(
104
+ "-c", "--config", type=str, default=None, help="配置文件路径(相对或绝对)"
105
+ )
106
+ mkdocs_build_parser.add_argument(
107
+ "--strict", action="store_true", help="严格模式(警告视为错误)"
108
+ )
109
+
110
+ # mkdocs batch
111
+ mkdocs_batch_parser = mkdocs_subparsers.add_parser(
112
+ "batch", help="批量构建多个 MkDocs 项目"
113
+ )
114
+ mkdocs_batch_parser.add_argument(
115
+ "config_file",
116
+ nargs="?",
117
+ default=None,
118
+ help="TOML 配置文件路径(默认: ~/.config/zxtool.toml)",
119
+ )
120
+ mkdocs_batch_parser.add_argument(
121
+ "--dry-run", action="store_true", help="仅打印构建计划,不实际执行"
122
+ )
123
+
124
+ # ========== config 子命令 ==========
125
+ config_parser = subparsers.add_parser("config", help="配置文件管理")
126
+ config_subparsers = config_parser.add_subparsers(
127
+ dest="config_command", help="配置子命令"
128
+ )
129
+
130
+ # config init
131
+ config_init_parser = config_subparsers.add_parser(
132
+ "init", help="交互式初始化配置文件"
133
+ )
134
+ config_init_parser.add_argument(
135
+ "--path",
136
+ type=str,
137
+ default=None,
138
+ help="配置文件路径(默认 ~/.config/zxtool.toml)",
139
+ )
140
+ config_init_parser.add_argument(
141
+ "--force", action="store_true", help="覆盖已存在的配置文件"
142
+ )
143
+
144
+ # config show
145
+ config_show_parser = config_subparsers.add_parser("show", help="显示当前配置内容")
146
+ config_show_parser.add_argument(
147
+ "--path",
148
+ type=str,
149
+ default=None,
150
+ help="配置文件路径(默认 ~/.config/zxtool.toml)",
151
+ )
152
+
153
+ # ========== git 子命令 ==========
154
+ git_parser = subparsers.add_parser("git", help="Git 仓库配置管理")
155
+ git_subparsers = git_parser.add_subparsers(dest="git_command", help="Git 子命令")
156
+
157
+ # git config
158
+ gc_config_parser = git_subparsers.add_parser("config", help="管理 Git 仓库 user 配置")
159
+ gc_config_parser.add_argument(
160
+ "config_command",
161
+ nargs="?",
162
+ default=None,
163
+ help="子命令: check (检查) / fill (填充)",
164
+ )
165
+ gc_config_parser.add_argument(
166
+ "project_dir",
167
+ nargs="?",
168
+ default=None,
169
+ help="项目目录路径(默认当前目录)",
170
+ )
171
+ gc_config_parser.add_argument(
172
+ "--config", type=str, default=None, help="zxtool.toml 配置文件路径"
173
+ )
174
+ gc_config_parser.add_argument("--name", type=str, default=None, help="git user.name")
175
+ gc_config_parser.add_argument("--email", type=str, default=None, help="git user.email")
176
+
177
+ # git pull
178
+ git_pull_parser = git_subparsers.add_parser("pull", help="从远程仓库拉取更新")
179
+ git_pull_parser.add_argument(
180
+ "project_dir",
181
+ nargs="?",
182
+ default=None,
183
+ help="项目目录路径(默认当前目录)",
184
+ )
185
+ git_pull_parser.add_argument(
186
+ "--remote", type=str, default=None, help="远程仓库名称(默认使用仓库配置的 upstream)"
187
+ )
188
+ git_pull_parser.add_argument(
189
+ "--branch", type=str, default=None, help="分支名称(默认使用当前分支)"
190
+ )
191
+
192
+ # ========== le 子命令 - Let's Encrypt ==========
193
+ le_parser = subparsers.add_parser("le", help="Let's Encrypt ACME v2 证书管理")
194
+ le_subparsers = le_parser.add_subparsers(dest="le_command", help="LE 子命令")
195
+
196
+ # le issue - 签发证书
197
+ le_issue_parser = le_subparsers.add_parser("issue", help="签发新证书")
198
+ le_issue_parser.add_argument(
199
+ "-d", "--domain", nargs="+", required=True, help="域名列表"
200
+ )
201
+ le_issue_parser.add_argument(
202
+ "--provider", default="manual", help="DNS 提供商 (manual/cloudflare/aliyun)"
203
+ )
204
+ le_issue_parser.add_argument(
205
+ "--provider-config", type=str, default=None, help="提供商配置 (JSON 字符串)"
206
+ )
207
+ le_issue_parser.add_argument(
208
+ "--production", action="store_true", help="使用生产环境(默认测试环境)"
209
+ )
210
+ le_issue_parser.add_argument("--email", default="", help="联系邮箱")
211
+ le_issue_parser.add_argument(
212
+ "--key-size", type=int, default=2048, choices=[2048, 4096], help="RSA 密钥长度"
213
+ )
214
+ le_issue_parser.add_argument("--output", type=str, default=None, help="输出目录")
215
+
216
+ # le renew - 续签
217
+ le_renew_parser = le_subparsers.add_parser("renew", help="续签即将到期的证书")
218
+ le_renew_parser.add_argument(
219
+ "--dry-run", action="store_true", help="仅检查,不执行续签"
220
+ )
221
+ le_renew_parser.add_argument(
222
+ "--provider-config", type=str, default=None, help="提供商配置 (JSON 字符串)"
223
+ )
224
+ le_renew_parser.add_argument("--output", type=str, default=None, help="输出目录")
225
+
226
+ # le batch - 根据配置文件批量签发/续签证书
227
+ le_batch_parser = le_subparsers.add_parser(
228
+ "batch", help="根据配置文件批量签发/续签证书"
229
+ )
230
+ le_batch_parser.add_argument(
231
+ "--le-config", type=str, default=None,
232
+ help="zxtool.toml 配置文件路径(默认: ~/.config/zxtool.toml)"
233
+ )
234
+ le_batch_parser.add_argument(
235
+ "--dry-run", action="store_true", help="仅打印计划,不实际执行"
236
+ )
237
+
238
+ # le status - 查看状态
239
+ le_status_parser = le_subparsers.add_parser("status", help="查看证书状态")
240
+ le_status_parser.add_argument("--output", type=str, default=None, help="输出目录")
241
+
242
+ # le revoke - 吊销
243
+ le_revoke_parser = le_subparsers.add_parser("revoke", help="吊销证书")
244
+ le_revoke_parser.add_argument("-d", "--domain", required=True, help="要吊销的域名")
245
+ le_revoke_parser.add_argument("--provider", default="manual", help="DNS 提供商")
246
+ le_revoke_parser.add_argument(
247
+ "--provider-config", type=str, default=None, help="提供商配置 (JSON 字符串)"
248
+ )
249
+ le_revoke_parser.add_argument("--output", type=str, default=None, help="输出目录")
250
+
251
+ # le init - 初始化
252
+ le_init_parser = le_subparsers.add_parser("init", help="初始化输出目录")
253
+ le_init_parser.add_argument("--output", type=str, default=None, help="输出目录")
254
+
255
+ # ========== 解析参数 ==========
256
+ args = parser.parse_args()
257
+
258
+ # ========== ci 子命令分发 ==========
259
+ if args.command == "ci":
260
+ if args.all:
261
+ cpi.detailed_info()
262
+ else:
263
+ cpi.summary_info()
264
+ return
265
+
266
+ # ========== totp 子命令分发 ==========
267
+ if args.command == "totp":
268
+ opt2fa.parseTotpCdoe(args.key)
269
+ return
270
+
271
+ # ========== video 子命令分发 ==========
272
+ if args.command == "video":
273
+ vd.download_with_progress(args.url, args.output)
274
+ return
275
+
276
+ # ========== ssl 子命令分发 ==========
277
+ if args.command == "ssl":
278
+ ssl_cmd = getattr(args, "ssl_command", None)
279
+ if ssl_cmd == "init":
280
+ out_dir = Path(args.output).resolve() if args.output else Path("out").resolve()
281
+ ssl.init(out_dir)
282
+ elif ssl_cmd == "root":
283
+ out_dir = Path(args.output).resolve() if args.output else Path("out").resolve()
284
+ ssl.generate_root(out_dir, force=args.force)
285
+ elif ssl_cmd == "cert":
286
+ out_dir = Path(args.output).resolve() if args.output else Path("out").resolve()
287
+ ssl.generate_cert(out_dir, args.domain)
288
+ else:
289
+ ssl_parser.print_help()
290
+ return
291
+
292
+ # ========== mkdocs 子命令分发 ==========
293
+ if args.command == "mkdocs":
294
+ import zxtoolbox.mkdocs_manager as mdm
295
+
296
+ mkdocs_cmd = getattr(args, "mkdocs_command", None)
297
+
298
+ if mkdocs_cmd == "create":
299
+ mdm.create_project(args.project_dir, site_name=args.name)
300
+ elif mkdocs_cmd == "build":
301
+ mdm.build_project(
302
+ project_dir=args.project_dir,
303
+ output_dir=args.output,
304
+ config_file=args.config,
305
+ strict=args.strict,
306
+ )
307
+ elif mkdocs_cmd == "batch":
308
+ mdm.batch_build(
309
+ config_path=args.config_file,
310
+ dry_run=args.dry_run,
311
+ )
312
+ else:
313
+ mkdocs_parser.print_help()
314
+ return
315
+
316
+ # ========== config 子命令分发 ==========
317
+ if args.command == "config":
318
+ config_cmd = getattr(args, "config_command", None)
319
+
320
+ if config_cmd == "init":
321
+ cm.interactive_init(config_path=args.path, force=args.force)
322
+ elif config_cmd == "show":
323
+ cm.show_config(config_path=args.path)
324
+ else:
325
+ config_parser.print_help()
326
+ return
327
+
328
+ # ========== git 子命令分发 ==========
329
+ if args.command == "git":
330
+ git_cmd = getattr(args, "git_command", None)
331
+
332
+ if git_cmd == "config":
333
+ config_cmd = getattr(args, "config_command", None)
334
+ if config_cmd == "check":
335
+ result = gc.check_git_config(args.project_dir)
336
+ if result:
337
+ print(f"name: {result['name']}")
338
+ print(f"email: {result['email']}")
339
+ elif config_cmd == "fill":
340
+ gc.fill_git_config(
341
+ project_dir=args.project_dir,
342
+ config_file=args.config,
343
+ name=args.name,
344
+ email=args.email,
345
+ )
346
+ else:
347
+ gc_config_parser.print_help()
348
+ elif git_cmd == "pull":
349
+ gc.git_pull(
350
+ project_dir=args.project_dir,
351
+ remote=args.remote,
352
+ branch=args.branch,
353
+ )
354
+ else:
355
+ git_parser.print_help()
356
+ return
357
+
358
+ # ========== le 子命令分发 ==========
359
+ if args.command == "le":
360
+ import zxtoolbox.letsencrypt as le
361
+
362
+ le_cmd = getattr(args, "le_command", None)
363
+
364
+ provider_config = None
365
+ if getattr(args, "provider_config", None):
366
+ try:
367
+ provider_config = json.loads(args.provider_config)
368
+ except json.JSONDecodeError as e:
369
+ print(f"错误: --provider-config 必须是有效的 JSON: {e}")
370
+ return
371
+
372
+ out_dir = (
373
+ Path(args.output).resolve()
374
+ if getattr(args, "output", None)
375
+ else Path("out_le").resolve()
376
+ )
377
+
378
+ if le_cmd == "issue":
379
+ le.obtain_cert(
380
+ out_dir=out_dir,
381
+ domains=args.domain,
382
+ provider=args.provider,
383
+ provider_config=provider_config,
384
+ staging=not args.production,
385
+ email=args.email,
386
+ key_size=args.key_size,
387
+ )
388
+ elif le_cmd == "renew":
389
+ le.renew_certs(
390
+ out_dir=out_dir,
391
+ provider_config=provider_config,
392
+ dry_run=args.dry_run,
393
+ )
394
+ elif le_cmd == "batch":
395
+ le.batch_obtain_certs(
396
+ config_path=args.le_config,
397
+ dry_run=args.dry_run,
398
+ )
399
+ elif le_cmd == "status":
400
+ le.show_status(out_dir)
401
+ elif le_cmd == "revoke":
402
+ le.revoke_cert(
403
+ out_dir=out_dir,
404
+ domain=args.domain,
405
+ provider=args.provider,
406
+ provider_config=provider_config,
407
+ )
408
+ elif le_cmd == "init":
409
+ le.init(out_dir)
410
+ else:
411
+ le_parser.print_help()
412
+ return
413
+
414
+ # 无子命令时显示帮助
415
+ parser.print_help()
416
+
417
+
418
+ if __name__ == "__main__":
419
+ main()