Compare commits

...

245 Commits

Author SHA1 Message Date
xiaojunnuo
339554bdbf v1.27.4 2024-11-14 21:53:00 +08:00
xiaojunnuo
9b6b614857 build: prepare to build 2024-11-14 21:51:23 +08:00
xiaojunnuo
e6e99d4239 chore: 2024-11-14 21:50:45 +08:00
xiaojunnuo
f4ae5125dc perf: 公共cname服务支持关闭 2024-11-14 18:31:17 +08:00
xiaojunnuo
c3cfbd8474 fix: 修复未设置pfx密码,导致jks转换报错的bug 2024-11-14 18:06:50 +08:00
xiaojunnuo
86dd03c917 chore: 2024-11-14 18:04:59 +08:00
xiaojunnuo
6410e34bf3 chore: 2024-11-14 00:48:48 +08:00
xiaojunnuo
2db7fee745 chore: 2024-11-14 00:48:20 +08:00
xiaojunnuo
4e8908e715 chore: 2024-11-14 00:42:40 +08:00
xiaojunnuo
67d8020147 chore: 2024-11-14 00:41:33 +08:00
xiaojunnuo
b4b9f33b2c chore: 2024-11-14 00:25:47 +08:00
xiaojunnuo
d091703dc0 chore: 降级为node20 2024-11-14 00:24:54 +08:00
xiaojunnuo
509b5291c3 chore: 2024-11-14 00:22:56 +08:00
xiaojunnuo
111a0823e9 build: publish 2024-11-14 00:20:17 +08:00
xiaojunnuo
48bc7a45a9 build: trigger build image 2024-11-14 00:19:58 +08:00
xiaojunnuo
1eb70d4cfd v1.27.3 2024-11-14 00:18:04 +08:00
xiaojunnuo
eae63b7c57 build: prepare to build 2024-11-14 00:16:24 +08:00
xiaojunnuo
ec0862f99e chore: 2024-11-14 00:15:46 +08:00
xiaojunnuo
79ca6f4acb chore: 升级为node22 2024-11-14 00:13:24 +08:00
xiaojunnuo
66a9690dc9 fix: 修复邮件配置,忽略证书校验设置不生效的bug 2024-11-14 00:12:01 +08:00
xiaojunnuo
01c65578b0 chore: 2024-11-13 23:51:34 +08:00
xiaojunnuo
dd462989b5 chore: 2024-11-13 23:44:01 +08:00
xiaojunnuo
da6ac1626b perf: ipv6支持 2024-11-13 23:37:35 +08:00
xiaojunnuo
a38ff69cbd chore: ipv6支持 2024-11-13 22:42:11 +08:00
xiaojunnuo
70db327eda chore: 2024-11-13 22:10:13 +08:00
xiaojunnuo
8c3f86c690 perf: 优化上传到主机插 路径选择,根据证书格式显示 2024-11-13 22:06:56 +08:00
xiaojunnuo
b66542cb40 chore: 2024-11-13 11:41:43 +08:00
xiaojunnuo
873ad871da chore: 2024-11-13 11:39:40 +08:00
xiaojunnuo
889eaaea92 perf: 支持jks 2024-11-13 11:34:34 +08:00
xiaojunnuo
d2ce72e4aa fix: 修复偶发性cname一直验证超时的bug 2024-11-13 11:11:37 +08:00
xiaojunnuo
bcfac02c96 perf: 修复站点个性化,浏览器标题没有生效的bug 2024-11-13 09:31:56 +08:00
xiaojunnuo
60a2ed48c2 chore: acme-client依赖于basic 2024-11-12 12:25:20 +08:00
xiaojunnuo
087c0b8253 chore: node-acme-client转换为esm 2024-11-12 12:15:06 +08:00
xiaojunnuo
a9a0967a6f fix: 修复ipv6未开启情况下,请求带有ipv6地址域名报ETIMEDOUT的bug 2024-11-12 11:14:48 +08:00
xiaojunnuo
7bbaa3806b chore: 2024-11-12 10:37:01 +08:00
xiaojunnuo
7f910a13d5 chore: 2024-11-12 10:16:36 +08:00
xiaojunnuo
6841c2328e chore: 2024-11-12 10:12:10 +08:00
xiaojunnuo
ae072929df chore: 2024-11-11 13:50:09 +08:00
xiaojunnuo
5d756eb54b chore: 2024-11-11 13:46:06 +08:00
xiaojunnuo
9fe5d6655c chore: 2024-11-11 13:44:49 +08:00
xiaojunnuo
37b5b22713 Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2024-11-11 13:44:08 +08:00
xiaojunnuo
ee731e4759 chore: 2024-11-11 13:43:25 +08:00
xiaojunnuo
0dbe3133cf chore: 2024-11-09 01:40:41 +08:00
xiaojunnuo
843219c38b chore: 2024-11-09 00:59:22 +08:00
xiaojunnuo
810d5f3c1f chore: 2024-11-09 00:40:02 +08:00
xiaojunnuo
4a5bd0db05 chore: 2024-11-09 00:30:55 +08:00
xiaojunnuo
0120e4d1f5 chore: 2024-11-08 23:58:54 +08:00
xiaojunnuo
d199a18a91 chore: 2024-11-08 23:58:04 +08:00
xiaojunnuo
ffc0981fbc chore: 2024-11-08 23:57:17 +08:00
xiaojunnuo
27ca9b027b chore: 2024-11-08 23:57:03 +08:00
xiaojunnuo
b0ff699b31 build: trigger build image 2024-11-08 23:55:20 +08:00
xiaojunnuo
3a0178b294 v1.27.2 2024-11-08 23:50:14 +08:00
xiaojunnuo
7bd40c94c7 build: prepare to build 2024-11-08 23:43:30 +08:00
xiaojunnuo
d1c497df7f chore: 2024-11-08 23:43:19 +08:00
xiaojunnuo
e59deb23c2 build: prepare to build 2024-11-08 23:41:44 +08:00
xiaojunnuo
dd3fc90372 chore: 2024-11-08 18:12:26 +08:00
xiaojunnuo
89686399f9 perf: 执行历史支持点击查看流水线详情 2024-11-08 18:11:26 +08:00
xiaojunnuo
e8b571590e chore: 2024-11-08 17:47:09 +08:00
xiaojunnuo
19294db942 chore: star 2024-11-08 17:33:30 +08:00
xiaojunnuo
f47e4a78d7 Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2024-11-08 16:55:42 +08:00
xiaojunnuo
c58250e1f0 perf: 专业版7天试用 2024-11-08 16:53:45 +08:00
xiaojunnuo
576e60a2b5 fix: 修复某些容器管理ui无法识别端口列表的bug 2024-11-08 14:14:21 +08:00
xiaojunnuo
3f5499be90 chore: 2024-11-08 11:21:13 +08:00
xiaojunnuo
4dcf6e87bc perf: 优化流水线页面切换回来不丢失查询条件 2024-11-08 10:51:41 +08:00
xiaojunnuo
028758c4e0 Merge branch 'v2-dev-cname-common' into v2-dev 2024-11-08 10:15:08 +08:00
xiaojunnuo
edd6615bcf chore: 2024-11-08 10:14:48 +08:00
xiaojunnuo
3c919ee5d1 perf: 支持公共cname服务 2024-11-08 01:31:20 +08:00
xiaojunnuo
fdc6eef921 chore: 2024-11-07 10:05:03 +08:00
xiaojunnuo
0c645b6e66 Merge remote-tracking branch 'origin/v2-dev-cname-common' into v2-dev-cname-common 2024-11-07 10:01:12 +08:00
xiaojunnuo
1ba1007261 fix: 修复删除腾讯云过期证书时间判断上的bug,导致已过期仍然没有删除证书 2024-11-07 09:50:08 +08:00
xiaojunnuo
1c33fb4e14 chore: 2024-11-07 02:22:14 +08:00
xiaojunnuo
cd83a6f209 chore: 2024-11-07 02:05:20 +08:00
xiaojunnuo
17638f3d3a chore: 2024-11-07 00:17:35 +08:00
xiaojunnuo
61b14b52d9 chore: 2024-11-06 02:20:52 +08:00
xiaojunnuo
8dd648f85d chore: 2024-11-06 02:15:30 +08:00
xiaojunnuo
256e27cd90 Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2024-11-06 01:30:16 +08:00
xiaojunnuo
0c3a812825 chore: 2024-11-06 01:29:13 +08:00
xiaojunnuo
80f8fd49f1 chore: 整理依赖 2024-11-06 01:17:36 +08:00
xiaojunnuo
80c500f618 perf: 优化部署到阿里云CDN插件,支持多域名,更易用 2024-11-05 11:38:27 +08:00
xiaojunnuo
77c9a7e2fa chore: 2024-11-04 22:38:58 +08:00
xiaojunnuo
027c0f41e5 chore: 2024-11-04 22:09:38 +08:00
xiaojunnuo
f9fc83bfb0 chore: 2024-11-04 22:09:23 +08:00
xiaojunnuo
372bc2d9d6 chore: 2024-11-04 21:59:32 +08:00
xiaojunnuo
82606e63c0 chore: 2024-11-04 21:58:18 +08:00
xiaojunnuo
3feb3f592c chore: 2024-11-04 21:57:13 +08:00
xiaojunnuo
dfd6857069 chore: 2024-11-04 21:40:41 +08:00
xiaojunnuo
b8724ac8c3 build: trigger build image 2024-11-04 21:38:44 +08:00
xiaojunnuo
1d8515bce0 v1.27.1 2024-11-04 21:37:00 +08:00
xiaojunnuo
c747ffee5a build: prepare to build 2024-11-04 21:35:10 +08:00
xiaojunnuo
7d601b45a2 chore: 2024-11-04 21:34:41 +08:00
xiaojunnuo
8be886daf6 chore: 2024-11-04 21:31:59 +08:00
xiaojunnuo
d471d2416d chore: 2024-11-04 18:09:24 +08:00
xiaojunnuo
f4c00ee0b6 chore: 2024-11-04 18:04:08 +08:00
xiaojunnuo
009c131819 chore: email帮助 2024-11-04 17:14:34 +08:00
xiaojunnuo
b6722897a0 chore: email帮助 2024-11-04 17:00:28 +08:00
xiaojunnuo
1b46278f86 chore: lego改成从github直接下载 2024-11-04 16:39:02 +08:00
xiaojunnuo
1274f56da8 chore: basic 从pipeline中移除 2024-11-04 15:14:56 +08:00
xiaojunnuo
0f572f4cb3 chore: 2024-11-04 13:32:02 +08:00
xiaojunnuo
cbe3498125 chore: 2024-11-04 11:58:44 +08:00
xiaojunnuo
e6ab0b6864 chore: simple nanoid 12位 2024-11-04 10:38:15 +08:00
xiaojunnuo
5b3931ecb7 chore: 2024-11-04 10:34:50 +08:00
xiaojunnuo
ddd70ab8ce Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2024-11-04 09:28:49 +08:00
xiaojunnuo
ba4cc234ae chore: 2024-11-04 09:27:59 +08:00
xiaojunnuo
3563a4cc36 chore: 解压lego之后,设置为可执行 2024-11-02 23:37:51 +08:00
xiaojunnuo
0526734ca1 Merge branch 'v2' into v2-dev 2024-11-02 22:58:16 +08:00
xiaojunnuo
f4f8d45a8e chore: docs 2024-11-02 22:48:11 +08:00
xiaojunnuo
b3153eed64 chore: docs 2024-11-02 22:34:32 +08:00
xiaojunnuo
ba11febad6 chore: docs 2024-11-02 22:26:35 +08:00
xiaojunnuo
0cea8db0f9 chore: 1 2024-11-02 22:05:37 +08:00
xiaojunnuo
8b7572a9e5 chore: 1 2024-11-02 22:04:05 +08:00
xiaojunnuo
dd20af4ba0 Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2024-11-02 21:28:34 +08:00
xiaojunnuo
222ef2f850 chore: 1 2024-11-02 21:23:42 +08:00
xiaojunnuo
b1117ed54a perf: cname 域名映射记录可读性优化 2024-11-01 18:09:32 +08:00
xiaojunnuo
7ad4b55ee0 perf: 禁止页面缓存,点击tab页签可以刷新数据 2024-11-01 10:23:45 +08:00
xiaojunnuo
396dc34a84 perf: 优化时间选择器,自动填写分钟和秒钟 2024-11-01 10:23:27 +08:00
xiaojunnuo
9b4a31fa6a fix: 修复头像没有更新的bug 2024-11-01 10:22:40 +08:00
xiaojunnuo
8c7f976ef5 chore: 1 2024-11-01 02:59:36 +08:00
xiaojunnuo
01f61d3c73 chore: 1 2024-11-01 02:50:39 +08:00
xiaojunnuo
69b4bcbd09 chore: 1 2024-11-01 02:41:02 +08:00
xiaojunnuo
bb397fb8d0 chore: 1 2024-11-01 02:40:33 +08:00
xiaojunnuo
17ecf05215 chore: 1 2024-11-01 02:35:58 +08:00
xiaojunnuo
e340b3e6fa chore: 1 2024-11-01 02:35:29 +08:00
xiaojunnuo
4ab8677173 build: publish 2024-11-01 02:24:43 +08:00
xiaojunnuo
343d803d8e build: trigger build image 2024-11-01 02:24:25 +08:00
xiaojunnuo
c643d7edc3 v1.27.0 2024-11-01 02:22:57 +08:00
xiaojunnuo
a4b37d01ab build: prepare to build 2024-11-01 02:21:12 +08:00
xiaojunnuo
a59eb0c4c7 chore: 1 2024-11-01 02:19:00 +08:00
xiaojunnuo
2dcc3206e1 build: prepare to build 2024-11-01 02:16:16 +08:00
xiaojunnuo
dc9040a68e chore: 1 2024-11-01 02:13:34 +08:00
xiaojunnuo
5160b9fbd6 chore: 1 2024-11-01 01:02:13 +08:00
xiaojunnuo
a2af45e1c7 build: prepare to build 2024-11-01 01:01:29 +08:00
xiaojunnuo
0165ccbaac chore: 1 2024-11-01 00:59:09 +08:00
xiaojunnuo
b817cb4a1b chore: 1 2024-10-31 22:35:05 +08:00
xiaojunnuo
584378a32b Merge branch 'v2' into v2-dev 2024-10-31 21:08:35 +08:00
xiaojunnuo
6d9ef26eca perf: 增加向导 2024-10-31 18:04:51 +08:00
xiaojunnuo
1bc170b069 chore: 2024-10-31 16:21:17 +08:00
xiaojunnuo
babd5897ae perf: 管理控制台数据统计 2024-10-31 16:19:35 +08:00
xiaojunnuo
63ec5b5519 feat: 首页全新改版 2024-10-31 15:14:56 +08:00
xiaojunnuo
e5e468a463 fix: pfx兼容windows server 2016 2024-10-31 13:37:25 +08:00
xiaojunnuo
ee65c9f47d chore: 2024-10-31 10:32:05 +08:00
xiaojunnuo
f92935d93f chore: 教程优化 2024-10-30 17:50:38 +08:00
xiaojunnuo
d282045683 chore: 2024-10-30 16:18:03 +08:00
xiaojunnuo
129bf53edc perf: lego 升级到 4.19.2 2024-10-30 16:12:08 +08:00
xiaojunnuo
6113c388b7 fix: 修复历史记录不能按名称查询的bug 2024-10-30 15:19:35 +08:00
xiaojunnuo
764326ab16 chore: 1 2024-10-30 14:22:48 +08:00
xiaojunnuo
262ad0b51c chore: 1 2024-10-30 13:42:02 +08:00
xiaojunnuo
8cc2b64066 chore: 1 2024-10-30 13:21:28 +08:00
xiaojunnuo
ceb4b76cdb build: publish 2024-10-30 11:14:54 +08:00
xiaojunnuo
80af1fa9e6 build: trigger build image 2024-10-30 11:14:37 +08:00
xiaojunnuo
844fd4358c v1.26.16 2024-10-30 11:13:20 +08:00
xiaojunnuo
79b41954f9 build: prepare to build 2024-10-30 11:12:00 +08:00
xiaojunnuo
5a4a7814e1 chore: 1 2024-10-30 11:11:16 +08:00
xiaojunnuo
c4630aaf7b chore: 1 2024-10-30 10:35:14 +08:00
xiaojunnuo
d35ad50254 chore: 暂时移除jks 2024-10-30 10:24:53 +08:00
xiaojunnuo
b1cc6f2a9c chore: 2024-10-30 10:18:05 +08:00
xiaojunnuo
0d94329940 chore: 2024-10-30 10:18:04 +08:00
xiaojunnuo
04150e1c0a chore: 2024-10-30 10:18:03 +08:00
xiaojunnuo
385757b54b chore: 证书支持jks格式 2024-10-30 10:18:01 +08:00
xiaojunnuo
ccfe922c30 chore: 2024-10-30 01:42:41 +08:00
xiaojunnuo
b3e0546f78 chore: 1 2024-10-30 01:42:40 +08:00
xiaojunnuo
aaaf8d7db3 fix: 修复lego No help topic for 错误 2024-10-29 23:08:40 +08:00
xiaojunnuo
b1b2cd088b perf: 支持白山云cdn部署 2024-10-29 22:18:45 +08:00
xiaojunnuo
d1ea61debc chore: 2024-10-29 18:15:38 +08:00
xiaojunnuo
12cebea29e chore: 2024-10-29 18:13:24 +08:00
xiaojunnuo
81a3fdbc29 perf: 支持华为云cdn 2024-10-29 13:59:20 +08:00
xiaojunnuo
fea4669d82 chore: 2024-10-29 09:54:42 +08:00
xiaojunnuo
241f9ed383 build: publish 2024-10-28 21:59:45 +08:00
xiaojunnuo
3d06ce444c build: trigger build image 2024-10-28 21:59:31 +08:00
xiaojunnuo
06fed944c9 v1.26.15 2024-10-28 21:58:30 +08:00
xiaojunnuo
5d225c2583 build: prepare to build 2024-10-28 21:56:17 +08:00
xiaojunnuo
e626367a06 chore: 2024-10-28 21:55:37 +08:00
xiaojunnuo
5c992c3214 Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2024-10-28 21:34:14 +08:00
xiaojunnuo
5575c83970 perf: 授权加密支持解密查看 2024-10-28 18:20:10 +08:00
xiaojunnuo
6dabad76ba fix: 顶部菜单变...的bug 2024-10-28 17:27:10 +08:00
xiaojunnuo
1c656f8b90 chore: 2024-10-28 16:44:10 +08:00
xiaojunnuo
51b6fed468 perf: 默认证书更新时间设置为35天,增加腾讯云删除过期证书插件,可以避免腾讯云过期证书邮件 2024-10-28 15:31:45 +08:00
xiaojunnuo
f92d918a1e perf: 重置管理员密码同时启用管理员账户,避免之前禁用了,重置密码还是登录不进去 2024-10-28 10:26:14 +08:00
xiaojunnuo
3e290f057f chore: 2024-10-27 11:03:09 +08:00
xiaojunnuo
13eb0231ac chore: 2024-10-27 03:13:09 +08:00
xiaojunnuo
6089f0aa8e chore: 2024-10-27 03:02:20 +08:00
xiaojunnuo
b0c4050567 chore: 2024-10-27 02:58:26 +08:00
xiaojunnuo
3f9244542d build: publish 2024-10-27 02:56:49 +08:00
xiaojunnuo
70b6098ee5 build: trigger build image 2024-10-27 02:56:36 +08:00
xiaojunnuo
1656e91296 v1.26.14 2024-10-27 02:55:31 +08:00
xiaojunnuo
5b7df9c175 build: prepare to build 2024-10-27 02:53:58 +08:00
xiaojunnuo
8d8600aaa8 chore: 2024-10-27 02:52:45 +08:00
xiaojunnuo
54d136cc6a perf: 顶部菜单自定义 2024-10-27 02:51:56 +08:00
xiaojunnuo
661293c189 perf: 用户管理优化头像上传 2024-10-27 00:52:26 +08:00
xiaojunnuo
d10d42e206 perf: 禁用readonly用户 2024-10-27 00:04:02 +08:00
xiaojunnuo
b780eab5f5 chore: 2024-10-26 23:56:13 +08:00
xiaojunnuo
315e43746b perf: 限制其他用户流水线数量 2024-10-26 23:54:49 +08:00
xiaojunnuo
526c48450b fix: 修复启动时自签证书无法保存的bug 2024-10-26 23:24:26 +08:00
xiaojunnuo
abd2dcf2e8 fix: 修复阿里云部署大杀器报插件_还未注册错误的bug 2024-10-26 23:08:10 +08:00
xiaojunnuo
87defa569c chore: 2024-10-26 22:11:10 +08:00
xiaojunnuo
b4db5518db chore: 2024-10-26 20:33:05 +08:00
xiaojunnuo
a50b635424 chore: 2024-10-26 20:32:09 +08:00
xiaojunnuo
40a794f624 build: publish 2024-10-26 20:31:58 +08:00
xiaojunnuo
6876790374 build: trigger build image 2024-10-26 20:31:45 +08:00
xiaojunnuo
586725a15c v1.26.13 2024-10-26 20:30:32 +08:00
xiaojunnuo
34300a19a6 chore: 2024-10-26 20:30:25 +08:00
xiaojunnuo
11def7e42a build: prepare to build 2024-10-26 20:27:43 +08:00
xiaojunnuo
4f3fb3766e chore: 2024-10-26 20:26:11 +08:00
xiaojunnuo
cbc3eb9453 chore: 2024-10-26 20:15:23 +08:00
xiaojunnuo
c31bc0266a chore: client timeout 2024-10-26 19:56:26 +08:00
xiaojunnuo
b6176d7629 chore: client timeout 2024-10-26 19:55:52 +08:00
xiaojunnuo
bc2e78db39 chore: 2024-10-26 19:51:18 +08:00
xiaojunnuo
fdda8985de chore: 2024-10-26 18:10:19 +08:00
xiaojunnuo
be2f0aa435 chore: 优化https server重启 2024-10-26 18:01:06 +08:00
xiaojunnuo
513a5b49c1 chore: 2024-10-26 17:24:48 +08:00
xiaojunnuo
0c50ede129 perf: 更新certd本身的证书文档说明 2024-10-26 17:14:55 +08:00
xiaojunnuo
d5a17f9e6a perf: 支持同时监听https端口,7002 2024-10-26 16:36:57 +08:00
xiaojunnuo
4b09a0a27f chore: 备份恢复 2024-10-26 13:58:04 +08:00
xiaojunnuo
cba38f6e12 chore: 备份恢复 2024-10-26 13:57:19 +08:00
xiaojunnuo
a672043e2e chore: 2024-10-26 13:54:45 +08:00
xiaojunnuo
e1c6d8a2d0 chore: 2024-10-26 12:10:28 +08:00
xiaojunnuo
9041602cfd chore: 2024-10-26 12:05:12 +08:00
xiaojunnuo
bcbefaaa35 fix: deprecated的运行时不要报错,只报警告 2024-10-26 11:20:50 +08:00
xiaojunnuo
925edef0a5 docs: 2024-10-26 11:01:47 +08:00
xiaojunnuo
90c54fd9e0 docs: 2024-10-26 10:59:25 +08:00
xiaojunnuo
95df56cc5c fix: 修复对话框全屏按钮与关闭按钮重叠的bug 2024-10-26 10:17:39 +08:00
xiaojunnuo
ebced940d4 Merge branch 'v2' into v2-dev 2024-10-26 00:10:12 +08:00
xiaojunnuo
b21db8da6b build: publish 2024-10-26 00:01:39 +08:00
xiaojunnuo
55c76cb89c build: trigger build image 2024-10-26 00:01:24 +08:00
xiaojunnuo
11d0daa59a v1.26.12 2024-10-26 00:00:16 +08:00
xiaojunnuo
22764abd38 build: prepare to build 2024-10-25 23:57:17 +08:00
xiaojunnuo
a7414047ee chore: header menu 初步 2024-10-25 23:56:24 +08:00
xiaojunnuo
c4164c66e2 perf: 文件名特殊字符限制输入 2024-10-25 22:49:05 +08:00
xiaojunnuo
a90d1e68ee perf: 支持配置公共ZeroSSL授权 2024-10-25 21:47:28 +08:00
xiaojunnuo
7aac1460c3 chore: 优化域名match 2024-10-25 18:32:47 +08:00
xiaojunnuo
1cc1d1c03c chore: 优化域名match 2024-10-25 17:47:39 +08:00
xiaojunnuo
b421798a1b chore: 2024-10-25 16:51:36 +08:00
xiaojunnuo
f876ac99b0 chore: 2024-10-25 10:57:38 +08:00
xiaojunnuo
4075be7849 perf: 部署到阿里云任意云资源,阿里云部署大杀器 2024-10-24 17:50:42 +08:00
xiaojunnuo
98ef9aa479 chore: 群晖doc 2024-10-24 16:02:29 +08:00
xiaojunnuo
25689efc99 chore: 2024-10-24 14:46:16 +08:00
xiaojunnuo
a8a45d7f75 perf: 支持部署到腾讯云COS 2024-10-24 14:45:51 +08:00
xiaojunnuo
a3ef3fb5cf chore: 2024-10-24 12:10:20 +08:00
xiaojunnuo
d782655cb4 perf: 新增部署到腾讯云CDN-v2,推荐使用 2024-10-23 17:49:38 +08:00
xiaojunnuo
f126f9f932 perf: 新增部署到百度云CDN插件 2024-10-23 16:33:53 +08:00
xiaojunnuo
5b148b7ed9 perf: 优化cron选择器,增加下次触发时间显示 2024-10-23 16:04:57 +08:00
xiaojunnuo
de62abf0e7 Merge branch 'refs/heads/v2-dev' into v2 2024-10-23 10:43:37 +08:00
xiaojunnuo
f9e29ef041 build: publish 2024-10-23 10:38:50 +08:00
xiaojunnuo
11255a1ecf build: trigger build image 2024-10-23 10:38:34 +08:00
xiaojunnuo
0e859d32ee Merge remote-tracking branch 'origin/v2' into v2
# Conflicts:
#	README.md
2024-10-21 11:20:19 +08:00
xiaojunnuo
5840247f3e chore: 1 2024-10-21 11:19:48 +08:00
431 changed files with 7048 additions and 8087 deletions

