@cocreate/acme 1.3.1 → 1.3.2

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +1 -1
  3. package/src/index.js +346 -300
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [1.3.2](https://github.com/CoCreate-app/CoCreate-acme/compare/v1.3.1...v1.3.2) (2025-09-06)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * await async function calls ([cf151f1](https://github.com/CoCreate-app/CoCreate-acme/commit/cf151f186b4893411f273a6a3e3f08b87093b72c))
7
+ * formating ([bcce249](https://github.com/CoCreate-app/CoCreate-acme/commit/bcce24906478e43aa4b1845c9f9099623551128e))
8
+
1
9
  ## [1.3.1](https://github.com/CoCreate-app/CoCreate-acme/compare/v1.3.0...v1.3.1) (2025-05-01)
2
10
 
3
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/acme",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "Dynamic SSL certificate management leveraging the ACME protocol, designed for direct API accessibility within applications. Automates certificate creation, renewal, and retrieval, ideal for distributed applications requiring real-time, secure certificate operations.",
5
5
  "keywords": [
6
6
  "acme",
package/src/index.js CHANGED
@@ -1,310 +1,356 @@
1
- const { Client, forge } = require('acme-client');
2
- const fs = require('fs');
3
- const util = require('node:util');
4
- const exec = util.promisify(require('node:child_process').exec);
1
+ const { Client, forge } = require("acme-client");
2
+ const fs = require("fs");
3
+ const util = require("node:util");
4
+ const exec = util.promisify(require("node:child_process").exec);
5
5
 
6
- const certificates = {}
7
- const email = 'ssl@cocreate.app';
8
- const keyPath = '/etc/certificates/';
9
- let client
10
- const hosts = {}
6
+ const certificates = {};
7
+ const email = "ssl@cocreate.app";
8
+ const keyPath = "/etc/certificates/";
9
+ let client;
10
+ const hosts = {};
11
11
 
12
12
  // Run this once per server to generate random constants
13
- const DAYS = Math.floor(Math.random() * 7); // Random days between 0-6
14
- const HOURS = Math.floor(Math.random() * 24); // Random hours between 0-23
15
- const MINUTES = Math.floor(Math.random() * 60); // Random minutes between 0-59
13
+ const DAYS = Math.floor(Math.random() * 7); // Random days between 0-6
14
+ const HOURS = Math.floor(Math.random() * 24); // Random hours between 0-23
15
+ const MINUTES = Math.floor(Math.random() * 60); // Random minutes between 0-59
16
16
 
17
17
  // Store these constants in a configuration file, environment variable, or database
18
18
 
19
19
  class CoCreateAcme {
20
- constructor(proxy, crud) {
21
- this.proxy = proxy
22
- this.crud = crud
23
- // this.check = this.checkCertificate
24
- this.init().catch(err => {
25
- console.error('Error initializing ACME client:', err);
26
- // TODO: Handle initialization error (possibly retry or exit)
27
- });
28
-
29
- }
30
-
31
- async init() {
32
- await exec('sudo mkdir -p /etc/certificates');
33
- await exec('sudo chmod 777 /etc/certificates');
34
-
35
- // if (!fs.existsSync(keyPath)) {
36
- // fs.mkdirSync(keyPath, { recursive: true }); // Create the directory if it doesn't exist
37
- // }
38
-
39
- const accountKeyPath = keyPath + 'account.pem';
40
-
41
- let accountKey = '';
42
- let isNewAccount = false; // Flag to check if the account is new
43
-
44
- // Check if the account key exists and load it; otherwise, create a new one
45
- if (!fs.existsSync(accountKeyPath)) {
46
- fs.writeFileSync(accountKeyPath, accountKey); // Store the account key
47
- accountKey = await forge.createPrivateKey();
48
- isNewAccount = true; // New account, so will need to register it
49
- fs.writeFileSync(accountKeyPath, accountKey); // Store the account key
50
- // fs.chmodSync(accountKeyPath, '400')
51
- } else {
52
- accountKey = fs.readFileSync(accountKeyPath, 'utf8');
53
- }
54
-
55
- // Initialize the ACME client with the account key
56
- client = new Client({
57
- directoryUrl: 'https://acme-v02.api.letsencrypt.org/directory', // https://acme-v02.api.letsencrypt.org/directory https://acme-staging-v02.api.letsencrypt.org/directory
58
- accountKey: accountKey
59
- });
60
-
61
- // Register the new account if it was just created
62
- if (isNewAccount) {
63
- try {
64
- // Attempt to create an account
65
- await client.createAccount({
66
- termsOfServiceAgreed: true,
67
- contact: ['mailto:' + email]
68
- });
69
- console.log("ACME account created successfully!");
70
- } catch (error) {
71
- fs.unlinkSync(accountKeyPath)
72
- // Handle errors that occur during account creation
73
- console.error("Error creating ACME account:", error.message);
74
- // Depending on the type of error, you might want to retry, log the error, alert someone, etc.
75
- }
76
- }
77
- }
78
-
79
- async requestCertificate(host, hostPosition, hostObject, organization_id, wildcard = false) {
80
- try {
81
-
82
- const self = this
83
-
84
- const hostKeyPath = keyPath + host + '/';
85
- if (!fs.existsSync(hostKeyPath)) {
86
- fs.mkdirSync(hostKeyPath, { recursive: true });
87
- }
88
-
89
- const domains = wildcard ? [host, `*.${host}`] : [host];
90
-
91
- /* Create certificate request */
92
- let [key, csr] = await forge.createCsr({
93
- commonName: domains[0],
94
- altNames: domains
95
- });
96
-
97
- let challenge_id = ''
98
-
99
- /* Request certificate */
100
- const cert = await client.auto({
101
- csr,
102
- email: [email], // Replace with your email
103
- termsOfServiceAgreed: true,
104
- challengeCreateFn: async (authz, challenge, keyAuthorization) => {
105
- console.log(`Challenge added for host: ${host} token: ${challenge.token}`);
106
-
107
- if (challenge.type === 'http-01') {
108
- const httpChallenge = await self.crud.send({
109
- method: 'object.create',
110
- host,
111
- array: 'files',
112
- object: {
113
- "content-type": "text/plain",
114
- "directory": "acme-challenge",
115
- "host": [
116
- authz.identifier.value
117
- ],
118
- "name": challenge.token,
119
- "organization_id": "652c8d62679eca03e0b116a7",
120
- "path": "/.well-known/acme-challenge/",
121
- "pathname": `/.well-known/acme-challenge/${challenge.token}`,
122
- "public": "true",
123
- "src": keyAuthorization
124
- },
125
- organization_id,
126
- });
127
-
128
- if (httpChallenge && httpChallenge.object && httpChallenge.object[0])
129
- challenge_id = httpChallenge.object[0]._id
130
- else
131
- console.error('error creating challenge url')
132
-
133
- } else if (challenge.type === 'dns-01') {
134
- // Calculate the DNS TXT record value
135
- const dnsRecordName = `_acme-challenge.${authz.identifier.value}`;
136
- const dnsRecordValue = await client.getChallengeKeyAuthorization(challenge);
137
-
138
- console.log(`Add this TXT record to your DNS:`);
139
- console.log(`Name: ${dnsRecordName}`);
140
- console.log(`Value: ${dnsRecordValue}`);
141
-
142
- // Here, implement the logic to add the TXT record to your DNS
143
- // await updateDnsTxtRecord(dnsRecordName, dnsRecordValue); // Hypothetical function to update DNS
144
- }
145
-
146
- },
147
-
148
- challengeRemoveFn: async (authz, challenge, keyAuthorization) => {
149
- /* Clean up challenge response here if necessary */
150
- console.log(`Challenge removed for token: ${challenge.token}`);
151
- if (challenge.type === 'http-01') {
152
- self.crud.send({
153
- method: 'object.delete',
154
- host,
155
- array: 'files',
156
- object: {
157
- _id: challenge_id
158
- },
159
- organization_id,
160
- });
161
- } else if (challenge.type === 'dns-01') {
162
- // await removeDnsTxtRecord(challenge); // A hypothetical function to clean up DNS
163
- }
164
-
165
- }
166
- });
167
-
168
- let expires = await forge.readCertificateInfo(cert);
169
- expires = expires.notAfter;
170
- this.setCertificate(host, expires, organization_id, hostKeyPath, cert, key)
171
-
172
- key = key.toString('utf8');
173
-
174
- this.crud.send({
175
- method: 'object.update',
176
- host,
177
- array: 'organizations',
178
- object: {
179
- _id: organization_id,
180
- [`host[${hostPosition}]`]: { ...hostObject, name: host, cert, key, expires },
181
- },
182
- organization_id
183
- });
184
-
185
- console.log(`Certificate successfully created for ${host}!'`);
186
- return true
187
- } catch (error) {
188
- console.log(`Certificate failed to create for ${host}!`);
189
- delete hosts[host]
190
- return false
191
- }
192
-
193
- }
194
-
195
- async getCertificate(host, organization_id) {
196
- const hostKeyPath = keyPath + host + '/';
197
- let hostPosition, hostObject
198
-
199
- let organization = await this.crud.getOrganization({ host, organization_id });
200
- if (organization.error)
201
- return false
202
- if (!organization_id)
203
- organization_id = organization._id
204
-
205
- if (organization.host) {
206
- for (let i = 0; i < organization.host.length; i++) {
207
- if (organization.host[i].name === host) {
208
- hostPosition = i
209
- hostObject = organization.host[i]
210
- if (organization.host[i].cert && organization.host[i].key) {
211
- let expires = await forge.readCertificateInfo(organization.host[i].cert);
212
- expires = expires.notAfter;
213
- if (this.isValid(expires)) {
214
- this.setCertificate(host, expires, organization_id, hostKeyPath, organization.host[i].cert, organization.host[i].key)
215
- return true
216
- }
217
- }
218
- break
219
- }
220
- }
221
-
222
- if (!hostPosition && hostPosition !== 0)
223
- return false
224
- }
225
-
226
- return await this.requestCertificate(host, hostPosition, hostObject, organization_id, false)
227
- }
228
-
229
- async checkCertificate(host, organization_id, pathname = '') {
230
- let hostname = host.split(':')[0]
231
- if (hostname === 'localhost' || hostname === '127.0.0.1' || pathname.startsWith('/.well-known/acme-challenge/'))
232
- return true
233
-
234
- if (certificates[host]) {
235
- return true
236
- }
237
-
238
- const hostKeyPath = keyPath + host + '/';
239
- if (fs.existsSync(hostKeyPath + 'fullchain.pem')) {
240
- let cert = fs.readFileSync(hostKeyPath + 'fullchain.pem', 'utf8');
241
- let expires = await forge.readCertificateInfo(cert);
242
- expires = expires.notAfter;
243
- if (this.isValid(expires)) {
244
- this.setCertificate(host, expires, organization_id)
245
- return true
246
- }
247
- }
248
-
249
- if (!hosts[host])
250
- hosts[host] = this.getCertificate(host, organization_id)
251
- return hosts[host]
252
- }
253
-
254
- isValid(expires) {
255
- let currentDate = new Date();
256
- currentDate.setDate(currentDate.getDate() + DAYS);
257
- currentDate.setHours(currentDate.getHours() + HOURS);
258
- currentDate.setMinutes(currentDate.getMinutes() + MINUTES);
259
-
260
- if (expires && currentDate < expires) {
261
- return true; // SSL is still valid, no need to renew
262
- }
263
- }
264
-
265
- setCertificate(host, expires, organization_id, hostKeyPath, cert, key) {
266
- // let expireDate = new Date(expires);
267
- // let currentDate = new Date();
268
-
269
- // Adjust the expireDate by the DAYS, HOURS, and MINUTES constants
270
- // expireDate.setDate(expireDate.getDate() - DAYS); // Subtracting to renew earlier
271
- // expireDate.setHours(expireDate.getHours() - HOURS);
272
- // expireDate.setMinutes(expireDate.getMinutes() - MINUTES);
273
-
274
- // Calculate the time difference in milliseconds
275
- // let timeoutDuration = expireDate.getTime() - currentDate.getTime();
276
-
277
- // Ensure we're not setting a negative timeout in case of past dates or errors
278
- // timeoutDuration = Math.max(timeoutDuration, 0);
279
-
280
- // Clear any existing timeout for the host
281
- // if (certificates[host] && certificates[host].timeout) {
282
- // clearTimeout(certificates[host].timeout);
283
- // }
284
-
285
- // Set the timeout to call checkCertificate before the actual expiration
286
- // let timeout = setTimeout(() => {
287
- // this.checkCertificate(host, organization_id);
288
- // }, timeoutDuration);
289
-
290
- // Store the timeout and organization_id for later reference or cancellation
291
-
292
- if (hostKeyPath) {
293
- if (!fs.existsSync(hostKeyPath)) {
294
- fs.mkdirSync(hostKeyPath, { recursive: true });
295
- }
296
- fs.writeFileSync(hostKeyPath + 'fullchain.pem', cert);
297
- // fs.chmodSync(keyPath + 'fullchain.pem', '444')
298
- fs.writeFileSync(hostKeyPath + 'private-key.pem', key);
299
- // fs.chmodSync(keyPath + 'private-key.pem', '400')
300
- }
301
-
302
- this.proxy.createServer(host)
303
-
304
- certificates[host] = { expires, organization_id }
305
-
306
- }
307
-
20
+ constructor(proxy, crud) {
21
+ this.proxy = proxy;
22
+ this.crud = crud;
23
+ // this.check = this.checkCertificate
24
+ this.init().catch((err) => {
25
+ console.error("Error initializing ACME client:", err);
26
+ // TODO: Handle initialization error (possibly retry or exit)
27
+ });
28
+ }
29
+
30
+ async init() {
31
+ await exec("sudo mkdir -p /etc/certificates");
32
+ await exec("sudo chmod 777 /etc/certificates");
33
+
34
+ // if (!fs.existsSync(keyPath)) {
35
+ // fs.mkdirSync(keyPath, { recursive: true }); // Create the directory if it doesn't exist
36
+ // }
37
+
38
+ const accountKeyPath = keyPath + "account.pem";
39
+
40
+ let accountKey = "";
41
+ let isNewAccount = false; // Flag to check if the account is new
42
+
43
+ // Check if the account key exists and load it; otherwise, create a new one
44
+ if (!fs.existsSync(accountKeyPath)) {
45
+ fs.writeFileSync(accountKeyPath, accountKey); // Store the account key
46
+ accountKey = await forge.createPrivateKey();
47
+ isNewAccount = true; // New account, so will need to register it
48
+ fs.writeFileSync(accountKeyPath, accountKey); // Store the account key
49
+ // fs.chmodSync(accountKeyPath, '400')
50
+ } else {
51
+ accountKey = fs.readFileSync(accountKeyPath, "utf8");
52
+ }
53
+
54
+ // Initialize the ACME client with the account key
55
+ client = new Client({
56
+ directoryUrl: "https://acme-v02.api.letsencrypt.org/directory", // https://acme-v02.api.letsencrypt.org/directory https://acme-staging-v02.api.letsencrypt.org/directory
57
+ accountKey: accountKey
58
+ });
59
+
60
+ // Register the new account if it was just created
61
+ if (isNewAccount) {
62
+ try {
63
+ // Attempt to create an account
64
+ await client.createAccount({
65
+ termsOfServiceAgreed: true,
66
+ contact: ["mailto:" + email]
67
+ });
68
+ console.log("ACME account created successfully!");
69
+ } catch (error) {
70
+ fs.unlinkSync(accountKeyPath);
71
+ // Handle errors that occur during account creation
72
+ console.error("Error creating ACME account:", error.message);
73
+ // Depending on the type of error, you might want to retry, log the error, alert someone, etc.
74
+ }
75
+ }
76
+ }
77
+
78
+ async requestCertificate(
79
+ host,
80
+ hostPosition,
81
+ hostObject,
82
+ organization_id,
83
+ wildcard = false
84
+ ) {
85
+ try {
86
+ const self = this;
87
+
88
+ const hostKeyPath = keyPath + host + "/";
89
+ if (!fs.existsSync(hostKeyPath)) {
90
+ fs.mkdirSync(hostKeyPath, { recursive: true });
91
+ }
92
+
93
+ const domains = wildcard ? [host, `*.${host}`] : [host];
94
+
95
+ /* Create certificate request */
96
+ let [key, csr] = await forge.createCsr({
97
+ commonName: domains[0],
98
+ altNames: domains
99
+ });
100
+
101
+ let challenge_id = "";
102
+
103
+ /* Request certificate */
104
+ const cert = await client.auto({
105
+ csr,
106
+ email: [email], // Replace with your email
107
+ termsOfServiceAgreed: true,
108
+ challengeCreateFn: async (
109
+ authz,
110
+ challenge,
111
+ keyAuthorization
112
+ ) => {
113
+ console.log(
114
+ `Challenge added for host: ${host} token: ${challenge.token}`
115
+ );
116
+
117
+ if (challenge.type === "http-01") {
118
+ const httpChallenge = await self.crud.send({
119
+ method: "object.create",
120
+ host,
121
+ array: "files",
122
+ object: {
123
+ "content-type": "text/plain",
124
+ directory: "acme-challenge",
125
+ host: [authz.identifier.value],
126
+ name: challenge.token,
127
+ organization_id: "652c8d62679eca03e0b116a7",
128
+ path: "/.well-known/acme-challenge/",
129
+ pathname: `/.well-known/acme-challenge/${challenge.token}`,
130
+ public: "true",
131
+ src: keyAuthorization
132
+ },
133
+ organization_id
134
+ });
135
+
136
+ if (
137
+ httpChallenge &&
138
+ httpChallenge.object &&
139
+ httpChallenge.object[0]
140
+ )
141
+ challenge_id = httpChallenge.object[0]._id;
142
+ else console.error("error creating challenge url");
143
+ } else if (challenge.type === "dns-01") {
144
+ // Calculate the DNS TXT record value
145
+ const dnsRecordName = `_acme-challenge.${authz.identifier.value}`;
146
+ const dnsRecordValue =
147
+ await client.getChallengeKeyAuthorization(
148
+ challenge
149
+ );
150
+
151
+ console.log(`Add this TXT record to your DNS:`);
152
+ console.log(`Name: ${dnsRecordName}`);
153
+ console.log(`Value: ${dnsRecordValue}`);
154
+
155
+ // Here, implement the logic to add the TXT record to your DNS
156
+ // await updateDnsTxtRecord(dnsRecordName, dnsRecordValue); // Hypothetical function to update DNS
157
+ }
158
+ },
159
+
160
+ challengeRemoveFn: async (
161
+ authz,
162
+ challenge,
163
+ keyAuthorization
164
+ ) => {
165
+ /* Clean up challenge response here if necessary */
166
+ console.log(
167
+ `Challenge removed for token: ${challenge.token}`
168
+ );
169
+ if (challenge.type === "http-01") {
170
+ self.crud.send({
171
+ method: "object.delete",
172
+ host,
173
+ array: "files",
174
+ object: {
175
+ _id: challenge_id
176
+ },
177
+ organization_id
178
+ });
179
+ } else if (challenge.type === "dns-01") {
180
+ // await removeDnsTxtRecord(challenge); // A hypothetical function to clean up DNS
181
+ }
182
+ }
183
+ });
184
+
185
+ let expires = await forge.readCertificateInfo(cert);
186
+ expires = expires.notAfter;
187
+ this.setCertificate(
188
+ host,
189
+ expires,
190
+ organization_id,
191
+ hostKeyPath,
192
+ cert,
193
+ key
194
+ );
195
+
196
+ key = key.toString("utf8");
197
+
198
+ this.crud.send({
199
+ method: "object.update",
200
+ host,
201
+ array: "organizations",
202
+ object: {
203
+ _id: organization_id,
204
+ [`host[${hostPosition}]`]: {
205
+ ...hostObject,
206
+ name: host,
207
+ cert,
208
+ key,
209
+ expires
210
+ }
211
+ },
212
+ organization_id
213
+ });
214
+
215
+ console.log(`Certificate successfully created for ${host}!'`);
216
+ return true;
217
+ } catch (error) {
218
+ console.log(`Certificate failed to create for ${host}!`);
219
+ delete hosts[host];
220
+ return false;
221
+ }
222
+ }
223
+
224
+ async getCertificate(host, organization_id) {
225
+ const hostKeyPath = keyPath + host + "/";
226
+ let hostPosition, hostObject;
227
+
228
+ let organization = await this.crud.getOrganization({
229
+ host,
230
+ organization_id
231
+ });
232
+ if (organization.error) return false;
233
+ if (!organization_id) organization_id = organization._id;
234
+
235
+ if (organization.host) {
236
+ for (let i = 0; i < organization.host.length; i++) {
237
+ if (organization.host[i].name === host) {
238
+ hostPosition = i;
239
+ hostObject = organization.host[i];
240
+ if (organization.host[i].cert && organization.host[i].key) {
241
+ let expires = await forge.readCertificateInfo(
242
+ organization.host[i].cert
243
+ );
244
+ expires = expires.notAfter;
245
+ if (this.isValid(expires)) {
246
+ this.setCertificate(
247
+ host,
248
+ expires,
249
+ organization_id,
250
+ hostKeyPath,
251
+ organization.host[i].cert,
252
+ organization.host[i].key
253
+ );
254
+ return true;
255
+ }
256
+ }
257
+ break;
258
+ }
259
+ }
260
+
261
+ if (!hostPosition && hostPosition !== 0) return false;
262
+ }
263
+
264
+ return await this.requestCertificate(
265
+ host,
266
+ hostPosition,
267
+ hostObject,
268
+ organization_id,
269
+ false
270
+ );
271
+ }
272
+
273
+ async checkCertificate(host, organization_id, pathname = "") {
274
+ let hostname = host.split(":")[0];
275
+ if (
276
+ hostname === "localhost" ||
277
+ hostname === "127.0.0.1" ||
278
+ pathname.startsWith("/.well-known/acme-challenge/")
279
+ )
280
+ return true;
281
+
282
+ if (certificates[host]) {
283
+ return true;
284
+ }
285
+
286
+ const hostKeyPath = keyPath + host + "/";
287
+ if (fs.existsSync(hostKeyPath + "fullchain.pem")) {
288
+ let cert = fs.readFileSync(hostKeyPath + "fullchain.pem", "utf8");
289
+ let expires = await forge.readCertificateInfo(cert);
290
+ expires = expires.notAfter;
291
+ if (this.isValid(expires)) {
292
+ this.setCertificate(host, expires, organization_id);
293
+ return true;
294
+ }
295
+ }
296
+
297
+ if (!hosts[host])
298
+ hosts[host] = await this.getCertificate(host, organization_id);
299
+ return hosts[host];
300
+ }
301
+
302
+ isValid(expires) {
303
+ let currentDate = new Date();
304
+ currentDate.setDate(currentDate.getDate() + DAYS);
305
+ currentDate.setHours(currentDate.getHours() + HOURS);
306
+ currentDate.setMinutes(currentDate.getMinutes() + MINUTES);
307
+
308
+ if (expires && currentDate < expires) {
309
+ return true; // SSL is still valid, no need to renew
310
+ }
311
+ }
312
+
313
+ setCertificate(host, expires, organization_id, hostKeyPath, cert, key) {
314
+ // let expireDate = new Date(expires);
315
+ // let currentDate = new Date();
316
+
317
+ // Adjust the expireDate by the DAYS, HOURS, and MINUTES constants
318
+ // expireDate.setDate(expireDate.getDate() - DAYS); // Subtracting to renew earlier
319
+ // expireDate.setHours(expireDate.getHours() - HOURS);
320
+ // expireDate.setMinutes(expireDate.getMinutes() - MINUTES);
321
+
322
+ // Calculate the time difference in milliseconds
323
+ // let timeoutDuration = expireDate.getTime() - currentDate.getTime();
324
+
325
+ // Ensure we're not setting a negative timeout in case of past dates or errors
326
+ // timeoutDuration = Math.max(timeoutDuration, 0);
327
+
328
+ // Clear any existing timeout for the host
329
+ // if (certificates[host] && certificates[host].timeout) {
330
+ // clearTimeout(certificates[host].timeout);
331
+ // }
332
+
333
+ // Set the timeout to call checkCertificate before the actual expiration
334
+ // let timeout = setTimeout(() => {
335
+ // this.checkCertificate(host, organization_id);
336
+ // }, timeoutDuration);
337
+
338
+ // Store the timeout and organization_id for later reference or cancellation
339
+
340
+ if (hostKeyPath) {
341
+ if (!fs.existsSync(hostKeyPath)) {
342
+ fs.mkdirSync(hostKeyPath, { recursive: true });
343
+ }
344
+ fs.writeFileSync(hostKeyPath + "fullchain.pem", cert);
345
+ // fs.chmodSync(keyPath + 'fullchain.pem', '444')
346
+ fs.writeFileSync(hostKeyPath + "private-key.pem", key);
347
+ // fs.chmodSync(keyPath + 'private-key.pem', '400')
348
+ }
349
+
350
+ this.proxy.createServer(host);
351
+
352
+ certificates[host] = { expires, organization_id };
353
+ }
308
354
  }
309
355
 
310
356
  module.exports = CoCreateAcme;