@abtnode/certificate-manager 1.16.20-beta-cf6dfce1 → 1.16.20-beta-148929e2

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.
@@ -41,11 +41,11 @@ class Manager extends EventEmitter {
41
41
  return;
42
42
  }
43
43
 
44
- await this._createOrRenewCert({ ...data, challenges: { 'http-01': http01 } });
44
+ await this.createOrRenewCert({ ...data, challenges: { 'http-01': http01 } });
45
45
  },
46
46
  options: {
47
- maxRetries: 0,
48
- retryDelay: 60 * 1000,
47
+ maxRetries: process.env.NODE_ENV === 'production' ? 5 : 0,
48
+ retryDelay: 10 * 1000,
49
49
  maxTimeout: 60 * 1000, // throw timeout error after 1 minutes
50
50
  id: (job) => this.getJobId(job),
51
51
  },
@@ -61,17 +61,41 @@ class Manager extends EventEmitter {
61
61
  };
62
62
  }
63
63
 
64
- async pushToJobQueue(domain) {
65
- const jobData = { domain, subscriberEmail: this.maintainerEmail };
64
+ /*
65
+ * @param {string} domain
66
+ * @param {object} options
67
+ * @param {number} options.delay - delay time in ms
68
+ * @return {Promise<void>}
69
+ */
70
+ async pushToJobQueue(domain, { delay = 0, metadata } = {}) {
71
+ const jobData = { domain, subscriberEmail: this.maintainerEmail, delay, metadata };
66
72
 
67
73
  const job = await this.queue.get(this.getJobId(jobData));
68
74
 
69
75
  if (!job) {
70
- this.queue.push(jobData);
76
+ const newJob = this.queue.push(jobData);
77
+ newJob.on('failed', async (data) => {
78
+ const tmpDomain = data?.job?.domain;
79
+ if (tmpDomain) {
80
+ await states.certificate.updateStatus(tmpDomain, CERT_STATUS.error);
81
+ }
82
+
83
+ this.emit('cert.error', data.job);
84
+ logger.error(`create certificate for ${tmpDomain} job failed`, { domain: tmpDomain, error: data.error });
85
+ });
71
86
  }
72
87
  }
73
88
 
74
- async add(domain) {
89
+ /**
90
+ *
91
+ * @param {object} data
92
+ * @param {string} data.domain
93
+ * @param {string} data.siteId
94
+ * @param {object} options
95
+ * @param {number} options.delay - delay time in ms
96
+ * @returns
97
+ */
98
+ async add({ domain, siteId }, { delay = 0, metadata } = {}) {
75
99
  if (!domain) {
76
100
  throw new Error('domain is required when add domain');
77
101
  }
@@ -80,13 +104,13 @@ class Manager extends EventEmitter {
80
104
  if (!cert) {
81
105
  cert = await states.certificate.insert({
82
106
  domain,
107
+ siteId,
83
108
  source: CERT_SOURCE.letsEncrypt,
84
109
  status: CERT_STATUS.waiting,
85
110
  });
86
111
  }
87
112
 
88
- await this.pushToJobQueue(domain);
89
-
113
+ await this.pushToJobQueue(domain, { delay, metadata });
90
114
  return cert;
91
115
  }
92
116
 
@@ -111,7 +135,7 @@ class Manager extends EventEmitter {
111
135
  };
112
136
  }
113
137
 
114
- async _createOrRenewCert({ domain, subscriberEmail, force = false, challenges }) {
138
+ async createOrRenewCert({ domain, subscriberEmail, force = false, challenges, metadata }) {
115
139
  try {
116
140
  if (!domain) {
117
141
  throw new Error('domain is required when create certificate');
@@ -120,7 +144,7 @@ class Manager extends EventEmitter {
120
144
  const cert = await states.certificate.findOne({ domain });
121
145
  if (!cert) {
122
146
  logger.warn(`create certificate failed: the cert ${domain} does not exist`);
123
- return;
147
+ return null;
124
148
  }
125
149
 
126
150
  let status = CERT_STATUS.creating;
@@ -131,18 +155,41 @@ class Manager extends EventEmitter {
131
155
 
132
156
  if (force === false && days > this.renewalOffsetInDay) {
133
157
  logger.info(`no need to renewal ${cert.domain}, the certificate will expires more than ${days} days`);
134
- return;
158
+ return null;
135
159
  }
136
160
 
137
161
  status = CERT_STATUS.renewaling;
138
162
  }
139
163
 
140
164
  await states.certificate.updateStatus(cert.domain, status);
141
- await this.acme.create({
165
+ const data = await this.acme.create({
142
166
  subject: cert.domain,
143
167
  subscriberEmail,
144
168
  challenges,
145
169
  });
170
+
171
+ logger.info(`create certificate for ${domain} job success`);
172
+
173
+ const [count, certs] = await states.certificate.update(
174
+ { domain: data.subject },
175
+ {
176
+ $set: {
177
+ domain: data.subject,
178
+ status: CERT_STATUS.generated,
179
+ certificate: data.fullchain,
180
+ privateKey: data.privkey,
181
+ },
182
+ }
183
+ );
184
+
185
+ if (count === 0) {
186
+ throw new Error(`update certificate for ${domain} failed`);
187
+ }
188
+
189
+ logger.info(`updated ${count} certificate for ${domain} job success`);
190
+
191
+ this.emit('cert.issued', { id: cert.id, domain: data.subject, metadata });
192
+ return certs[0];
146
193
  } catch (error) {
147
194
  logger.error(`create certificate for ${domain} job failed`, error);
148
195
  throw error;
@@ -151,7 +198,11 @@ class Manager extends EventEmitter {
151
198
 
152
199
  async checkRenewalCerts() {
153
200
  logger.info('run renewal certificate job');
201
+
154
202
  const certs = await states.certificate.findRenewCerts(this.renewalOffsetInDay);
203
+
204
+ logger.info(`found ${certs.length} certs need to renewal`);
205
+
155
206
  for (const cert of certs) {
156
207
  // eslint-disable-next-line no-await-in-loop
157
208
  await this.pushToJobQueue(cert.domain);
@@ -177,31 +228,6 @@ Manager.getInstance = async ({ maintainerEmail, baseDataDir }) => {
177
228
 
178
229
  await instance.acme.init();
179
230
 
180
- instance.acme.on('cert.issued', async (data) => {
181
- await states.certificate.update(
182
- { domain: data.subject },
183
- {
184
- $set: {
185
- domain: data.subject,
186
- status: CERT_STATUS.generated,
187
- certificate: data.fullchain,
188
- privateKey: data.privkey,
189
- },
190
- }
191
- );
192
-
193
- const cert = await states.certificate.findOne({ domain: data.subject });
194
- instance.emit('cert.issued', { id: cert.id, domain: cert.domain });
195
- });
196
-
197
- instance.acme.on('cert.error', async (data) => {
198
- if (data.domain) {
199
- await states.certificate.updateStatus(data.domain, CERT_STATUS.error);
200
- }
201
-
202
- instance.emit('cert.error', data);
203
- });
204
-
205
231
  return instance;
206
232
  };
207
233
 
@@ -85,17 +85,20 @@ class AcmeWrapper extends EventEmitter {
85
85
 
86
86
  logger.info('certificates generated!');
87
87
 
88
- this.emit('cert.issued', {
88
+ const data = {
89
89
  subject,
90
90
  privkey: serverPem,
91
91
  cert: pems.cert,
92
92
  chain: pems.chain,
93
93
  fullchain,
94
94
  challenges,
95
- });
95
+ };
96
+
97
+ return data;
96
98
  } catch (error) {
97
99
  logger.error('create certificate error', { domain: subject, error });
98
100
  this.emit('cert.error', { domain: subject, error_message: error.message });
101
+ throw error;
99
102
  }
100
103
  }
101
104
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abtnode/certificate-manager",
3
- "version": "1.16.20-beta-cf6dfce1",
3
+ "version": "1.16.20-beta-148929e2",
4
4
  "description": "Manage ABT Node SSL certificates",
5
5
  "author": "polunzh <polunzh@gmail.com>",
6
6
  "homepage": "https://github.com/ArcBlock/blocklet-server#readme",
@@ -32,11 +32,11 @@
32
32
  "url": "https://github.com/ArcBlock/blocklet-server/issues"
33
33
  },
34
34
  "dependencies": {
35
- "@abtnode/cron": "1.16.20-beta-cf6dfce1",
36
- "@abtnode/logger": "1.16.20-beta-cf6dfce1",
37
- "@abtnode/models": "1.16.20-beta-cf6dfce1",
38
- "@abtnode/queue": "1.16.20-beta-cf6dfce1",
39
- "@abtnode/util": "1.16.20-beta-cf6dfce1",
35
+ "@abtnode/cron": "1.16.20-beta-148929e2",
36
+ "@abtnode/logger": "1.16.20-beta-148929e2",
37
+ "@abtnode/models": "1.16.20-beta-148929e2",
38
+ "@abtnode/queue": "1.16.20-beta-148929e2",
39
+ "@abtnode/util": "1.16.20-beta-148929e2",
40
40
  "@fidm/x509": "^1.2.1",
41
41
  "@greenlock/manager": "^3.1.0",
42
42
  "@root/acme": "^3.1.0",
@@ -49,5 +49,5 @@
49
49
  "lodash.get": "^4.4.2",
50
50
  "punycode": "^2.1.1"
51
51
  },
52
- "gitHead": "bc2f0c9c5a39b124c7a7b64ec907527b3e8ccdf1"
52
+ "gitHead": "99e23380f73b63a8e63ce1fe8f75908194df5d80"
53
53
  }
package/sdk/manager.js CHANGED
@@ -128,15 +128,19 @@ class Manager extends EventEmitter {
128
128
 
129
129
  /**
130
130
  * 生成域名,certificate-manager 会自动生成并更新该证书
131
- * @param {string} domain
131
+ * @param {object} data
132
+ * @param {string} data.domain
133
+ * @param {string} data.siteId
134
+ * @param {object} options
135
+ * @param {number} options.delay - delay time in ms
132
136
  * @returns
133
137
  */
134
- issue(domain) {
138
+ issue({ domain, siteId }, options) {
135
139
  if (!domain) {
136
140
  throw new Error('domain is required');
137
141
  }
138
142
 
139
- return this.acmeManager.add(domain.toLowerCase());
143
+ return this.acmeManager.add({ domain: domain.toLowerCase(), siteId }, options);
140
144
  }
141
145
 
142
146
  async upsertByDomain(tmpData) {
@@ -17,6 +17,14 @@ class Certificate extends BaseState {
17
17
  return super.update({ domain }, { $set: { status } });
18
18
  }
19
19
 
20
+ updateStatusById(id, status) {
21
+ if (!Object.values(CERT_STATUS).includes(status)) {
22
+ throw new Error('invalid domain status');
23
+ }
24
+
25
+ return super.update({ id }, { $set: { status } });
26
+ }
27
+
20
28
  insert(data) {
21
29
  if (data.certificate) {
22
30
  data.meta = getCertInfo(data.certificate);