View File

@@ -0,0 +1,79 @@
name: build-image-for-test
on:
push:
branches: ['v2-dev']
paths:
- "build-dev.trigger"
# schedule:
# - # 国际时间 19:17 执行北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
# - cron: '17 19 * * *'
permissions:
contents: read
jobs:
build-certd-image:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: v2-dev
- name: get_certd_version
id: get_certd_version
uses: actions/github-script@v6
with:
result-encoding: string
script: |
const fs = require('fs');
const path = require('path');
const pnpmWorkspace = "./pnpm-workspace.yaml";
fs.unlinkSync(pnpmWorkspace)
const jsonFilePath = "./packages/ui/certd-server/package.json";
const jsonContent = fs.readFileSync(jsonFilePath, 'utf-8');
const pkg = JSON.parse(jsonContent)
console.log("certd_version:",pkg.version);
return pkg.version
# - name: Use Node.js
# uses: actions/setup-node@v4
# with:
# node-version: 18
# cache: 'npm'
# working-directory: ./packages/ui/certd-client
- run: |
npm install -g pnpm@8.15.7
pnpm install
npm run build
working-directory: ./packages/ui/certd-client
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to aliyun container Registry
uses: docker/login-action@v3
with:
registry: registry.cn-shenzhen.aliyuncs.com
username: ${{ secrets.aliyun_cs_username }}
password: ${{ secrets.aliyun_cs_password }}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.dockerhub_username }}
password: ${{ secrets.dockerhub_password }}
- name: Build default platforms
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
context: ./packages/ui/
tags: |
registry.cn-shenzhen.aliyuncs.com/handsfree/certd-dev:latest
greper/certd-dev:latest

View File

@@ -17,6 +17,9 @@ jobs:
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: true
- name: get_certd_version - name: get_certd_version
id: get_certd_version id: get_certd_version
@@ -76,17 +79,17 @@ jobs:
greper/certd:latest greper/certd:latest
greper/certd:${{steps.get_certd_version.outputs.result}} greper/certd:${{steps.get_certd_version.outputs.result}}
- name: Build armv7 # - name: Build armv7
uses: docker/build-push-action@v6 # uses: docker/build-push-action@v6
with: # with:
platforms: linux/arm/v7 # platforms: linux/arm/v7
push: true # push: true
context: ./packages/ui/ # context: ./packages/ui/
tags: | # tags: |
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7 # registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-armv7 # registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-armv7
greper/certd:armv7 # greper/certd:armv7
greper/certd:${{steps.get_certd_version.outputs.result}}-armv7 # greper/certd:${{steps.get_certd_version.outputs.result}}-armv7
- name: Build agent - name: Build agent
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6

View File

@@ -2,8 +2,8 @@ name: deploy-demo
on: on:
push: push:
branches: ['v2-dev'] branches: ['v2-dev']
# paths: paths:
# - "deploy.trigger" - "deploy.trigger"
workflow_run: workflow_run:
workflows: [ "build-image" ] workflows: [ "build-image" ]
types: types:
@@ -21,6 +21,9 @@ jobs:
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
fetch-depth: 0
ref: v2-dev
- name: get_certd_version - name: get_certd_version
id: get_certd_version id: get_certd_version
uses: actions/github-script@v6 uses: actions/github-script@v6
@@ -37,8 +40,7 @@ jobs:
- uses: GuillaumeFalourd/wait-sleep-action@v1 - uses: GuillaumeFalourd/wait-sleep-action@v1
with: with:
time: '10' # for 60 seconds time: '10' # for 60 seconds
- name: Send HTTP request - name: deploy-certd-demo
id: request
uses: tyrrrz/action-http-request@master uses: tyrrrz/action-http-request@master
with: with:
url: http://flow-openapi.aliyun.com/pipeline/webhook/lzCzlGrLCOHQaTMMt0mG url: http://flow-openapi.aliyun.com/pipeline/webhook/lzCzlGrLCOHQaTMMt0mG
@@ -52,4 +54,14 @@ jobs:
retry-count: 3 retry-count: 3
retry-delay: 5000 retry-delay: 5000
- name: deploy-certd-doc
uses: tyrrrz/action-http-request@master
with:
url: http://flow-openapi.aliyun.com/pipeline/webhook/IiSxLDp9aOhgDUxJPytv
method: POST
body: |
{}
headers: |
Content-Type: application/json
retry-count: 3
retry-delay: 5000

View File

@@ -13,9 +13,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work) - name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
lfs: true
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email - name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
run: | run: |
git config --global user.name "xiaojunnuo" git config --global user.name "xiaojunnuo"

View File

@@ -13,9 +13,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work) - name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
lfs: true
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email - name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
run: | run: |
git config --global user.name "xiaojunnuo" git config --global user.name "xiaojunnuo"

2
.gitignore vendored
View File

@@ -29,3 +29,5 @@ test/**/*.js
/packages/ui/certd-server/data/db.sqlite /packages/ui/certd-server/data/db.sqlite
/packages/ui/certd-server/data/keys.yaml /packages/ui/certd-server/data/keys.yaml
/packages/pro/ /packages/pro/
test.js

View File

