@certd/plugin-cert 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +1 -0
- package/CHANGELOG.md +8 -0
- package/dist/bundle.js +1 -0
- package/dist/d/dns-provider/api.d.ts +20 -0
- package/dist/d/dns-provider/decorator.d.ts +3 -0
- package/{src/dns-provider/index.ts → dist/d/dns-provider/index.d.ts} +3 -3
- package/dist/d/dns-provider/registry.d.ts +2 -0
- package/{src/index.ts → dist/d/index.d.ts} +2 -2
- package/dist/d/plugin/cert-plugin/acme.d.ts +45 -0
- package/dist/d/plugin/cert-plugin/cert-reader.d.ts +16 -0
- package/dist/d/plugin/cert-plugin/index.d.ts +48 -0
- package/{src/plugin/index.ts → dist/d/plugin/index.d.ts} +1 -2
- package/dist/plugin-cert.mjs +33826 -0
- package/dist/plugin-cert.umd.js +106 -0
- package/package.json +15 -6
- package/rollup.config.js +43 -0
- package/tsconfig.json +1 -2
- package/vite.config.ts +38 -6
- package/index.ts +0 -1
- package/src/dns-provider/api.ts +0 -23
- package/src/dns-provider/decorator.ts +0 -30
- package/src/dns-provider/registry.ts +0 -3
- package/src/plugin/cert-plugin/acme.ts +0 -205
- package/src/plugin/cert-plugin/cert-reader.ts +0 -52
- package/src/plugin/cert-plugin/index.ts +0 -276
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
import { AbstractTaskPlugin, Autowire, HttpClient, IAccessService, IContext, IsTaskPlugin, RunStrategy, Step, TaskInput, TaskOutput } from "@certd/pipeline";
|
|
2
|
-
import dayjs from "dayjs";
|
|
3
|
-
import { AcmeService, CertInfo } from "./acme";
|
|
4
|
-
import _ from "lodash";
|
|
5
|
-
import { Logger } from "log4js";
|
|
6
|
-
import { Decorator } from "@certd/pipeline/src/decorator";
|
|
7
|
-
import { DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider";
|
|
8
|
-
import { CertReader } from "./cert-reader";
|
|
9
|
-
|
|
10
|
-
export { CertReader };
|
|
11
|
-
export type { CertInfo };
|
|
12
|
-
|
|
13
|
-
@IsTaskPlugin({
|
|
14
|
-
name: "CertApply",
|
|
15
|
-
title: "证书申请",
|
|
16
|
-
desc: "免费通配符域名证书申请,支持多个域名打到同一个证书上",
|
|
17
|
-
default: {
|
|
18
|
-
input: {
|
|
19
|
-
renewDays: 20,
|
|
20
|
-
forceUpdate: false,
|
|
21
|
-
},
|
|
22
|
-
strategy: {
|
|
23
|
-
runStrategy: RunStrategy.AlwaysRun,
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
})
|
|
27
|
-
export class CertApplyPlugin extends AbstractTaskPlugin {
|
|
28
|
-
@TaskInput({
|
|
29
|
-
title: "域名",
|
|
30
|
-
component: {
|
|
31
|
-
name: "a-select",
|
|
32
|
-
vModel: "value",
|
|
33
|
-
mode: "tags",
|
|
34
|
-
open: false,
|
|
35
|
-
},
|
|
36
|
-
required: true,
|
|
37
|
-
col: {
|
|
38
|
-
span: 24,
|
|
39
|
-
},
|
|
40
|
-
helper:
|
|
41
|
-
"支持通配符域名,例如: *.foo.com 、 *.test.handsfree.work\n" +
|
|
42
|
-
"支持多个域名、多个子域名、多个通配符域名打到一个证书上(域名必须是在同一个DNS提供商解析)\n" +
|
|
43
|
-
"多级子域名要分成多个域名输入(*.foo.com的证书不能用于xxx.yyy.foo.com)\n" +
|
|
44
|
-
"输入一个回车之后,再输入下一个",
|
|
45
|
-
})
|
|
46
|
-
domains!: string;
|
|
47
|
-
|
|
48
|
-
@TaskInput({
|
|
49
|
-
title: "邮箱",
|
|
50
|
-
component: {
|
|
51
|
-
name: "a-input",
|
|
52
|
-
vModel: "value",
|
|
53
|
-
},
|
|
54
|
-
required: true,
|
|
55
|
-
helper: "请输入邮箱",
|
|
56
|
-
})
|
|
57
|
-
email!: string;
|
|
58
|
-
|
|
59
|
-
@TaskInput({
|
|
60
|
-
title: "DNS提供商",
|
|
61
|
-
component: {
|
|
62
|
-
name: "pi-dns-provider-selector",
|
|
63
|
-
},
|
|
64
|
-
required: true,
|
|
65
|
-
helper: "请选择dns解析提供商",
|
|
66
|
-
})
|
|
67
|
-
dnsProviderType!: string;
|
|
68
|
-
|
|
69
|
-
@TaskInput({
|
|
70
|
-
title: "DNS解析授权",
|
|
71
|
-
component: {
|
|
72
|
-
name: "pi-access-selector",
|
|
73
|
-
},
|
|
74
|
-
required: true,
|
|
75
|
-
helper: "请选择dns解析提供商授权",
|
|
76
|
-
})
|
|
77
|
-
dnsProviderAccess!: string;
|
|
78
|
-
|
|
79
|
-
@TaskInput({
|
|
80
|
-
title: "更新天数",
|
|
81
|
-
component: {
|
|
82
|
-
name: "a-input-number",
|
|
83
|
-
vModel: "value",
|
|
84
|
-
},
|
|
85
|
-
required: true,
|
|
86
|
-
helper: "到期前多少天后更新证书",
|
|
87
|
-
})
|
|
88
|
-
renewDays!: number;
|
|
89
|
-
|
|
90
|
-
@TaskInput({
|
|
91
|
-
title: "强制更新",
|
|
92
|
-
component: {
|
|
93
|
-
name: "a-switch",
|
|
94
|
-
vModel: "checked",
|
|
95
|
-
},
|
|
96
|
-
helper: "是否强制重新申请证书",
|
|
97
|
-
})
|
|
98
|
-
forceUpdate!: string;
|
|
99
|
-
|
|
100
|
-
@TaskInput({
|
|
101
|
-
title: "CsrInfo",
|
|
102
|
-
})
|
|
103
|
-
csrInfo: any;
|
|
104
|
-
|
|
105
|
-
// @ts-ignore
|
|
106
|
-
acme: AcmeService;
|
|
107
|
-
|
|
108
|
-
@Autowire()
|
|
109
|
-
logger!: Logger;
|
|
110
|
-
|
|
111
|
-
@Autowire()
|
|
112
|
-
userContext!: IContext;
|
|
113
|
-
|
|
114
|
-
@Autowire()
|
|
115
|
-
accessService!: IAccessService;
|
|
116
|
-
|
|
117
|
-
@Autowire()
|
|
118
|
-
http!: HttpClient;
|
|
119
|
-
|
|
120
|
-
@Autowire()
|
|
121
|
-
lastStatus!: Step;
|
|
122
|
-
|
|
123
|
-
@TaskOutput({
|
|
124
|
-
title: "域名证书",
|
|
125
|
-
})
|
|
126
|
-
cert?: CertInfo;
|
|
127
|
-
|
|
128
|
-
async onInstance() {
|
|
129
|
-
this.acme = new AcmeService({ userContext: this.userContext, logger: this.logger });
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async execute(): Promise<void> {
|
|
133
|
-
const oldCert = await this.condition();
|
|
134
|
-
if (oldCert != null) {
|
|
135
|
-
return this.output(oldCert);
|
|
136
|
-
}
|
|
137
|
-
const cert = await this.doCertApply();
|
|
138
|
-
if (cert != null) {
|
|
139
|
-
this.output(cert.toCertInfo());
|
|
140
|
-
//清空后续任务的状态,让后续任务能够重新执行
|
|
141
|
-
this.clearLastStatus();
|
|
142
|
-
} else {
|
|
143
|
-
throw new Error("申请证书失败");
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
output(cert: CertInfo) {
|
|
148
|
-
this.cert = cert;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* 是否更新证书
|
|
153
|
-
* @param input
|
|
154
|
-
*/
|
|
155
|
-
async condition() {
|
|
156
|
-
if (this.forceUpdate) {
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let inputChanged = false;
|
|
161
|
-
const oldInput = JSON.stringify(this.lastStatus?.input?.domains);
|
|
162
|
-
const thisInput = JSON.stringify(this.domains);
|
|
163
|
-
if (oldInput !== thisInput) {
|
|
164
|
-
inputChanged = true;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
let oldCert: CertReader | undefined = undefined;
|
|
168
|
-
try {
|
|
169
|
-
oldCert = await this.readLastCert();
|
|
170
|
-
} catch (e) {
|
|
171
|
-
this.logger.warn("读取cert失败:", e);
|
|
172
|
-
}
|
|
173
|
-
if (oldCert == null) {
|
|
174
|
-
this.logger.info("还未申请过,准备申请新证书");
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (inputChanged) {
|
|
179
|
-
this.logger.info("输入参数变更,申请新证书");
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const ret = this.isWillExpire(oldCert.expires, this.renewDays);
|
|
184
|
-
if (!ret.isWillExpire) {
|
|
185
|
-
this.logger.info(`证书还未过期:过期时间${dayjs(oldCert.expires).format("YYYY-MM-DD HH:mm:ss")},剩余${ret.leftDays}天`);
|
|
186
|
-
return oldCert;
|
|
187
|
-
}
|
|
188
|
-
this.logger.info("即将过期,开始更新证书");
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
async doCertApply() {
|
|
193
|
-
const email = this["email"];
|
|
194
|
-
const domains = this["domains"];
|
|
195
|
-
const dnsProviderType = this["dnsProviderType"];
|
|
196
|
-
const dnsProviderAccessId = this["dnsProviderAccess"];
|
|
197
|
-
const csrInfo = _.merge(
|
|
198
|
-
{
|
|
199
|
-
country: "CN",
|
|
200
|
-
state: "GuangDong",
|
|
201
|
-
locality: "ShengZhen",
|
|
202
|
-
organization: "CertD Org.",
|
|
203
|
-
organizationUnit: "IT Department",
|
|
204
|
-
emailAddress: email,
|
|
205
|
-
},
|
|
206
|
-
this.csrInfo
|
|
207
|
-
);
|
|
208
|
-
this.logger.info("开始申请证书,", email, domains);
|
|
209
|
-
|
|
210
|
-
const dnsProviderPlugin = dnsProviderRegistry.get(dnsProviderType);
|
|
211
|
-
const DnsProviderClass = dnsProviderPlugin.target;
|
|
212
|
-
const dnsProviderDefine = dnsProviderPlugin.define as DnsProviderDefine;
|
|
213
|
-
const access = await this.accessService.getById(dnsProviderAccessId);
|
|
214
|
-
|
|
215
|
-
// @ts-ignore
|
|
216
|
-
const dnsProvider: IDnsProvider = new DnsProviderClass();
|
|
217
|
-
const context = { access, logger: this.logger, http: this.http };
|
|
218
|
-
Decorator.inject(dnsProviderDefine.autowire, dnsProvider, context);
|
|
219
|
-
await dnsProvider.onInstance();
|
|
220
|
-
|
|
221
|
-
const cert = await this.acme.order({
|
|
222
|
-
email,
|
|
223
|
-
domains,
|
|
224
|
-
dnsProvider,
|
|
225
|
-
csrInfo,
|
|
226
|
-
isTest: false,
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
const certInfo = this.formatCerts(cert);
|
|
230
|
-
return new CertReader(certInfo);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
formatCert(pem: string) {
|
|
234
|
-
pem = pem.replace(/\r/g, "");
|
|
235
|
-
pem = pem.replace(/\n\n/g, "\n");
|
|
236
|
-
pem = pem.replace(/\n$/g, "");
|
|
237
|
-
return pem;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
formatCerts(cert: { crt: string; key: string; csr: string }) {
|
|
241
|
-
const newCert: CertInfo = {
|
|
242
|
-
crt: this.formatCert(cert.crt),
|
|
243
|
-
key: this.formatCert(cert.key),
|
|
244
|
-
csr: this.formatCert(cert.csr),
|
|
245
|
-
};
|
|
246
|
-
return newCert;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
async readLastCert(): Promise<CertReader | undefined> {
|
|
250
|
-
const cert = this.lastStatus?.status?.output?.cert;
|
|
251
|
-
if (cert == null) {
|
|
252
|
-
return undefined;
|
|
253
|
-
}
|
|
254
|
-
return new CertReader(cert);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* 检查是否过期,默认提前20天
|
|
259
|
-
* @param expires
|
|
260
|
-
* @param maxDays
|
|
261
|
-
* @returns {boolean}
|
|
262
|
-
*/
|
|
263
|
-
isWillExpire(expires: number, maxDays = 20) {
|
|
264
|
-
if (expires == null) {
|
|
265
|
-
throw new Error("过期时间不能为空");
|
|
266
|
-
}
|
|
267
|
-
// 检查有效期
|
|
268
|
-
const leftDays = dayjs(expires).diff(dayjs(), "day");
|
|
269
|
-
return {
|
|
270
|
-
isWillExpire: leftDays < maxDays,
|
|
271
|
-
leftDays,
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
new CertApplyPlugin();
|