@@ -3,6 +3,136 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.4](https://github.com/certd/certd/compare/v1.27.3...v1.27.4) (2024-11-14)
### Bug Fixes
* 修复未设置pfx密码导致jks转换报错的bug ([c3cfbd8](https://github.com/certd/certd/commit/c3cfbd8474155aed4379f91075de37d5d8c73ef0))
### Performance Improvements
* 公共cname服务支持关闭 ([f4ae512](https://github.com/certd/certd/commit/f4ae5125dc4cd97816976779cb3586b5ee78947e))
## [1.27.3](https://github.com/certd/certd/compare/v1.27.2...v1.27.3) (2024-11-13)
### Bug Fixes
* 修复偶发性cname一直验证超时的bug ([d2ce72e](https://github.com/certd/certd/commit/d2ce72e4aaacdf726ba8b91fcd71db40a27714ba))
* 修复邮件配置忽略证书校验设置不生效的bug ([66a9690](https://github.com/certd/certd/commit/66a9690dc958732e1b3c672d965db502296446f9))
* 修复ipv6未开启情况下请求带有ipv6地址域名报ETIMEDOUT的bug ([a9a0967](https://github.com/certd/certd/commit/a9a0967a6f1d0bd27e69f3ec52c31d90d470bc23))
### Performance Improvements
* 修复站点个性化浏览器标题没有生效的bug ([bcfac02](https://github.com/certd/certd/commit/bcfac02c96ceaf23d1a0b05b48d8047da933beaf))
* 优化上传到主机插 路径选择,根据证书格式显示 ([8c3f86c](https://github.com/certd/certd/commit/8c3f86c6909ed91f48bb2880e78834e22f6f6a29))
* 支持jks ([889eaae](https://github.com/certd/certd/commit/889eaaea92818f628b922dae540c026630611707))
* ipv6支持 ([da6ac16](https://github.com/certd/certd/commit/da6ac1626b3574be2fabeeb18a1f10d60bdcbe49))
## [1.27.2](https://github.com/certd/certd/compare/v1.27.1...v1.27.2) (2024-11-08)
### Bug Fixes
* 修复某些容器管理ui无法识别端口列表的bug ([576e60a](https://github.com/certd/certd/commit/576e60a2b52315909e659d2a58cf98b130e69e6f))
* 修复删除腾讯云过期证书时间判断上的bug导致已过期仍然没有删除证书 ([1ba1007](https://github.com/certd/certd/commit/1ba10072615015d91b81fc56a3b01dae6a2ae9d1))
### Performance Improvements
* 优化部署到阿里云CDN插件支持多域名更易用 ([80c500f](https://github.com/certd/certd/commit/80c500f618b169a1f64c57fe442242a4d0d9d833))
* 优化流水线页面切换回来不丢失查询条件 ([4dcf6e8](https://github.com/certd/certd/commit/4dcf6e87bc5f7657ce8a56c5331e8723a0fee8ee))
* 支持公共cname服务 ([3c919ee](https://github.com/certd/certd/commit/3c919ee5d1aef5d26cf3620a7c49d920786bc941))
* 执行历史支持点击查看流水线详情 ([8968639](https://github.com/certd/certd/commit/89686399f90058835435b92872fc236fac990148))
* 专业版7天试用 ([c58250e](https://github.com/certd/certd/commit/c58250e1f065a9bd8b4e82acc1df754504c0010c))
## [1.27.1](https://github.com/certd/certd/compare/v1.27.0...v1.27.1) (2024-11-04)
### Bug Fixes
* 修复头像没有更新的bug ([9b4a31f](https://github.com/certd/certd/commit/9b4a31fa6a32b9cab2e22bd141cf96ca29120445))
### Performance Improvements
* 禁止页面缓存点击tab页签可以刷新数据 ([7ad4b55](https://github.com/certd/certd/commit/7ad4b55ee000c1dd0747832b11107f32b0ffb889))
* 优化时间选择器,自动填写分钟和秒钟 ([396dc34](https://github.com/certd/certd/commit/396dc34a841c7d016b033736afdba8366fb2d211))
* cname 域名映射记录可读性优化 ([b1117ed](https://github.com/certd/certd/commit/b1117ed54a3ef015752999324ff72b821ef5e4b9))
# [1.27.0](https://github.com/certd/certd/compare/v1.26.16...v1.27.0) (2024-10-31)
### Bug Fixes
* 修复历史记录不能按名称查询的bug ([6113c38](https://github.com/certd/certd/commit/6113c388b7fc58b11ca19ff05cc1286d096c8d28))
* pfx兼容windows server 2016 ([e5e468a](https://github.com/certd/certd/commit/e5e468a463f66d02f235de54b7c1e09ace5f1cb1))
### Features
* 首页全新改版 ([63ec5b5](https://github.com/certd/certd/commit/63ec5b5519c760a3330569c0da6dac157302a330))
### Performance Improvements
* 管理控制台数据统计 ([babd589](https://github.com/certd/certd/commit/babd5897ae013ff7c04ebfcbfac8a00d84dd627c))
* 增加向导 ([6d9ef26](https://github.com/certd/certd/commit/6d9ef26ecab71d752c2c55d75aed4fb5f6c05a39))
* lego 升级到 4.19.2 ([129bf53](https://github.com/certd/certd/commit/129bf53edc9bbb001fe49fbd7e239bd1d09cc128))
## [1.26.16](https://github.com/certd/certd/compare/v1.26.15...v1.26.16) (2024-10-30)
### Bug Fixes
* 修复lego No help topic for 错误 ([aaaf8d7](https://github.com/certd/certd/commit/aaaf8d7db34896cf8f2ff8f12eec1ab0cae58f0f))
### Performance Improvements
* 支持白山云cdn部署 ([b1b2cd0](https://github.com/certd/certd/commit/b1b2cd088b684eda764962abd61754c26a204d1c))
* 支持华为云cdn ([81a3fdb](https://github.com/certd/certd/commit/81a3fdbc29b71f380762008cc151493ec97458f9))
## [1.26.15](https://github.com/certd/certd/compare/v1.26.14...v1.26.15) (2024-10-28)
### Bug Fixes
* 顶部菜单变...的bug ([6dabad7](https://github.com/certd/certd/commit/6dabad76baba96be0f8af36a3fbfb9f5182aecf1))
### Performance Improvements
* 默认证书更新时间设置为35天增加腾讯云删除过期证书插件可以避免腾讯云过期证书邮件 ([51b6fed](https://github.com/certd/certd/commit/51b6fed468eaa6f28ce4497ce303ace1a52abb96))
* 授权加密支持解密查看 ([5575c83](https://github.com/certd/certd/commit/5575c839705f6987ad2bdcd33256b0962c6a9c6a))
* 重置管理员密码同时启用管理员账户,避免之前禁用了,重置密码还是登录不进去 ([f92d918](https://github.com/certd/certd/commit/f92d918a1e28e29b794ad4754661ea760c18af46))
## [1.26.14](https://github.com/certd/certd/compare/v1.26.13...v1.26.14) (2024-10-26)
### Bug Fixes
* 修复阿里云部署大杀器报插件_还未注册错误的bug ([abd2dcf](https://github.com/certd/certd/commit/abd2dcf2e85a545321bae6451406d081f773b132))
* 修复启动时自签证书无法保存的bug ([526c484](https://github.com/certd/certd/commit/526c48450bcd37b3ccded9b448f17de8140bdc6e))
### Performance Improvements
* 顶部菜单自定义 ([54d136c](https://github.com/certd/certd/commit/54d136cc6ae122f7c891b7a5c7232fe5de8e5cb5))
* 禁用readonly用户 ([d10d42e](https://github.com/certd/certd/commit/d10d42e20619bb55a50d636b8867ff33db4e3b4b))
* 限制其他用户流水线数量 ([315e437](https://github.com/certd/certd/commit/315e43746baf01682737f82e41579237a48409af))
* 用户管理优化头像上传 ([661293c](https://github.com/certd/certd/commit/661293c189a3abf3cdc953b5225192372f57930d))
## [1.26.13](https://github.com/certd/certd/compare/v1.26.12...v1.26.13) (2024-10-26)
### Bug Fixes
* 修复对话框全屏按钮与关闭按钮重叠的bug ([95df56c](https://github.com/certd/certd/commit/95df56cc5ca5e3eb843cd17cb7078cde47729f1e))
* deprecated的运行时不要报错只报警告 ([bcbefaa](https://github.com/certd/certd/commit/bcbefaaa35cf6d0eec085b3a2c5bfc7c6a8de9e1))
### Performance Improvements
* 更新certd本身的证书文档说明 ([0c50ede](https://github.com/certd/certd/commit/0c50ede129337b82df54575cbd2f4c2a783a0732))
* 支持同时监听https端口7002 ([d5a17f9](https://github.com/certd/certd/commit/d5a17f9e6afd63fda2df0981118480f25a1fac2e))
## [1.26.12](https://github.com/certd/certd/compare/v1.26.11...v1.26.12) (2024-10-25)
### Performance Improvements
* 部署到阿里云任意云资源,阿里云部署大杀器 ([4075be7](https://github.com/certd/certd/commit/4075be7849b140acb92bd8da8a9acbf4eef85180))
* 文件名特殊字符限制输入 ([c4164c6](https://github.com/certd/certd/commit/c4164c66e29f3ec799f98108a344806ca61e94ff))
* 新增部署到百度云CDN插件 ([f126f9f](https://github.com/certd/certd/commit/f126f9f932d37fa01fff1accc7bdd17d349f8db5))
* 新增部署到腾讯云CDN-v2推荐使用 ([d782655](https://github.com/certd/certd/commit/d782655cb4dfbb74138178afbffeee76fc755115))
* 优化cron选择器增加下次触发时间显示 ([5b148b7](https://github.com/certd/certd/commit/5b148b7ed960ca6f7f5b733b2eadd56eeecbd4c2))
* 支持部署到腾讯云COS ([a8a45d7](https://github.com/certd/certd/commit/a8a45d7f757820990e278533277a3deda5ba48f3))
* 支持配置公共ZeroSSL授权 ([a90d1e6](https://github.com/certd/certd/commit/a90d1e68ee9cbc3705223457b8a86f071b150968))
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23) ## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
### Bug Fixes ### Bug Fixes

190
README.md
View File

@@ -5,7 +5,6 @@ Certd 是一个免费全自动申请和自动部署更新SSL证书的管理系
关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具 关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具
## 一、特性 ## 一、特性
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。 本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
@@ -13,91 +12,65 @@ Certd 是一个免费全自动申请和自动部署更新SSL证书的管理系
* 全自动部署更新证书目前支持部署到主机、部署到阿里云、腾讯云等目前已支持30+部署插件) * 全自动部署更新证书目前支持部署到主机、部署到阿里云、腾讯云等目前已支持30+部署插件)
* 支持通配符域名/泛域名,支持多个域名打到一个证书上 * 支持通配符域名/泛域名,支持多个域名打到一个证书上
* 邮件通知 * 邮件通知
* 私有化部署,保障数据安全 * 私有化部署,数据保存本地镜像由Github Actions构建过程公开透明
* 支持sqlitepostgresql数据库 * 支持sqlitepostgresql数据库
## 二、在线体验 ## 二、在线体验
官方Demo地址自助注册后体验 官方Demo地址自助注册后体验
https://certd.handsfree.work/ https://certd.handfree.work/
> 注意数据将不定期清理,不定期停止定时任务,生产使用请自行部署 > 注意数据将不定期清理,不定期停止定时任务,生产使用请自行部署
> 包含敏感信息,务必自己本地部署进行生产使用 > 包含敏感信息,务必自己本地部署进行生产使用
## 三、使用教程 ![首页](./docs/images/start/home.png)
本案例演示如何配置自动申请证书并部署到阿里云CDN然后快要到期前自动更新证书并重新部署
![演示](packages/ui/certd-client/public/static/doc/images/5-view.png) ## 三、使用教程
![演示](packages/ui/certd-client/public/static/doc/images/9-start.png)
![演示](packages/ui/certd-client/public/static/doc/images/10-1-log.png) 仅需3步让你的证书永不过期
### 1. 创建证书流水线
![演示](packages/ui/certd-client/public/static/doc/images/1-add.png)
> 添加成功后,就可以直接运行流水线申请证书了
### 2. 添加部署任务
当然我们一般需要把证书部署到应用上certd支持海量的部署插件您可以根据自身实际情况进行选择比如部署到Nginx、阿里云、腾讯云、K8S、CDN、宝塔、1Panel等等
此处演示部署证书到主机的nginx上
![演示](packages/ui/certd-client/public/static/doc/images/5-1-add-host.png)
如果目前的部署插件都无法满足,您也可以手动下载,然后自行部署
![演示](packages/ui/certd-client/public/static/doc/images/13-3-download.png) ![演示](packages/ui/certd-client/public/static/doc/images/13-3-download.png)
![演示](packages/ui/certd-client/public/static/doc/images/13-1-result.png)
### 3. 定时运行
![演示](packages/ui/certd-client/public/static/doc/images/12-1-log-success.png)
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
-------> [点我查看详细使用步骤演示](./step.md) <-------- -------> [点我查看详细使用步骤演示](./step.md) <--------
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
当前支持的部署插件列表 更多教程请访问文档网站 [certd.docmirror.cn](https://certd.docmirror.cn/)
![演示](./doc/images/plugins.png)
## 四、私有化部署 ## 四、私有化部署
由于证书、授权信息等属于高度敏感数据,请务必私有化部署,保障数据安全 由于证书、授权信息等属于高度敏感数据,请务必私有化部署,保障数据安全
### 4.1 宝塔面板一键部署 您可以根据实际情况从如下方式中选择一种方式进行私有化部署:
1. 安装宝塔面板,前往 [宝塔面板](https://www.bt.cn/u/CL3JHS) 官网选择9.2.0以上正式版的脚本下载安装 1. [宝塔面板方式部署](https://certd.docmirror.cn/guide/install/docker/)
2. [1Panel面板方式部署](https://certd.docmirror.cn/guide/install/1panel/)
3. [Docker方式部署](https://certd.docmirror.cn/guide/install/docker/)
4. [源码方式部署](https://certd.docmirror.cn/guide/install/source/)
2. 安装后登录宝塔面板,在菜单栏中点击 Docker首次进入会提示安装Docker服务点击立即安装按提示完成安装 #### Docker镜像说明
3. 安装完成后在应用商店中找到`certd`(要先点右上角更新应用),点击安装,配置域名等基本信息即可完成安装
### 4.2 宝塔面板容器编排部署
[宝塔面板容器编排部署教程](./doc/deploy/baota/baota.md)
### 4.3 Docker部署
#### 1. 安装docker、docker-compose
1.1 准备一台云服务器
* 【阿里云】云服务器2核2G新老用户同享99元/年,续费同价!【 [立即购买](https://www.aliyun.com/benefit?scm=20140722.M_10244282._.V_1&source=5176.11533457&userCode=qya11txb )】
* 【腾讯云】云服务器2核2G新老用户同享99元/年,续费同价!【 [立即购买](https://cloud.tencent.com/act/cps/redirect?redirect=6094&cps_key=b3ef73330335d7a6efa4a4bbeeb6b2c9&from=console)】
1.2 安装docker
https://docs.docker.com/engine/install/
选择对应的操作系统,按照官方文档执行命令即可
#### 2. 运行certd
[docker-compose.yaml 下载](https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml)
当前版本号: ![](https://img.shields.io/npm/v/%40certd%2Fpipeline)
```bash
# 随便创建一个目录
mkdir certd
# 进入目录
cd certd
# 下载docker-compose.yaml文件或者手动下载放到certd目录下
wget https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml
# 可以根据需要修改里面的配置
# 1.修改镜像版本号【可选】
# 2.配置数据保存路径【可选】
# 3.修改端口号【可选】
vi docker-compose.yaml # 【可选】
# 启动certd
docker compose up -d
```
> 如果提示 没有compose命令,请安装docker-compose
> https://docs.docker.com/compose/install/linux/
#### 3. 镜像说明:
* 国内镜像地址: * 国内镜像地址:
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest` * `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest`
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7``[version]-armv7` * `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7``[version]-armv7`
@@ -109,25 +82,7 @@ docker compose up -d
* 镜像构建通过`Actions`自动执行,过程公开透明,请放心使用 * 镜像构建通过`Actions`自动执行,过程公开透明,请放心使用
* [点我查看镜像构建日志](https://github.com/certd/certd/actions/workflows/build-image.yml) * [点我查看镜像构建日志](https://github.com/certd/certd/actions/workflows/build-image.yml)
![](./doc/images/action-build.jpg) ![](./docs/images/action/action-build.jpg)
#### 4. 访问测试
http://your_server_ip:7001
默认账号密码admin/123456
记得修改密码
### 4.4 源码部署
```shell
# 克隆代码
git clone https://github.com/certd/certd
git checkout v1.26.7 # 这里换成最新版本号
cd certd
# 启动服务
./start.sh
# 数据默认保存在 ./packages/ui/certd-server/data 目录下,注意数据备份
```
如果是windows请先安装`git for windows` ,然后右键,选择`open git bash here`打开终端,再执行`./start.sh`命令
## 五、 升级 ## 五、 升级
@@ -151,7 +106,7 @@ docker compose up -d
## 六、一些说明 ## 六、一些说明
* 本项目ssl证书提供商为letencrypt * 本项目ssl证书提供商为letencrypt/Google/ZeroSSL
* 申请过程遵循acme协议 * 申请过程遵循acme协议
* 需要验证域名所有权一般有两种方式目前本项目仅支持dns-01 * 需要验证域名所有权一般有两种方式目前本项目仅支持dns-01
* http-01 在网站根目录下放置一份txt文件 * http-01 在网站根目录下放置一份txt文件
@@ -165,48 +120,26 @@ docker compose up -d
## 七、不同平台的设置说明 ## 七、不同平台的设置说明
* [Cloudflare](./docs/plugins/cf/cf.md) * 已迁移到新的文档网站,请到常见问题章节查看
* [腾讯云](./docs/plugins/tencent/tencent.md) * [最新文档站链接 https://certd.docmirror.cn](https://certd.docmirror.cn/)
* [windows主机](./docs/plugins/host/host.md)
* [google证书](./docs/plugins/google/google.md)
* [群晖部署certd及证书更新教程](./docs/plugins/synology/index.md)
* [CNAME证书校验方式说明](./docs/feature/cname/index.md)
## 八、问题处理 ## 八、问题处理
### 7.1 忘记管理员密码 ### 7.1 忘记管理员密码
解决方法如下: [重置管理员密码方法](https://certd.docmirror.cn/guide/use/forgotpasswd/)
1. 修改docker-compose.yaml文件将环境变量`certd_system_resetAdminPasswd`改为`true`
```yaml
services:
certd:
environment: # 环境变量
- certd_system_resetAdminPasswd=false
```
2. 重启容器
```shell
docker compose up -d
docker logs -f --tail 500 certd
# 观察日志当日志中输出“重置1号管理员用户的密码完成”即可操作下一步
```
3. 修改docker-compose.yaml`certd_system_resetAdminPasswd`改回`false`
4. 再次重启容器
```shell
docker compose up -d
```
5. 使用`admin/123456`登录系统,请及时修改管理员密码
## 九、联系作者 ## 九、联系作者
如有疑问欢迎加入群聊请备注certd 如有疑问欢迎加入群聊请备注certd
* QQ群141236433
* 微信群:
![](https://ai.handsfree.work/images/exchange_wxqroup.png)
| 加群 | 微信群 | QQ群 |
|---------|-------|-------|
| 二维码 | <img height="230" src="./docs/guide/contact/images/wx.png"> | <img height="230" src="./docs/guide/contact/images/qq.png"> |
也可以加作者好友
| 加作者好友 | 微信 QQ |
|---------|-------------------------------------------------------------|
| 二维码 | <img height="230" src="./docs/guide/contact/images/me.png"> |
加作者好友
<p align="center">
<img height="230" src="./doc/images/me.png">
</p>
## 十、捐赠 ## 十、捐赠
************************ ************************
@@ -222,18 +155,18 @@ https://afdian.com/a/greper
专业版特权对比 专业版特权对比
| 功能 | 免费版 | 专业版 | | 功能 | 免费版 | 专业版 |
|---------|------------------------|-----------------------| |---------|-------------------|-----------------------|
| 免费证书申请 | 免费无限制 | 免费无限制 | | 免费证书申请 | 免费无限制 | 免费无限制 |
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署等 | 支持群晖、宝塔、1Panel等持续开发中 | | 自动部署插件 | 阿里云、腾讯云、七牛、主机部署等 | 支持群晖、宝塔、1Panel等持续开发中 |
| 发邮件功能 | 需要配置 | 免配置 | | 发邮件功能 | 需要配置 | 免配置 |
| 证书流水线条数 | 10条 | 无限制 | | 证书流水线条数 | 10条 | 无限制 |
************************ ************************
## 十一、贡献代码 ## 十一、贡献代码
1. 本地开发 [贡献插件教程](./doc/dev/development.md) 1. 本地开发 [贡献插件](https://certd.docmirror.cn/guide/development/)
2. 作为贡献者,代表您同意您贡献的代码如下许可: 2. 作为贡献者,代表您同意您贡献的代码如下许可:
1. 可以调整开源协议以使其更严格或更宽松。 1. 可以调整开源协议以使其更严格或更宽松。
2. 可以用于商业用途。 2. 可以用于商业用途。
@@ -242,14 +175,17 @@ https://afdian.com/a/greper
## 十二、 开源许可 ## 十二、 开源许可
* 本项目遵循 GNU Affero General Public LicenseAGPL开源协议。 * 本项目遵循 GNU Affero General Public LicenseAGPL开源协议。
* 允许个人和公司使用、复制、修改和分发本项目,禁止任何形式的商业用途 * 允许个人和公司内部自由使用、复制、修改和分发本项目,未获得商业授权情况下禁止任何形式的商业用途
* 未获得商业授权情况下禁止任何对logo、版权信息及授权许可相关代码的修改。 * 未获得商业授权情况下禁止任何对logo、版权信息及授权许可相关代码的修改。
* 如需商业授权,请联系作者。 * 如需商业授权,请联系作者。
## 十三、我的其他项目求Star ## 十三、我的其他项目求Star
* [袖手GPT](https://ai.handsfree.work/) ChatGPT国内可用无需FQ每日免费额度
* [fast-crud](https://gitee.com/fast-crud/fast-crud/) 基于vue3的crud快速开发框架 | 项目名称 | stars | 项目描述 |
* [dev-sidecar](https://github.com/docmirror/dev-sidecar/) 直连访问github工具无需FQ解决github无法访问的问题 |---------------------------------------------------------|-------------------------------------------------------------------------------------------------------|-----------------------------------|
| [袖手AI](https://ai.handsfree.work/) | | 袖手GPT国内可用无需FQ每日免费额度 |
| [fast-crud](https://gitee.com/fast-crud/fast-crud/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/fast-crud/fast-crud?logo=github"/> | 基于vue3的crud快速开发框架 |
| [dev-sidecar](https://github.com/docmirror/dev-sidecar/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/docmirror/dev-sidecar?logo=github"/> | 直连访问github工具无需FQ解决github无法访问的问题 |

1
build-dev.trigger Normal file
View File

@@ -0,0 +1 @@
1

View File

@@ -1 +1 @@
12:34 1

View File

@@ -1,32 +0,0 @@
# 宝塔部署教程
## 编排模版部署
### 创建docker模版
打开docker-compose.yaml
https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml
整个内容复制下来
然后到宝塔里面进到docker的编排模版新建模版
![](./images/1.png)
### 启动应用
![img.png](./images/2.png)
等待启动完成
### 打开应用
http://ip:7001
## 二、一键应用部署
需要宝塔9.2.0版本
### 应用商店
进入应用商店,更新应用列表
### 搜索certd
点击安装

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

View File

View File

@@ -1,96 +0,0 @@
# 本地开发
欢迎贡献插件
## 1.本地调试运行
### 克隆代码
```shell
# 克隆代码
git clone https://github.com/certd/certd
#进入项目目录
cd certd
# 切换到最新版本代码
git checkout v1.26.7 # 这里换成最新版本号
```
### 修改pnpm-workspace.yaml文件
重要否则无法正确加载专业版的access和plugin
```yaml
# pnpm-workspace.yaml
packages:
- 'packages/**' # <--------------注释掉这一行PR时不要提交此修改
- 'packages/ui/**'
```
### 安装依赖和初始化:
```shell
# 安装pnpm如果提示npm命令不存在就需要先安装nodejs
npm install -g pnpm@8.15.7 --registry=https://registry.npmmirror.com
# 使用国内镜像源,如果有代理,就不需要
pnpm config set registry https://registry.npmmirror.com
# 安装依赖
pnpm install
# 初始化构建
npm run init
```
### 启动 server:
```shell
cd packages/ui/certd-server
npm run dev
```
### 启动 client:
```shell
cd packages/ui/certd-client
npm run dev
# 会自动打开浏览器,确认正常运行
```
## 开发插件
进入 `packages/ui/certd-server/src/plugins`
### 1.复制`plugin-demo`目录作为你的插件目录
比如你想做`cloudflare`的插件,那么你可以复制`plugin-demo`目录,将其命名成`plugin-cloudflare`
以下均以`plugin-cloudflare`为例进行说明,你需要将其替换成你的插件名称
### 2. access授权
如果这是一个新的平台它应该有授权方式比如accessKey accessSecret之类的
参考`plugin-cloudflare/access.ts` 修改为你要做的平台的`access`
这样用户就可以在`certd`后台中创建这种授权凭证了
### 3. dns-provider
如果域名是这个平台进行解析的那么你需要实现dns-provider申请证书需要
参考`plugin-cloudflare/dns-provider.ts` 修改为你要做的平台的`dns-provider`
### 4. plugin-deploy
如果这个平台有需要部署证书的地方
参考`plugin-cloudflare/plugins/plugin-deploy-to-xx.ts` 修改为你要做的平台的`plugin-deploy-to-xx`
### 5. 增加导入
`plugin-cloudflare/index.ts`中增加你的插件的`import`
```ts
export * from './dns-provider'
export * from './access'
export * from './plugins/plugin-deploy-to-xx'
````
`./src/plugins/index.ts``import`
```ts
export * from "./plugin-cloudflare.js"
```
## 重启服务进行调试
刷新浏览器,检查你的插件是否工作正常, 确保能够正常进行证书申请和部署
## 提交PR
我们将尽快审核PR

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 KiB

View File

@@ -11,40 +11,44 @@ services:
ports: # 端口映射 ports: # 端口映射
# ↓↓↓↓ ---------------------------------------------------------- 如果端口有冲突可以修改第一个7001为其他不冲突的端口号 # ↓↓↓↓ ---------------------------------------------------------- 如果端口有冲突可以修改第一个7001为其他不冲突的端口号
- "7001:7001" - "7001:7001"
# ↓↓↓↓ ---------------------------------------------------------- https端口可以根据实际情况是否暴露该端口
- "7002:7002"
#↓↓↓↓ -------------------------------------------------------------- 如果出现getaddrinfo ENOTFOUND错误可以尝试设置dns
dns: dns:
# ↓↓↓↓ ---------------------------------------------------------- 如果出现getaddrinfo ENOTFOUND等错误可以尝试修改或注释dns配置 - 223.5.5.5 # 阿里云公共dns
- 223.5.5.5
- 223.6.6.6 - 223.6.6.6
# ↓↓↓↓ ---------------------------------------------------------- 如果你服务器部署在国外可以用8.8.8.8替换上面的dns # # ↓↓↓↓ --------------------------------------------------------- 如果你服务器在腾讯云,可以用这个替换上面阿里云的公共dns
# - 8.8.8.8 # - 119.29.29.29 # 腾讯云公共dns
# - 182.254.116.116
# # ↓↓↓↓ --------------------------------------------------------- 如果你服务器部署在国外可以用这个替换上面阿里云的公共dns
# - 8.8.8.8 # 谷歌公共dns
# - 8.8.4.4 # - 8.8.4.4
# extra_hosts: # extra_hosts:
# ↓↓↓↓ ---------------------------------------------------------- 这里可以配置自定义hosts外网域名可以指向本地局域网ip地址 # # ↓↓↓↓ -------------------------------------------------------- 这里可以配置自定义hosts外网域名可以指向本地局域网ip地址
# - "localdomain.comm:192.168.1.3" # - "localdomain.comm:192.168.1.3"
environment: # 环境变量
- TZ=Asia/Shanghai
# 设置环境变量即可自定义certd配置
# 配置项见: packages/ui/certd-server/src/config/config.default.ts
# 配置规则: certd_ + 配置项, 点号用_代替
# ↓↓↓↓ ------------------------------------ 这里可以设置http代理 environment:
#- HTTPS_PROXY=http://xxxxxx:xx # 设置环境变量即可自定义certd配置
#- HTTP_PROXY=http://xxxxxx:xx # 配置项见: packages/ui/certd-server/src/config/config.default.ts
# ↓↓↓↓ ----------------------------- 如果忘记管理员密码可以设置为true重启之后管理员密码将改成123456然后请及时修改回false # 配置规则: certd_ + 配置项, 点号用_代替
# #↓↓↓↓ ----------------------------- 如果忘记管理员密码可以设置为true重启之后管理员密码将改成123456然后请及时修改回false
- certd_system_resetAdminPasswd=false - certd_system_resetAdminPasswd=false
# ↓↓↓↓ -------------------------- 如果设置为true启动后所有配置了cron的流水线任务都将被立即触发一次 # #↓↓↓↓ ----------------------------- 使用postgresql数据库
- certd_cron_immediateTriggerOnce=false # - certd_flyway_scriptDir=./db/migration-pg # 升级脚本目录
# ↓↓↓↓ -------------------------------- 配置证书和key则表示https方式启动使用https协议访问https://your.domain:7001 # - certd_typeorm_dataSource_default_type=postgres # 数据库类型
#- certd_koa_key=./data/ssl/cert.key # - certd_typeorm_dataSource_default_host=localhost # 数据库地址
#- certd_koa_cert=./data/ssl/cert.crt # - certd_typeorm_dataSource_default_port=5433 # 数据库端口
# - certd_typeorm_dataSource_default_username=postgres # 用户名
# ↓↓↓↓ ------------------------------- 使用postgresql数据库 # - certd_typeorm_dataSource_default_password=yourpasswd # 密码
# - certd_flyway_scriptDir=./db/migration-pg # 升级脚本目录 # - certd_typeorm_dataSource_default_database=certd # 数据库名
# - certd_typeorm_dataSource_default_type=postgres # 数据库类型
# - certd_typeorm_dataSource_default_host=localhost # 数据库地址
# - certd_typeorm_dataSource_default_port=5433 # 数据库端口
# - certd_typeorm_dataSource_default_username=postgres # 用户名
# - certd_typeorm_dataSource_default_password=yourpasswd # 密码
# - certd_typeorm_dataSource_default_database=certd # 数据库名
# #↓↓↓↓ ------------------------------------------------------------- 启用ipv6网络
# networks:
# - ip6net
#networks:
# ip6net:
# enable_ipv6: true
# ipam:
# config:
# - subnet: 2001:db8::/64

View File

@@ -5,7 +5,7 @@ import lightbox from "vitepress-plugin-lightbox";
// https://vitepress.dev/reference/site-config // https://vitepress.dev/reference/site-config
export default defineConfig({ export default defineConfig({
title: "Certd", title: "Certd",
description: "Certd帮助文档,Certd是一款开源免费的全自动SSL证书管理工具自动证书申请、更新、续期通配符证书泛域名证书申请证书自动化部署到阿里云、腾讯云、主机、群晖、宝塔。", description: "Certd帮助文档,Certd是一款开源免费的全自动SSL证书管理工具证书自动化申请部署流水线;自动证书申请、更新、续期;通配符证书,泛域名证书申请;证书自动化部署到阿里云、腾讯云、主机、群晖、宝塔。",
markdown: { markdown: {
config: (md) => { config: (md) => {
// Use lightbox plugin // Use lightbox plugin
@@ -23,12 +23,11 @@ export default defineConfig({
// ], // ],
["meta", { ["meta", {
name: "keywords", name: "keywords",
content: "证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具、Certd、SSL证书自动部署、证书自动化https证书pfx证书der证书TLS证书nginx证书自动续签自动部署" content: "证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具、Certd、SSL证书自动部署、证书自动化https证书pfx证书der证书TLS证书nginx证书自动续签自动部署,SSL平台证书管理平台证书流水线"
}],
["meta", {
name: "google-site-verification",
content: "V5XLTSnXoT15uQotwpxJoQolUo2d5UbSL-TacsyOsC0"
}], }],
["meta", { name: "google-site-verification",content: "V5XLTSnXoT15uQotwpxJoQolUo2d5UbSL-TacsyOsC0"}],
//<meta name="baidu-site-verification" content="codeva-MiWN8Y07Ua" />
["meta", {name: "baidu-site-verification",content: "codeva-MiWN8Y07Ua"}],
["link", { rel: "icon", href: "/static/logo/logo.svg" }] ["link", { rel: "icon", href: "/static/logo/logo.svg" }]
], ],
themeConfig: { themeConfig: {
@@ -58,7 +57,7 @@ export default defineConfig({
nav: [ nav: [
{ text: "首页", link: "/" }, { text: "首页", link: "/" },
{ text: "指南", link: "/guide/" }, { text: "指南", link: "/guide/" },
{ text: "Demo体验", link: "https://certd.handsfree.work" } { text: "Demo体验", link: "https://certd.handfree.work" }
], ],
sidebar: { sidebar: {
"/guide/": [ "/guide/": [
@@ -95,8 +94,13 @@ export default defineConfig({
{ text: "腾讯云密钥获取", link: "/guide/use/tencent/" }, { text: "腾讯云密钥获取", link: "/guide/use/tencent/" },
{ text: "连接windows主机", link: "/guide/use/host/windows.md" }, { text: "连接windows主机", link: "/guide/use/host/windows.md" },
{ text: "Google EAB获取", link: "/guide/use/google/" }, { text: "Google EAB获取", link: "/guide/use/google/" },
{ text: "阿里云相关", link: "/guide/use/aliyun/" },
{ text: "忘记密码", link: "/guide/use/forgotpasswd/" }, { text: "忘记密码", link: "/guide/use/forgotpasswd/" },
{ text: "数据备份", link: "/guide/use/backup/" }, { text: "数据备份", link: "/guide/use/backup/" },
{ text: "Certd本身的证书更新", link: "/guide/use/https/index.md" },
{ text: "js脚本插件使用", link: "/guide/use/custom-script/index.md" },
{ text: "邮箱配置", link: "/guide/use/email/index.md" },
{ text: "IPv6支持", link: "/guide/use/setting/ipv6.md" },
{ text: "如何贡献代码", link: "/guide/development/index.md" }, { text: "如何贡献代码", link: "/guide/development/index.md" },
] ]
}, },

View File

@@ -3,6 +3,165 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.3](https://github.com/certd/certd/compare/v1.27.2...v1.27.3) (2024-11-13)
### Bug Fixes
* 修复偶发性cname一直验证超时的bug ([d2ce72e](https://github.com/certd/certd/commit/d2ce72e4aaacdf726ba8b91fcd71db40a27714ba))
* 修复邮件配置忽略证书校验设置不生效的bug ([66a9690](https://github.com/certd/certd/commit/66a9690dc958732e1b3c672d965db502296446f9))
* 修复ipv6未开启情况下请求带有ipv6地址域名报ETIMEDOUT的bug ([a9a0967](https://github.com/certd/certd/commit/a9a0967a6f1d0bd27e69f3ec52c31d90d470bc23))
### Performance Improvements
* 修复站点个性化浏览器标题没有生效的bug ([bcfac02](https://github.com/certd/certd/commit/bcfac02c96ceaf23d1a0b05b48d8047da933beaf))
* 优化上传到主机插 路径选择,根据证书格式显示 ([8c3f86c](https://github.com/certd/certd/commit/8c3f86c6909ed91f48bb2880e78834e22f6f6a29))
* 支持jks ([889eaae](https://github.com/certd/certd/commit/889eaaea92818f628b922dae540c026630611707))
* ipv6支持 ([da6ac16](https://github.com/certd/certd/commit/da6ac1626b3574be2fabeeb18a1f10d60bdcbe49))
## [1.27.2](https://github.com/certd/certd/compare/v1.27.1...v1.27.2) (2024-11-08)
### Bug Fixes
* 修复某些容器管理ui无法识别端口列表的bug ([576e60a](https://github.com/certd/certd/commit/576e60a2b52315909e659d2a58cf98b130e69e6f))
* 修复删除腾讯云过期证书时间判断上的bug导致已过期仍然没有删除证书 ([1ba1007](https://github.com/certd/certd/commit/1ba10072615015d91b81fc56a3b01dae6a2ae9d1))
### Performance Improvements
* 优化部署到阿里云CDN插件支持多域名更易用 ([80c500f](https://github.com/certd/certd/commit/80c500f618b169a1f64c57fe442242a4d0d9d833))
* 优化流水线页面切换回来不丢失查询条件 ([4dcf6e8](https://github.com/certd/certd/commit/4dcf6e87bc5f7657ce8a56c5331e8723a0fee8ee))
* 支持公共cname服务 ([3c919ee](https://github.com/certd/certd/commit/3c919ee5d1aef5d26cf3620a7c49d920786bc941))
* 执行历史支持点击查看流水线详情 ([8968639](https://github.com/certd/certd/commit/89686399f90058835435b92872fc236fac990148))
* 专业版7天试用 ([c58250e](https://github.com/certd/certd/commit/c58250e1f065a9bd8b4e82acc1df754504c0010c))
## [1.27.1](https://github.com/certd/certd/compare/v1.27.0...v1.27.1) (2024-11-04)
### Bug Fixes
* 修复头像没有更新的bug ([9b4a31f](https://github.com/certd/certd/commit/9b4a31fa6a32b9cab2e22bd141cf96ca29120445))
### Performance Improvements
* 禁止页面缓存点击tab页签可以刷新数据 ([7ad4b55](https://github.com/certd/certd/commit/7ad4b55ee000c1dd0747832b11107f32b0ffb889))
* 优化时间选择器,自动填写分钟和秒钟 ([396dc34](https://github.com/certd/certd/commit/396dc34a841c7d016b033736afdba8366fb2d211))
* cname 域名映射记录可读性优化 ([b1117ed](https://github.com/certd/certd/commit/b1117ed54a3ef015752999324ff72b821ef5e4b9))
# [1.27.0](https://github.com/certd/certd/compare/v1.26.16...v1.27.0) (2024-10-31)
### Bug Fixes
* 修复历史记录不能按名称查询的bug ([6113c38](https://github.com/certd/certd/commit/6113c388b7fc58b11ca19ff05cc1286d096c8d28))
* pfx兼容windows server 2016 ([e5e468a](https://github.com/certd/certd/commit/e5e468a463f66d02f235de54b7c1e09ace5f1cb1))
### Features
* 首页全新改版 ([63ec5b5](https://github.com/certd/certd/commit/63ec5b5519c760a3330569c0da6dac157302a330))
### Performance Improvements
* 管理控制台数据统计 ([babd589](https://github.com/certd/certd/commit/babd5897ae013ff7c04ebfcbfac8a00d84dd627c))
* 增加向导 ([6d9ef26](https://github.com/certd/certd/commit/6d9ef26ecab71d752c2c55d75aed4fb5f6c05a39))
* lego 升级到 4.19.2 ([129bf53](https://github.com/certd/certd/commit/129bf53edc9bbb001fe49fbd7e239bd1d09cc128))
## [1.26.16](https://github.com/certd/certd/compare/v1.26.15...v1.26.16) (2024-10-30)
### Bug Fixes
* 修复lego No help topic for 错误 ([aaaf8d7](https://github.com/certd/certd/commit/aaaf8d7db34896cf8f2ff8f12eec1ab0cae58f0f))
### Performance Improvements
* 支持白山云cdn部署 ([b1b2cd0](https://github.com/certd/certd/commit/b1b2cd088b684eda764962abd61754c26a204d1c))
* 支持华为云cdn ([81a3fdb](https://github.com/certd/certd/commit/81a3fdbc29b71f380762008cc151493ec97458f9))
## [1.26.15](https://github.com/certd/certd/compare/v1.26.14...v1.26.15) (2024-10-28)
### Bug Fixes
* 顶部菜单变...的bug ([6dabad7](https://github.com/certd/certd/commit/6dabad76baba96be0f8af36a3fbfb9f5182aecf1))
### Performance Improvements
* 默认证书更新时间设置为35天增加腾讯云删除过期证书插件可以避免腾讯云过期证书邮件 ([51b6fed](https://github.com/certd/certd/commit/51b6fed468eaa6f28ce4497ce303ace1a52abb96))
* 授权加密支持解密查看 ([5575c83](https://github.com/certd/certd/commit/5575c839705f6987ad2bdcd33256b0962c6a9c6a))
* 重置管理员密码同时启用管理员账户,避免之前禁用了,重置密码还是登录不进去 ([f92d918](https://github.com/certd/certd/commit/f92d918a1e28e29b794ad4754661ea760c18af46))
## [1.26.14](https://github.com/certd/certd/compare/v1.26.13...v1.26.14) (2024-10-26)
### Bug Fixes
* 修复阿里云部署大杀器报插件_还未注册错误的bug ([abd2dcf](https://github.com/certd/certd/commit/abd2dcf2e85a545321bae6451406d081f773b132))
* 修复启动时自签证书无法保存的bug ([526c484](https://github.com/certd/certd/commit/526c48450bcd37b3ccded9b448f17de8140bdc6e))
### Performance Improvements
* 顶部菜单自定义 ([54d136c](https://github.com/certd/certd/commit/54d136cc6ae122f7c891b7a5c7232fe5de8e5cb5))
* 禁用readonly用户 ([d10d42e](https://github.com/certd/certd/commit/d10d42e20619bb55a50d636b8867ff33db4e3b4b))
* 限制其他用户流水线数量 ([315e437](https://github.com/certd/certd/commit/315e43746baf01682737f82e41579237a48409af))
* 用户管理优化头像上传 ([661293c](https://github.com/certd/certd/commit/661293c189a3abf3cdc953b5225192372f57930d))
## [1.26.13](https://github.com/certd/certd/compare/v1.26.12...v1.26.13) (2024-10-26)
### Bug Fixes
* 修复对话框全屏按钮与关闭按钮重叠的bug ([95df56c](https://github.com/certd/certd/commit/95df56cc5ca5e3eb843cd17cb7078cde47729f1e))
* deprecated的运行时不要报错只报警告 ([bcbefaa](https://github.com/certd/certd/commit/bcbefaaa35cf6d0eec085b3a2c5bfc7c6a8de9e1))
### Performance Improvements
* 更新certd本身的证书文档说明 ([0c50ede](https://github.com/certd/certd/commit/0c50ede129337b82df54575cbd2f4c2a783a0732))
* 支持同时监听https端口7002 ([d5a17f9](https://github.com/certd/certd/commit/d5a17f9e6afd63fda2df0981118480f25a1fac2e))
## [1.26.12](https://github.com/certd/certd/compare/v1.26.11...v1.26.12) (2024-10-25)
### Performance Improvements
* 部署到阿里云任意云资源,阿里云部署大杀器 ([4075be7](https://github.com/certd/certd/commit/4075be7849b140acb92bd8da8a9acbf4eef85180))
* 文件名特殊字符限制输入 ([c4164c6](https://github.com/certd/certd/commit/c4164c66e29f3ec799f98108a344806ca61e94ff))
* 新增部署到百度云CDN插件 ([f126f9f](https://github.com/certd/certd/commit/f126f9f932d37fa01fff1accc7bdd17d349f8db5))
* 新增部署到腾讯云CDN-v2推荐使用 ([d782655](https://github.com/certd/certd/commit/d782655cb4dfbb74138178afbffeee76fc755115))
* 优化cron选择器增加下次触发时间显示 ([5b148b7](https://github.com/certd/certd/commit/5b148b7ed960ca6f7f5b733b2eadd56eeecbd4c2))
* 支持部署到腾讯云COS ([a8a45d7](https://github.com/certd/certd/commit/a8a45d7f757820990e278533277a3deda5ba48f3))
* 支持配置公共ZeroSSL授权 ([a90d1e6](https://github.com/certd/certd/commit/a90d1e68ee9cbc3705223457b8a86f071b150968))
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
### Bug Fixes
* 申请证书没有使用到系统设置的http代理的bug ([3db216f](https://github.com/certd/certd/commit/3db216f515ba404cb4330fdab452971b22a50f08))
* 修复移动任务后出现空阶段的bug ([4ea3edd](https://github.com/certd/certd/commit/4ea3edd59e93ca4f5b2e43b20dd4ef33909caddb))
* 修复google证书*.xx.com与xx.com同时申请时报错的bug ([f8b99b8](https://github.com/certd/certd/commit/f8b99b81a23e7e9fd5e05ebd5caf355c41d67a90))
* 允许七牛云cdn插件输入.号开头的通配符域名 ([18ee87d](https://github.com/certd/certd/commit/18ee87daff6eafc2201b58e28d85aafd3cb7a5b9))
### Performance Improvements
* 申请证书启用新的反代地址 ([a705182](https://github.com/certd/certd/commit/a705182b85e51157883e48f23463263793bf3c12))
* 优化日志颜色 ([1291e98](https://github.com/certd/certd/commit/1291e98e821c5b1810aab7f0aebe3f5f5cd44a20))
* 优化证书申请速度和成功率反代地址优化google基本可以稳定请求。增加请求重试。 ([41d9c3a](https://github.com/certd/certd/commit/41d9c3ac8398def541e65351cbe920d4a927182d))
* 优化pfx密码密码输入框让浏览器不自动填写密码 ([ffeede3](https://github.com/certd/certd/commit/ffeede38afa70c5ff6f2015516bead23d2c4df87))
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
### Bug Fixes
* 修复cname服务普通用户access访问权限问题 ([c1e3e2e](https://github.com/certd/certd/commit/c1e3e2ee1f923ee5806479dd5f178c3286a01ae0))
## [1.26.9](https://github.com/certd/certd/compare/v1.26.8...v1.26.9) (2024-10-19)
### Bug Fixes
* 修复普通用户无法校验cname配置的bug ([6285497](https://github.com/certd/certd/commit/62854978bf0bdbe749b42f8e40ab227ab31ec92f))
* 修复切换普通用户登录时左侧菜单没有同步更新的bug ([12116a8](https://github.com/certd/certd/commit/12116a89f43cf8b98f16d2ea6073f6b72a643215))
* 修正邮箱设置跳转路由 ([17d8890](https://github.com/certd/certd/commit/17d88900a1f0e3af609b74597f5b1978230db32d))
### Performance Improvements
* 触发证书重新申请input变化对比规则优化减少升级版本后触发申请证书的情况 ([c46a2a9](https://github.com/certd/certd/commit/c46a2a9a399c2a9a8bb59a48b9fb6e93227cce9b))
* 任务下所有步骤都跳过时,整个任务显示跳过 ([84fd3b2](https://github.com/certd/certd/commit/84fd3b250dd1161ea06c5582fdadece4b29c2e53))
* 授权配置去除前后空格 ([57d8d48](https://github.com/certd/certd/commit/57d8d48046fbf51c52b041d2dec03d51fb018587))
* 数据库备份插件,先压缩再备份 ([304ef49](https://github.com/certd/certd/commit/304ef494fd5787c996ad0dcb6edd2f517afce9e2))
* 优化菜单 ([1f4f157](https://github.com/certd/certd/commit/1f4f15757de1015cf7563f7022599eef58cc93d7))
* 增加文档站 https://certd.docmirror.cn ([6e2ac1c](https://github.com/certd/certd/commit/6e2ac1c089f6ddccb396f1f2738509c05333e1bb))
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15) ## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
### Bug Fixes ### Bug Fixes

View File

@@ -2,19 +2,14 @@
## 1. 交流群 ## 1. 交流群
如有疑问欢迎加入群聊请备注certd 如有疑问欢迎加入群聊请备注certd
### QQ群141236433 如有疑问欢迎加入群聊请备注certd
<p align="center">
<img height="230" src="./images/qq.png">
</p>
### 微信群:
<p align="center">
<img height="230" src="./images/wx.png">
</p>
| 加群 | 微信群 | QQ群 |
|---------|-------|-------|
| 二维码 | <img height="230" src="./images/wx.png"> | <img height="230" src="./images/qq.png"> |
## 2. 加作者好友 ## 2. 加作者好友
<p align="center">
<img height="230" src="./images/me.png"> | 加作者好友 | 微信 QQ |
</p> |---------|-------------------------------------------------------------|
| 二维码 | <img height="230" src="./images/me.png"> |

View File

@@ -1,19 +1,21 @@
# 本地开发 # 本地开发
欢迎贡献插件 欢迎贡献插件
建议nodejs版本 `20.x` 及以上
## 1.本地调试运行 ## 1.本地调试运行
### 克隆代码 ### 克隆代码
```shell ```shell
# 克隆代码 # 克隆代码
git clone https://github.com/certd/certd git clone https://github.com/certd/certd --depth=1
#进入项目目录 #进入项目目录
cd certd cd certd
# 切换到最新版本代码 # 切换到最新版本代码【如果v2分支无法编译请尝试切换到最新版tag】
git checkout v1.26.7 # 这里换成最新版本号 # git checkout v1.27.0 # 这里换成最新版本号
``` ```

View File

@@ -11,7 +11,7 @@
## 2. 原理 ## 2. 原理
* 假设你要申请证书的域名叫:`cert.com` ,它是在`Certd`不支持的服务商注册的 * 假设你要申请证书的域名叫:`cert.com` ,它是在`Certd`不支持的服务商注册的
* 假设还有另外一个域名叫:`proxy.com`,它是在`Certd`支持的服务商注册的。 * 假设我们还有另外一个域名叫:`proxy.com`,它是在`Certd`支持的服务商注册的。
* 当我们按照如下进行配置时 * 当我们按照如下进行配置时
``` ```
CNAME记录手动、固定 TXT记录自动、随机 CNAME记录手动、固定 TXT记录自动、随机
@@ -19,23 +19,21 @@ _acme-challenge.cert.com ---> xxxxx.cname.proxy.com ----> txt-record-abcdefg
``` ```
* 证书颁发机构就可以从`_acme-challenge.cert.com`查到TXT记录 `txt-record-abcdefg`,从而完成域名所有权校验。 * 证书颁发机构就可以从`_acme-challenge.cert.com`查到TXT记录 `txt-record-abcdefg`,从而完成域名所有权校验。
* 以上可以看出 `xxxxx.cname.proxy.com ----> txt-record-abcdefg` 这一段`Certd`可以自动添加的。 * 以上可以看出 `xxxxx.cname.proxy.com ----> txt-record-abcdefg` 这一段`Certd`可以自动添加的。
* 剩下的只需要在你的`proxy.com`域名中手动添加一条固定的`CNAME解析`即可 * 剩下的只需要在你的`cert.com`域名中手动添加一条固定的`CNAME解析`即可
## 3. Certd CNAME使用步骤 ## 3. Certd CNAME使用步骤
1. 准备`一个`支持的服务商的注册的域名(`proxy.com`),或者将你众多域名其中`一个``DNS服务器`转到这几家服务商。
2. 然后到`Certd``CNAME服务管理`界面,用`cname.proxy.com`创建一条默认的CNAME服务提供DNS提供商授权。 1. 创建证书流水线,输入你要申请证书的域名,假设就是`cert.com`,然后选择`CNAME`校验方式
![](./images/cname1.png) 2. 此时需要配置验证计划Certd会生成一个随机的CNAME记录模版例如`_acme-challenge`->`xxxxxx.cname.proxy.com`
2. 然后创建证书流水线,输入`cert.com`,选择`CNAME`校验方式
3. 此时需要配置验证计划Certd会生成一个随机的CNAME记录例如`_acme-challenge`->`xxxxxx.cname.proxy.com`
![](./images/cname2.png) ![](./images/cname2.png)
3. 您需要手动在你的`cert.com`域名中添加CNAME解析点击验,校验成功后就可以开始申请证书了 (此操作每个域名只需要做一次后续可以重复使用注意不要删除添加的CNAME记录) 3. 您需要手动在你的`cert.com`域名中添加CNAME解析点击验,校验成功后就可以开始申请证书了 (此操作每个域名只需要做一次后续可以重复使用注意不要删除添加的CNAME记录)
![](./images/cname3.png) ![](./images/cname3.png)
![](./images/cname4.png) ![](./images/cname4.png)
4. 申请过程中Certd会在`xxxxxx.cname.proxy.com`下自动添加TXT记录。 4. 申请过程中Certd会在`xxxxxx.cname.proxy.com`下自动添加TXT记录。
5. 到此即可自动化申请证书了

View File

@@ -28,3 +28,7 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
* 免费证书过期时间90天以后可能还会缩短所以自动化部署必不可少 * 免费证书过期时间90天以后可能还会缩短所以自动化部署必不可少
* 设置每天自动运行当证书过期前20天会自动重新申请证书并部署 * 设置每天自动运行当证书过期前20天会自动重新申请证书并部署
## 三、证书颁发机构对比
* Let's Encrypt申请最简单。
* Google: 大厂光环兼容性好需要翻墙获取EAB。
* ZeroSSL 有数量限制获取EAB无需翻墙。

View File

@@ -23,8 +23,11 @@ https://1panel.cn/docs/installation/online_installation/
3. 访问测试 3. 访问测试
http://ip:7001 http://ip:7001
https://ip:7002
默认账号密码
admin/123456
登录后请及时修改密码
## 三、升级 ## 三、升级
@@ -42,3 +45,7 @@ http://ip:7001
> 默认数据保存在`/data/certd`目录下,可以手动备份 > 默认数据保存在`/data/certd`目录下,可以手动备份
> 建议配置一条 [数据库备份流水线](../../use/backup/),自动备份 > 建议配置一条 [数据库备份流水线](../../use/backup/),自动备份
## 五、备份恢复
将备份的`db.sqlite`覆盖到原来的位置即可

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -31,19 +31,26 @@
## 二、访问应用 ## 二、访问应用
http://ip:7001 http://ip:7001
https://ip:7002
默认账号密码
admin/123456
登录后请及时修改密码
## 三、如何升级 ## 三、如何升级
### 1. 通用方式 ### 1. 应用商店安装,直接更新镜像即可
先主机上拉取最新镜像,然后面板上重启容器 ![img.png](./images/upgrade.png)
### 2. latest更新方式
在主机上拉取最新镜像,然后面板上重启容器
```shell ```shell
docker pull registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest docker pull registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
``` ```
### 2. 固定版本号方式 ### 3. 固定版本号方式
修改容器编排模版中的镜像版本号,然后面板上重启容器 修改容器编排模版中的镜像版本号,然后面板上重启容器
```shell ```shell
@@ -71,3 +78,7 @@ services:
### 4.3 自动备份 ### 4.3 自动备份
> 建议配置一条 [数据库备份流水线](../../use/backup/),自动备份 > 建议配置一条 [数据库备份流水线](../../use/backup/),自动备份
## 五、备份恢复
将备份的`db.sqlite`覆盖到原来的位置即可

View File

@@ -45,9 +45,10 @@ docker compose up -d
### 3. 访问测试 ### 3. 访问测试
http://your_server_ip:7001 http://your_server_ip:7001
默认账号密码admin/123456 https://your_server_ip:7002
记得修改密码 默认账号密码admin/123456
记得修改密码
## 二、升级 ## 二、升级
@@ -64,6 +65,10 @@ docker pull registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
docker compose down docker compose down
docker compose up -d docker compose up -d
``` ```
## 三、数据备份
> 数据默认存在`/data/certd`目录下,不用担心数据丢失 > 数据默认存在`/data/certd`目录下,不用担心数据丢失
> 建议配置一条[数据库备份流水线](../../use/backup/) 自动备份 > 建议配置一条[数据库备份流水线](../../use/backup/) 自动备份
## 四、备份恢复
将备份的`db.sqlite`覆盖到原来的位置即可

View File

@@ -17,7 +17,8 @@ cd certd
### 访问测试 ### 访问测试
http://your_server_ip:7001 http://your_server_ip:7001
https://your_server_ip:7002
默认账号密码admin/123456 默认账号密码admin/123456
记得修改密码 记得修改密码
@@ -33,8 +34,12 @@ kill -9 $(lsof -t -i:7001)
# 重新编译启动 # 重新编译启动
./start.sh ./start.sh
``` ```
## 三、数据备份
> 数据默认保存在 `./packages/ui/certd-server/data` 目录下 > 数据默认保存在 `./packages/ui/certd-server/data` 目录下
> 建议配置一条[数据库备份流水线](../../use/backup/) 自动备份 > 建议配置一条[数据库备份流水线](../../use/backup/) 自动备份
## 四、备份恢复
将备份的`db.sqlite`覆盖到原来的位置即可

View File

@@ -25,7 +25,8 @@ https://certd.handsfree.work/
### 2. 访问测试 ### 2. 访问测试
http://your_server_ip:7001 http://your_server_ip:7001
https://your_server_ip:7002
默认账号密码admin/123456 默认账号密码admin/123456
记得修改密码 记得修改密码

View File

@@ -0,0 +1,11 @@
# 阿里云相关
## 阿里云客户端请求超时配置
配置环境变量
```shell
ALIYUN_CLIENT_CONNECT_TIMEOUT=10000 # 连接超时,单位毫秒
ALIYUN_CLIENT_READ_TIMEOUT=10000 #读取数据超时,单位毫秒
```

View File

@@ -1,5 +1,17 @@
# 数据库自动备份 # 数据库自动备份
## 一、手动备份
数据库文件根据不同的部署方式保存的位置不一样,您可以手动复制出来进行备份
* docker 默认保存在`/data/certd/db.sqlite`
* 源码: 默认保存在 `./packages/ui/certd-server/data/db.sqlite`
* 宝塔: [手动数据备份位置](https://certd.docmirror.cn/guide/install/baota/#%E5%9B%9B%E3%80%81%E6%95%B0%E6%8D%AE%E5%A4%87%E4%BB%BD)
* 1panel: 默认保存在`/data/certd/db.sqlite`
## 二、自动备份
通过配置数据库自动备份流水线实现数据备份
## 1. 创建自动备份流水线 ## 1. 创建自动备份流水线
![](./images/1.png) ![](./images/1.png)
@@ -7,7 +19,12 @@
![](./images/2.png) ![](./images/2.png)
## 3. 选择备份方法 ## 3. 选择备份方法
![img.png](./images/3.png) ![](./images/3.png)
## 4. 配置定时和失败通知 ## 4. 配置定时和失败通知
![img.png](./images/4.png) ![](./images/4.png)
## 三、备份恢复
将备份的`db.sqlite`覆盖到原来的位置即可

View File

@@ -0,0 +1,81 @@
# 自定义脚本插件
## 1. 介绍
自定义脚本插件是一个通用的插件可以通过编写脚本来实现各种功能例如调用第三方API、执行系统命令、发送邮件等。
## 2. 使用示例
```js
const certPem = ctx.self.cert.crt
const certKey = ctx.self.cert.key
//axios发起http请求上传证书
const res = await ctx.http.request({
url:"your_cert_deploy_url",
method:"post",
data:{
crt : certPem,
key : certKey
}
})
//不能用console.log需要用ctx.logger 才能把日志打印在ui上
ctx.logger.info("上传成功",res.data)
```
## 3. API
下面是`ctx`对象的`typescript`类型定义
```ts
type ctx = {
CertReader: typeof CertReader;
self: CustomScriptPlugin;
//流水线定义
pipeline: Pipeline;
//步骤定义
step: Step;
//日志
logger: Logger;
//当前步骤输入参数跟上一次执行比较是否有变化
inputChanged: boolean;
//授权获取服务
accessService: IAccessService;
//邮件服务
emailService: IEmailService;
//cname记录服务
cnameProxyService: ICnameProxyService;
//插件配置服务
pluginConfigService: IPluginConfigService;
//流水线上下文
pipelineContext: IContext;
//用户上下文
userContext: IContext;
//http请求客户端
http: HttpClient; // http.request(AxiosConfig)
//文件存储
fileStore: FileStore;
//上一次执行结果状态
lastStatus?: Runnable;
//用户取消信号
signal: AbortSignal;
//工具类
utils: typeof utils;
//用户信息
user: UserInfo;
}
type CertInfo = {
crt:string; //fullchain证书即 cert.pem cert.crt
key:string; // 私钥
ic: string; //中间证书
pfx: string;//PFX证书base64编码
der: string;//DER证书base64编码
}
type CustomScriptPlugin = {
//可以获取证书
cert: CertInfo
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@@ -0,0 +1,23 @@
# 邮箱配置
## 腾讯企业邮箱配置
1. 开启smtp
![](./images/qq-3.png)
2. 获取授权码作为密码
![](./images/qq-1.png)
![](./images/qq-2.png)
3. 填写域名、端口和密码
![](./images/qq-0.png)
## QQ邮箱配置
1. smtp配置
```yaml
smtp域名: smtp.qq.com
smtp端口: 465
密码: 授权码,获取方式见下方
是否SSL:
```
2. 获取授权码
![](./images/qq-11.png)

View File

@@ -4,7 +4,10 @@
## windows开启OpenSSH Server ## windows开启OpenSSH Server
### 1. 安装OpenSSH Server ### 1. 安装OpenSSH Server
请前往Microsoft官方文档查看如何开启openSSH
* 下载安装包安装: https://github.com/PowerShell/Win32-OpenSSH/releases OpenSSH-Win64-vxx.xx.x.msi
* 前往Microsoft官方文档查看如何开启openSSH以及其他设置
https://learn.microsoft.com/zh-cn/windows-server/administration/openssh/openssh_install_firstuse?tabs=gui#install-openssh-for-windows https://learn.microsoft.com/zh-cn/windows-server/administration/openssh/openssh_install_firstuse?tabs=gui#install-openssh-for-windows
### 2. 启动OpenSSH Server服务 ### 2. 启动OpenSSH Server服务

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,38 @@
# Certd本身的https证书配置
## 一、启用https
`Certd`默认启用https监听7002端口
如果你想关闭https或者修改端口可以在环境变量中配置
```shell
CERTD_HTTPS_ENABLE=true
CERTD_HTTPS_port=7002
```
## 二、自动更新Certd的https证书
### 1、创建证书流水线
参考Certd顶部的创建证书流水线教程
### 2、配置复制到本机任务
将证书复制到certd的证书安装位置
![](./images/1.png)
![](./images/2.png)
### 3、配置重启Certd任务
重启certd的https server让证书生效
![img.png](./images/3.png)
### 4、配置定时任务
每天定时执行,最终效果如下
![](./images/ok.png)
:::warning
建议将本流水线的触发时间与其他流水线时间错开,避免重启时影响其他流水线的执行
:::

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -0,0 +1,21 @@
# IPv6支持
## 启用IPv6
`docker-compose.yaml`中启用IPv6支持放开如下注释
```yaml
# #↓↓↓↓ ------------------------------------------------------------- 启用ipv6网络
networks:
- ip6net
networks:
ip6net:
enable_ipv6: true
ipam:
config:
- subnet: 2001:db8::/64
```
## 设置双栈网络优先级
可根据实际情况设置
![img.png](./images/ipv6.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -37,4 +37,22 @@
* 群晖上已经设置好了证书(证书建议设置好描述,插件需要根据描述查找证书) * 群晖上已经设置好了证书(证书建议设置好描述,插件需要根据描述查找证书)
## 2. 在certd上配置自动更新群晖证书插件 ## 2. 在certd上配置自动更新群晖证书插件
![](./images/deploy.png) ![](./images/deploy.png)
## 3. 配置任务参数
![](./images/deploy1.png)
## 4. 创建授权
![](./images/deploy2.png)
> 注意群晖上要做两个设置
![](./images/setting2.png)
![](./images/setting1.png)
## 5. 运行部署
点击手动运行即可
![](./images/deploy3.png)
![](./images/deploy4.png)
## 6. 配置通知和自动运行
![](./images/notify.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -6,3 +6,16 @@
打开 https://console.cloud.tencent.com/cam/capi 打开 https://console.cloud.tencent.com/cam/capi
然后按如下方式获取腾讯云的API密钥 然后按如下方式获取腾讯云的API密钥
![](./tencent-access.png) ![](./tencent-access.png)
## 如何避免收到腾讯云证书过期邮件
腾讯云在证书有效期还剩28天时会发送过期通知邮件
您可以通过配置“腾讯云过期证书删除”任务来避免收到此类邮件。
![](./images/delete.png)
注意点:
> 1. 选择腾讯云授权,需授权`服务角色SSL_QCSLinkedRoleInReplaceLoadCertificate`权限
> 2. `1.26.14`版本之前Certd创建的证书流水线默认是到期前20天才更新证书需要将之前创建的证书申请任务的更新天数修改为35天保证删除之前就已经替换掉即将过期证书
![](./images/delete2.png)

BIN
docs/images/start/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

View File

@@ -18,7 +18,7 @@ hero:
link: /guide/tutorial.md link: /guide/tutorial.md
- theme: alt - theme: alt
text: demo体验 text: demo体验
link: https://certd.handsfree.work link: https://certd.handfree.work
features: features:
- title: 全自动申请证书 - title: 全自动申请证书
@@ -28,7 +28,7 @@ features:
- title: 多域名、泛域名打到一个证书上 - title: 多域名、泛域名打到一个证书上
details: 支持通配符域名/泛域名,支持多个域名打到一个证书上 details: 支持通配符域名/泛域名,支持多个域名打到一个证书上
- title: 多证书格式支持 - title: 多证书格式支持
details: 支持pem、pfx、der等多种证书格式支持Google、Letsencrypt、ZeroSSL证书颁发机构 details: 支持pem、pfx、der、jks等多种证书格式支持Google、Letsencrypt、ZeroSSL证书颁发机构
- title: 支持私有化部署 - title: 支持私有化部署
details: 保障数据安全 details: 保障数据安全
- title: 多数据库支持 - title: 多数据库支持

2
docs/public/robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-agent: *
Allow: /

View File

@@ -9,5 +9,5 @@
} }
}, },
"npmClient": "pnpm", "npmClient": "pnpm",
"version": "1.26.11" "version": "1.27.4"
} }

View File

@@ -14,13 +14,14 @@
}, },
"scripts": { "scripts": {
"start": "lerna bootstrap --hoist", "start": "lerna bootstrap --hoist",
"devb": "lerna run dev-build",
"i-all": "lerna link && lerna exec npm install ", "i-all": "lerna link && lerna exec npm install ",
"publish": "npm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits --create-release github && npm run afterpublishOnly && npm run commitAll", "publish": "npm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits --create-release github && npm run afterpublishOnly && npm run commitAll",
"afterpublishOnly": "time /t >build.trigger && git add ./build.trigger && git commit -m \"build: trigger build image\" && TIMEOUT /T 10 && git push", "afterpublishOnly": "npm run copylogs && time /t >build.trigger && git add ./build.trigger && git commit -m \"build: trigger build image\" && TIMEOUT /T 10 && git push",
"transform-sql": "cd ./packages/ui/certd-server/db/ && node --experimental-json-modules transform.js", "transform-sql": "cd ./packages/ui/certd-server/db/ && node --experimental-json-modules transform.js",
"commitAll": "git add . && git commit -m \"build: publish\" && git push && npm run commitPro", "commitAll": "git add . && git commit -m \"build: publish\" && git push && npm run commitPro",
"commitPro": "cd ./packages/core/ && git add . && git commit -m \"build: publish\" && git push", "commitPro": "cd ./packages/core/ && git add . && git commit -m \"build: publish\" && git push",
"copylogs": "copyfiles \"CHANGELOG.md\" ./docs/guide/other/changelogs/", "copylogs": "copyfiles \"CHANGELOG.md\" ./docs/guide/changelogs/",
"prepublishOnly1": "npm run check && lerna run build ", "prepublishOnly1": "npm run check && lerna run build ",
"prepublishOnly2": "npm run check && npm run before-build && lerna run build ", "prepublishOnly2": "npm run check && npm run before-build && lerna run build ",
"before-build": "npm run transform-sql && cd ./packages/core/basic && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"", "before-build": "npm run transform-sql && cd ./packages/core/basic && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"",
@@ -34,9 +35,11 @@
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"axios": "^1.7.7", "axios": "^1.7.7",
"lodash-es": "^4.17.21" "copyfiles": "^2.4.1",
"lodash-es": "^4.17.21",
"typescript": "^5.4.2"
}, },
"workspaces": [ "workspaces": [
"packages/**" "packages/**"
] ]
} }

View File

@@ -0,0 +1,28 @@
{
"extends": [
"plugin:prettier/recommended",
"prettier"
],
"plugins": [
"eslint-plugin-import"
],
"env": {
"mocha": true
},
"rules": {
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off",
"max-len": [
0,
160,
2,
{
"ignoreUrls": true
}
]
}
}

View File

@@ -1,16 +0,0 @@
extends:
- 'airbnb-base'
env:
browser: false
node: true
mocha: true
rules:
indent: [2, 4, { SwitchCase: 1, VariableDeclarator: 1 }]
brace-style: [2, 'stroustrup', { allowSingleLine: true }]
func-names: 0
class-methods-use-this: 0
no-param-reassign: 0
max-len: [1, 200, 2, { ignoreUrls: true, ignoreComments: false }]
import/no-useless-path-segments: 0

View File

@@ -3,6 +3,48 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.4](https://github.com/publishlab/node-acme-client/compare/v1.27.3...v1.27.4) (2024-11-14)
**Note:** Version bump only for package @certd/acme-client
## [1.27.3](https://github.com/publishlab/node-acme-client/compare/v1.27.2...v1.27.3) (2024-11-13)
**Note:** Version bump only for package @certd/acme-client
## [1.27.2](https://github.com/publishlab/node-acme-client/compare/v1.27.1...v1.27.2) (2024-11-08)
**Note:** Version bump only for package @certd/acme-client
## [1.27.1](https://github.com/publishlab/node-acme-client/compare/v1.27.0...v1.27.1) (2024-11-04)
**Note:** Version bump only for package @certd/acme-client
# [1.27.0](https://github.com/publishlab/node-acme-client/compare/v1.26.16...v1.27.0) (2024-10-31)
**Note:** Version bump only for package @certd/acme-client
## [1.26.16](https://github.com/publishlab/node-acme-client/compare/v1.26.15...v1.26.16) (2024-10-30)
**Note:** Version bump only for package @certd/acme-client
## [1.26.15](https://github.com/publishlab/node-acme-client/compare/v1.26.14...v1.26.15) (2024-10-28)
**Note:** Version bump only for package @certd/acme-client
## [1.26.14](https://github.com/publishlab/node-acme-client/compare/v1.26.13...v1.26.14) (2024-10-26)
### Bug Fixes
* 修复启动时自签证书无法保存的bug ([526c484](https://github.com/publishlab/node-acme-client/commit/526c48450bcd37b3ccded9b448f17de8140bdc6e))
## [1.26.13](https://github.com/publishlab/node-acme-client/compare/v1.26.12...v1.26.13) (2024-10-26)
**Note:** Version bump only for package @certd/acme-client
## [1.26.12](https://github.com/publishlab/node-acme-client/compare/v1.26.11...v1.26.12) (2024-10-25)
**Note:** Version bump only for package @certd/acme-client
## [1.26.11](https://github.com/publishlab/node-acme-client/compare/v1.26.10...v1.26.11) (2024-10-23) ## [1.26.11](https://github.com/publishlab/node-acme-client/compare/v1.26.10...v1.26.11) (2024-10-23)
### Bug Fixes ### Bug Fixes

View File

@@ -6,6 +6,38 @@
</dd> </dd>
</dl> </dl>
## Constants
<dl>
<dt><a href="#createPrivateEcdsaKey">createPrivateEcdsaKey</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Generate a private ECDSA key</p>
</dd>
<dt><a href="#getPublicKey">getPublicKey</a> ⇒ <code>buffer</code></dt>
<dd><p>Get a public key derived from a RSA or ECDSA key</p>
</dd>
<dt><a href="#getPemBodyAsB64u">getPemBodyAsB64u</a> ⇒ <code>string</code></dt>
<dd><p>Parse body of PEM encoded object and return a Base64URL string
If multiple objects are chained, the first body will be returned</p>
</dd>
<dt><a href="#readCsrDomains">readCsrDomains</a> ⇒ <code>object</code></dt>
<dd><p>Read domains from a Certificate Signing Request</p>
</dd>
<dt><a href="#readCertificateInfo">readCertificateInfo</a> ⇒ <code>object</code></dt>
<dd><p>Read information from a certificate
If multiple certificates are chained, the first will be read</p>
</dd>
<dt><a href="#createCsr">createCsr</a> ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code></dt>
<dd><p>Create a Certificate Signing Request</p>
</dd>
<dt><a href="#createAlpnCertificate">createAlpnCertificate</a> ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code></dt>
<dd><p>Create a self-signed ALPN certificate for TLS-ALPN-01 challenges</p>
<p><a href="https://datatracker.ietf.org/doc/html/rfc8737">https://datatracker.ietf.org/doc/html/rfc8737</a></p>
</dd>
<dt><a href="#isAlpnCertificateAuthorizationValid">isAlpnCertificateAuthorizationValid</a> ⇒ <code>boolean</code></dt>
<dd><p>Validate that a ALPN certificate contains the expected key authorization</p>
</dd>
</dl>
## Functions ## Functions
<dl> <dl>
@@ -15,12 +47,6 @@
<dt><a href="#createPrivateKey">createPrivateKey()</a></dt> <dt><a href="#createPrivateKey">createPrivateKey()</a></dt>
<dd><p>Alias of <code>createPrivateRsaKey()</code></p> <dd><p>Alias of <code>createPrivateRsaKey()</code></p>
</dd> </dd>
<dt><a href="#createPrivateEcdsaKey">createPrivateEcdsaKey([namedCurve])</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Generate a private ECDSA key</p>
</dd>
<dt><a href="#getPublicKey">getPublicKey(keyPem)</a> ⇒ <code>buffer</code></dt>
<dd><p>Get a public key derived from a RSA or ECDSA key</p>
</dd>
<dt><a href="#getJwk">getJwk(keyPem)</a> ⇒ <code>object</code></dt> <dt><a href="#getJwk">getJwk(keyPem)</a> ⇒ <code>object</code></dt>
<dd><p>Get a JSON Web Key derived from a RSA or ECDSA key</p> <dd><p>Get a JSON Web Key derived from a RSA or ECDSA key</p>
<p><a href="https://datatracker.ietf.org/doc/html/rfc7517">https://datatracker.ietf.org/doc/html/rfc7517</a></p> <p><a href="https://datatracker.ietf.org/doc/html/rfc7517">https://datatracker.ietf.org/doc/html/rfc7517</a></p>
@@ -28,27 +54,6 @@
<dt><a href="#splitPemChain">splitPemChain(chainPem)</a> ⇒ <code>Array.&lt;string&gt;</code></dt> <dt><a href="#splitPemChain">splitPemChain(chainPem)</a> ⇒ <code>Array.&lt;string&gt;</code></dt>
<dd><p>Split chain of PEM encoded objects from string into array</p> <dd><p>Split chain of PEM encoded objects from string into array</p>
</dd> </dd>
<dt><a href="#getPemBodyAsB64u">getPemBodyAsB64u(pem)</a> ⇒ <code>string</code></dt>
<dd><p>Parse body of PEM encoded object and return a Base64URL string
If multiple objects are chained, the first body will be returned</p>
</dd>
<dt><a href="#readCsrDomains">readCsrDomains(csrPem)</a> ⇒ <code>object</code></dt>
<dd><p>Read domains from a Certificate Signing Request</p>
</dd>
<dt><a href="#readCertificateInfo">readCertificateInfo(certPem)</a> ⇒ <code>object</code></dt>
<dd><p>Read information from a certificate
If multiple certificates are chained, the first will be read</p>
</dd>
<dt><a href="#createCsr">createCsr(data, [keyPem])</a> ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code></dt>
<dd><p>Create a Certificate Signing Request</p>
</dd>
<dt><a href="#createAlpnCertificate">createAlpnCertificate(authz, keyAuthorization, [keyPem])</a> ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code></dt>
<dd><p>Create a self-signed ALPN certificate for TLS-ALPN-01 challenges</p>
<p><a href="https://datatracker.ietf.org/doc/html/rfc8737">https://datatracker.ietf.org/doc/html/rfc8737</a></p>
</dd>
<dt><a href="#isAlpnCertificateAuthorizationValid">isAlpnCertificateAuthorizationValid(certPem, keyAuthorization)</a> ⇒ <code>boolean</code></dt>
<dd><p>Validate that a ALPN certificate contains the expected key authorization</p>
</dd>
</dl> </dl>
<a name="crypto"></a> <a name="crypto"></a>
@@ -57,40 +62,12 @@ If multiple certificates are chained, the first will be read</p>
Native Node.js crypto interface Native Node.js crypto interface
**Kind**: global namespace **Kind**: global namespace
<a name="createPrivateRsaKey"></a>
## createPrivateRsaKey([modulusLength]) ⇒ <code>Promise.&lt;buffer&gt;</code>
Generate a private RSA key
**Kind**: global function
**Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded private RSA key
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [modulusLength] | <code>number</code> | <code>2048</code> | Size of the keys modulus in bits, default: `2048` |
**Example**
Generate private RSA key
```js
const privateKey = await acme.crypto.createPrivateRsaKey();
```
**Example**
Private RSA key with modulus size 4096
```js
const privateKey = await acme.crypto.createPrivateRsaKey(4096);
```
<a name="createPrivateKey"></a>
## createPrivateKey()
Alias of `createPrivateRsaKey()`
**Kind**: global function
<a name="createPrivateEcdsaKey"></a> <a name="createPrivateEcdsaKey"></a>
## createPrivateEcdsaKey([namedCurve]) ⇒ <code>Promise.&lt;buffer&gt;</code> ## createPrivateEcdsaKey ⇒ <code>Promise.&lt;buffer&gt;</code>
Generate a private ECDSA key Generate a private ECDSA key
**Kind**: global function **Kind**: global constant
**Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded private ECDSA key **Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded private ECDSA key
| Param | Type | Description | | Param | Type | Description |
@@ -109,10 +86,10 @@ const privateKey = await acme.crypto.createPrivateEcdsaKey('P-384');
``` ```
<a name="getPublicKey"></a> <a name="getPublicKey"></a>
## getPublicKey(keyPem) ⇒ <code>buffer</code> ## getPublicKey ⇒ <code>buffer</code>
Get a public key derived from a RSA or ECDSA key Get a public key derived from a RSA or ECDSA key
**Kind**: global function **Kind**: global constant
**Returns**: <code>buffer</code> - PEM encoded public key **Returns**: <code>buffer</code> - PEM encoded public key
| Param | Type | Description | | Param | Type | Description |
@@ -124,44 +101,13 @@ Get public key
```js ```js
const publicKey = acme.crypto.getPublicKey(privateKey); const publicKey = acme.crypto.getPublicKey(privateKey);
``` ```
<a name="getJwk"></a>
## getJwk(keyPem) ⇒ <code>object</code>
Get a JSON Web Key derived from a RSA or ECDSA key
https://datatracker.ietf.org/doc/html/rfc7517
**Kind**: global function
**Returns**: <code>object</code> - JSON Web Key
| Param | Type | Description |
| --- | --- | --- |
| keyPem | <code>buffer</code> \| <code>string</code> | PEM encoded private or public key |
**Example**
Get JWK
```js
const jwk = acme.crypto.getJwk(privateKey);
```
<a name="splitPemChain"></a>
## splitPemChain(chainPem) ⇒ <code>Array.&lt;string&gt;</code>
Split chain of PEM encoded objects from string into array
**Kind**: global function
**Returns**: <code>Array.&lt;string&gt;</code> - Array of PEM objects including headers
| Param | Type | Description |
| --- | --- | --- |
| chainPem | <code>buffer</code> \| <code>string</code> | PEM encoded object chain |
<a name="getPemBodyAsB64u"></a> <a name="getPemBodyAsB64u"></a>
## getPemBodyAsB64u(pem) ⇒ <code>string</code> ## getPemBodyAsB64u ⇒ <code>string</code>
Parse body of PEM encoded object and return a Base64URL string Parse body of PEM encoded object and return a Base64URL string
If multiple objects are chained, the first body will be returned If multiple objects are chained, the first body will be returned
**Kind**: global function **Kind**: global constant
**Returns**: <code>string</code> - Base64URL-encoded body **Returns**: <code>string</code> - Base64URL-encoded body
| Param | Type | Description | | Param | Type | Description |
@@ -170,10 +116,10 @@ If multiple objects are chained, the first body will be returned
<a name="readCsrDomains"></a> <a name="readCsrDomains"></a>
## readCsrDomains(csrPem) ⇒ <code>object</code> ## readCsrDomains ⇒ <code>object</code>
Read domains from a Certificate Signing Request Read domains from a Certificate Signing Request
**Kind**: global function **Kind**: global constant
**Returns**: <code>object</code> - {commonName, altNames} **Returns**: <code>object</code> - {commonName, altNames}
| Param | Type | Description | | Param | Type | Description |
@@ -190,11 +136,11 @@ console.log(`Alt names: ${altNames.join(', ')}`);
``` ```
<a name="readCertificateInfo"></a> <a name="readCertificateInfo"></a>
## readCertificateInfo(certPem) ⇒ <code>object</code> ## readCertificateInfo ⇒ <code>object</code>
Read information from a certificate Read information from a certificate
If multiple certificates are chained, the first will be read If multiple certificates are chained, the first will be read
**Kind**: global function **Kind**: global constant
**Returns**: <code>object</code> - Certificate info **Returns**: <code>object</code> - Certificate info
| Param | Type | Description | | Param | Type | Description |
@@ -215,10 +161,10 @@ console.log(`Alt names: ${altNames.join(', ')}`);
``` ```
<a name="createCsr"></a> <a name="createCsr"></a>
## createCsr(data, [keyPem]) ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code> ## createCsr ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code>
Create a Certificate Signing Request Create a Certificate Signing Request
**Kind**: global function **Kind**: global constant
**Returns**: <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code> - [privateKey, certificateSigningRequest] **Returns**: <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code> - [privateKey, certificateSigningRequest]
| Param | Type | Description | | Param | Type | Description |
@@ -276,12 +222,12 @@ const [, certificateRequest] = await acme.crypto.createCsr({
``` ```
<a name="createAlpnCertificate"></a> <a name="createAlpnCertificate"></a>
## createAlpnCertificate(authz, keyAuthorization, [keyPem]) ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code> ## createAlpnCertificate ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code>
Create a self-signed ALPN certificate for TLS-ALPN-01 challenges Create a self-signed ALPN certificate for TLS-ALPN-01 challenges
https://datatracker.ietf.org/doc/html/rfc8737 https://datatracker.ietf.org/doc/html/rfc8737
**Kind**: global function **Kind**: global constant
**Returns**: <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code> - [privateKey, certificate] **Returns**: <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code> - [privateKey, certificate]
| Param | Type | Description | | Param | Type | Description |
@@ -303,10 +249,10 @@ const [, alpnCertificate] = await acme.crypto.createAlpnCertificate(authz, keyAu
``` ```
<a name="isAlpnCertificateAuthorizationValid"></a> <a name="isAlpnCertificateAuthorizationValid"></a>
## isAlpnCertificateAuthorizationValid(certPem, keyAuthorization) ⇒ <code>boolean</code> ## isAlpnCertificateAuthorizationValid ⇒ <code>boolean</code>
Validate that a ALPN certificate contains the expected key authorization Validate that a ALPN certificate contains the expected key authorization
**Kind**: global function **Kind**: global constant
**Returns**: <code>boolean</code> - True when valid **Returns**: <code>boolean</code> - True when valid
| Param | Type | Description | | Param | Type | Description |
@@ -314,3 +260,62 @@ Validate that a ALPN certificate contains the expected key authorization
| certPem | <code>buffer</code> \| <code>string</code> | PEM encoded certificate | | certPem | <code>buffer</code> \| <code>string</code> | PEM encoded certificate |
| keyAuthorization | <code>string</code> | Expected challenge key authorization | | keyAuthorization | <code>string</code> | Expected challenge key authorization |
<a name="createPrivateRsaKey"></a>
## createPrivateRsaKey([modulusLength]) ⇒ <code>Promise.&lt;buffer&gt;</code>
Generate a private RSA key
**Kind**: global function
**Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded private RSA key
| Param | Type | Description |
| --- | --- | --- |
| [modulusLength] | <code>number</code> | Size of the keys modulus in bits, default: `2048` |
**Example**
Generate private RSA key
```js
const privateKey = await acme.crypto.createPrivateRsaKey();
```
**Example**
Private RSA key with modulus size 4096
```js
const privateKey = await acme.crypto.createPrivateRsaKey(4096);
```
<a name="createPrivateKey"></a>
## createPrivateKey()
Alias of `createPrivateRsaKey()`
**Kind**: global function
<a name="getJwk"></a>
## getJwk(keyPem) ⇒ <code>object</code>
Get a JSON Web Key derived from a RSA or ECDSA key
https://datatracker.ietf.org/doc/html/rfc7517
**Kind**: global function
**Returns**: <code>object</code> - JSON Web Key
| Param | Type | Description |
| --- | --- | --- |
| keyPem | <code>buffer</code> \| <code>string</code> | PEM encoded private or public key |
**Example**
Get JWK
```js
const jwk = acme.crypto.getJwk(privateKey);
```
<a name="splitPemChain"></a>
## splitPemChain(chainPem) ⇒ <code>Array.&lt;string&gt;</code>
Split chain of PEM encoded objects from string into array
**Kind**: global function
**Returns**: <code>Array.&lt;string&gt;</code> - Array of PEM objects including headers
| Param | Type | Description |
| --- | --- | --- |
| chainPem | <code>buffer</code> \| <code>string</code> | PEM encoded object chain |

View File

@@ -8,37 +8,42 @@ major release. Please migrate to the new <code>acme.crypto</code> interface at y
</dd> </dd>
</dl> </dl>
## Constants
<dl>
<dt><a href="#createPublicKey">createPublicKey</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Create public key from a private RSA key</p>
</dd>
<dt><a href="#getPemBody">getPemBody</a> ⇒ <code>string</code></dt>
<dd><p>Parse body of PEM encoded object from buffer or string
If multiple objects are chained, the first body will be returned</p>
</dd>
<dt><a href="#splitPemChain">splitPemChain</a> ⇒ <code>Array.&lt;string&gt;</code></dt>
<dd><p>Split chain of PEM encoded objects from buffer or string into array</p>
</dd>
<dt><a href="#getModulus">getModulus</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Get modulus</p>
</dd>
<dt><a href="#getPublicExponent">getPublicExponent</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Get public exponent</p>
</dd>
<dt><a href="#readCsrDomains">readCsrDomains</a> ⇒ <code>Promise.&lt;object&gt;</code></dt>
<dd><p>Read domains from a Certificate Signing Request</p>
</dd>
<dt><a href="#readCertificateInfo">readCertificateInfo</a> ⇒ <code>Promise.&lt;object&gt;</code></dt>
<dd><p>Read information from a certificate</p>
</dd>
<dt><a href="#createCsr">createCsr</a> ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code></dt>
<dd><p>Create a Certificate Signing Request</p>
</dd>
</dl>
## Functions ## Functions
<dl> <dl>
<dt><a href="#createPrivateKey">createPrivateKey([size])</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt> <dt><a href="#createPrivateKey">createPrivateKey([size])</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Generate a private RSA key</p> <dd><p>Generate a private RSA key</p>
</dd> </dd>
<dt><a href="#createPublicKey">createPublicKey(key)</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Create public key from a private RSA key</p>
</dd>
<dt><a href="#getPemBody">getPemBody(str)</a> ⇒ <code>string</code></dt>
<dd><p>Parse body of PEM encoded object from buffer or string
If multiple objects are chained, the first body will be returned</p>
</dd>
<dt><a href="#splitPemChain">splitPemChain(str)</a> ⇒ <code>Array.&lt;string&gt;</code></dt>
<dd><p>Split chain of PEM encoded objects from buffer or string into array</p>
</dd>
<dt><a href="#getModulus">getModulus(input)</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Get modulus</p>
</dd>
<dt><a href="#getPublicExponent">getPublicExponent(input)</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Get public exponent</p>
</dd>
<dt><a href="#readCsrDomains">readCsrDomains(csr)</a> ⇒ <code>Promise.&lt;object&gt;</code></dt>
<dd><p>Read domains from a Certificate Signing Request</p>
</dd>
<dt><a href="#readCertificateInfo">readCertificateInfo(cert)</a> ⇒ <code>Promise.&lt;object&gt;</code></dt>
<dd><p>Read information from a certificate</p>
</dd>
<dt><a href="#createCsr">createCsr(data, [key])</a> ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code></dt>
<dd><p>Create a Certificate Signing Request</p>
</dd>
</dl> </dl>
<a name="forge"></a> <a name="forge"></a>
@@ -50,34 +55,12 @@ DEPRECATION WARNING: This crypto interface is deprecated and will be removed fro
major release. Please migrate to the new `acme.crypto` interface at your earliest convenience. major release. Please migrate to the new `acme.crypto` interface at your earliest convenience.
**Kind**: global namespace **Kind**: global namespace
<a name="createPrivateKey"></a>
## createPrivateKey([size]) ⇒ <code>Promise.&lt;buffer&gt;</code>
Generate a private RSA key
**Kind**: global function
**Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded private RSA key
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [size] | <code>number</code> | <code>2048</code> | Size of the key, default: `2048` |
**Example**
Generate private RSA key
```js
const privateKey = await acme.forge.createPrivateKey();
```
**Example**
Private RSA key with defined size
```js
const privateKey = await acme.forge.createPrivateKey(4096);
```
<a name="createPublicKey"></a> <a name="createPublicKey"></a>
## createPublicKey(key) ⇒ <code>Promise.&lt;buffer&gt;</code> ## createPublicKey ⇒ <code>Promise.&lt;buffer&gt;</code>
Create public key from a private RSA key Create public key from a private RSA key
**Kind**: global function **Kind**: global constant
**Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded public RSA key **Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded public RSA key
| Param | Type | Description | | Param | Type | Description |
@@ -91,11 +74,11 @@ const publicKey = await acme.forge.createPublicKey(privateKey);
``` ```
<a name="getPemBody"></a> <a name="getPemBody"></a>
## getPemBody(str) ⇒ <code>string</code> ## getPemBody ⇒ <code>string</code>
Parse body of PEM encoded object from buffer or string Parse body of PEM encoded object from buffer or string
If multiple objects are chained, the first body will be returned If multiple objects are chained, the first body will be returned
**Kind**: global function **Kind**: global constant
**Returns**: <code>string</code> - PEM body **Returns**: <code>string</code> - PEM body
| Param | Type | Description | | Param | Type | Description |
@@ -104,10 +87,10 @@ If multiple objects are chained, the first body will be returned
<a name="splitPemChain"></a> <a name="splitPemChain"></a>
## splitPemChain(str) ⇒ <code>Array.&lt;string&gt;</code> ## splitPemChain ⇒ <code>Array.&lt;string&gt;</code>
Split chain of PEM encoded objects from buffer or string into array Split chain of PEM encoded objects from buffer or string into array
**Kind**: global function **Kind**: global constant
**Returns**: <code>Array.&lt;string&gt;</code> - Array of PEM bodies **Returns**: <code>Array.&lt;string&gt;</code> - Array of PEM bodies
| Param | Type | Description | | Param | Type | Description |
@@ -116,10 +99,10 @@ Split chain of PEM encoded objects from buffer or string into array
<a name="getModulus"></a> <a name="getModulus"></a>
## getModulus(input) ⇒ <code>Promise.&lt;buffer&gt;</code> ## getModulus ⇒ <code>Promise.&lt;buffer&gt;</code>
Get modulus Get modulus
**Kind**: global function **Kind**: global constant
**Returns**: <code>Promise.&lt;buffer&gt;</code> - Modulus **Returns**: <code>Promise.&lt;buffer&gt;</code> - Modulus
| Param | Type | Description | | Param | Type | Description |
@@ -135,10 +118,10 @@ const m3 = await acme.forge.getModulus(certificateRequest);
``` ```
<a name="getPublicExponent"></a> <a name="getPublicExponent"></a>
## getPublicExponent(input) ⇒ <code>Promise.&lt;buffer&gt;</code> ## getPublicExponent ⇒ <code>Promise.&lt;buffer&gt;</code>
Get public exponent Get public exponent
**Kind**: global function **Kind**: global constant
**Returns**: <code>Promise.&lt;buffer&gt;</code> - Exponent **Returns**: <code>Promise.&lt;buffer&gt;</code> - Exponent
| Param | Type | Description | | Param | Type | Description |
@@ -154,10 +137,10 @@ const e3 = await acme.forge.getPublicExponent(certificateRequest);
``` ```
<a name="readCsrDomains"></a> <a name="readCsrDomains"></a>
## readCsrDomains(csr) ⇒ <code>Promise.&lt;object&gt;</code> ## readCsrDomains ⇒ <code>Promise.&lt;object&gt;</code>
Read domains from a Certificate Signing Request Read domains from a Certificate Signing Request
**Kind**: global function **Kind**: global constant
**Returns**: <code>Promise.&lt;object&gt;</code> - {commonName, altNames} **Returns**: <code>Promise.&lt;object&gt;</code> - {commonName, altNames}
| Param | Type | Description | | Param | Type | Description |
@@ -174,10 +157,10 @@ console.log(`Alt names: ${altNames.join(', ')}`);
``` ```
<a name="readCertificateInfo"></a> <a name="readCertificateInfo"></a>
## readCertificateInfo(cert) ⇒ <code>Promise.&lt;object&gt;</code> ## readCertificateInfo ⇒ <code>Promise.&lt;object&gt;</code>
Read information from a certificate Read information from a certificate
**Kind**: global function **Kind**: global constant
**Returns**: <code>Promise.&lt;object&gt;</code> - Certificate info **Returns**: <code>Promise.&lt;object&gt;</code> - Certificate info
| Param | Type | Description | | Param | Type | Description |
@@ -198,10 +181,10 @@ console.log(`Alt names: ${altNames.join(', ')}`);
``` ```
<a name="createCsr"></a> <a name="createCsr"></a>
## createCsr(data, [key]) ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code> ## createCsr ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code>
Create a Certificate Signing Request Create a Certificate Signing Request
**Kind**: global function **Kind**: global constant
**Returns**: <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code> - [privateKey, certificateSigningRequest] **Returns**: <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code> - [privateKey, certificateSigningRequest]
| Param | Type | Description | | Param | Type | Description |
@@ -256,3 +239,25 @@ const certificateKey = await acme.forge.createPrivateKey();
const [, certificateRequest] = await acme.forge.createCsr({ const [, certificateRequest] = await acme.forge.createCsr({
altNames: ['test.example.com'], altNames: ['test.example.com'],
}, certificateKey); }, certificateKey);
<a name="createPrivateKey"></a>
## createPrivateKey([size]) <code>Promise.&lt;buffer&gt;</code>
Generate a private RSA key
**Kind**: global function
**Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded private RSA key
| Param | Type | Description |
| --- | --- | --- |
| [size] | <code>number</code> | Size of the key, default: `2048` |
**Example**
Generate private RSA key
```js
const privateKey = await acme.forge.createPrivateKey();
```
**Example**
Private RSA key with defined size
```js
const privateKey = await acme.forge.createPrivateKey(4096);
```

View File

@@ -3,7 +3,9 @@
"description": "Simple and unopinionated ACME client", "description": "Simple and unopinionated ACME client",
"private": false, "private": false,
"author": "nmorsman", "author": "nmorsman",
"version": "1.26.11", "version": "1.27.4",
"type": "module",
"module": "scr/index.js",
"main": "src/index.js", "main": "src/index.js",
"types": "types/index.d.ts", "types": "types/index.d.ts",
"license": "MIT", "license": "MIT",
@@ -16,12 +18,14 @@
"types" "types"
], ],
"dependencies": { "dependencies": {
"@certd/basic": "^1.27.4",
"@peculiar/x509": "^1.11.0", "@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5", "asn1js": "^3.0.5",
"axios": "^1.7.2", "axios": "^1.7.2",
"debug": "^4.3.5", "debug": "^4.3.5",
"http-proxy-agent": "^7.0.2", "http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.5", "https-proxy-agent": "^7.0.5",
"lodash-es": "^4.17.21",
"node-forge": "^1.3.1" "node-forge": "^1.3.1"
}, },
"devDependencies": { "devDependencies": {
@@ -29,14 +33,15 @@
"chai": "^4.4.1", "chai": "^4.4.1",
"chai-as-promised": "^7.1.2", "chai-as-promised": "^7.1.2",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^4.2.1",
"jsdoc-to-markdown": "^8.0.1", "jsdoc-to-markdown": "^8.0.1",
"mocha": "^10.6.0", "mocha": "^10.6.0",
"nock": "^13.5.4", "nock": "^13.5.4",
"prettier": "^2.8.8",
"tsd": "^0.31.1", "tsd": "^0.31.1",
"typescript": "^5.4.2", "typescript": "^5.4.2"
"uuid": "^8.3.2"
}, },
"scripts": { "scripts": {
"build-docs": "jsdoc2md src/client.js > docs/client.md && jsdoc2md src/crypto/index.js > docs/crypto.md && jsdoc2md src/crypto/forge.js > docs/forge.md", "build-docs": "jsdoc2md src/client.js > docs/client.md && jsdoc2md src/crypto/index.js > docs/crypto.md && jsdoc2md src/crypto/forge.js > docs/forge.md",
@@ -60,5 +65,5 @@
"bugs": { "bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues" "url": "https://github.com/publishlab/node-acme-client/issues"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "1eb70d4cfd1ed2f746369658db2559fe01718324"
} }

View File

@@ -1,101 +0,0 @@
const nodeHttp = require('node:http');
const https = require('node:https');
const { HttpProxyAgent } = require('http-proxy-agent');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { log } = require('./logger');
function createAgent(opts = {}) {
let httpAgent;
let
httpsAgent;
const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy;
if (httpProxy) {
log(`acme use httpProxy:${httpProxy}`);
httpAgent = new HttpProxyAgent(httpProxy, opts);
}
else {
httpAgent = new nodeHttp.Agent(opts);
}
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
if (httpsProxy) {
log(`acme use httpsProxy:${httpsProxy}`);
httpsAgent = new HttpsProxyAgent(httpsProxy, opts);
}
else {
httpsAgent = new https.Agent(opts);
}
return {
httpAgent,
httpsAgent,
};
}
let defaultAgents = createAgent();
function getGlobalAgents() {
return defaultAgents;
}
function setGlobalProxy(opts) {
log('acme setGlobalProxy:', opts);
if (opts.httpProxy) {
process.env.HTTP_PROXY = opts.httpProxy;
}
if (opts.httpsProxy) {
process.env.HTTPS_PROXY = opts.httpsProxy;
}
defaultAgents = createAgent();
}
class HttpError extends Error {
constructor(error) {
super(error || error.message);
if (!error) {
return;
}
if (error.message.indexOf('ssl3_get_record:wrong version number') >= 0) {
this.message = 'http协议错误服务端要求http协议请检查是否使用了https请求';
}
this.name = error.name;
this.code = error.code;
this.cause = error.cause;
if (error.response) {
this.status = error.response.status;
this.statusText = error.response.statusText;
this.response = {
data: error.response.data,
};
}
let url = '';
if (error.config) {
this.request = {
baseURL: error.config.baseURL,
url: error.config.url,
method: error.config.method,
params: error.config.params,
data: error.config.data,
};
url = error.config.baseURL + error.config.url;
}
if (url) {
this.message = `${this.message}:${url}`;
}
delete error.response;
delete error.config;
delete error.request;
// logger.error(error);
}
}
module.exports = {
setGlobalProxy,
createAgent,
getGlobalAgents,
HttpError,
};

View File

@@ -1,9 +1,7 @@
/** /**
* ACME API client * ACME API client
*/ */
import * as util from './util.js';
const util = require('./util');
const { log } = require('./logger');
/** /**
* AcmeApi * AcmeApi
@@ -248,4 +246,4 @@ class AcmeApi {
} }
/* Export API */ /* Export API */
module.exports = AcmeApi; export default AcmeApi;

View File

@@ -1,10 +1,11 @@
/** /**
* ACME auto helper * ACME auto helper
*/ */
import { readCsrDomains } from './crypto/index.js';
import { log } from './logger.js';
import { wait } from './wait.js';
import { CancelError } from './error.js';
const { readCsrDomains } = require('./crypto');
const { log } = require('./logger');
const { wait } = require('./wait');
const defaultOpts = { const defaultOpts = {
csr: null, csr: null,
@@ -29,7 +30,7 @@ const defaultOpts = {
* @returns {Promise<buffer>} Certificate * @returns {Promise<buffer>} Certificate
*/ */
module.exports = async (client, userOpts) => { export default async (client, userOpts) => {
const opts = { ...defaultOpts, ...userOpts }; const opts = { ...defaultOpts, ...userOpts };
const accountPayload = { termsOfServiceAgreed: opts.termsOfServiceAgreed }; const accountPayload = { termsOfServiceAgreed: opts.termsOfServiceAgreed };
@@ -250,7 +251,7 @@ module.exports = async (client, userOpts) => {
i += 1; i += 1;
log(`开始第${i}`); log(`开始第${i}`);
if (opts.signal && opts.signal.aborted) { if (opts.signal && opts.signal.aborted) {
throw new Error('用户取消'); throw new CancelError('用户取消');
} }
try { try {

View File

@@ -1,14 +1,11 @@
/** /**
* Axios instance * Axios instance
*/ */
const axios = require('axios'); import axios from 'axios';
const { parseRetryAfterHeader } = require('./util'); import { parseRetryAfterHeader } from './util.js';
const { log } = require('./logger'); import { log } from './logger.js';
const pkg = require('./../package.json');
const Agents = require('./agents');
const { AxiosError } = axios; const { AxiosError } = axios;
import {getGlobalAgents, HttpError} from '@certd/basic'
/** /**
* Defaults * Defaults
*/ */
@@ -16,7 +13,7 @@ const { AxiosError } = axios;
const instance = axios.create(); const instance = axios.create();
/* Default User-Agent */ /* Default User-Agent */
instance.defaults.headers.common['User-Agent'] = `node-${pkg.name}/${pkg.version}`; instance.defaults.headers.common['User-Agent'] = `@certd/acme-client`;
/* Default ACME settings */ /* Default ACME settings */
instance.defaults.acmeSettings = { instance.defaults.acmeSettings = {
@@ -75,7 +72,7 @@ function validateStatus(response) {
response, response,
); );
throw new Agents.HttpError(err); throw new HttpError(err);
} }
/* Pass all responses through the error interceptor */ /* Pass all responses through the error interceptor */
@@ -85,7 +82,7 @@ instance.interceptors.request.use((config) => {
} }
config.validateStatus = () => false; config.validateStatus = () => false;
const agents = Agents.getGlobalAgents(); const agents = getGlobalAgents();
// if (config.skipSslVerify) { // if (config.skipSslVerify) {
// logger.info('跳过SSL验证'); // logger.info('跳过SSL验证');
// agents = createAgent({ rejectUnauthorized: false } as any); // agents = createAgent({ rejectUnauthorized: false } as any);
@@ -102,7 +99,7 @@ instance.interceptors.response.use(null, async (error) => {
const { config, response } = error; const { config, response } = error;
if (!config) { if (!config) {
return Promise.reject(new Agents.HttpError(error)); return Promise.reject(new HttpError(error));
} }
/* Pick up errors we want to retry */ /* Pick up errors we want to retry */
@@ -114,17 +111,19 @@ instance.interceptors.response.use(null, async (error) => {
const code = response ? `HTTP ${response.status}` : error.code; const code = response ? `HTTP ${response.status}` : error.code;
log(`Caught ${code}, retry attempt ${config.retryAttempt}/${retryMaxAttempts} to URL ${config.url}`); log(`Caught ${code}, retry attempt ${config.retryAttempt}/${retryMaxAttempts} to URL ${config.url}`);
const retryAfter = (retryDefaultDelay * config.retryAttempt);
/* Attempt to parse Retry-After header, fallback to default delay */ /* Attempt to parse Retry-After header, fallback to default delay */
let retryAfter = response ? parseRetryAfterHeader(response.headers['retry-after']) : 0; const headerRetryAfter = response ? parseRetryAfterHeader(response.headers['retry-after']) : 0;
if (retryAfter > 0) { if (headerRetryAfter > 0) {
log(`Found retry-after response header with value: ${response.headers['retry-after']}, waiting ${retryAfter} seconds`); const waitMinutes = (headerRetryAfter / 60).toFixed(1);
} log(`Found retry-after response header with value: ${response.headers['retry-after']}, waiting ${waitMinutes} minutes`);
else { log(JSON.stringify(response.data));
retryAfter = (retryDefaultDelay * config.retryAttempt); return Promise.reject(new HttpError(error));
log(`Unable to locate or parse retry-after response header, waiting ${retryAfter} seconds`);
} }
log(`waiting ${retryAfter} seconds`);
/* Wait and retry the request */ /* Wait and retry the request */
await new Promise((resolve) => { setTimeout(resolve, (retryAfter * 1000)); }); await new Promise((resolve) => { setTimeout(resolve, (retryAfter * 1000)); });
return instance(config); return instance(config);
@@ -132,7 +131,7 @@ instance.interceptors.response.use(null, async (error) => {
} }
if (!response) { if (!response) {
return Promise.reject(new Agents.HttpError(error)); return Promise.reject(new HttpError(error));
} }
/* Validate and return response */ /* Validate and return response */
return validateStatus(response); return validateStatus(response);
@@ -142,4 +141,4 @@ instance.interceptors.response.use(null, async (error) => {
* Export instance * Export instance
*/ */
module.exports = instance; export default instance;

View File

@@ -3,15 +3,17 @@
* *
* @namespace Client * @namespace Client
*/ */
import { createHash } from 'crypto';
import { getPemBodyAsB64u } from './crypto/index.js';
import { log } from './logger.js';
import HttpClient from './http.js';
import AcmeApi from './api.js';
import verify from './verify.js';
import * as util from './util.js';
import auto from './auto.js';
import { CancelError } from './error.js';
const { createHash } = require('crypto');
const { getPemBodyAsB64u } = require('./crypto');
const { log } = require('./logger');
const HttpClient = require('./http');
const AcmeApi = require('./api');
const verify = require('./verify');
const util = require('./util');
const auto = require('./auto');
/** /**
* ACME states * ACME states
@@ -490,9 +492,10 @@ class AcmeClient {
const keyAuthorization = await this.getChallengeKeyAuthorization(challenge); const keyAuthorization = await this.getChallengeKeyAuthorization(challenge);
const verifyFn = async () => { const verifyFn = async (abort) => {
if (this.opts.signal && this.opts.signal.aborted) { if (this.opts.signal && this.opts.signal.aborted) {
throw new Error('用户取消'); abort();
throw new CancelError('用户取消');
} }
await verify[challenge.type](authz, challenge, keyAuthorization); await verify[challenge.type](authz, challenge, keyAuthorization);
}; };
@@ -518,7 +521,7 @@ class AcmeClient {
async completeChallenge(challenge) { async completeChallenge(challenge) {
if (this.opts.signal && this.opts.signal.aborted) { if (this.opts.signal && this.opts.signal.aborted) {
throw new Error('用户取消'); throw new CancelError('用户取消');
} }
const resp = await this.api.completeChallenge(challenge.url, {}); const resp = await this.api.completeChallenge(challenge.url, {});
return resp.data; return resp.data;
@@ -559,7 +562,7 @@ class AcmeClient {
const verifyFn = async (abort) => { const verifyFn = async (abort) => {
if (this.opts.signal && this.opts.signal.aborted) { if (this.opts.signal && this.opts.signal.aborted) {
abort(); abort();
throw new Error('用户取消'); throw new CancelError('用户取消');
} }
const resp = await this.api.apiRequest(item.url, null, [200]); const resp = await this.api.apiRequest(item.url, null, [200]);
@@ -717,4 +720,4 @@ class AcmeClient {
} }
/* Export client */ /* Export client */
module.exports = AcmeClient; export default AcmeClient;

View File

@@ -6,11 +6,10 @@
* *
* @namespace forge * @namespace forge
*/ */
import net from 'net';
const net = require('net'); import { promisify } from 'util';
const { promisify } = require('util'); import forge from 'node-forge';
const forge = require('node-forge'); import { createPrivateEcdsaKey } from './index.js';
const { createPrivateEcdsaKey, getPublicKey } = require('./index');
const generateKeyPair = promisify(forge.pki.rsa.generateKeyPair); const generateKeyPair = promisify(forge.pki.rsa.generateKeyPair);
@@ -113,13 +112,12 @@ function parseDomains(obj) {
* ``` * ```
*/ */
async function createPrivateKey(size = 2048) { export async function createPrivateKey(size = 2048) {
const keyPair = await generateKeyPair({ bits: size }); const keyPair = await generateKeyPair({ bits: size });
const pemKey = forge.pki.privateKeyToPem(keyPair.privateKey); const pemKey = forge.pki.privateKeyToPem(keyPair.privateKey);
return Buffer.from(pemKey); return Buffer.from(pemKey);
} }
exports.createPrivateKey = createPrivateKey;
/** /**
* Create public key from a private RSA key * Create public key from a private RSA key
@@ -133,7 +131,7 @@ exports.createPrivateKey = createPrivateKey;
* ``` * ```
*/ */
exports.createPublicKey = async (key) => { export const createPublicKey = async (key) => {
const privateKey = forge.pki.privateKeyFromPem(key); const privateKey = forge.pki.privateKeyFromPem(key);
const publicKey = forge.pki.rsa.setPublicKey(privateKey.n, privateKey.e); const publicKey = forge.pki.rsa.setPublicKey(privateKey.n, privateKey.e);
const pemKey = forge.pki.publicKeyToPem(publicKey); const pemKey = forge.pki.publicKeyToPem(publicKey);
@@ -148,7 +146,7 @@ exports.createPublicKey = async (key) => {
* @returns {string} PEM body * @returns {string} PEM body
*/ */
exports.getPemBody = (str) => { export const getPemBody = (str) => {
const msg = forge.pem.decode(str)[0]; const msg = forge.pem.decode(str)[0];
return forge.util.encode64(msg.body); return forge.util.encode64(msg.body);
}; };
@@ -160,7 +158,7 @@ exports.getPemBody = (str) => {
* @returns {string[]} Array of PEM bodies * @returns {string[]} Array of PEM bodies
*/ */
exports.splitPemChain = (str) => forge.pem.decode(str).map(forge.pem.encode); export const splitPemChain = (str) => forge.pem.decode(str).map(forge.pem.encode);
/** /**
* Get modulus * Get modulus
@@ -176,7 +174,7 @@ exports.splitPemChain = (str) => forge.pem.decode(str).map(forge.pem.encode);
* ``` * ```
*/ */
exports.getModulus = async (input) => { export const getModulus = async (input) => {
if (!Buffer.isBuffer(input)) { if (!Buffer.isBuffer(input)) {
input = Buffer.from(input); input = Buffer.from(input);
} }
@@ -199,7 +197,7 @@ exports.getModulus = async (input) => {
* ``` * ```
*/ */
exports.getPublicExponent = async (input) => { export const getPublicExponent = async (input) => {
if (!Buffer.isBuffer(input)) { if (!Buffer.isBuffer(input)) {
input = Buffer.from(input); input = Buffer.from(input);
} }
@@ -223,7 +221,7 @@ exports.getPublicExponent = async (input) => {
* ``` * ```
*/ */
exports.readCsrDomains = async (csr) => { export const readCsrDomains = async (csr) => {
if (!Buffer.isBuffer(csr)) { if (!Buffer.isBuffer(csr)) {
csr = Buffer.from(csr); csr = Buffer.from(csr);
} }
@@ -251,7 +249,7 @@ exports.readCsrDomains = async (csr) => {
* ``` * ```
*/ */
exports.readCertificateInfo = async (cert) => { export const readCertificateInfo = async (cert) => {
if (!Buffer.isBuffer(cert)) { if (!Buffer.isBuffer(cert)) {
cert = Buffer.from(cert); cert = Buffer.from(cert);
} }
@@ -379,7 +377,7 @@ function formatCsrAltNames(altNames) {
* }, certificateKey); * }, certificateKey);
*/ */
exports.createCsr = async (data, keyType = null) => { export const createCsr = async (data, keyType = null) => {
let key = null; let key = null;
if (keyType === 'ec') { if (keyType === 'ec') {
key = await createPrivateEcdsaKey(); key = await createPrivateEcdsaKey();

View File

@@ -3,12 +3,12 @@
* *
* @namespace crypto * @namespace crypto
*/ */
import net from 'net';
import { promisify } from 'util';
import crypto from 'crypto';
import asn1js from 'asn1js';
import x509 from '@peculiar/x509';
const net = require('net');
const { promisify } = require('util');
const crypto = require('crypto');
const asn1js = require('asn1js');
const x509 = require('@peculiar/x509');
const randomInt = promisify(crypto.randomInt); const randomInt = promisify(crypto.randomInt);
const generateKeyPair = promisify(crypto.generateKeyPair); const generateKeyPair = promisify(crypto.generateKeyPair);
@@ -67,7 +67,7 @@ function getKeyInfo(keyPem) {
* ``` * ```
*/ */
async function createPrivateRsaKey(modulusLength = 2048, encodingType = 'pkcs8') { export async function createPrivateRsaKey(modulusLength = 2048, encodingType = 'pkcs8') {
const pair = await generateKeyPair('rsa', { const pair = await generateKeyPair('rsa', {
modulusLength, modulusLength,
privateKeyEncoding: { privateKeyEncoding: {
@@ -79,7 +79,6 @@ async function createPrivateRsaKey(modulusLength = 2048, encodingType = 'pkcs8')
return Buffer.from(pair.privateKey); return Buffer.from(pair.privateKey);
} }
exports.createPrivateRsaKey = createPrivateRsaKey;
/** /**
* Alias of `createPrivateRsaKey()` * Alias of `createPrivateRsaKey()`
@@ -87,7 +86,7 @@ exports.createPrivateRsaKey = createPrivateRsaKey;
* @function * @function
*/ */
exports.createPrivateKey = createPrivateRsaKey; export const createPrivateKey = createPrivateRsaKey;
/** /**
* Generate a private ECDSA key * Generate a private ECDSA key
@@ -106,7 +105,7 @@ exports.createPrivateKey = createPrivateRsaKey;
* ``` * ```
*/ */
exports.createPrivateEcdsaKey = async (namedCurve = 'P-256', encodingType = 'pkcs8') => { export const createPrivateEcdsaKey = async (namedCurve = 'P-256', encodingType = 'pkcs8') => {
const pair = await generateKeyPair('ec', { const pair = await generateKeyPair('ec', {
namedCurve, namedCurve,
privateKeyEncoding: { privateKeyEncoding: {
@@ -130,7 +129,7 @@ exports.createPrivateEcdsaKey = async (namedCurve = 'P-256', encodingType = 'pkc
* ``` * ```
*/ */
exports.getPublicKey = (keyPem) => { export const getPublicKey = (keyPem) => {
const info = getKeyInfo(keyPem); const info = getKeyInfo(keyPem);
const publicKey = info.publicKey.export({ const publicKey = info.publicKey.export({
@@ -155,7 +154,7 @@ exports.getPublicKey = (keyPem) => {
* ``` * ```
*/ */
function getJwk(keyPem) { export function getJwk(keyPem) {
const jwk = crypto.createPublicKey(keyPem).export({ const jwk = crypto.createPublicKey(keyPem).export({
format: 'jwk', format: 'jwk',
}); });
@@ -167,7 +166,6 @@ function getJwk(keyPem) {
}, {}); }, {});
} }
exports.getJwk = getJwk;
/** /**
* Produce CryptoKeyPair and signing algorithm from a PEM encoded private key * Produce CryptoKeyPair and signing algorithm from a PEM encoded private key
@@ -215,7 +213,7 @@ async function getWebCryptoKeyPair(keyPem) {
* @returns {string[]} Array of PEM objects including headers * @returns {string[]} Array of PEM objects including headers
*/ */
function splitPemChain(chainPem) { export function splitPemChain(chainPem) {
if (Buffer.isBuffer(chainPem)) { if (Buffer.isBuffer(chainPem)) {
chainPem = chainPem.toString(); chainPem = chainPem.toString();
} }
@@ -225,7 +223,6 @@ function splitPemChain(chainPem) {
.map((params) => x509.PemConverter.encode([params])); .map((params) => x509.PemConverter.encode([params]));
} }
exports.splitPemChain = splitPemChain;
/** /**
* Parse body of PEM encoded object and return a Base64URL string * Parse body of PEM encoded object and return a Base64URL string
@@ -235,7 +232,7 @@ exports.splitPemChain = splitPemChain;
* @returns {string} Base64URL-encoded body * @returns {string} Base64URL-encoded body
*/ */
exports.getPemBodyAsB64u = (pem) => { export const getPemBodyAsB64u = (pem) => {
const chain = splitPemChain(pem); const chain = splitPemChain(pem);
if (!chain.length) { if (!chain.length) {
@@ -286,7 +283,7 @@ function parseDomains(input) {
* ``` * ```
*/ */
exports.readCsrDomains = (csrPem) => { export const readCsrDomains = (csrPem) => {
if (Buffer.isBuffer(csrPem)) { if (Buffer.isBuffer(csrPem)) {
csrPem = csrPem.toString(); csrPem = csrPem.toString();
} }
@@ -315,7 +312,7 @@ exports.readCsrDomains = (csrPem) => {
* ``` * ```
*/ */
exports.readCertificateInfo = (certPem) => { export const readCertificateInfo = (certPem) => {
if (Buffer.isBuffer(certPem)) { if (Buffer.isBuffer(certPem)) {
certPem = certPem.toString(); certPem = certPem.toString();
} }
@@ -449,7 +446,7 @@ function createSubjectAltNameExtension(altNames) {
* ``` * ```
*/ */
exports.createCsr = async (data, keyPem = null) => { export const createCsr = async (data, keyPem = null) => {
if (!keyPem) { if (!keyPem) {
keyPem = await createPrivateRsaKey(data.keySize); keyPem = await createPrivateRsaKey(data.keySize);
} }
@@ -520,7 +517,7 @@ exports.createCsr = async (data, keyPem = null) => {
* ``` * ```
*/ */
exports.createAlpnCertificate = async (authz, keyAuthorization, keyPem = null) => { export const createAlpnCertificate = async (authz, keyAuthorization, keyPem = null) => {
if (!keyPem) { if (!keyPem) {
keyPem = await createPrivateRsaKey(); keyPem = await createPrivateRsaKey();
} }
@@ -583,7 +580,7 @@ exports.createAlpnCertificate = async (authz, keyAuthorization, keyPem = null) =
* @returns {boolean} True when valid * @returns {boolean} True when valid
*/ */
exports.isAlpnCertificateAuthorizationValid = (certPem, keyAuthorization) => { export const isAlpnCertificateAuthorizationValid = (certPem, keyAuthorization) => {
const expected = crypto.createHash('sha256').update(keyAuthorization).digest('hex'); const expected = crypto.createHash('sha256').update(keyAuthorization).digest('hex');
/* Attempt to locate ALPN extension */ /* Attempt to locate ALPN extension */

View File

@@ -0,0 +1,7 @@
export class CancelError extends Error {
constructor(message) {
super(message);
this.name = 'CancelError';
}
}

View File

@@ -1,11 +1,11 @@
/** /**
* ACME HTTP client * ACME HTTP client
*/ */
import { createHmac, createSign, constants } from 'crypto';
const { createHmac, createSign, constants: { RSA_PKCS1_PADDING } } = require('crypto'); const { RSA_PKCS1_PADDING } = constants;
const { getJwk } = require('./crypto'); import axios from './axios.js';
const { log } = require('./logger'); import { log } from './logger.js';
const axios = require('./axios'); import { getJwk } from './crypto/index.js';
/** /**
* ACME HTTP client * ACME HTTP client
@@ -324,4 +324,4 @@ class HttpClient {
} }
/* Export client */ /* Export client */
module.exports = HttpClient; export default HttpClient;

View File

@@ -1,14 +1,14 @@
/** /**
* acme-client * acme-client
*/ */
import AcmeClinet from './client.js'
exports.Client = require('./client'); export const Client = AcmeClinet
/** /**
* Directory URLs * Directory URLs
*/ */
exports.directory = { export const directory = {
buypass: { buypass: {
staging: 'https://api.test4.buypass.no/acme/directory', staging: 'https://api.test4.buypass.no/acme/directory',
production: 'https://api.buypass.com/acme/directory', production: 'https://api.buypass.com/acme/directory',
@@ -31,20 +31,18 @@ exports.directory = {
* Crypto * Crypto
*/ */
exports.crypto = require('./crypto'); export * as crypto from './crypto/index.js'
exports.forge = require('./crypto/forge'); export * as forge from './crypto/forge.js'
/** /**
* Axios * Axios
*/ */
exports.axios = require('./axios'); export * from './axios.js'
exports.agents = require('./agents');
/** /**
* Logger * Logger
*/ */
exports.setLogger = require('./logger').setLogger; export * from './logger.js'
export * from './verify.js'
exports.walkTxtRecord = require('./verify').walkTxtRecord; export * from './error.js'

View File

@@ -2,7 +2,8 @@
* ACME logger * ACME logger
*/ */
const debug = require('debug')('acme-client'); import debugg from 'debug'
const debug = debugg('acme-client');
let logger = () => {}; let logger = () => {};
@@ -12,7 +13,7 @@ let logger = () => {};
* @param {function} fn Logger function * @param {function} fn Logger function
*/ */
exports.setLogger = (fn) => { export const setLogger = (fn) => {
logger = fn; logger = fn;
}; };
@@ -22,7 +23,7 @@ exports.setLogger = (fn) => {
* @param {string} msg Message * @param {string} msg Message
*/ */
exports.log = (...msg) => { export const log = (...msg) => {
debug(...msg); debug(...msg);
logger(...msg); logger(...msg);
}; };

View File

@@ -2,11 +2,12 @@
* Utility methods * Utility methods
*/ */
const tls = require('tls'); import tls from 'tls';
const dns = require('dns').promises; import dnsSdk from 'dns';
const { readCertificateInfo, splitPemChain } = require('./crypto'); import { readCertificateInfo, splitPemChain }from './crypto/index.js'
const { log } = require('./logger'); import { log } from './logger.js'
const dns = dnsSdk.promises;
/** /**
* Exponential backoff * Exponential backoff
* *
@@ -329,7 +330,7 @@ async function retrieveTlsAlpnCertificate(host, port, timeout = 30000) {
* Export utils * Export utils
*/ */
module.exports = { export {
retry, retry,
parseLinkHeader, parseLinkHeader,
parseRetryAfterHeader, parseRetryAfterHeader,
@@ -338,3 +339,4 @@ module.exports = {
getAuthoritativeDnsResolver, getAuthoritativeDnsResolver,
retrieveTlsAlpnCertificate, retrieveTlsAlpnCertificate,
}; };

View File

@@ -2,13 +2,15 @@
* ACME challenge verification * ACME challenge verification
*/ */
const dns = require('dns').promises; import dnsSdk from "dns"
const https = require('https'); import https from 'https'
const { log } = require('./logger'); import {log} from './logger.js'
const axios = require('./axios'); import axios from './axios.js'
const util = require('./util'); import * as util from './util.js'
const { isAlpnCertificateAuthorizationValid } = require('./crypto'); import {isAlpnCertificateAuthorizationValid} from './crypto/index.js'
const dns = dnsSdk.promises
/** /**
* Verify ACME HTTP challenge * Verify ACME HTTP challenge
* *
@@ -79,7 +81,7 @@ async function walkDnsChallengeRecord(recordName, resolver = dns) {
} }
} }
async function walkTxtRecord(recordName) { export async function walkTxtRecord(recordName) {
try { try {
/* Default DNS resolver first */ /* Default DNS resolver first */
log('Attempting to resolve TXT with default DNS resolver first'); log('Attempting to resolve TXT with default DNS resolver first');
@@ -153,9 +155,8 @@ async function verifyTlsAlpnChallenge(authz, challenge, keyAuthorization) {
* Export API * Export API
*/ */
module.exports = { export default {
'http-01': verifyHttpChallenge, 'http-01': verifyHttpChallenge,
'dns-01': verifyDnsChallenge, 'dns-01': verifyDnsChallenge,
'tls-alpn-01': verifyTlsAlpnChallenge, 'tls-alpn-01': verifyTlsAlpnChallenge,
walkTxtRecord,
}; };

View File

@@ -1,9 +1,5 @@
async function wait(ms) { export async function wait(ms) {
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(resolve, ms); setTimeout(resolve, ms);
}); });
} }
module.exports = {
wait
};

View File

@@ -1,13 +0,0 @@
{
"compileOnSave": false,
"compilerOptions": {
"module": "commonjs",
"lib": ["es6"],
"strict": true,
"noEmit": false,
"esModuleInterop": true,
"baseUrl": ".",
"composite": true,
"paths": { "acme-client": ["."] }
}
}

View File

@@ -198,6 +198,8 @@ export const agents: any;
* Logger * Logger
*/ */
export function setLogger(fn: (msg: string) => void): void; export function setLogger(fn: (message: any, ...args: any[]) => void): void;
export function walkTxtRecord(record: any): Promise<string[]>; export function walkTxtRecord(record: any): Promise<string[]>;
export const CancelError: Error;

View File

@@ -3,6 +3,58 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.4](https://github.com/certd/certd/compare/v1.27.3...v1.27.4) (2024-11-14)
**Note:** Version bump only for package @certd/basic
## [1.27.3](https://github.com/certd/certd/compare/v1.27.2...v1.27.3) (2024-11-13)
### Bug Fixes
* 修复ipv6未开启情况下请求带有ipv6地址域名报ETIMEDOUT的bug ([a9a0967](https://github.com/certd/certd/commit/a9a0967a6f1d0bd27e69f3ec52c31d90d470bc23))
## [1.27.2](https://github.com/certd/certd/compare/v1.27.1...v1.27.2) (2024-11-08)
### Performance Improvements
* 支持公共cname服务 ([3c919ee](https://github.com/certd/certd/commit/3c919ee5d1aef5d26cf3620a7c49d920786bc941))
## [1.27.1](https://github.com/certd/certd/compare/v1.27.0...v1.27.1) (2024-11-04)
### Performance Improvements
* cname 域名映射记录可读性优化 ([b1117ed](https://github.com/certd/certd/commit/b1117ed54a3ef015752999324ff72b821ef5e4b9))
# [1.27.0](https://github.com/certd/certd/compare/v1.26.16...v1.27.0) (2024-10-31)
**Note:** Version bump only for package @certd/basic
## [1.26.16](https://github.com/certd/certd/compare/v1.26.15...v1.26.16) (2024-10-30)
### Performance Improvements
* 支持华为云cdn ([81a3fdb](https://github.com/certd/certd/commit/81a3fdbc29b71f380762008cc151493ec97458f9))
## [1.26.15](https://github.com/certd/certd/compare/v1.26.14...v1.26.15) (2024-10-28)
**Note:** Version bump only for package @certd/basic
## [1.26.14](https://github.com/certd/certd/compare/v1.26.13...v1.26.14) (2024-10-26)
### Bug Fixes
* 修复启动时自签证书无法保存的bug ([526c484](https://github.com/certd/certd/commit/526c48450bcd37b3ccded9b448f17de8140bdc6e))
## [1.26.13](https://github.com/certd/certd/compare/v1.26.12...v1.26.13) (2024-10-26)
**Note:** Version bump only for package @certd/basic
## [1.26.12](https://github.com/certd/certd/compare/v1.26.11...v1.26.12) (2024-10-25)
### Performance Improvements
* 新增部署到百度云CDN插件 ([f126f9f](https://github.com/certd/certd/commit/f126f9f932d37fa01fff1accc7bdd17d349f8db5))
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23) ## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
### Bug Fixes ### Bug Fixes

View File

@@ -1 +1 @@
10:35 21:51

View File

@@ -1,7 +1,7 @@
{ {
"name": "@certd/basic", "name": "@certd/basic",
"private": false, "private": false,
"version": "1.26.11", "version": "1.27.4",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@@ -17,52 +17,31 @@
"dependencies": { "dependencies": {
"axios": "^1.7.2", "axios": "^1.7.2",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"fix-path": "^4.0.0",
"http-proxy-agent": "^7.0.2", "http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.5", "https-proxy-agent": "^7.0.5",
"iconv-lite": "^0.6.3",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"log4js": "^6.9.1",
"lru-cache": "^10.0.0", "lru-cache": "^10.0.0",
"nanoid": "^5.0.7", "nanoid": "^5.0.7",
"node-forge": "^1.3.1", "node-forge": "^1.3.1",
"nodemailer": "^6.9.3", "nodemailer": "^6.9.3"
"proxy-agent": "^6.4.0",
"qs": "^6.11.2"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "^23.0.4",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-terser": "^0.4.3",
"@rollup/plugin-typescript": "^11.0.0",
"@types/chai": "^4.3.10", "@types/chai": "^4.3.10",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/mocha": "^10.0.1", "@types/mocha": "^10.0.1",
"@types/node-forge": "^1.3.2", "@types/node-forge": "^1.3.2",
"@types/uuid": "^9.0.2",
"@typescript-eslint/eslint-plugin": "^5.59.7", "@typescript-eslint/eslint-plugin": "^5.59.7",
"@typescript-eslint/parser": "^5.59.7", "@typescript-eslint/parser": "^5.59.7",
"chai": "4.3.10", "chai": "4.3.10",
"dayjs": "^1.11.7",
"eslint": "^8.41.0", "eslint": "^8.41.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"iconv-lite": "^0.6.3",
"log4js": "^6.9.1",
"mocha": "^10.2.0",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"reflect-metadata": "^0.1.13",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"rollup": "^3.7.4", "tslib": "^2.8.1",
"rollup-plugin-typescript2": "^0.34.1", "typescript": "^5.4.2"
"rollup-plugin-visualizer": "^5.8.2",
"ts-node": "^10.9.1",
"tsc-esm-fix": "^3.0.0",
"tslib": "^2.5.2",
"typescript": "^5.4.2",
"vite": "^4.3.8",
"vue-tsc": "^1.6.5"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "1eb70d4cfd1ed2f746369658db2559fe01718324"
} }

View File

@@ -1 +1,2 @@
export * from './utils/index.js'; export * from './utils/index.js';
export * from './utils/util.id.js';

View File

@@ -1,4 +1,5 @@
export * from './util.request.js'; export * from './util.request.js';
export * from './util.env.js';
export * from './util.log.js'; export * from './util.log.js';
export * from './util.file.js'; export * from './util.file.js';
export * from './util.sp.js'; export * from './util.sp.js';
@@ -6,9 +7,11 @@ export * from './util.promise.js';
export * from './util.hash.js'; export * from './util.hash.js';
export * from './util.merge.js'; export * from './util.merge.js';
export * from './util.cache.js'; export * from './util.cache.js';
export * from './util.string.js';
import { stringUtils } from './util.string.js';
import sleep from './util.sleep.js'; import sleep from './util.sleep.js';
import { http } from './util.request.js'; import { http, download } from './util.request.js';
import { nanoid } from 'nanoid';
import { mergeUtils } from './util.merge.js'; import { mergeUtils } from './util.merge.js';
import { sp } from './util.sp.js'; import { sp } from './util.sp.js';
import { hashUtils } from './util.hash.js'; import { hashUtils } from './util.hash.js';
@@ -17,10 +20,14 @@ import { fileUtils } from './util.file.js';
import * as _ from 'lodash-es'; import * as _ from 'lodash-es';
import { cache } from './util.cache.js'; import { cache } from './util.cache.js';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { domainUtils } from './util.domain.js';
import { optionsUtils } from './util.options.js';
import { nanoid } from 'nanoid';
import * as id from './util.id.js';
export const utils = { export const utils = {
sleep, sleep,
http, http,
download,
sp, sp,
hash: hashUtils, hash: hashUtils,
promises, promises,
@@ -29,5 +36,9 @@ export const utils = {
mergeUtils, mergeUtils,
cache, cache,
nanoid, nanoid,
id,
dayjs, dayjs,
domain: domainUtils,
options: optionsUtils,
string: stringUtils,
}; };

View File

@@ -1,6 +1,6 @@
// LRUCache // LRUCache
import { LRUCache } from "lru-cache"; import { LRUCache } from 'lru-cache';
export const cache = new LRUCache<string, any>({ export const cache = new LRUCache<string, any>({
max: 1000, max: 1000,

View File

@@ -0,0 +1,51 @@
//域名是否匹配,支持通配符
function match(targetDomains: string | string[], inDomains: string[]) {
if (!targetDomains || targetDomains.length == 0) {
return false;
}
if (!inDomains || inDomains.length == 0) {
return false;
}
if (typeof targetDomains === 'string') {
targetDomains = [targetDomains];
}
for (let targetDomain of targetDomains) {
let matched = false;
if (targetDomain.startsWith('.')) {
targetDomain = '*' + targetDomain;
}
for (let inDomain of inDomains) {
if (inDomain.startsWith('.')) {
inDomain = '*' + inDomain;
}
if (targetDomain === inDomain) {
matched = true;
break;
}
if (!inDomain.startsWith('*.')) {
//不可能匹配
continue;
}
//子域名匹配通配符即可
const firstDotIndex = targetDomain.indexOf('.');
const targetDomainSuffix = targetDomain.substring(firstDotIndex + 1);
if (targetDomainSuffix === inDomain.substring(2)) {
matched = true;
break;
}
}
//有一个没有匹配上,就失败
if (!matched) {
return false;
}
//这个匹配上了,检查下一个
}
//没有提前return 全部匹配上了
return true;
}
export const domainUtils = {
match,
};

View File

@@ -0,0 +1,4 @@
export function isDev() {
const nodeEnv = process.env.NODE_ENV || '';
return nodeEnv === 'development' || nodeEnv.indexOf('local') >= 0;
}

View File

@@ -1,7 +1,7 @@
import crypto from "crypto"; import crypto from 'crypto';
function md5(data: string) { function md5(data: string) {
return crypto.createHash("md5").update(data).digest("hex"); return crypto.createHash('md5').update(data).digest('hex');
} }
export const hashUtils = { export const hashUtils = {

View File

@@ -0,0 +1,3 @@
import { customAlphabet } from 'nanoid';
export const simpleNanoId = customAlphabet('1234567890abcdefghijklmopqrstuvwxyz', 12);

View File

@@ -1,4 +1,4 @@
import log4js, { LoggingEvent, Logger } from "log4js"; import log4js, { LoggingEvent, Logger } from 'log4js';
const OutputAppender = { const OutputAppender = {
configure: (config: any, layouts: any, findAppender: any, levels: any) => { configure: (config: any, layouts: any, findAppender: any, levels: any) => {
@@ -18,18 +18,22 @@ const OutputAppender = {
}, },
}; };
// @ts-ignore export function resetLogConfigure() {
log4js.configure({ // @ts-ignore
appenders: { std: { type: "stdout" }, output: { type: OutputAppender } }, log4js.configure({
categories: { default: { appenders: ["std"], level: "info" }, pipeline: { appenders: ["std", "output"], level: "info" } }, appenders: { std: { type: 'stdout' }, output: { type: OutputAppender } },
}); categories: { default: { appenders: ['std'], level: 'info' }, pipeline: { appenders: ['std', 'output'], level: 'info' } },
export const logger = log4js.getLogger("default"); });
}
resetLogConfigure();
export const logger = log4js.getLogger('default');
export function buildLogger(write: (text: string) => void) { export function buildLogger(write: (text: string) => void) {
const logger = log4js.getLogger("pipeline"); const logger = log4js.getLogger('pipeline');
logger.addContext("outputHandler", { logger.addContext('outputHandler', {
write, write,
}); });
return logger; return logger;
} }
export type ILogger = Logger; export type ILogger = Logger;

View File

@@ -1,4 +1,4 @@
import _ from "lodash-es"; import * as _ from 'lodash-es';
function isUnMergeable(srcValue: any) { function isUnMergeable(srcValue: any) {
return srcValue != null && srcValue instanceof UnMergeable; return srcValue != null && srcValue instanceof UnMergeable;
} }

Some files were not shown because too many files have changed in this diff Show More